From 8ddf3eb13282cbeedd4dde968d2f82f890e4ebc7 Mon Sep 17 00:00:00 2001 From: tartaric_acid Date: Tue, 19 Dec 2023 17:55:50 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=201.18.2=20=E7=9A=84?= =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gradle.properties | 2 +- .../touhoulittlemaid/TouhouLittleMaid.java | 5 + .../touhoulittlemaid/api/ILittleMaid.java | 9 + .../api/backpack/IBackpackData.java | 15 + .../api/backpack/IMaidBackpack.java | 63 + .../api/game/gomoku/AIService.java | 84 + .../api/game/gomoku/Point.java | 78 + .../api/game/gomoku/Statue.java | 6 + .../api/game/gomoku/ZhiZhangAIService.java | 1683 +++++++++++++++++ .../touhoulittlemaid/block/BlockAltar.java | 7 +- .../block/BlockBookshelf.java | 49 + .../touhoulittlemaid/block/BlockComputer.java | 53 + .../touhoulittlemaid/block/BlockGomoku.java | 365 ++++ .../touhoulittlemaid/block/BlockJoy.java | 114 ++ .../touhoulittlemaid/block/BlockKeyboard.java | 50 + .../touhoulittlemaid/block/BlockShrine.java | 118 ++ .../block/properties/GomokuPart.java | 58 + .../animation/gecko/AnimationRegister.java | 8 +- .../animation/inner/MaidBaseAnimation.java | 23 +- .../animation/script/EntityMaidWrapper.java | 3 +- ...ionEvent.java => AddInformationEvent.java} | 58 +- .../client/event/ScrollRenderEvent.java | 54 + .../client/gui/block/MaidBeaconGui.java | 69 +- .../entity/maid/AbstractMaidContainerGui.java | 33 +- .../gui/entity/maid/MaidMainContainerGui.java | 50 - .../client/gui/entity/maid/MaidTabs.java | 3 +- .../backpack/BigBackpackContainerScreen.java | 32 + .../CraftingTableBackpackContainerScreen.java | 32 + .../EmptyBackpackContainerScreen.java | 38 + .../EnderChestBackpackContainerScreen.java | 32 + .../FurnaceBackpackContainerScreen.java | 35 + .../backpack/IBackpackContainerScreen.java | 4 + .../MiddleBackpackContainerScreen.java | 34 + .../SmallBackpackContainerScreen.java | 36 + .../entity/maid/backpack/package-info.java | 7 + .../client/gui/item/FoxScrollScreen.java | 156 ++ .../gui/widget/button/BeaconEffectButton.java | 10 + .../client/init/InitContainerGui.java | 13 +- .../client/init/InitEntitiesRender.java | 39 +- .../client/model/BookshelfModel.java | 857 +++++++++ .../client/model/ComputerModel.java | 266 +++ .../client/model/GomokuModel.java | 68 + .../client/model/KeyboardModel.java | 208 ++ .../client/model/PieceModel.java | 37 + .../client/model/ShrineModel.java | 177 ++ .../client/model/TombstoneModel.java | 66 + .../BigBackpackModel.java} | 6 +- .../backpack/CraftingTableBackpackModel.java | 101 + .../backpack/EnderChestBackpackModel.java | 88 + .../model/backpack/FurnaceBackpackModel.java | 101 + .../MiddleBackpackModel.java} | 6 +- .../SmallBackpackModel.java} | 6 +- .../renderer/entity/EntitySitRenderer.java | 26 + .../entity/EntityTombstoneRenderer.java | 86 + .../geckolayer/GeckoLayerMaidBackItem.java | 20 +- .../geckolayer/GeckoLayerMaidBackpack.java | 30 +- .../entity/layer/LayerMaidBackItem.java | 23 +- .../entity/layer/LayerMaidBackpack.java | 35 +- .../TileEntityBookshelfRenderer.java | 41 + .../TileEntityComputerRenderer.java | 41 + .../tileentity/TileEntityGomokuRenderer.java | 146 ++ .../TileEntityKeyboardRenderer.java | 41 + .../tileentity/TileEntityShrineRenderer.java | 53 + .../compat/cloth/MenuIntegration.java | 7 +- .../domesticationinnovation/PetBedDrop.java | 16 + .../compat/jade/provider/MaidProvider.java | 2 + .../compat/jei/MaidPlugin.java | 23 +- .../compat/slashblade/SlashBladeCompat.java | 3 +- .../compat/top/provider/MaidProvider.java | 6 + .../config/subconfig/MiscConfig.java | 7 - .../entity/ai/brain/MaidBrain.java | 17 +- .../ai/brain/task/MaidFindGomokuTask.java | 36 + .../entity/ai/brain/task/MaidFindJoyTask.java | 36 + .../entity/ai/brain/task/MaidSitJoyTask.java | 71 + .../entity/backpack/BackpackManager.java | 95 + .../entity/backpack/BigBackpack.java | 105 + .../backpack/CraftingTableBackpack.java | 101 + .../entity/backpack/EmptyBackpack.java | 91 + .../entity/backpack/EnderChestBackpack.java | 92 + .../entity/backpack/FurnaceBackpack.java | 131 ++ .../entity/backpack/MiddleBackpack.java | 104 + .../entity/backpack/SmallBackpack.java | 96 + .../backpack/data/FurnaceBackpackData.java | 234 +++ .../entity/backpack/package-info.java | 7 + .../favorability/FavorabilityManager.java | 274 +++ .../entity/favorability/Type.java | 40 + .../entity/item/EntitySit.java | 186 ++ .../entity/item/EntityTombstone.java | 173 ++ .../entity/monster/EntityFairy.java | 5 - .../entity/passive/EntityMaid.java | 210 +- .../entity/task/TaskBoardGames.java | 46 + .../entity/task/TaskCocoa.java | 3 +- .../entity/task/TaskManager.java | 1 + .../entity/task/TaskSugarCane.java | 4 +- .../event/maid/HandleBackpackEvent.java | 51 + .../event/maid/MaidMountEvent.java | 3 +- .../event/maid/SlabClickEvent.java | 3 - .../event/maid/TakeOffBackpackEvent.java | 42 - .../event/maid/UseFavorabilityToolEvent.java | 40 + .../touhoulittlemaid/init/InitBlocks.java | 10 + .../touhoulittlemaid/init/InitContainer.java | 11 +- .../touhoulittlemaid/init/InitEntities.java | 7 +- .../touhoulittlemaid/init/InitItems.java | 20 +- .../touhoulittlemaid/init/InitSounds.java | 2 + .../init/registry/CommonRegistry.java | 2 + .../init/registry/MobSpawnInfoRegistry.java | 20 +- .../container/MaidMainContainer.java | 73 +- .../backpack/BigBackpackContainer.java | 36 + .../CraftingTableBackpackContainer.java | 124 ++ .../backpack/EmptyBackpackContainer.java | 18 + .../backpack/EnderChestBackpackContainer.java | 31 + .../backpack/FurnaceBackpackContainer.java | 91 + .../backpack/MiddleBackpackContainer.java | 30 + .../backpack/SmallBackpackContainer.java | 22 + .../handler/MaidBackpackHandler.java | 13 +- .../touhoulittlemaid/item/BackpackLevel.java | 20 +- .../touhoulittlemaid/item/ItemCamera.java | 5 +- .../item/ItemFavorabilityTool.java | 33 + .../touhoulittlemaid/item/ItemFilm.java | 53 +- .../touhoulittlemaid/item/ItemFoxScroll.java | 103 + .../item/ItemMaidBackpack.java | 60 +- .../touhoulittlemaid/item/ItemPhoto.java | 19 +- .../touhoulittlemaid/item/ItemSmartSlab.java | 30 +- .../touhoulittlemaid/item/ItemTrumpet.java | 10 + .../item/bauble/ExtraLifeBauble.java | 2 +- .../item/bauble/NimbleFabricBauble.java | 39 +- .../network/NetworkHandler.java | 8 + .../message/ChessDataToClientMessage.java | 69 + .../message/ChessDataToServerMessage.java | 75 + .../network/message/FoxScrollMessage.java | 95 + .../network/message/MaidConfigMessage.java | 15 +- .../network/message/SetScrollData.java | 46 + .../network/message/SpawnParticleMessage.java | 5 +- .../tileentity/TileEntityBookshelf.java | 14 + .../tileentity/TileEntityComputer.java | 14 + .../tileentity/TileEntityGomoku.java | 101 + .../tileentity/TileEntityJoy.java | 72 + .../tileentity/TileEntityKeyboard.java | 14 + .../tileentity/TileEntityShrine.java | 89 + .../util/EntityCacheUtil.java | 3 +- .../touhoulittlemaid/util/ItemsUtil.java | 7 + .../touhoulittlemaid/util/RenderHelper.java | 44 +- .../touhoulittlemaid/util/TeleportHelper.java | 41 + .../touhoulittlemaid/world/data/MaidInfo.java | 48 + .../world/data/MaidWorldData.java | 195 ++ .../animation/maid.animation.json | 296 +++ .../blockstates/bookshelf.json | 7 + .../blockstates/computer.json | 7 + .../blockstates/gomoku.json | 7 + .../blockstates/keyboard.json | 7 + .../blockstates/shrine.json | 7 + .../assets/touhou_little_maid/lang/en_us.json | 65 +- .../assets/touhou_little_maid/lang/es_es.json | 7 - .../assets/touhou_little_maid/lang/fr_fr.json | 2 - .../assets/touhou_little_maid/lang/ja_jp.json | 18 +- .../assets/touhou_little_maid/lang/ko_kr.json | 4 - .../assets/touhou_little_maid/lang/pt_br.json | 8 - .../assets/touhou_little_maid/lang/ru_ru.json | 8 - .../assets/touhou_little_maid/lang/tr_tr.json | 17 +- .../assets/touhou_little_maid/lang/vi_vn.json | 8 - .../assets/touhou_little_maid/lang/zh_cn.json | 61 +- .../models/block/bookshelf.json | 5 + .../models/block/computer.json | 5 + .../models/block/gomoku.json | 5 + .../models/block/keyboard.json | 5 + .../models/block/shrine.json | 5 + .../models/item/bookshelf.json | 6 + .../models/item/computer.json | 6 + .../models/item/crafting_table_backpack.json | 6 + .../models/item/ender_chest_backpack.json | 6 + .../models/item/favorability_tool_add.json | 6 + .../models/item/favorability_tool_full.json | 6 + .../models/item/favorability_tool_reduce.json | 6 + .../models/item/furnace_backpack.json | 6 + .../models/item/gomoku.json | 6 + .../models/item/keyboard.json | 6 + .../models/item/model_switcher.json | 7 +- .../models/item/red_fox_scroll.json | 6 + .../models/item/shrine.json | 6 + .../models/item/white_fox_scroll.json | 6 + .../assets/touhou_little_maid/sounds.json | 14 + .../sounds/block/gomoku_reset.ogg | Bin 0 -> 11628 bytes .../touhou_little_maid/sounds/block/piece.ogg | Bin 0 -> 7995 bytes .../textures/book/fox_scroll.png | Bin 0 -> 14946 bytes .../textures/book/gomoku.png | Bin 0 -> 12002 bytes .../textures/book/other_backpack.png | Bin 0 -> 3868 bytes .../textures/entity/black_piece.png | Bin 0 -> 86 bytes .../textures/entity/bookshelf.png | Bin 0 -> 26472 bytes .../textures/entity/computer.png | Bin 0 -> 15461 bytes .../entity/crafting_table_backpack.png | Bin 0 -> 8310 bytes .../textures/entity/ender_chest_backpack.png | Bin 0 -> 8028 bytes .../textures/entity/furnace_backpack.png | Bin 0 -> 8296 bytes .../textures/entity/gomoku.png | Bin 0 -> 30647 bytes .../textures/entity/keyboard.png | Bin 0 -> 2168 bytes .../textures/entity/shrine.png | Bin 0 -> 2752 bytes .../textures/entity/tombstone.png | Bin 0 -> 2569 bytes .../textures/entity/tombstone_the_end.png | Bin 0 -> 2151 bytes .../textures/entity/tombstone_the_nether.png | Bin 0 -> 2182 bytes .../entity/tombstone_twilight_forest.png | Bin 0 -> 2099 bytes .../textures/entity/white_piece.png | Bin 0 -> 90 bytes .../textures/gui/maid_beacon.png | Bin 1222 -> 4699 bytes .../textures/gui/maid_crafting_table.png | Bin 0 -> 7239 bytes .../textures/gui/maid_ender_chest.png | Bin 0 -> 5190 bytes .../textures/gui/maid_furnace.png | Bin 0 -> 3732 bytes .../textures/gui/maid_gui_backpack.png | Bin 2668 -> 2957 bytes .../textures/gui/maid_gui_side.png | Bin 3059 -> 3187 bytes .../textures/item/bookshelf.png | Bin 0 -> 348 bytes .../textures/item/computer.png | Bin 0 -> 355 bytes .../textures/item/crafting_table_backpack.png | Bin 0 -> 253 bytes .../textures/item/empty_back_show_slot.png | Bin 0 -> 168 bytes .../textures/item/empty_bauble_slot.png | Bin 0 -> 151 bytes .../textures/item/empty_mainhand_slot.png | Bin 0 -> 164 bytes .../textures/item/ender_chest_backpack.png | Bin 0 -> 300 bytes .../textures/item/favorability_tool_add.png | Bin 0 -> 284 bytes .../textures/item/favorability_tool_full.png | Bin 0 -> 300 bytes .../item/favorability_tool_reduce.png | Bin 0 -> 264 bytes .../textures/item/furnace_backpack.png | Bin 0 -> 299 bytes .../textures/item/gomoku.png | Bin 0 -> 223 bytes .../textures/item/keyboard.png | Bin 0 -> 258 bytes .../textures/item/model_switcher.png | Bin 0 -> 281 bytes .../textures/item/power_point.png | Bin 308 -> 250 bytes .../textures/item/red_fox_scroll.png | Bin 0 -> 233 bytes .../textures/item/shrine.png | Bin 0 -> 348 bytes .../textures/item/trumpet.png | Bin 358 -> 405 bytes .../textures/item/white_fox_scroll.png | Bin 0 -> 233 bytes .../textures/slot/empty_back_show_slot.png | Bin 168 -> 168 bytes .../textures/slot/empty_bauble_slot.png | Bin 151 -> 155 bytes .../touhou_little_maid-1.0.0.zip | Bin 6133166 -> 6133213 bytes .../loot_modifiers/global_loot_modifiers.json | 3 +- .../loot_modifiers/shrine.json | 11 + .../loot_tables/blocks/bookshelf.json | 20 + .../loot_tables/blocks/computer.json | 20 + .../loot_tables/blocks/keyboard.json | 20 + .../loot_tables/blocks/model_switcher.json | 20 + .../loot_tables/blocks/shrine.json | 20 + .../loot_tables/chest/shrine.json | 14 + .../memorizable_gensokyo/book.json | 2 +- .../en_us/entries/maid/other_backpack.json | 19 + .../entries/other/favorability_tool.json | 13 + .../en_us/entries/other/fox_scroll.json | 19 + .../en_us/entries/other/gomoku.json | 19 + .../en_us/entries/other/maid_joy.json | 13 + .../en_us/entries/other/shrine.json | 13 + .../en_us/entries/overview/favorability.json | 12 + .../recipes/altar/craft_bookshelf.json | 33 + .../recipes/altar/craft_computer.json | 33 + .../altar/craft_crafting_table_backpack.json | 21 + .../altar/craft_ender_chest_backpack.json | 21 + .../recipes/altar/craft_furnace_backpack.json | 21 + .../recipes/altar/craft_gomoku.json | 33 + .../recipes/altar/craft_keyboard.json | 33 + .../recipes/altar/craft_red_fox_scroll.json | 33 + .../recipes/altar/craft_white_fox_scroll.json | 33 + 253 files changed, 10743 insertions(+), 666 deletions(-) create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/api/backpack/IBackpackData.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/api/backpack/IMaidBackpack.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/api/game/gomoku/AIService.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/api/game/gomoku/Point.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/api/game/gomoku/Statue.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/api/game/gomoku/ZhiZhangAIService.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/block/BlockBookshelf.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/block/BlockComputer.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/block/BlockGomoku.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/block/BlockJoy.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/block/BlockKeyboard.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/block/BlockShrine.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/block/properties/GomokuPart.java rename src/main/java/com/github/tartaricacid/touhoulittlemaid/client/event/{AddBaubleInformationEvent.java => AddInformationEvent.java} (71%) create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/client/event/ScrollRenderEvent.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/entity/maid/MaidMainContainerGui.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/entity/maid/backpack/BigBackpackContainerScreen.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/entity/maid/backpack/CraftingTableBackpackContainerScreen.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/entity/maid/backpack/EmptyBackpackContainerScreen.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/entity/maid/backpack/EnderChestBackpackContainerScreen.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/entity/maid/backpack/FurnaceBackpackContainerScreen.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/entity/maid/backpack/IBackpackContainerScreen.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/entity/maid/backpack/MiddleBackpackContainerScreen.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/entity/maid/backpack/SmallBackpackContainerScreen.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/entity/maid/backpack/package-info.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/item/FoxScrollScreen.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/BookshelfModel.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/ComputerModel.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/GomokuModel.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/KeyboardModel.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/PieceModel.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/ShrineModel.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/TombstoneModel.java rename src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/{MaidBackpackBigModel.java => backpack/BigBackpackModel.java} (95%) create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/backpack/CraftingTableBackpackModel.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/backpack/EnderChestBackpackModel.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/backpack/FurnaceBackpackModel.java rename src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/{MaidBackpackMiddleModel.java => backpack/MiddleBackpackModel.java} (99%) rename src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/{MaidBackpackSmallModel.java => backpack/SmallBackpackModel.java} (99%) create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/client/renderer/entity/EntitySitRenderer.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/client/renderer/entity/EntityTombstoneRenderer.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/client/renderer/tileentity/TileEntityBookshelfRenderer.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/client/renderer/tileentity/TileEntityComputerRenderer.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/client/renderer/tileentity/TileEntityGomokuRenderer.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/client/renderer/tileentity/TileEntityKeyboardRenderer.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/client/renderer/tileentity/TileEntityShrineRenderer.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/compat/domesticationinnovation/PetBedDrop.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/ai/brain/task/MaidFindGomokuTask.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/ai/brain/task/MaidFindJoyTask.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/ai/brain/task/MaidSitJoyTask.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/backpack/BackpackManager.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/backpack/BigBackpack.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/backpack/CraftingTableBackpack.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/backpack/EmptyBackpack.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/backpack/EnderChestBackpack.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/backpack/FurnaceBackpack.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/backpack/MiddleBackpack.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/backpack/SmallBackpack.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/backpack/data/FurnaceBackpackData.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/backpack/package-info.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/favorability/FavorabilityManager.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/favorability/Type.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/item/EntitySit.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/item/EntityTombstone.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/task/TaskBoardGames.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/event/maid/HandleBackpackEvent.java delete mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/event/maid/TakeOffBackpackEvent.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/event/maid/UseFavorabilityToolEvent.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/inventory/container/backpack/BigBackpackContainer.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/inventory/container/backpack/CraftingTableBackpackContainer.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/inventory/container/backpack/EmptyBackpackContainer.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/inventory/container/backpack/EnderChestBackpackContainer.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/inventory/container/backpack/FurnaceBackpackContainer.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/inventory/container/backpack/MiddleBackpackContainer.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/inventory/container/backpack/SmallBackpackContainer.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/item/ItemFavorabilityTool.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/item/ItemFoxScroll.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/network/message/ChessDataToClientMessage.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/network/message/ChessDataToServerMessage.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/network/message/FoxScrollMessage.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/network/message/SetScrollData.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/tileentity/TileEntityBookshelf.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/tileentity/TileEntityComputer.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/tileentity/TileEntityGomoku.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/tileentity/TileEntityJoy.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/tileentity/TileEntityKeyboard.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/tileentity/TileEntityShrine.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/util/TeleportHelper.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/world/data/MaidInfo.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/world/data/MaidWorldData.java create mode 100644 src/main/resources/assets/touhou_little_maid/blockstates/bookshelf.json create mode 100644 src/main/resources/assets/touhou_little_maid/blockstates/computer.json create mode 100644 src/main/resources/assets/touhou_little_maid/blockstates/gomoku.json create mode 100644 src/main/resources/assets/touhou_little_maid/blockstates/keyboard.json create mode 100644 src/main/resources/assets/touhou_little_maid/blockstates/shrine.json create mode 100644 src/main/resources/assets/touhou_little_maid/models/block/bookshelf.json create mode 100644 src/main/resources/assets/touhou_little_maid/models/block/computer.json create mode 100644 src/main/resources/assets/touhou_little_maid/models/block/gomoku.json create mode 100644 src/main/resources/assets/touhou_little_maid/models/block/keyboard.json create mode 100644 src/main/resources/assets/touhou_little_maid/models/block/shrine.json create mode 100644 src/main/resources/assets/touhou_little_maid/models/item/bookshelf.json create mode 100644 src/main/resources/assets/touhou_little_maid/models/item/computer.json create mode 100644 src/main/resources/assets/touhou_little_maid/models/item/crafting_table_backpack.json create mode 100644 src/main/resources/assets/touhou_little_maid/models/item/ender_chest_backpack.json create mode 100644 src/main/resources/assets/touhou_little_maid/models/item/favorability_tool_add.json create mode 100644 src/main/resources/assets/touhou_little_maid/models/item/favorability_tool_full.json create mode 100644 src/main/resources/assets/touhou_little_maid/models/item/favorability_tool_reduce.json create mode 100644 src/main/resources/assets/touhou_little_maid/models/item/furnace_backpack.json create mode 100644 src/main/resources/assets/touhou_little_maid/models/item/gomoku.json create mode 100644 src/main/resources/assets/touhou_little_maid/models/item/keyboard.json create mode 100644 src/main/resources/assets/touhou_little_maid/models/item/red_fox_scroll.json create mode 100644 src/main/resources/assets/touhou_little_maid/models/item/shrine.json create mode 100644 src/main/resources/assets/touhou_little_maid/models/item/white_fox_scroll.json create mode 100644 src/main/resources/assets/touhou_little_maid/sounds/block/gomoku_reset.ogg create mode 100644 src/main/resources/assets/touhou_little_maid/sounds/block/piece.ogg create mode 100644 src/main/resources/assets/touhou_little_maid/textures/book/fox_scroll.png create mode 100644 src/main/resources/assets/touhou_little_maid/textures/book/gomoku.png create mode 100644 src/main/resources/assets/touhou_little_maid/textures/book/other_backpack.png create mode 100644 src/main/resources/assets/touhou_little_maid/textures/entity/black_piece.png create mode 100644 src/main/resources/assets/touhou_little_maid/textures/entity/bookshelf.png create mode 100644 src/main/resources/assets/touhou_little_maid/textures/entity/computer.png create mode 100644 src/main/resources/assets/touhou_little_maid/textures/entity/crafting_table_backpack.png create mode 100644 src/main/resources/assets/touhou_little_maid/textures/entity/ender_chest_backpack.png create mode 100644 src/main/resources/assets/touhou_little_maid/textures/entity/furnace_backpack.png create mode 100644 src/main/resources/assets/touhou_little_maid/textures/entity/gomoku.png create mode 100644 src/main/resources/assets/touhou_little_maid/textures/entity/keyboard.png create mode 100644 src/main/resources/assets/touhou_little_maid/textures/entity/shrine.png create mode 100644 src/main/resources/assets/touhou_little_maid/textures/entity/tombstone.png create mode 100644 src/main/resources/assets/touhou_little_maid/textures/entity/tombstone_the_end.png create mode 100644 src/main/resources/assets/touhou_little_maid/textures/entity/tombstone_the_nether.png create mode 100644 src/main/resources/assets/touhou_little_maid/textures/entity/tombstone_twilight_forest.png create mode 100644 src/main/resources/assets/touhou_little_maid/textures/entity/white_piece.png create mode 100644 src/main/resources/assets/touhou_little_maid/textures/gui/maid_crafting_table.png create mode 100644 src/main/resources/assets/touhou_little_maid/textures/gui/maid_ender_chest.png create mode 100644 src/main/resources/assets/touhou_little_maid/textures/gui/maid_furnace.png create mode 100644 src/main/resources/assets/touhou_little_maid/textures/item/bookshelf.png create mode 100644 src/main/resources/assets/touhou_little_maid/textures/item/computer.png create mode 100644 src/main/resources/assets/touhou_little_maid/textures/item/crafting_table_backpack.png create mode 100644 src/main/resources/assets/touhou_little_maid/textures/item/empty_back_show_slot.png create mode 100644 src/main/resources/assets/touhou_little_maid/textures/item/empty_bauble_slot.png create mode 100644 src/main/resources/assets/touhou_little_maid/textures/item/empty_mainhand_slot.png create mode 100644 src/main/resources/assets/touhou_little_maid/textures/item/ender_chest_backpack.png create mode 100644 src/main/resources/assets/touhou_little_maid/textures/item/favorability_tool_add.png create mode 100644 src/main/resources/assets/touhou_little_maid/textures/item/favorability_tool_full.png create mode 100644 src/main/resources/assets/touhou_little_maid/textures/item/favorability_tool_reduce.png create mode 100644 src/main/resources/assets/touhou_little_maid/textures/item/furnace_backpack.png create mode 100644 src/main/resources/assets/touhou_little_maid/textures/item/gomoku.png create mode 100644 src/main/resources/assets/touhou_little_maid/textures/item/keyboard.png create mode 100644 src/main/resources/assets/touhou_little_maid/textures/item/model_switcher.png create mode 100644 src/main/resources/assets/touhou_little_maid/textures/item/red_fox_scroll.png create mode 100644 src/main/resources/assets/touhou_little_maid/textures/item/shrine.png create mode 100644 src/main/resources/assets/touhou_little_maid/textures/item/white_fox_scroll.png create mode 100644 src/main/resources/data/touhou_little_maid/loot_modifiers/shrine.json create mode 100644 src/main/resources/data/touhou_little_maid/loot_tables/blocks/bookshelf.json create mode 100644 src/main/resources/data/touhou_little_maid/loot_tables/blocks/computer.json create mode 100644 src/main/resources/data/touhou_little_maid/loot_tables/blocks/keyboard.json create mode 100644 src/main/resources/data/touhou_little_maid/loot_tables/blocks/model_switcher.json create mode 100644 src/main/resources/data/touhou_little_maid/loot_tables/blocks/shrine.json create mode 100644 src/main/resources/data/touhou_little_maid/loot_tables/chest/shrine.json create mode 100644 src/main/resources/data/touhou_little_maid/patchouli_books/memorizable_gensokyo/en_us/entries/maid/other_backpack.json create mode 100644 src/main/resources/data/touhou_little_maid/patchouli_books/memorizable_gensokyo/en_us/entries/other/favorability_tool.json create mode 100644 src/main/resources/data/touhou_little_maid/patchouli_books/memorizable_gensokyo/en_us/entries/other/fox_scroll.json create mode 100644 src/main/resources/data/touhou_little_maid/patchouli_books/memorizable_gensokyo/en_us/entries/other/gomoku.json create mode 100644 src/main/resources/data/touhou_little_maid/patchouli_books/memorizable_gensokyo/en_us/entries/other/maid_joy.json create mode 100644 src/main/resources/data/touhou_little_maid/patchouli_books/memorizable_gensokyo/en_us/entries/other/shrine.json create mode 100644 src/main/resources/data/touhou_little_maid/patchouli_books/memorizable_gensokyo/en_us/entries/overview/favorability.json create mode 100644 src/main/resources/data/touhou_little_maid/recipes/altar/craft_bookshelf.json create mode 100644 src/main/resources/data/touhou_little_maid/recipes/altar/craft_computer.json create mode 100644 src/main/resources/data/touhou_little_maid/recipes/altar/craft_crafting_table_backpack.json create mode 100644 src/main/resources/data/touhou_little_maid/recipes/altar/craft_ender_chest_backpack.json create mode 100644 src/main/resources/data/touhou_little_maid/recipes/altar/craft_furnace_backpack.json create mode 100644 src/main/resources/data/touhou_little_maid/recipes/altar/craft_gomoku.json create mode 100644 src/main/resources/data/touhou_little_maid/recipes/altar/craft_keyboard.json create mode 100644 src/main/resources/data/touhou_little_maid/recipes/altar/craft_red_fox_scroll.json create mode 100644 src/main/resources/data/touhou_little_maid/recipes/altar/craft_white_fox_scroll.json diff --git a/gradle.properties b/gradle.properties index db777fca0..f828b3b85 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ org.gradle.jvmargs=-Xmx4G org.gradle.daemon=false -mod_version=1.1.4-hotfix +mod_version=1.1.5 forge_version=1.18.2-40.1.0 jei_version=9.7.0.194 patchouli_version=1.18.2-67 diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/TouhouLittleMaid.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/TouhouLittleMaid.java index be70bf2b6..bfba74e5d 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/TouhouLittleMaid.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/TouhouLittleMaid.java @@ -1,8 +1,11 @@ package com.github.tartaricacid.touhoulittlemaid; import com.github.tartaricacid.touhoulittlemaid.api.ILittleMaid; +import com.github.tartaricacid.touhoulittlemaid.api.game.gomoku.AIService; +import com.github.tartaricacid.touhoulittlemaid.api.game.gomoku.ZhiZhangAIService; import com.github.tartaricacid.touhoulittlemaid.block.multiblock.MultiBlockManager; import com.github.tartaricacid.touhoulittlemaid.config.GeneralConfig; +import com.github.tartaricacid.touhoulittlemaid.entity.backpack.BackpackManager; import com.github.tartaricacid.touhoulittlemaid.entity.chatbubble.ChatBubbleManger; import com.github.tartaricacid.touhoulittlemaid.entity.task.TaskManager; import com.github.tartaricacid.touhoulittlemaid.init.*; @@ -23,6 +26,7 @@ public final class TouhouLittleMaid { public static final String MOD_ID = "touhou_little_maid"; public static final Logger LOGGER = LogManager.getLogger(MOD_ID); public static List EXTENSIONS = Lists.newArrayList(); + public static AIService SERVICE = new ZhiZhangAIService(new AIService.AIConfig(4, 10, false, 0, 6)); public TouhouLittleMaid() { InitEntities.ENTITY_TYPES.register(FMLJavaModLoadingContext.get().getModEventBus()); @@ -42,6 +46,7 @@ public TouhouLittleMaid() { ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, GeneralConfig.init()); EXTENSIONS = AnnotatedInstanceUtil.getModExtensions(); TaskManager.init(); + BackpackManager.init(); BaubleManager.init(); MultiBlockManager.init(); ChatBubbleManger.initDefaultChat(); diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/api/ILittleMaid.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/api/ILittleMaid.java index beb3ec20d..5b5377993 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/api/ILittleMaid.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/api/ILittleMaid.java @@ -1,6 +1,7 @@ package com.github.tartaricacid.touhoulittlemaid.api; import com.github.tartaricacid.touhoulittlemaid.block.multiblock.MultiBlockManager; +import com.github.tartaricacid.touhoulittlemaid.entity.backpack.BackpackManager; import com.github.tartaricacid.touhoulittlemaid.entity.task.TaskManager; import com.github.tartaricacid.touhoulittlemaid.item.bauble.BaubleManager; @@ -21,6 +22,14 @@ default void bindMaidBauble(BaubleManager manager) { default void addMaidTask(TaskManager manager) { } + /** + * 添加女仆的背包 + * + * @param manager 注册器 + */ + default void addMaidBackpack(BackpackManager manager) { + } + /** * 添加多方块结构 * diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/api/backpack/IBackpackData.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/api/backpack/IBackpackData.java new file mode 100644 index 000000000..e48ec2502 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/api/backpack/IBackpackData.java @@ -0,0 +1,15 @@ +package com.github.tartaricacid.touhoulittlemaid.api.backpack; + +import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.inventory.ContainerData; + +public interface IBackpackData { + ContainerData getDataAccess(); + + void load(CompoundTag tag, EntityMaid maid); + + void save(CompoundTag tag, EntityMaid maid); + + void serverTick(EntityMaid maid); +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/api/backpack/IMaidBackpack.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/api/backpack/IMaidBackpack.java new file mode 100644 index 000000000..fd52a37e3 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/api/backpack/IMaidBackpack.java @@ -0,0 +1,63 @@ +package com.github.tartaricacid.touhoulittlemaid.api.backpack; + +import com.github.tartaricacid.touhoulittlemaid.entity.item.EntityTombstone; +import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; +import com.github.tartaricacid.touhoulittlemaid.item.BackpackLevel; +import com.github.tartaricacid.touhoulittlemaid.util.ItemsUtil; +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.client.model.EntityModel; +import net.minecraft.client.model.geom.EntityModelSet; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.MenuProvider; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +import javax.annotation.Nullable; + + +public abstract class IMaidBackpack { + public abstract ResourceLocation getId(); + + public abstract Item getItem(); + + public abstract void onPutOn(ItemStack stack, Player player, EntityMaid maid); + + public ItemStack getTakeOffItemStack(ItemStack stack, @Nullable Player player, EntityMaid maid) { + return this.getItem().getDefaultInstance(); + } + + public abstract void onTakeOff(ItemStack stack, Player player, EntityMaid maid); + + public abstract void onSpawnTombstone(EntityMaid maid, EntityTombstone tombstone); + + public abstract MenuProvider getGuiProvider(int entityId); + + public boolean hasBackpackData() { + return false; + } + + @Nullable + public IBackpackData getBackpackData(EntityMaid maid) { + return null; + } + + public abstract int getAvailableMaxContainerIndex(); + + @OnlyIn(Dist.CLIENT) + public abstract void offsetBackpackItem(PoseStack poseStack); + + @Nullable + @OnlyIn(Dist.CLIENT) + public abstract EntityModel getBackpackModel(EntityModelSet modelSet); + + @Nullable + @OnlyIn(Dist.CLIENT) + public abstract ResourceLocation getBackpackTexture(); + + protected final void dropAllItems(EntityMaid maid) { + ItemsUtil.dropEntityItems(maid, maid.getMaidInv(), BackpackLevel.EMPTY_CAPACITY); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/api/game/gomoku/AIService.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/api/game/gomoku/AIService.java new file mode 100644 index 000000000..60dc21a48 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/api/game/gomoku/AIService.java @@ -0,0 +1,84 @@ +/* + * https://github.com/anlingyi/xechat-idea + * + * Apache License + * Version 2.0, January 2004 + * http://www.apache.org/licenses/ + */ +package com.github.tartaricacid.touhoulittlemaid.api.game.gomoku; + +/** + * @author anlingyi + * @date 2021/11/1 3:26 下午 + */ +public interface AIService { + /** + * 获取AI棋位 + * + * @param chessData 已下棋子数据 + * @param point 对手棋位 + */ + Point getPoint(int[][] chessData, Point point); + + /** + * 获取棋局状态 + * + * @param chessData 已下棋子数据 + * @param point 落子棋位 + */ + Statue getStatue(int[][] chessData, Point point); + + /** + * AI配置 + */ + class AIConfig { + /** + * 搜索深度 + */ + private final int depth; + /** + * 最大启发式节点数 + */ + private final int maxNodes; + /** + * debug + */ + private final boolean debug; + /** + * 算杀 0.不开启 1.VCT 2.VCF + */ + private final int vcx; + /** + * 算杀深度 + */ + private final int vcxDepth; + + public AIConfig(int depth, int maxNodes, boolean debug, int vcx, int vcxDepth) { + this.depth = depth; + this.maxNodes = maxNodes; + this.debug = debug; + this.vcx = vcx; + this.vcxDepth = vcxDepth; + } + + public int getDepth() { + return depth; + } + + public int getMaxNodes() { + return maxNodes; + } + + public boolean isDebug() { + return debug; + } + + public int getVcx() { + return vcx; + } + + public int getVcxDepth() { + return vcxDepth; + } + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/api/game/gomoku/Point.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/api/game/gomoku/Point.java new file mode 100644 index 000000000..fa953bc87 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/api/game/gomoku/Point.java @@ -0,0 +1,78 @@ +/* + * https://github.com/anlingyi/xechat-idea + * + * Apache License + * Version 2.0, January 2004 + * http://www.apache.org/licenses/ + */ +package com.github.tartaricacid.touhoulittlemaid.api.game.gomoku; + +import net.minecraft.nbt.CompoundTag; + +/** + * 棋子点位 + * + * @author anlingyi + * @date 2021/11/7 5:59 下午 + */ +public class Point { + public static final int EMPTY = 0; + public static final int BLACK = 1; + public static final int WHITE = 2; + public static final Point NULL = new Point(-1, -1, 0); + /** + * 横坐标 + */ + public final int x; + /** + * 纵坐标 + */ + public final int y; + /** + * 棋子类型 1.黑 2.白 + */ + public int type; + /** + * 得分 + */ + public int score; + + public Point(int x, int y, int type) { + this.x = x; + this.y = y; + this.type = type; + } + + public static CompoundTag toTag(Point point) { + CompoundTag tag = new CompoundTag(); + tag.putInt("x", point.x); + tag.putInt("y", point.y); + tag.putInt("type", point.type); + return tag; + } + + public static Point fromTag(CompoundTag tag) { + int x = tag.getInt("x"); + int y = tag.getInt("y"); + int type = tag.getInt("type"); + return new Point(x, y, type); + } + + @Override + public boolean equals(Object other) { + if (other == null) { + return false; + } else if (this == other) { + return true; + } else if (other instanceof Point otherPoint) { + return otherPoint.x == this.x && otherPoint.y == this.y && otherPoint.type == this.type; + } else { + return false; + } + } + + @Override + public String toString() { + return (type == 1 ? "Black" : "White") + "[" + x + "," + y + ']'; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/api/game/gomoku/Statue.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/api/game/gomoku/Statue.java new file mode 100644 index 000000000..4c61813e1 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/api/game/gomoku/Statue.java @@ -0,0 +1,6 @@ +package com.github.tartaricacid.touhoulittlemaid.api.game.gomoku; + +public enum Statue { + WIN, + IN_PROGRESS +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/api/game/gomoku/ZhiZhangAIService.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/api/game/gomoku/ZhiZhangAIService.java new file mode 100644 index 000000000..41896a99e --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/api/game/gomoku/ZhiZhangAIService.java @@ -0,0 +1,1683 @@ +/* + * https://github.com/anlingyi/xechat-idea + * + * Apache License + * Version 2.0, January 2004 + * http://www.apache.org/licenses/ + */ +package com.github.tartaricacid.touhoulittlemaid.api.game.gomoku; + +import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; + +import java.util.*; +import java.util.concurrent.ThreadLocalRandom; + +/** + * @author anlingyi + * @date 2021/11/1 3:31 下午 + */ +public class ZhiZhangAIService implements AIService { + /** + * 用于初始化 Zobrist 随机值 + */ + private static final ThreadLocalRandom RANDOM = ThreadLocalRandom.current(); + + /** + * AI配置 + */ + private final AIConfig aiConfig; + + /** + * 已下棋子数据 + */ + private int[][] chessData; + /** + * 棋盘行数 + */ + private int rows; + /** + * 棋盘列数 + */ + private int cols; + /** + * AI棋子类型 + */ + private int ai; + /** + * 进攻系数 + */ + private float attack; + /** + * AI最佳下棋点位 + */ + private Point bestPoint; + + /** + * 当前回合数 + */ + private int rounds; + + /** + * 统计 + */ + private Statistics statistics; + + /** + * 最佳落子路径 + */ + private Stack pathStack; + + /** + * 最佳落子路径 + */ + private Stack bestPathStack; + + /** + * 当前局面的hashcode + */ + private long hashcode; + + /** + * 局面缓存 + */ + private Map situationCacheMap; + + /** + * 声明一个最大值 + */ + private static final int INFINITY = 999999999; + + /** + * 棋型分数表 + */ + private static final Map SCORE = new LinkedHashMap<>(); + + /** + * 黑子 Zobrist + */ + private static final long[][] BLACK_ZOBRIST = new long[15][15]; + + /** + * 白子 Zobrist + */ + private static final long[][] WHITE_ZOBRIST = new long[15][15]; + + static { + // 初始化棋型分数表 + for (ChessModel chessScore : ChessModel.values()) { + for (String value : chessScore.values) { + SCORE.put(value, chessScore.score); + } + } + + // 初始化Zobrist随机值 + for (int i = 0; i < BLACK_ZOBRIST.length; i++) { + for (int j = 0; j < BLACK_ZOBRIST.length; j++) { + BLACK_ZOBRIST[i][j] = RANDOM.nextLong(); + WHITE_ZOBRIST[i][j] = RANDOM.nextLong(); + } + } + } + + private enum ChessModel { + /** + * 连五 + */ + LIANWU(10000000, new String[]{"11111"}), + /** + * 活四 + */ + HUOSI(1000000, new String[]{"011110"}), + /** + * 活三 + */ + HUOSAN(10000, new String[]{"001110", "011100", "010110", "011010"}), + /** + * 冲四 + */ + CHONGSI(9000, new String[]{"11110", "01111", "10111", "11011", "11101"}), + /** + * 活二 + */ + HUOER(100, new String[]{"001100", "011000", "000110", "001010", "010100"}), + /** + * 活一 + */ + HUOYI(80, new String[]{"010200", "002010", "020100", "001020", "201000", "000102", "000201"}), + /** + * 眠三 + */ + MIANSAN(30, new String[]{"001112", "010112", "011012", "211100", "211010"}), + /** + * 眠二 + */ + MIANER(10, new String[]{"011200", "001120", "002110", "021100", "110000", "000011", "000112", "211000"}), + /** + * 眠一 + */ + MIANYI(1, new String[]{"001200", "002100", "000210", "000120", "210000", "000012"}); + + /** + * 分数 + */ + final int score; + /** + * 局势数组 + */ + final String[] values; + + ChessModel(int score, String[] values) { + this.score = score; + this.values = values; + } + } + + /** + * 风险评分 + */ + private enum RiskScore { + /** + * 高风险 + */ + HIGH_RISK(800000), + /** + * 中风险 + */ + MEDIUM_RISK(500000), + /** + * 低风险 + */ + LOW_RISK(100000); + + final int score; + + RiskScore(int score) { + this.score = score; + } + + /** + * 判断分数是否处于区间 [leftScore, rightScore) + * + * @param score 分数 + * @param leftScore 左区分值 + * @param rightScore 右区分值 + * @return + */ + public static boolean between(int score, RiskScore leftScore, RiskScore rightScore) { + return score >= leftScore.score && score < rightScore.score; + } + } + + /** + * 统计 + */ + private static class Statistics { + /** + * 搜索深度 + */ + private int depth; + /** + * 最佳点位 + */ + private Point point; + /** + * 分数 + */ + private int score; + /** + * 极大极小搜索耗时(秒) + */ + private double minimaxTime; + /** + * 搜索节点数 + */ + private int nodes; + /** + * 剪枝数 + */ + private int cuts; + /** + * 算杀深度 + */ + private int vcxDepth; + /** + * 算杀命中 0.未命中 1.vcf 2.vct + */ + private int vcx; + /** + * 算杀耗时(秒) + */ + private double vcxTime; + /** + * 局面缓存数 + */ + private int caches; + /** + * 局面缓存命中数 + */ + private int cacheHits; + + public void incrNodes() { + this.nodes++; + } + + public void incrCuts() { + this.cuts++; + } + + public void incrCacheHits() { + this.cacheHits++; + } + + public int getDepth() { + return depth; + } + + public Point getPoint() { + return point; + } + + public int getScore() { + return score; + } + + public double getMinimaxTime() { + return minimaxTime; + } + + public int getNodes() { + return nodes; + } + + public int getCuts() { + return cuts; + } + + public int getVcxDepth() { + return vcxDepth; + } + + public int getVcx() { + return vcx; + } + + public double getVcxTime() { + return vcxTime; + } + + public int getCaches() { + return caches; + } + + public int getCacheHits() { + return cacheHits; + } + + public void setDepth(int depth) { + this.depth = depth; + } + + public void setPoint(Point point) { + this.point = point; + } + + public void setScore(int score) { + this.score = score; + } + + public void setMinimaxTime(double minimaxTime) { + this.minimaxTime = minimaxTime; + } + + public void setNodes(int nodes) { + this.nodes = nodes; + } + + public void setCuts(int cuts) { + this.cuts = cuts; + } + + public void setVcxDepth(int vcxDepth) { + this.vcxDepth = vcxDepth; + } + + public void setVcx(int vcx) { + this.vcx = vcx; + } + + public void setVcxTime(double vcxTime) { + this.vcxTime = vcxTime; + } + + public void setCaches(int caches) { + this.caches = caches; + } + + public void setCacheHits(int cacheHits) { + this.cacheHits = cacheHits; + } + } + + /** + * 局面缓存 + */ + private static class SituationCache { + /** + * 存储VCX点位 + */ + private Point point; + /** + * 局面分数 + */ + private int score; + /** + * 搜索深度 + */ + private final int depth; + + public SituationCache(int score, int depth) { + this.score = score; + this.depth = depth; + } + + public SituationCache(Point point, int depth) { + this.point = point; + this.depth = depth; + } + } + + public ZhiZhangAIService() { + this(new AIConfig(6, 10, false, 0, 6)); + } + + public ZhiZhangAIService(AIConfig aiConfig) { + this.aiConfig = aiConfig; + } + + @Override + public Point getPoint(int[][] chessData, Point point) { + initChessData(chessData); + this.statistics = new Statistics(); + this.ai = 3 - point.type; + this.bestPoint = null; + // AI是黑棋则偏进攻,是白棋则偏防守 + this.attack = this.ai == 1 ? 1.8f : 0.5f; + int depth = this.aiConfig.getDepth(); + + if (this.rounds == 1 && this.ai == 1) { + // AI先下,首子天元 + int centerX = this.cols / 2; + int centerY = this.rows / 2; + return new Point(centerX, centerY, this.ai); + } + + // 基于普通方式获取最佳棋位 + if (this.aiConfig.getDepth() < 2) { + return getBestPoint(point); + } + + if (this.aiConfig.getDepth() > 4 && this.rounds < 4) { + // 当AI级别大于4时,将前三个回合的搜索深度设置为4 + depth = 4; + } + + // 算杀模式 + int vcx = aiConfig.getVcx(); + if (vcx > 0) { + // 算杀最大深度 + int vcxDepth = aiConfig.getVcxDepth(); + long vcxStartTime = System.currentTimeMillis(); + // VCT/VCF + this.bestPoint = deepeningVcx(true, vcxDepth, vcx == 2); + long vcxEndTime = System.currentTimeMillis(); + this.statistics.setVcxTime((vcxEndTime - vcxStartTime) / 1000.00d); + } + + if (this.bestPoint == null) { + this.statistics.setNodes(0); + this.statistics.setCacheHits(0); + long minimaxStartTime = System.currentTimeMillis(); + // 基于极大极小值搜索获取最佳棋位 + this.bestPoint = deepeningMinimax(2, depth); + long minimaxEndTime = System.currentTimeMillis(); + this.statistics.setMinimaxTime((minimaxEndTime - minimaxStartTime) / 1000.00d); + } + + this.statistics.setPoint(this.bestPoint); + this.statistics.setScore(this.bestPoint.score); + this.statistics.setCaches(situationCacheMap.size()); + if (this.aiConfig.isDebug()) { + TouhouLittleMaid.LOGGER.debug("============AI 统计[第" + this.rounds + "回合]=========="); + TouhouLittleMaid.LOGGER.debug("搜索深度:" + this.statistics.getDepth()); + TouhouLittleMaid.LOGGER.debug("搜索节点数:" + this.statistics.getNodes()); + TouhouLittleMaid.LOGGER.debug("发生剪枝数:" + this.statistics.getCuts()); + TouhouLittleMaid.LOGGER.debug("缓存总数:" + this.statistics.getCaches()); + TouhouLittleMaid.LOGGER.debug("缓存命中数:" + this.statistics.getCacheHits()); + if (vcx > 0) { + TouhouLittleMaid.LOGGER.debug("算杀深度:" + this.statistics.getVcxDepth()); + TouhouLittleMaid.LOGGER.debug("算杀命中:" + (this.statistics.getVcx() == 1 ? "VCF" : this.statistics.getVcx() == 2 ? "VCT" : "未命中")); + } + TouhouLittleMaid.LOGGER.debug("最佳落子点:" + this.statistics.getPoint()); + TouhouLittleMaid.LOGGER.debug("得分:" + this.statistics.getScore()); + double time = this.statistics.getMinimaxTime() + this.statistics.getVcxTime(); + TouhouLittleMaid.LOGGER.debug("耗时:" + String.format("%.3f", time) + "s" + (vcx > 0 ? ", VCX(" + this.statistics.getVcxTime() + "s)" : "") + ", MINIMAX(" + this.statistics.getMinimaxTime() + "s)"); + TouhouLittleMaid.LOGGER.debug("=================================="); + } + this.situationCacheMap = null; + + return this.bestPoint; + } + + @Override + public Statue getStatue(int[][] chessData, Point point) { + int rows = chessData[0].length; + int cols = chessData.length; + int x = point.x; + int y = point.y; + int type = point.type; + + // 横轴 + int k = 1; + for (int i = 1; i < 5; i++) { + int preX = x - i; + if (preX < 0) { + break; + } + if (chessData[preX][y] != type) { + break; + } + if (++k == 5) { + return Statue.WIN; + } + } + for (int i = 1; i < 5; i++) { + int nextX = x + i; + if (nextX > rows - 1) { + break; + } + if (chessData[nextX][y] != type) { + break; + } + if (++k == 5) { + return Statue.WIN; + } + } + + // 纵轴 + k = 1; + for (int i = 1; i < 5; i++) { + int preY = y - i; + if (preY < 0) { + break; + } + if (chessData[x][preY] != type) { + break; + } + if (++k == 5) { + return Statue.WIN; + } + } + for (int i = 1; i < 5; i++) { + int nextY = y + i; + if (nextY > cols - 1) { + break; + } + if (chessData[x][nextY] != type) { + break; + } + if (++k == 5) { + return Statue.WIN; + } + } + + // 左对角线 + k = 1; + for (int i = 1; i < 5; i++) { + int preX = x - i; + int preY = y - i; + if (preX < 0 || preY < 0) { + break; + } + if (chessData[preX][preY] != type) { + break; + } + if (++k == 5) { + return Statue.WIN; + } + } + for (int i = 1; i < 5; i++) { + int nextX = x + i; + int nextY = y + i; + if (nextX > rows - 1 || nextY > cols - 1) { + break; + } + if (chessData[nextX][nextY] != type) { + break; + } + if (++k == 5) { + return Statue.WIN; + } + } + + // 右对角线 + k = 1; + for (int i = 1; i < 5; i++) { + int nextX = x + i; + int preY = y - i; + if (nextX > rows - 1 || preY < 0) { + break; + } + if (chessData[nextX][preY] != type) { + break; + } + if (++k == 5) { + return Statue.WIN; + } + } + for (int i = 1; i < 5; i++) { + int preX = x - i; + int nextY = y + i; + if (preX < 0 || nextY > cols - 1) { + break; + } + if (chessData[preX][nextY] != type) { + break; + } + if (++k == 5) { + return Statue.WIN; + } + } + + return Statue.IN_PROGRESS; + } + + /** + * 初始化棋盘数据 + * + * @param chessData 当前棋盘数据 + */ + private void initChessData(int[][] chessData) { + this.rows = chessData.length; + this.cols = chessData[0].length; + this.chessData = new int[this.cols][this.rows]; + int chessTotal = 0; + + for (int i = 0; i < cols; i++) { + for (int j = 0; j < rows; j++) { + int type = chessData[i][j]; + if (type != 0) { + putChess(new Point(i, j, type)); + chessTotal++; + } + } + } + + this.rounds = chessTotal / 2 + 1; + } + + /** + * 迭代加深VCX + * + * @param isAi 是否是AI + * @param maxDepth 最大深度 + * @param isVcf true:VCF false:VCT + * @return + */ + private Point deepeningVcx(boolean isAi, int maxDepth, boolean isVcf) { + this.ai = isAi ? this.ai : 3 - this.ai; + Point point = deepening(1, maxDepth, isVcf); + if (!isAi) { + this.ai = 3 - this.ai; + if (point != null) { + point.type = this.ai; + } + } + + return point; + } + + /** + * 迭代加深算杀搜索 + * + * @param depth 当前搜索深度 + * @param maxDepth 最大搜索深度 + * @param isVcf true:VCF false:VCT + * @return + */ + private Point deepening(int depth, int maxDepth, boolean isVcf) { + this.situationCacheMap = new HashMap<>(2048); + Point point = null; + for (; depth <= maxDepth; depth += 2) { + this.statistics.setVcxDepth(depth); + if (this.aiConfig.isDebug()) { + this.pathStack = new Stack<>(); + } + + point = vcx(0, depth, isVcf); + if (point != null) { + if (this.aiConfig.isDebug()) { + StringBuilder pathOut = new StringBuilder(); + pathOut.append(isVcf ? "VCF" : "VCT").append("路径:"); + this.bestPathStack.forEach(p -> pathOut.append(p).append(" ")); + //TouhouLittleMaid.LOGGER.debug(pathOut.toString()); + } + + // 算杀成功 + this.statistics.setVcx(isVcf ? 1 : 2); + break; + } + } + + return point; + } + + /** + * 算杀(VCF、VCT) + * + * @param type 当前走棋方 1.AI 2.玩家 + * @param depth 搜索深度 + * @param isVcf true:VCF false:VCT + * @return + */ + private Point vcx(int type, int depth, boolean isVcf) { + // 先查看当前局面是否存在缓存 + SituationCache situationCache = this.situationCacheMap.get(this.hashcode); + if (situationCache != null && situationCache.depth >= depth) { + if (this.aiConfig.isDebug()) { + this.statistics.incrCacheHits(); + } + + // 缓存存在,且记录的搜索深度比当前深度要深或相等,则直接返回记录的局面点位 + return situationCache.point; + } + + if (depth == 0) { + // 算杀失败 + return null; + } + + boolean isRoot = type == 0; + if (isRoot) { + type = this.ai; + } + boolean isAI = type == this.ai; + + Point best = null; + List pointList = getVcxPoints(type, isVcf); + for (Point point : pointList) { + if (this.aiConfig.isDebug()) { + this.statistics.incrNodes(); + this.pathStack.push(point); + } + + if (point.score >= RiskScore.HIGH_RISK.score) { + if (this.aiConfig.isDebug()) { + if (isAI) { + this.bestPathStack = (Stack) this.pathStack.clone(); + } + this.pathStack.pop(); + } + + // 已经可以形成必胜棋型了,如果是AI落子,就返回当前节点,否则返回空 + return isAI ? point : null; + } + + putChess(point); + best = vcx(3 - type, depth - 1, isVcf); + revokeChess(point); + + if (this.aiConfig.isDebug()) { + this.pathStack.pop(); + } + + if (best == null) { + if (isAI) { + // AI还没找到可以算杀成功的棋子,继续找... + continue; + } + + // 对手拦截成功了,直接返回空出去,表示算杀失败了 + return null; + } + + // 记录当前节点 + best = point; + + if (isAI) { + // AI已经找到可以算杀的棋子了,同层后续的节点都不用看了 + break; + } + } + + // 缓存当前局面点位 + this.situationCacheMap.put(this.hashcode, new SituationCache(best, depth)); + + return best; + } + + /** + * 获取算杀落子点 + * + * @param type 当前走棋方 0.根节点表示AI走棋 1.黑棋 2.白棋 + * @param isVcf 是否是连续冲四 + * @return + */ + private List getVcxPoints(int type, boolean isVcf) { + boolean isAI = type == this.ai; + // 进攻点列表 + List attackPointList = new ArrayList<>(); + // 防守点列表 + List defensePointList = new ArrayList<>(); + // VCX列表 + List vcxPointList = new ArrayList<>(); + + // 局势是否危险 + boolean isDanger = false; + for (int i = 0; i < this.cols; i++) { + for (int j = 0; j < this.rows; j++) { + if (this.chessData[i][j] != 0) { + // 该处已有棋子,跳过 + continue; + } + + // 考虑自己的落子情况 + Point point = new Point(i, j, type); + int score = evaluate(point); + if (score >= ChessModel.LIANWU.score) { + // 自己可以连五,直接返回 + return Collections.singletonList(point); + } + + if (isDanger) { + // 存在危险,继续找自己可以连五的棋子 + continue; + } + + // 考虑对手的落子情况 + Point foePoint = new Point(i, j, 3 - type); + int foeScore = evaluate(foePoint); + if (foeScore >= ChessModel.LIANWU.score) { + // 对手连五了,局势很危险!!赶紧找自己可以连五的点位,不行就防守 + isDanger = true; + defensePointList.clear(); + defensePointList.add(point); + continue; + } + + // 看看自己有没有大于等于中风险分值的点位 + if (score >= RiskScore.MEDIUM_RISK.score) { + attackPointList.add(point); + continue; + } + + if (isAI) { + // AI才进行VCX + if (checkSituation(point, ChessModel.CHONGSI)) { + // 不论是VCF还是VCT,AI都可先选择冲四 + vcxPointList.add(point); + } else if (!isVcf && checkSituation(point, ChessModel.HUOSAN)) { + // 记录VCT点位 + vcxPointList.add(point); + } + } else { + // 对手需防守VCT + if (!isVcf) { + if (checkSituation(point, ChessModel.CHONGSI) || foeScore >= ChessModel.HUOSI.score) { + // 选择冲四或防守对方的活四 + defensePointList.add(point); + } + } + } + } + } + + List pointList = new ArrayList<>(); + // 没风险就进攻 + if (!isDanger) { + // 优先强进攻 + if (!attackPointList.isEmpty()) { + // 按分数从大到小排序 + attackPointList.sort((p1, p2) -> { + if (p1.score == p2.score) { + return 0; + } else if (p1.score > p2.score) { + return -1; + } + return 1; + }); + + if (isAI) { + // AI有强进攻点位了,就不用考虑后面的点位了 + return attackPointList; + } + + // 对手可以选择进攻和防守 + pointList.addAll(attackPointList); + } + + // VCX进攻 + if (!vcxPointList.isEmpty()) { + pointList.addAll(vcxPointList); + } + } + + if (!defensePointList.isEmpty()) { + // 进行防守 + if (isAI) { + // AI优先选择进攻,把防守点位放后面 + pointList.addAll(defensePointList); + } else { + // 对手优先考虑防守,把防守点位放前面 + pointList.addAll(0, defensePointList); + } + } + + return pointList; + } + + /** + * 下棋子 + * + * @param point 棋子 + */ + private void putChess(Point point) { + this.chessData[point.x][point.y] = point.type; + // 计算当前局面的hashcode + calculateHashCode(point); + } + + /** + * 撤销下的棋子 + * + * @param point 棋子 + */ + private void revokeChess(Point point) { + this.chessData[point.x][point.y] = 0; + // 计算当前局面的hashcode + calculateHashCode(point); + } + + /** + * 计算当前落子局面的hashcode + * + * @param point + * @return + */ + private long calculateHashCode(Point point) { + int x = point.x; + int y = point.y; + this.hashcode ^= point.type == 1 ? BLACK_ZOBRIST[x][y] : WHITE_ZOBRIST[x][y]; + return this.hashcode; + } + + /** + * 获取最佳下棋点位(只考虑一步) + * + * @param point 对手下棋点位 + * @return + */ + private Point getBestPoint(Point point) { + Point best = null; + // 初始分值为最小 + int score = -INFINITY; + + /* 遍历所有能下棋的点位,评估各个点位的分值,选择分值最大的点位 */ + for (int i = 0; i < this.cols; i++) { + for (int j = 0; j < this.rows; j++) { + if (this.chessData[i][j] != 0) { + // 该点已有棋子,跳过 + continue; + } + + Point p = new Point(i, j, this.ai); + // 该点得分 = AI落子得分 * 进攻系数 + 对手落子得分 + int val = Math.round(evaluate(p) * this.attack) + evaluate(new Point(i, j, 3 - this.ai)); + // 选择得分最高的点位 + if (val > score) { + // 最高分被刷新 + score = val; + // 更新最佳点位 + best = p; + } + } + } + + return best; + } + + /** + * 极大极小值搜索 + * + * @param type 当前走棋方 0.根节点表示AI走棋 1.AI 2.玩家 + * @param depth 搜索深度 + * @return + */ + private int minimax(int type, int depth) { + // 是否是根节点 + boolean isRoot = type == 0; + if (isRoot) { + // 根节点是AI走棋 + type = this.ai; + } + + // 当前是否是AI走棋 + boolean isAI = type == this.ai; + // 当前分值, + int score; + if (isAI) { + // AI因为要选择最高分,所以初始化一个难以到达的低分 + score = -INFINITY; + } else { + // 对手要选择最低分,所以初始化一个难以到达的高分 + score = INFINITY; + } + + // 到达叶子结点 + if (depth == 0) { + /* + * 评估每棵博弈树的叶子结点的局势 + * 比如:depth=2时,表示从AI开始走两步棋之后的局势评估,AI(走第一步) -> 玩家(走第二步),然后对局势进行评估 + * 注意:局势评估是以AI角度进行的,分值越大对AI越有利,对玩家越不利 + */ + return evaluateAll(); + } + + for (int i = 0; i < this.cols; i++) { + for (int j = 0; j < this.rows; j++) { + if (this.chessData[i][j] != 0) { + // 该处已有棋子,跳过 + continue; + } + + /* 模拟 AI -> 玩家 交替落子 */ + Point p = new Point(i, j, type); + // 落子 + putChess(p); + // 递归生成博弈树,并评估叶子结点的局势获取分值 + int curScore = minimax(3 - type, depth - 1); + // 撤销落子 + revokeChess(p); + + if (isAI) { + // AI要选对自己最有利的节点(分最高的) + if (curScore > score) { + // 最高值被刷新 + score = curScore; + if (isRoot) { + // 根节点处更新AI最好的棋位 + this.bestPoint = p; + } + } + } else { + // 对手要选对AI最不利的节点(分最低的) + if (curScore < score) { + // 最低值被刷新 + score = curScore; + } + } + } + } + + return score; + } + + /** + * 极大极小值搜索、AlphaBeta剪枝 + * + * @param type 当前走棋方 0.根节点表示AI走棋 1.AI 2.玩家 + * @param depth 搜索深度 + * @param alpha 极大值 + * @param beta 极小值 + * @return + */ + private int minimax(int type, int depth, int alpha, int beta) { + // 是否是根节点 + boolean isRoot = type == 0; + if (isRoot) { + // 根节点是AI走棋 + type = this.ai; + } + + // 当前是否是AI走棋 + boolean isAI = type == this.ai; + + // 先查看当前局面是否存在缓存 + SituationCache situationCache = this.situationCacheMap.get(this.hashcode); + if (situationCache != null && situationCache.depth >= depth) { + if (this.aiConfig.isDebug()) { + this.statistics.incrCacheHits(); + } + + // 缓存存在,且记录的搜索深度比当前深度要深或相等,则直接返回记录的分值 + return situationCache.score; + } + + // 到达叶子结点 + if (depth == 0) { + /** + * 评估每棵博弈树的叶子结点的局势 + * 比如:depth=2时,表示从AI开始走两步棋之后的局势评估,AI(走第一步) -> 玩家(走第二步),然后对局势进行评估 + * 注意:局势评估是以AI角度进行的,分值越大对AI越有利,对玩家越不利 + */ + return evaluateAll(); + } + + // 启发式搜索 + List pointList = getHeuristicPoints(type); + if (isRoot && pointList.size() == 1) { + if (this.aiConfig.isDebug()) { + this.statistics.incrNodes(); + } + + // 只有一个落子点,直接返回就好 + this.bestPoint = pointList.get(0); + return this.bestPoint.score; + } + + // 记录选择的最好落子点 + List bestPointList = new ArrayList<>(); + for (Point point : pointList) { + if (this.aiConfig.isDebug()) { + this.statistics.incrNodes(); + } + + if (point.score >= ChessModel.LIANWU.score) { + // 落子到这里就赢了,如果是AI落的子就返回最高分,否则返回最低分 + point.score = isAI ? INFINITY - 1 : -INFINITY + 1; + } else { + /* 模拟 AI -> 玩家 交替落子 */ + // 落子 + putChess(point); + // 递归生成博弈树,并评估叶子结点的局势 + point.score = minimax(3 - type, depth - 1, alpha, beta); + // 撤销落子 + revokeChess(point); + } + + if (isAI) { + // AI要选对自己最有利的节点(分最高的) + if (point.score >= alpha) { + if (isRoot) { + if (point.score > alpha) { + // 找到了更好的落子点,将之前选择的落子点清空 + bestPointList.clear(); + } + // 记录该落子点 + bestPointList.add(point); + } + + // 最高值被刷新,更新alpha值 + alpha = point.score; + } + } else { + // 对手要选对AI最不利的节点(分最低的) + if (point.score < beta) { + // 最低值被刷新,更新beta值 + beta = point.score; + } + } + + if (alpha >= beta) { + if (this.aiConfig.isDebug()) { + this.statistics.incrCuts(); + } + + /* + AlphaBeta剪枝 + + 解释: + AI当前最大分数为:alpha 搜索区间 (alpha, +∞] + 对手当前最小分数为:beta 搜索区间 [-∞, beta) + + 因为对手要选择分数小于beta的分支,AI要从对手给的分支里面选最大的分支,这个最大的分支要和当前的分支(alpha)做比较, + 现在alpha都比beta大了,下面搜索给出的分支也都是小于alpha的,所以搜索下去没有意义,剪掉提高搜索效率。 + */ + break; + } + } + + if (isRoot) { + // 如果有多个落子点,则通过getBestPoint方法选择一个最好的 + this.bestPoint = bestPointList.size() > 1 ? getBestPoint(bestPointList) : bestPointList.get(0); + } + + int score = isAI ? alpha : beta; + // 缓存当前局面分值 + this.situationCacheMap.put(this.hashcode, new SituationCache(score, depth)); + + return score; + } + + /** + * 启发式获取落子点位 + * + * @param type 当前走棋方 1.黑棋 2.白棋 + * @return + */ + private List getHeuristicPoints(int type) { + // 落子点上限 + int max = this.aiConfig.getMaxNodes(); + // 高优先级落子点 + List highPriorityPointList = new ArrayList<>(); + // 低优先级落子点 + List lowPriorityPointList = new ArrayList<>(); + // 候补落子点 + List alternatePointList = new ArrayList<>(); + // 杀棋点 + List killPointList = new ArrayList<>(); + + // 局势危险等级 0.不危险 1.有危险 2.很危险 + int dangerLevel = 0; + for (int i = 0; i < this.cols; i++) { + for (int j = 0; j < this.rows; j++) { + if (this.chessData[i][j] != 0) { + // 该处已有棋子,跳过 + continue; + } + + // 考虑自己的落子情况 + Point point = new Point(i, j, type); + int score = evaluate(point); + if (score >= ChessModel.LIANWU.score) { + // 优先检查自己连五的情况,如果该落子点可以形成连五,则结束循环,直接返回 + return Collections.singletonList(point); + } + + if (dangerLevel == 2) { + // 局势很危险,只检查自己可以连五的落子点 + continue; + } + + if (score >= RiskScore.MEDIUM_RISK.score) { + // 必杀棋 + killPointList.add(point); + } + + // 考虑对手的落子情况 + Point foePoint = new Point(i, j, 3 - type); + int foeScore = evaluate(foePoint); + // 当前局势危险等级 + int level = 0; + if (foeScore >= ChessModel.LIANWU.score) { + // 对手连五了,局势很危险!! + level = 2; + } else if (foeScore >= RiskScore.MEDIUM_RISK.score) { + // 对手有活四、双冲四、冲四活三的点位了,局势有危险! + level = 1; + } + + if (level > 0) { + // 当前局势存在危险 + if (dangerLevel < level) { + // 危险升级 + dangerLevel = level; + // 局势危险等级如果上升,则清空之前选择的高优先级节点,防止AI误入歧途 + highPriorityPointList.clear(); + } + + // 将此节点加入到高优先级队列 + highPriorityPointList.add(point); + } + + if (dangerLevel > 0) { + // 局势有危险,下面的检查逻辑不用走了 + continue; + } + + if (RiskScore.between(score, RiskScore.LOW_RISK, RiskScore.MEDIUM_RISK) || RiskScore.between(foeScore, RiskScore.LOW_RISK, RiskScore.MEDIUM_RISK)) { + // 高优先级落子点:多活三,需考虑对手 + highPriorityPointList.add(point); + continue; + } + + if (highPriorityPointList.isEmpty()) { + if (score >= ChessModel.CHONGSI.score || foeScore >= ChessModel.CHONGSI.score) { + // 低优先级落子点:冲四、活三,需考虑对手 + lowPriorityPointList.add(point); + continue; + } + + if (lowPriorityPointList.isEmpty() && score >= ChessModel.MIANYI.score) { + // 候补落子点:活二、活一、眠三、眠二、眠一,不用考虑对手 + alternatePointList.add(point); + } + } + } + } + + if (dangerLevel < 2 && !killPointList.isEmpty()) { + // 局势不是特别危险,且杀棋队列不为空,直接进攻就好 + return killPointList; + } + + List pointList; + if (highPriorityPointList.isEmpty()) { + // 无高优先级落子点,则判断是否有低优先级落子点 + if (lowPriorityPointList.isEmpty()) { + // 低优先级落子点也没有,就返回候补落子点 + if (alternatePointList.isEmpty()) { + // 候补落子点也没有,就随机取 + return randomPoint(type, 1); + } + + // 打乱一下 + Collections.shuffle(alternatePointList); + // 返回打乱后的候补落子点 + pointList = alternatePointList; + } else { + // 返回低优先级落子点 + pointList = lowPriorityPointList; + } + } else { + // 返回高优先级落子点 + pointList = highPriorityPointList; + } + + // 按分数从大到小排序 + pointList.sort((p1, p2) -> { + if (p1.score == p2.score) { + return 0; + } else if (p1.score > p2.score) { + return -1; + } + return 1; + }); + + // 取最大节点个数 + return pointList.subList(0, Math.min(pointList.size(), max)); + } + + /** + * 迭代加深 minimax 搜索 + * + * @param depth 当前搜索深度 + * @param maxDepth 最大搜索深度 + * @return + */ + private Point deepeningMinimax(int depth, int maxDepth) { + this.situationCacheMap = new HashMap<>(2048); + + Point best = null; + for (; depth <= maxDepth; depth += 2) { + int score = minimax(0, depth, -INFINITY, INFINITY); + best = this.bestPoint; + this.statistics.setPoint(best); + this.statistics.setDepth(depth); + this.statistics.setScore(score); + + if (Math.abs(score) >= INFINITY - 1) { + // 找到最优解了,结束搜索 + break; + } + } + + return best; + } + + /** + * 检查高优先级落子点 + * + * @param point 检查的点位 + * @return + */ + private boolean checkHighPriorityPoint(Point point) { + // 活三数 + int huosanTotal = 0; + // 冲四数 + int chongsiTotal = 0; + // 活二数 + int huoerTotal = 0; + + for (int i = 1; i < 5; i++) { + // 获取当前局势 + String situation = getSituation(point, i); + // 获取当前局势的棋型 + ChessModel chessModel = getChessModel(situation); + + // 棋型统计 + if (chessModel != null) { + switch (chessModel) { + case HUOSI: + return true; + case HUOSAN: + // 活三+1 + huosanTotal++; + break; + case CHONGSI: + // 冲四+1 + chongsiTotal++; + break; + case HUOER: + // 活二+1 + huoerTotal++; + break; + } + } + } + + if (chongsiTotal > 1 || (chongsiTotal > 0 && huosanTotal > 0)) { + // 冲四数大于1、冲四又活三 + return true; + } + if (huosanTotal > 1) { + // 活三数大于1 + return true; + } + if (huosanTotal > 0 && huoerTotal > 0) { + // 活三又活二 + // return true; + } + + return false; + } + + /** + * 从给定的点位列表中获取最佳点位 + * + * @param pointList 点位列表 + * @return + */ + private Point getBestPoint(List pointList) { + Point bestPoint = null; + int bestScore = -INFINITY; + + for (Point point : pointList) { + int score = Math.round(evaluate(point) * this.attack) + evaluate(new Point(point.x, point.y, 3 - point.type)); + if (score > bestScore) { + bestScore = score; + bestPoint = point; + } + } + + return bestPoint; + } + + /** + * 随机获取落子点 + * + * @param type 棋子类型 + * @param num 数量 + * @return + */ + private List randomPoint(int type, int num) { + List pointList = new ArrayList<>(); + for (int i = 0; i < this.cols; i++) { + for (int j = 0; j < this.rows; j++) { + if (this.chessData[i][j] == 0) { + pointList.add(new Point(i, j, type)); + } + } + } + + Collections.shuffle(pointList); + return pointList.subList(0, Math.min(num, pointList.size())); + } + + /** + * 对当前棋位进行评估 + * + * @param point 当前棋位 + * @return + */ + private int evaluate(Point point) { + // 分值 + int score = 0; + // 活三数 + int huosanTotal = 0; + // 冲四数 + int chongsiTotal = 0; + // 统计同一方向既冲四又活三的情况,出现这种情况的优先按活三分计算 + int tfTotal = 0; + + for (int i = 1; i < 5; i++) { + // 获取当前局势 + String situation = getSituation(point, i); + // 获取当前局势的棋型 + ChessModel chessModel = getChessModel(situation); + + // 棋型统计 + if (chessModel != null) { + switch (chessModel) { + case HUOSAN: + // 活三+1 + huosanTotal++; + if (checkSituation(situation, ChessModel.CHONGSI)) { + // 同一方向出现活三,又出现冲四 + tfTotal++; + } + break; + case CHONGSI: + // 冲四+1 + chongsiTotal++; + break; + } + + // 下此步的得分 + score += chessModel.score; + } + } + + if (chongsiTotal > 1 || tfTotal > 1) { + // 冲四数大于1,+高风险评分 + score += RiskScore.HIGH_RISK.score; + } else if (chongsiTotal > 0 && huosanTotal > 0 || tfTotal > 0 && huosanTotal > 1) { + // 冲四又活三,+中风险评分 + score += RiskScore.MEDIUM_RISK.score; + } else if (huosanTotal > 1) { + // 活三数大于1,+低风险评分 + score += RiskScore.LOW_RISK.score; + } + + point.score = score; + return score; + } + + /** + * 以AI角度对当前局势进行评估,分数越大对AI越有利 + * + * @return + */ + private int evaluateAll() { + // AI得分 + int aiScore = 0; + // 对手得分 + int foeScore = 0; + + for (int i = 0; i < this.cols; i++) { + for (int j = 0; j < this.rows; j++) { + int type = this.chessData[i][j]; + if (type == 0) { + // 该点没有棋子,跳过 + continue; + } + + // 评估该棋位分值 + int val = evaluate(new Point(i, j, type)); + if (type == this.ai) { + // 累积AI得分 + aiScore += val; + } else { + // 累积对手得分 + foeScore += val; + } + } + } + + // 该局AI最终得分 = AI得分 * 进攻系数 - 对手得分 + return Math.round(aiScore * this.attack) - foeScore; + } + + /** + * 检查当前落子是否处于某一局势 + * + * @param point 当前棋位 + * @param chessModels 检查的局势 + * @return + */ + private boolean checkSituation(Point point, ChessModel... chessModels) { + // 要检查4个大方向 + for (int i = 1; i < 5; i++) { + String situation = getSituation(point, i); + for (ChessModel chessModel : chessModels) { + if (checkSituation(situation, chessModel)) { + return true; + } + } + } + return false; + } + + /** + * 检查当前局势是否处于某个局势 + * + * @param situation 当前局势 + * @param chessModel 检查的局势 + * @return + */ + private boolean checkSituation(String situation, ChessModel chessModel) { + for (String value : chessModel.values) { + if (situation.contains(value)) { + return true; + } + } + return false; + } + + /** + * 获取当前局势的棋型(按顺序匹配) + *

+ * 如当前局势棋型为:"210111002"(同时包含活三和冲四的棋型) + * 该方法会优先匹配到活三 "011100",然后返回该棋型 + * 满足冲四 "10111" 棋型,但由于顺序问题,将不会返回 + * + * @param situation 当前局势 + * @return + */ + private ChessModel getChessModel(String situation) { + for (ChessModel chessModel : ChessModel.values()) { + for (String value : chessModel.values) { + if (situation.contains(value)) { + return chessModel; + } + } + } + + return null; + } + + /** + * 获取局势分数 + * + * @param situation 局势 + * @return + */ + private int getScore(String situation) { + for (String key : SCORE.keySet()) { + if (situation.contains(key)) { + return SCORE.get(key); + } + } + return 0; + } + + /** + * 获取棋位局势 + * + * @param point 当前棋位 + * @param direction 大方向 1.横 2.纵 3.左斜 4.右斜 + * @return + */ + private String getSituation(Point point, int direction) { + // 下面用到了 relativePoint 函数,根据传入的四个大方向做转换 + direction = direction * 2 - 1; + // 以下是将各个方向的棋子拼接成字符串返回 + StringBuilder sb = new StringBuilder(); + appendChess(sb, point, direction, 4); + appendChess(sb, point, direction, 3); + appendChess(sb, point, direction, 2); + appendChess(sb, point, direction, 1); + sb.append(1); // 当前棋子统一标记为1(黑) + appendChess(sb, point, direction + 1, 1); + appendChess(sb, point, direction + 1, 2); + appendChess(sb, point, direction + 1, 3); + appendChess(sb, point, direction + 1, 4); + return sb.toString(); + } + + /** + * 拼接各个方向的棋子 + *

+ * 由于现有评估模型是对黑棋进行评估 + * 所以,为了方便对局势进行评估,如果当前是白棋方,需要将扫描到的白棋转换为黑棋,黑棋转换为白棋 + * 如:point(x=0,y=0,type=2) 即当前为白棋方 + * 扫描到的某个方向局势为:20212 -> 转换后 -> 10121 + * + * @param sb 字符串容器 + * @param point 当前棋子 + * @param direction 方向 1.左横 2.右横 3.上纵 4.下纵 5.左斜上 6.左斜下 7.右斜上 8.右斜下 + * @param offset 偏移量 + */ + private void appendChess(StringBuilder sb, Point point, int direction, int offset) { + int chess = relativePoint(point, direction, offset); + if (chess > -1) { + if (point.type == 2) { + // 对白棋进行转换 + if (chess > 0) { + // 对棋子颜色进行转换,2->1,1->2 + chess = 3 - chess; + } + } + sb.append(chess); + } + } + + /** + * 获取相对点位棋子 + * + * @param point 当前棋位 + * @param direction 方向 1.左横 2.右横 3.上纵 4.下纵 5.左斜上 6.左斜下 7.右斜上 8.右斜下 + * @param offset 偏移量 + * @return -1:越界 0:空位 1:黑棋 2:白棋 + */ + private int relativePoint(Point point, int direction, int offset) { + int x = point.x, y = point.y; + switch (direction) { + case 1: + x -= offset; + break; + case 2: + x += offset; + break; + case 3: + y -= offset; + break; + case 4: + y += offset; + break; + case 5: + x += offset; + y -= offset; + break; + case 6: + x -= offset; + y += offset; + break; + case 7: + x -= offset; + y -= offset; + break; + case 8: + x += offset; + y += offset; + break; + } + + if (x < 0 || y < 0 || x >= this.cols || y >= this.rows) { + // 越界 + return -1; + } + + // 返回该位置的棋子 + return this.chessData[x][y]; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/block/BlockAltar.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/block/BlockAltar.java index 7e9c3d4bd..460792033 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/block/BlockAltar.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/block/BlockAltar.java @@ -66,7 +66,7 @@ public BlockEntity newBlockEntity(BlockPos pos, BlockState state) { public InteractionResult use(BlockState state, Level worldIn, BlockPos pos, Player player, InteractionHand handIn, BlockHitResult hit) { return this.getAltar(worldIn, pos).filter(altar -> handIn == InteractionHand.MAIN_HAND).map(altar -> { if (player.isShiftKeyDown() || player.getMainHandItem().isEmpty()) { - takeOutItem(worldIn, pos, altar, player); + takeOutItem(worldIn, altar, player); } else { takeInOrCraft(worldIn, altar, player); } @@ -208,10 +208,11 @@ private void restoreStorageBlock(Level worldIn, BlockPos currentPos, PosListData } } - private void takeOutItem(Level world, BlockPos pos, TileEntityAltar altar, Player player) { + private void takeOutItem(Level world, TileEntityAltar altar, Player player) { if (altar.isCanPlaceItem()) { if (!altar.handler.getStackInSlot(0).isEmpty()) { - Block.popResource(world, pos.offset(0, 1, 0), altar.handler.extractItem(0, 1, false)); + ItemStack extractItem = altar.handler.extractItem(0, 1, false); + ItemHandlerHelper.giveItemToPlayer(player, extractItem); altarCraft(world, altar, player); } } diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/block/BlockBookshelf.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/block/BlockBookshelf.java new file mode 100644 index 000000000..84dc1b18f --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/block/BlockBookshelf.java @@ -0,0 +1,49 @@ +package com.github.tartaricacid.touhoulittlemaid.block; + +import com.github.tartaricacid.touhoulittlemaid.entity.favorability.Type; +import com.github.tartaricacid.touhoulittlemaid.tileentity.TileEntityBookshelf; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.pathfinder.PathComputationType; +import net.minecraft.world.phys.Vec3; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.VoxelShape; +import org.jetbrains.annotations.Nullable; + +public class BlockBookshelf extends BlockJoy { + public static final VoxelShape SHAPE = Block.box(1, 0, 1, 15, 5, 15); + + @Override + protected Vec3 sitPosition() { + return new Vec3(0.5, 0.375, 0.5); + } + + @Override + protected int sitYRot() { + return -90; + } + + @Override + protected String getTypeName() { + return Type.BOOKSHELF.getTypeName(); + } + + @Nullable + @Override + public BlockEntity newBlockEntity(BlockPos pPos, BlockState pState) { + return new TileEntityBookshelf(pPos, pState); + } + + @Override + public VoxelShape getShape(BlockState state, BlockGetter worldIn, BlockPos pos, CollisionContext context) { + return SHAPE; + } + + @Override + public boolean isPathfindable(BlockState state, BlockGetter worldIn, BlockPos pos, PathComputationType type) { + return false; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/block/BlockComputer.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/block/BlockComputer.java new file mode 100644 index 000000000..4322ee775 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/block/BlockComputer.java @@ -0,0 +1,53 @@ +package com.github.tartaricacid.touhoulittlemaid.block; + +import com.github.tartaricacid.touhoulittlemaid.entity.favorability.Type; +import com.github.tartaricacid.touhoulittlemaid.tileentity.TileEntityComputer; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.pathfinder.PathComputationType; +import net.minecraft.world.phys.Vec3; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.Shapes; +import net.minecraft.world.phys.shapes.VoxelShape; +import org.jetbrains.annotations.Nullable; + +public class BlockComputer extends BlockJoy { + public static final VoxelShape SHAPE = Shapes.or(Block.box(0, 0, 0, 16, 3, 16), + Block.box(6, 3, 6, 10, 9, 10), + Block.box(1, 9, 1, 15, 12, 15), + Block.box(0, 12, 0, 16, 14, 16)); + + @Override + protected Vec3 sitPosition() { + return new Vec3(0.5, 1, 0.5); + } + + @Override + protected int sitYRot() { + return 180; + } + + @Override + protected String getTypeName() { + return Type.COMPUTER.getTypeName(); + } + + @Nullable + @Override + public BlockEntity newBlockEntity(BlockPos pPos, BlockState pState) { + return new TileEntityComputer(pPos, pState); + } + + @Override + public boolean isPathfindable(BlockState state, BlockGetter worldIn, BlockPos pos, PathComputationType type) { + return false; + } + + @Override + public VoxelShape getShape(BlockState state, BlockGetter worldIn, BlockPos pos, CollisionContext context) { + return SHAPE; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/block/BlockGomoku.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/block/BlockGomoku.java new file mode 100644 index 000000000..dd8dd7346 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/block/BlockGomoku.java @@ -0,0 +1,365 @@ +package com.github.tartaricacid.touhoulittlemaid.block; + +import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; +import com.github.tartaricacid.touhoulittlemaid.api.game.gomoku.Point; +import com.github.tartaricacid.touhoulittlemaid.api.game.gomoku.Statue; +import com.github.tartaricacid.touhoulittlemaid.block.properties.GomokuPart; +import com.github.tartaricacid.touhoulittlemaid.entity.favorability.Type; +import com.github.tartaricacid.touhoulittlemaid.entity.item.EntitySit; +import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; +import com.github.tartaricacid.touhoulittlemaid.init.InitItems; +import com.github.tartaricacid.touhoulittlemaid.init.InitSounds; +import com.github.tartaricacid.touhoulittlemaid.network.NetworkHandler; +import com.github.tartaricacid.touhoulittlemaid.network.message.ChessDataToClientMessage; +import com.github.tartaricacid.touhoulittlemaid.tileentity.TileEntityGomoku; +import com.github.tartaricacid.touhoulittlemaid.tileentity.TileEntityJoy; +import net.minecraft.Util; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.Vec3i; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.Explosion; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.RenderShape; +import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.block.state.properties.BlockStateProperties; +import net.minecraft.world.level.block.state.properties.DirectionProperty; +import net.minecraft.world.level.block.state.properties.EnumProperty; +import net.minecraft.world.level.material.Material; +import net.minecraft.world.level.pathfinder.PathComputationType; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.Vec3; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.Shapes; +import net.minecraft.world.phys.shapes.VoxelShape; + +import javax.annotation.Nullable; + +public class BlockGomoku extends BlockJoy { + public static final EnumProperty PART = EnumProperty.create("part", GomokuPart.class); + public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING; + public static final VoxelShape LEFT_UP = Block.box(8, 0, 8, 16, 2, 16); + public static final VoxelShape LEFT_UP_WITH_BOX = Shapes.or(LEFT_UP, Block.box(11, 0, 2, 16, 4, 7)); + public static final VoxelShape UP = Block.box(0, 0, 8, 16, 2, 16); + public static final VoxelShape RIGHT_UP = Block.box(0, 0, 8, 8, 2, 16); + public static final VoxelShape RIGHT_UP_WITH_BOX = Shapes.or(RIGHT_UP, Block.box(9, 0, 11, 14, 4, 16)); + public static final VoxelShape LEFT_CENTER = Block.box(8, 0, 0, 16, 2, 16); + public static final VoxelShape CENTER = Block.box(0, 0, 0, 16, 2, 16); + public static final VoxelShape RIGHT_CENTER = Block.box(0, 0, 0, 8, 2, 16); + public static final VoxelShape LEFT_DOWN = Block.box(8, 0, 0, 16, 2, 8); + public static final VoxelShape LEFT_DOWN_WITH_BOX = Shapes.or(LEFT_DOWN, Block.box(2, 0, 0, 7, 4, 5)); + public static final VoxelShape DOWN = Block.box(0, 0, 0, 16, 2, 8); + public static final VoxelShape RIGHT_DOWN = Block.box(0, 0, 0, 8, 2, 8); + public static final VoxelShape RIGHT_DOWN_WITH_BOX = Shapes.or(RIGHT_DOWN, Block.box(0, 0, 9, 5, 4, 14)); + + public BlockGomoku() { + super(Properties.of(Material.WOOD).sound(SoundType.WOOD).strength(2.0F, 3.0F).noOcclusion()); + this.registerDefaultState(this.stateDefinition.any().setValue(PART, GomokuPart.CENTER).setValue(FACING, Direction.NORTH)); + } + + @Override + protected Vec3 sitPosition() { + return Vec3.ZERO; + } + + @Override + protected String getTypeName() { + return Type.GOMOKU.getTypeName(); + } + + @Override + protected int sitYRot() { + return 0; + } + + private static void handleGomokuRemove(Level world, BlockPos pos, BlockState state) { + if (!world.isClientSide) { + GomokuPart part = state.getValue(PART); + BlockPos centerPos = pos.subtract(new Vec3i(part.getPosX(), 0, part.getPosY())); + BlockEntity te = world.getBlockEntity(centerPos); + popResource(world, centerPos, InitItems.GOMOKU.get().getDefaultInstance()); + if (te instanceof TileEntityGomoku) { + for (int i = -1; i < 2; i++) { + for (int j = -1; j < 2; j++) { + world.setBlockAndUpdate(centerPos.offset(i, 0, j), Blocks.AIR.defaultBlockState()); + } + } + } + } + } + + @Nullable + private static int[] getChessPos(double x, double y, GomokuPart part) { + switch (part) { + case LEFT_UP -> { + return getData(x, y, 0.505, 0.505, 0.54, 0.54, 0, 0); + } + case UP -> { + return getData(x, y, 0.037, 0.505, 0.08, 0.54, 4, 0); + } + case RIGHT_UP -> { + return getData(x, y, -0.037, 0.505, -0.01, 0.54, 11, 0); + } + case LEFT_CENTER -> { + return getData(x, y, 0.505, 0.037, 0.54, 0.07, 0, 4); + } + case CENTER -> { + return getData(x, y, 0.037, 0.037, 0.08, 0.07, 4, 4); + } + case RIGHT_CENTER -> { + return getData(x, y, -0.037, 0.037, -0.01, 0.07, 11, 4); + } + case LEFT_DOWN -> { + return getData(x, y, 0.505, 0, 0.54, 0, 0, 11); + } + case DOWN -> { + return getData(x, y, 0.037, 0, 0.08, 0, 4, 11); + } + case RIGHT_DOWN -> { + return getData(x, y, -0.037, 0, -0.01, 0, 11, 11); + } + default -> { + return null; + } + } + } + + private static boolean isClickChessBox(double x, double y, GomokuPart part, Direction direction) { + if (direction.getAxis() == Direction.Axis.Z) { + if (part == GomokuPart.RIGHT_UP) { + return 0.5625 <= x && x <= 0.875 && 0.6875 <= y && y <= 1; + } + if (part == GomokuPart.LEFT_DOWN) { + return 0.125 <= x && x <= 0.4375 && 0 <= y && y <= 0.3125; + } + } + if (direction.getAxis() == Direction.Axis.X) { + if (part == GomokuPart.LEFT_UP) { + return 0.6875 <= x && x <= 1 && 0.125 <= y && y <= 0.4375; + } + if (part == GomokuPart.RIGHT_DOWN) { + return 0 <= x && x <= 0.3125 && 0.5625 <= y && y <= 0.875; + } + } + return false; + } + + @Nullable + private static int[] getData(double x, double y, double xOffset, double yOffset, double xStartOffset, double yStartOffset, int xIndexOffset, int yIndexOffset) { + int xIndex = (int) ((x - xOffset) / 0.1316); + int yIndex = (int) ((y - yOffset) / 0.1316); + double xStart = xStartOffset + xIndex * 0.1316; + double xEnd = xStart + 0.07; + double yStart = yStartOffset + yIndex * 0.1316; + double yEnd = yStart + 0.07; + xIndex += xIndexOffset; + yIndex += yIndexOffset; + boolean checkIndex = 0 <= xIndex && xIndex <= 14 && 0 <= yIndex && yIndex <= 14; + boolean checkClick = xStart < x && x < xEnd && yStart < y && y < yEnd; + if (checkIndex && checkClick) { + return new int[]{xIndex, yIndex}; + } + return null; + } + + @Override + public void playerWillDestroy(Level world, BlockPos pos, BlockState state, Player player) { + handleGomokuRemove(world, pos, state); + super.playerWillDestroy(world, pos, state, player); + } + + @Override + public void onBlockExploded(BlockState state, Level world, BlockPos pos, Explosion explosion) { + handleGomokuRemove(world, pos, state); + super.onBlockExploded(state, world, pos, explosion); + } + + @Nullable + @Override + public BlockState getStateForPlacement(BlockPlaceContext context) { + BlockPos centerPos = context.getClickedPos(); + for (int i = -1; i < 2; i++) { + for (int j = -1; j < 2; j++) { + BlockPos searchPos = centerPos.offset(i, 0, j); + if (!context.getLevel().getBlockState(searchPos).canBeReplaced(context)) { + return null; + } + } + } + return this.defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite()); + } + + @Override + public void setPlacedBy(Level worldIn, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack) { + super.setPlacedBy(worldIn, pos, state, placer, stack); + if (worldIn.isClientSide) { + return; + } + for (int i = -1; i < 2; i++) { + for (int j = -1; j < 2; j++) { + BlockPos searchPos = pos.offset(i, 0, j); + GomokuPart part = GomokuPart.getPartByPos(i, j); + if (part != null && !part.isCenter()) { + worldIn.setBlock(searchPos, state.setValue(PART, part), Block.UPDATE_ALL); + } + } + } + } + + @Override + public InteractionResult use(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) { + if (level instanceof ServerLevel serverLevel && hand == InteractionHand.MAIN_HAND && player.getMainHandItem().isEmpty()) { + GomokuPart part = state.getValue(PART); + BlockPos centerPos = pos.subtract(new Vec3i(part.getPosX(), 0, part.getPosY())); + BlockEntity te = level.getBlockEntity(centerPos); + if (!(te instanceof TileEntityGomoku gomoku)) { + return InteractionResult.FAIL; + } + Vec3 location = hit.getLocation().subtract(pos.getX(), pos.getY(), pos.getZ()); + Direction facing = state.getValue(FACING); + if (isClickChessBox(location.x, location.z, part, facing)) { + level.playSound(null, centerPos, InitSounds.GOMOKU_RESET.get(), SoundSource.BLOCKS, 1.0f, 1.0f); + gomoku.reset(); + gomoku.refresh(); + return InteractionResult.SUCCESS; + } + Entity sitEntity = serverLevel.getEntity(gomoku.getSitId()); + if (sitEntity == null || !sitEntity.isAlive() || !(sitEntity.getFirstPassenger() instanceof EntityMaid)) { + player.sendMessage(new TranslatableComponent("message.touhou_little_maid.gomoku.no_maid"), Util.NIL_UUID); + return InteractionResult.FAIL; + } + if (!gomoku.isPlayerTurn()) { + return InteractionResult.FAIL; + } + int[][] chessData = gomoku.getChessData(); + int[] clickPos = getChessPos(location.x, location.z, part); + if (clickPos == null) { + return InteractionResult.FAIL; + } + Point playerPoint = new Point(clickPos[0], clickPos[1], Point.BLACK); + if (gomoku.isInProgress() && chessData[playerPoint.x][playerPoint.y] == Point.EMPTY) { + gomoku.setChessData(playerPoint.x, playerPoint.y, playerPoint.type); + Statue statue = TouhouLittleMaid.SERVICE.getStatue(chessData, playerPoint); + if (statue == Statue.WIN && sitEntity.getFirstPassenger() instanceof EntityMaid maid && maid.isOwnedBy(player)) { + maid.getFavorabilityManager().apply(Type.GOMOKU_WIN); + } + gomoku.setInProgress(statue == Statue.IN_PROGRESS); + level.playSound(null, pos, InitSounds.GOMOKU.get(), SoundSource.BLOCKS, 1.0f, 0.8F + level.random.nextFloat() * 0.4F); + if (gomoku.isInProgress()) { + gomoku.setPlayerTurn(false); + NetworkHandler.sendToClientPlayer(new ChessDataToClientMessage(centerPos, chessData, playerPoint), player); + } + gomoku.refresh(); + return InteractionResult.SUCCESS; + } + } + return InteractionResult.PASS; + } + + @Override + public void startMaidSit(EntityMaid maid, BlockState state, Level worldIn, BlockPos pos) { + if (worldIn instanceof ServerLevel serverLevel && worldIn.getBlockEntity(pos) instanceof TileEntityJoy joy) { + Entity oldSitEntity = serverLevel.getEntity(joy.getSitId()); + if (oldSitEntity != null && oldSitEntity.isAlive()) { + return; + } + Direction face = state.getValue(FACING).getClockWise(); + Vec3 position = new Vec3(0.5 + face.getStepX() * 1.5, 0.1, 0.5 + face.getStepZ() * 1.5); + Vec3 sitPos = new Vec3(pos.getX() + position.x, pos.getY() + position.y, pos.getZ() + position.z); + EntitySit newSitEntity = new EntitySit(worldIn, sitPos, this.getTypeName()); + newSitEntity.setYRot(face.getOpposite().toYRot() + this.sitYRot()); + worldIn.addFreshEntity(newSitEntity); + joy.setSitId(newSitEntity.getUUID()); + joy.setChanged(); + maid.startRiding(newSitEntity); + } + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder builder) { + builder.add(PART, FACING); + } + + @Nullable + @Override + public BlockEntity newBlockEntity(BlockPos pos, BlockState state) { + if (state.getValue(PART).isCenter()) { + return new TileEntityGomoku(pos, state); + } + return null; + } + + @Override + public RenderShape getRenderShape(BlockState state) { + return RenderShape.ENTITYBLOCK_ANIMATED; + } + + @Override + public VoxelShape getShape(BlockState state, BlockGetter worldIn, BlockPos pos, CollisionContext context) { + switch (state.getValue(PART)) { + case LEFT_UP -> { + if (state.getValue(FACING).getAxis() == Direction.Axis.X) { + return LEFT_UP_WITH_BOX; + } else { + return LEFT_UP; + } + } + case UP -> { + return UP; + } + case RIGHT_UP -> { + if (state.getValue(FACING).getAxis() == Direction.Axis.Z) { + return RIGHT_UP_WITH_BOX; + } else { + return RIGHT_UP; + } + } + case LEFT_CENTER -> { + return LEFT_CENTER; + } + case RIGHT_CENTER -> { + return RIGHT_CENTER; + } + case LEFT_DOWN -> { + if (state.getValue(FACING).getAxis() == Direction.Axis.Z) { + return LEFT_DOWN_WITH_BOX; + } else { + return LEFT_DOWN; + } + } + case DOWN -> { + return DOWN; + } + case RIGHT_DOWN -> { + if (state.getValue(FACING).getAxis() == Direction.Axis.X) { + return RIGHT_DOWN_WITH_BOX; + } else { + return RIGHT_DOWN; + } + } + default -> { + return CENTER; + } + } + } + + @Override + public boolean isPathfindable(BlockState state, BlockGetter worldIn, BlockPos pos, PathComputationType type) { + return false; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/block/BlockJoy.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/block/BlockJoy.java new file mode 100644 index 000000000..7b4d5fb2f --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/block/BlockJoy.java @@ -0,0 +1,114 @@ +package com.github.tartaricacid.touhoulittlemaid.block; + +import com.github.tartaricacid.touhoulittlemaid.entity.item.EntitySit; +import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; +import com.github.tartaricacid.touhoulittlemaid.tileentity.TileEntityJoy; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.BaseEntityBlock; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.RenderShape; +import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.block.state.properties.BlockStateProperties; +import net.minecraft.world.level.block.state.properties.DirectionProperty; +import net.minecraft.world.level.material.Material; +import net.minecraft.world.level.pathfinder.PathComputationType; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.Vec3; + +public abstract class BlockJoy extends BaseEntityBlock { + public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING; + + protected BlockJoy(Properties properties) { + super(properties); + } + + public BlockJoy() { + this(Properties.of(Material.WOOD).sound(SoundType.WOOD).strength(2.0F, 3.0F).noOcclusion()); + this.registerDefaultState(this.stateDefinition.any().setValue(FACING, Direction.NORTH)); + } + + protected abstract Vec3 sitPosition(); + + protected abstract String getTypeName(); + + protected abstract int sitYRot(); + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder builder) { + builder.add(FACING); + } + + @Override + public InteractionResult use(BlockState state, Level worldIn, BlockPos pos, Player playerIn, InteractionHand hand, BlockHitResult hit) { + if (worldIn instanceof ServerLevel serverLevel && playerIn.getItemInHand(hand).isEmpty() && worldIn.getBlockEntity(pos) instanceof TileEntityJoy joy) { + Entity oldSitEntity = serverLevel.getEntity(joy.getSitId()); + if (oldSitEntity != null && oldSitEntity.isAlive()) { + return super.use(state, worldIn, pos, playerIn, hand, hit); + } + Vec3 sitPos = new Vec3(pos.getX() + this.sitPosition().x, pos.getY() + this.sitPosition().y, pos.getZ() + this.sitPosition().z); + EntitySit newSitEntity = new EntitySit(worldIn, sitPos, this.getTypeName()); + newSitEntity.setYRot(state.getValue(FACING).getOpposite().toYRot() + this.sitYRot()); + worldIn.addFreshEntity(newSitEntity); + joy.setSitId(newSitEntity.getUUID()); + joy.setChanged(); + playerIn.startRiding(newSitEntity); + return InteractionResult.SUCCESS; + } + return super.use(state, worldIn, pos, playerIn, hand, hit); + } + + public void startMaidSit(EntityMaid maid, BlockState state, Level worldIn, BlockPos pos) { + if (worldIn instanceof ServerLevel serverLevel && worldIn.getBlockEntity(pos) instanceof TileEntityJoy joy) { + Entity oldSitEntity = serverLevel.getEntity(joy.getSitId()); + if (oldSitEntity != null && oldSitEntity.isAlive()) { + return; + } + Vec3 sitPos = new Vec3(pos.getX() + this.sitPosition().x, pos.getY() + this.sitPosition().y, pos.getZ() + this.sitPosition().z); + EntitySit newSitEntity = new EntitySit(worldIn, sitPos, this.getTypeName()); + newSitEntity.setYRot(state.getValue(FACING).getOpposite().toYRot() + this.sitYRot()); + worldIn.addFreshEntity(newSitEntity); + joy.setSitId(newSitEntity.getUUID()); + joy.setChanged(); + maid.startRiding(newSitEntity); + } + } + + @Override + public void onRemove(BlockState state, Level worldIn, BlockPos pos, BlockState newState, boolean isMoving) { + BlockEntity blockEntity = worldIn.getBlockEntity(pos); + if (blockEntity instanceof TileEntityJoy joy && worldIn instanceof ServerLevel serverLevel) { + Entity entity = serverLevel.getEntity(joy.getSitId()); + if (entity instanceof EntitySit) { + entity.discard(); + } + } + super.onRemove(state, worldIn, pos, newState, isMoving); + } + + @Override + public BlockState getStateForPlacement(BlockPlaceContext context) { + return this.defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite()); + } + + @Override + public boolean isPathfindable(BlockState state, BlockGetter worldIn, BlockPos pos, PathComputationType type) { + return false; + } + + @Override + public RenderShape getRenderShape(BlockState pState) { + return RenderShape.ENTITYBLOCK_ANIMATED; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/block/BlockKeyboard.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/block/BlockKeyboard.java new file mode 100644 index 000000000..94b48a83d --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/block/BlockKeyboard.java @@ -0,0 +1,50 @@ +package com.github.tartaricacid.touhoulittlemaid.block; + +import com.github.tartaricacid.touhoulittlemaid.entity.favorability.Type; +import com.github.tartaricacid.touhoulittlemaid.tileentity.TileEntityKeyboard; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.pathfinder.PathComputationType; +import net.minecraft.world.phys.Vec3; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.VoxelShape; + +import javax.annotation.Nullable; + +public class BlockKeyboard extends BlockJoy { + public static final VoxelShape SHAPE = Block.box(4, 0, 4, 12, 10, 12); + + @Override + protected Vec3 sitPosition() { + return new Vec3(0.5, 0.625, 0.5); + } + + @Override + protected int sitYRot() { + return 0; + } + + @Override + protected String getTypeName() { + return Type.KEYBOARD.getTypeName(); + } + + @Nullable + @Override + public BlockEntity newBlockEntity(BlockPos pPos, BlockState pState) { + return new TileEntityKeyboard(pPos, pState); + } + + @Override + public VoxelShape getShape(BlockState state, BlockGetter worldIn, BlockPos pos, CollisionContext context) { + return SHAPE; + } + + @Override + public boolean isPathfindable(BlockState state, BlockGetter worldIn, BlockPos pos, PathComputationType type) { + return false; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/block/BlockShrine.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/block/BlockShrine.java new file mode 100644 index 000000000..347384828 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/block/BlockShrine.java @@ -0,0 +1,118 @@ +package com.github.tartaricacid.touhoulittlemaid.block; + +import com.github.tartaricacid.touhoulittlemaid.item.ItemFilm; +import com.github.tartaricacid.touhoulittlemaid.tileentity.TileEntityShrine; +import net.minecraft.Util; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.BaseEntityBlock; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.RenderShape; +import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.block.state.properties.BlockStateProperties; +import net.minecraft.world.level.block.state.properties.DirectionProperty; +import net.minecraft.world.level.material.Material; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.Shapes; +import net.minecraft.world.phys.shapes.VoxelShape; +import net.minecraftforge.items.ItemHandlerHelper; +import org.jetbrains.annotations.Nullable; + +public class BlockShrine extends BaseEntityBlock { + public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING; + public static final VoxelShape SHAPE = Shapes.or(Block.box(0, 0, 0, 16, 5, 16), + Block.box(2, 5, 2, 14, 10, 14), + Block.box(4, 10, 4, 12, 16, 12)); + + public BlockShrine() { + super(Properties.of(Material.WOOD).sound(SoundType.WOOD).strength(2.0F, 3.0F).noOcclusion()); + this.registerDefaultState(this.stateDefinition.any().setValue(FACING, Direction.NORTH)); + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder builder) { + builder.add(FACING); + } + + @Override + public InteractionResult use(BlockState state, Level worldIn, BlockPos pos, Player playerIn, InteractionHand hand, BlockHitResult hit) { + if (hand == InteractionHand.MAIN_HAND && worldIn.getBlockEntity(pos) instanceof TileEntityShrine shrine) { + if (playerIn.isShiftKeyDown()) { + if (!shrine.isEmpty()) { + ItemStack storageItem = shrine.extractStorageItem(); + ItemHandlerHelper.giveItemToPlayer(playerIn, storageItem); + return InteractionResult.SUCCESS; + } + return InteractionResult.PASS; + } + if (shrine.isEmpty()) { + if (shrine.canInsert(playerIn.getMainHandItem())) { + shrine.insertStorageItem(ItemHandlerHelper.copyStackWithSize(playerIn.getMainHandItem(), 1)); + playerIn.getMainHandItem().shrink(1); + return InteractionResult.SUCCESS; + } + if (!worldIn.isClientSide) { + playerIn.sendMessage(new TranslatableComponent("message.touhou_little_maid.shrine.not_film"), Util.NIL_UUID); + } + return InteractionResult.PASS; + } + if (playerIn.getMainHandItem().isEmpty()) { + if (playerIn.getHealth() < (playerIn.getMaxHealth() / 2) + 1) { + if (!worldIn.isClientSide) { + playerIn.sendMessage(new TranslatableComponent("message.touhou_little_maid.shrine.health_low"), Util.NIL_UUID); + } + return InteractionResult.FAIL; + } + playerIn.setHealth(0.25f); + ItemStack film = shrine.getStorageItem(); + ItemFilm.filmToMaid(film, worldIn, pos.above(), playerIn); + } + } + return super.use(state, worldIn, pos, playerIn, hand, hit); + } + + @Override + public void onRemove(BlockState state, Level worldIn, BlockPos pos, BlockState newState, boolean isMoving) { + BlockEntity blockEntity = worldIn.getBlockEntity(pos); + if (blockEntity instanceof TileEntityShrine shrine) { + ItemStack storageItem = shrine.extractStorageItem(); + if (!storageItem.isEmpty()) { + Block.popResource(worldIn, pos.offset(0, 1, 0), storageItem); + } + } + super.onRemove(state, worldIn, pos, newState, isMoving); + } + + @Override + public BlockState getStateForPlacement(BlockPlaceContext context) { + return this.defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite()); + } + + @Override + public RenderShape getRenderShape(BlockState pState) { + return RenderShape.ENTITYBLOCK_ANIMATED; + } + + @Nullable + @Override + public BlockEntity newBlockEntity(BlockPos pos, BlockState blockState) { + return new TileEntityShrine(pos, blockState); + } + + @Override + public VoxelShape getShape(BlockState state, BlockGetter worldIn, BlockPos pos, CollisionContext context) { + return SHAPE; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/block/properties/GomokuPart.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/block/properties/GomokuPart.java new file mode 100644 index 000000000..447ae0310 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/block/properties/GomokuPart.java @@ -0,0 +1,58 @@ +package com.github.tartaricacid.touhoulittlemaid.block.properties; + +import net.minecraft.util.StringRepresentable; + +import javax.annotation.Nullable; + +public enum GomokuPart implements StringRepresentable { + LEFT_UP("left_up", -1, -1), + UP("up", 0, -1), + RIGHT_UP("right_up", 1, -1), + LEFT_CENTER("left_center", -1, 0), + CENTER("center", 0, 0), + RIGHT_CENTER("right_center", 1, 0), + LEFT_DOWN("left_down", -1, 1), + DOWN("down", 0, 1), + RIGHT_DOWN("right_down", 1, 1); + + private final String name; + private final int posX; + private final int posY; + + GomokuPart(String name, int posX, int posY) { + this.name = name; + this.posX = posX; + this.posY = posY; + } + + public int getPosX() { + return posX; + } + + public int getPosY() { + return posY; + } + + public String toString() { + return String.format("%s[%d, %d]", this.getSerializedName(), this.posX, this.posY); + } + + @Override + public String getSerializedName() { + return this.name; + } + + public boolean isCenter() { + return this == CENTER; + } + + @Nullable + public static GomokuPart getPartByPos(int x, int y) { + for (GomokuPart part : GomokuPart.values()) { + if (part.getPosX() == x && part.getPosY() == y) { + return part; + } + } + return null; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/AnimationRegister.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/AnimationRegister.java index 3cf705f54..3bfc3f132 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/AnimationRegister.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/gecko/AnimationRegister.java @@ -1,6 +1,8 @@ package com.github.tartaricacid.touhoulittlemaid.client.animation.gecko; import com.github.tartaricacid.touhoulittlemaid.client.entity.GeckoMaidEntity; +import com.github.tartaricacid.touhoulittlemaid.entity.favorability.Type; +import com.github.tartaricacid.touhoulittlemaid.entity.item.EntitySit; import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.builder.ILoopType; import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.event.predicate.AnimationEvent; @@ -31,6 +33,10 @@ public static void registerAnimationState() { register("swim", Priority.HIGHEST, (maid, event) -> maid.isSwimming()); register("boat", Priority.HIGH, (maid, event) -> maid.getVehicle() instanceof Boat); + register("gomoku", Priority.HIGH, (maid, event) -> maid.getVehicle() instanceof EntitySit sit && sit.getJoyType().equals(Type.GOMOKU.getTypeName())); + register("bookshelf", Priority.HIGH, (maid, event) -> maid.getVehicle() instanceof EntitySit sit && sit.getJoyType().equals(Type.BOOKSHELF.getTypeName())); + register("computer", Priority.HIGH, (maid, event) -> maid.getVehicle() instanceof EntitySit sit && sit.getJoyType().equals(Type.COMPUTER.getTypeName())); + register("keyboard", Priority.HIGH, (maid, event) -> maid.getVehicle() instanceof EntitySit sit && sit.getJoyType().equals(Type.KEYBOARD.getTypeName())); register("sit", Priority.HIGH, (maid, event) -> maid.isInSittingPose()); register("chair", Priority.HIGH, (maid, event) -> maid.isPassenger()); @@ -127,7 +133,6 @@ public static void registerVariables() { parser.register(new LazyVariable("tlm.is_begging", MolangUtils.FALSE)); parser.register(new LazyVariable("tlm.is_sitting", MolangUtils.FALSE)); parser.register(new LazyVariable("tlm.has_backpack", MolangUtils.FALSE)); - parser.register(new LazyVariable("tlm.backpack_level", 0)); } public static void setParserValue(AnimationEvent animationEvent, MolangParser parser, EntityModelData data, EntityMaid maid) { @@ -206,7 +211,6 @@ public static void setParserValue(AnimationEvent animationEvent parser.setValue("tlm.is_begging", () -> MolangUtils.booleanToFloat(maid.isBegging())); parser.setValue("tlm.is_sitting", () -> MolangUtils.booleanToFloat(maid.isInSittingPose())); parser.setValue("tlm.has_backpack", () -> MolangUtils.booleanToFloat(maid.hasBackpack())); - parser.setValue("tlm.backpack_level", maid::getBackpackLevel); } private static void register(String animationName, ILoopType loopType, int priority, BiPredicate> predicate) { diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/inner/MaidBaseAnimation.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/inner/MaidBaseAnimation.java index bebf37d82..be502b752 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/inner/MaidBaseAnimation.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/inner/MaidBaseAnimation.java @@ -499,32 +499,11 @@ public void setRotationAngles(float limbSwing, float limbSwingAmount, float ageI }; } + @Deprecated public static IAnimation getStatusBackpackLevel() { return new IAnimation() { @Override public void setRotationAngles(float limbSwing, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch, float scaleFactor, EntityMaid maid, HashMap modelMap) { - ModelRendererWrapper backpackLevelEmpty = modelMap.get("backpackLevelEmpty"); - ModelRendererWrapper backpackLevelSmall = modelMap.get("backpackLevelSmall"); - ModelRendererWrapper backpackLevelMiddle = modelMap.get("backpackLevelMiddle"); - ModelRendererWrapper backpackLevelBig = modelMap.get("backpackLevelBig"); - - int level = maid.getBackpackLevel(); - - if (backpackLevelEmpty != null) { - backpackLevelEmpty.setHidden(level != 0); - } - - if (backpackLevelSmall != null) { - backpackLevelSmall.setHidden(level != 1); - } - - if (backpackLevelMiddle != null) { - backpackLevelMiddle.setHidden(level != 2); - } - - if (backpackLevelBig != null) { - backpackLevelBig.setHidden(level != 3); - } } }; } diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/script/EntityMaidWrapper.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/script/EntityMaidWrapper.java index a89bb6e9e..c4fee97b0 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/script/EntityMaidWrapper.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/animation/script/EntityMaidWrapper.java @@ -148,8 +148,9 @@ public boolean hasBackpack() { } @Override + @Deprecated public int getBackpackLevel() { - return maid.getBackpackLevel(); + return 0; } @Override diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/event/AddBaubleInformationEvent.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/event/AddInformationEvent.java similarity index 71% rename from src/main/java/com/github/tartaricacid/touhoulittlemaid/client/event/AddBaubleInformationEvent.java rename to src/main/java/com/github/tartaricacid/touhoulittlemaid/client/event/AddInformationEvent.java index 9544f72e6..2ad705a02 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/event/AddBaubleInformationEvent.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/event/AddInformationEvent.java @@ -1,26 +1,32 @@ -package com.github.tartaricacid.touhoulittlemaid.client.event; - -import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; -import com.github.tartaricacid.touhoulittlemaid.item.bauble.BaubleManager; -import net.minecraft.ChatFormatting; -import net.minecraft.network.chat.TextComponent; -import net.minecraft.network.chat.TranslatableComponent; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.api.distmarker.OnlyIn; -import net.minecraftforge.event.entity.player.ItemTooltipEvent; -import net.minecraftforge.eventbus.api.EventPriority; -import net.minecraftforge.eventbus.api.SubscribeEvent; -import net.minecraftforge.fml.common.Mod; - -@OnlyIn(Dist.CLIENT) -@Mod.EventBusSubscriber(modid = TouhouLittleMaid.MOD_ID, value = Dist.CLIENT) -public final class AddBaubleInformationEvent { - @SubscribeEvent(priority = EventPriority.LOWEST) - public static void onRenderTooltips(ItemTooltipEvent event) { - if (BaubleManager.getBauble(event.getItemStack()) != null) { - event.getToolTip().add(new TextComponent(" ")); - event.getToolTip().add(new TranslatableComponent("tooltips.touhou_little_maid.bauble.desc")); - event.getToolTip().add(new TranslatableComponent("tooltips.touhou_little_maid.bauble.usage").withStyle(ChatFormatting.GRAY)); - } - } -} +package com.github.tartaricacid.touhoulittlemaid.client.event; + +import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; +import com.github.tartaricacid.touhoulittlemaid.entity.backpack.BackpackManager; +import com.github.tartaricacid.touhoulittlemaid.item.bauble.BaubleManager; +import net.minecraft.ChatFormatting; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import net.minecraftforge.event.entity.player.ItemTooltipEvent; +import net.minecraftforge.eventbus.api.EventPriority; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; + +@OnlyIn(Dist.CLIENT) +@Mod.EventBusSubscriber(modid = TouhouLittleMaid.MOD_ID, value = Dist.CLIENT) +public final class AddInformationEvent { + @SubscribeEvent(priority = EventPriority.LOWEST) + public static void onRenderTooltips(ItemTooltipEvent event) { + if (BaubleManager.getBauble(event.getItemStack()) != null) { + event.getToolTip().add(new TextComponent(" ")); + event.getToolTip().add(new TranslatableComponent("tooltips.touhou_little_maid.bauble.desc")); + event.getToolTip().add(new TranslatableComponent("tooltips.touhou_little_maid.bauble.usage").withStyle(ChatFormatting.GRAY)); + } + if (BackpackManager.findBackpack(event.getItemStack()).isPresent()) { + event.getToolTip().add(new TextComponent(" ")); + event.getToolTip().add(new TranslatableComponent("tooltips.touhou_little_maid.backpack.desc")); + event.getToolTip().add(new TranslatableComponent("tooltips.touhou_little_maid.backpack.usage").withStyle(ChatFormatting.GRAY)); + } + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/event/ScrollRenderEvent.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/event/ScrollRenderEvent.java new file mode 100644 index 000000000..542bd0af6 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/event/ScrollRenderEvent.java @@ -0,0 +1,54 @@ +package com.github.tartaricacid.touhoulittlemaid.client.event; + +import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; +import com.github.tartaricacid.touhoulittlemaid.item.ItemFoxScroll; +import com.github.tartaricacid.touhoulittlemaid.util.RenderHelper; +import com.mojang.blaze3d.systems.RenderSystem; +import net.minecraft.client.Minecraft; +import net.minecraft.core.BlockPos; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.phys.Vec3; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import net.minecraftforge.client.event.RenderLevelLastEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; +import org.apache.commons.lang3.tuple.Pair; + +@OnlyIn(Dist.CLIENT) +@Mod.EventBusSubscriber(modid = TouhouLittleMaid.MOD_ID, value = Dist.CLIENT) +public class ScrollRenderEvent { + @SubscribeEvent + public static void onRenderWorldLastEvent(RenderLevelLastEvent event) { + Minecraft mc = Minecraft.getInstance(); + Player player = mc.player; + if (player != null && player.getMainHandItem().getItem() instanceof ItemFoxScroll) { + Pair info = ItemFoxScroll.getTrackInfo(player.getMainHandItem()); + if (info == null) { + return; + } + String dimension = info.getLeft(); + Vec3 trackVec = new Vec3(info.getRight().getX(), info.getRight().getY(), info.getRight().getZ()); + if (!dimension.equals(player.level.dimension().location().toString())) { + return; + } + Vec3 playerVec = player.position(); + double actualDistance = playerVec.distanceTo(trackVec); + if (actualDistance < 5) { + return; + } + double viewDistance = actualDistance; + double maxRenderDistance = mc.options.renderDistance * 16; + if (actualDistance > maxRenderDistance) { + Vec3 delta = trackVec.subtract(playerVec).normalize(); + trackVec = playerVec.add(delta.x * maxRenderDistance, delta.y * maxRenderDistance, delta.z * maxRenderDistance); + viewDistance = maxRenderDistance; + } + float scale = 0.02f * (((float) viewDistance + 4.0f) / 3.0f); + RenderSystem.disableDepthTest(); + RenderSystem.depthMask(false); + RenderHelper.renderFloatingText(event.getPoseStack(), Math.round(actualDistance) + " m", trackVec, 0xff8800, scale, -17); + RenderHelper.renderFloatingText(event.getPoseStack(), "▼", trackVec, 0xff0000, scale * 1.2f, -5); + } + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/block/MaidBeaconGui.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/block/MaidBeaconGui.java index 051087b45..634f19b01 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/block/MaidBeaconGui.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/block/MaidBeaconGui.java @@ -1,6 +1,7 @@ package com.github.tartaricacid.touhoulittlemaid.client.gui.block; import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; +import com.github.tartaricacid.touhoulittlemaid.capability.PowerCapability; import com.github.tartaricacid.touhoulittlemaid.capability.PowerCapabilityProvider; import com.github.tartaricacid.touhoulittlemaid.client.gui.widget.button.BeaconEffectButton; import com.github.tartaricacid.touhoulittlemaid.network.NetworkHandler; @@ -8,17 +9,19 @@ import com.github.tartaricacid.touhoulittlemaid.network.message.StorageAndTakePowerMessage; import com.github.tartaricacid.touhoulittlemaid.tileentity.TileEntityMaidBeacon; import com.github.tartaricacid.touhoulittlemaid.tileentity.TileEntityMaidBeacon.BeaconEffect; -import com.github.tartaricacid.touhoulittlemaid.util.ShapeDraw; import com.mojang.blaze3d.platform.InputConstants; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.PoseStack; import net.minecraft.ChatFormatting; import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.player.LocalPlayer; import net.minecraft.client.renderer.GameRenderer; import net.minecraft.client.resources.language.I18n; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; import net.minecraft.network.chat.TextComponent; import net.minecraft.network.chat.TranslatableComponent; import net.minecraft.resources.ResourceLocation; @@ -30,8 +33,8 @@ public class MaidBeaconGui extends Screen { private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("0.00"); private final TileEntityMaidBeacon beacon; - protected int imageWidth = 256; - protected int imageHeight = 105; + protected int imageWidth = 300; + protected int imageHeight = 113; protected int leftPos; protected int topPos; private boolean overflowDelete; @@ -49,13 +52,10 @@ protected void init() { this.clearWidgets(); this.leftPos = (this.width - this.imageWidth) / 2; this.topPos = (this.height - this.imageHeight) / 2; - int start = this.leftPos + 16; - int spacing = 26; - int y = this.topPos + 15; - this.addEffectButton(start, spacing, y); + this.addEffectButton(this.leftPos + 146, 26, this.topPos + 19); this.addStorageAndTakeButton(); - this.addRenderableWidget(new Button(leftPos + 7, topPos - 22, 242, 20, getOverflowDeleteButtonText(this.overflowDelete), b -> { + this.addRenderableWidget(new Button(leftPos + 118, topPos + 94, 154, 20, getOverflowDeleteButtonText(this.overflowDelete), b -> { this.overflowDelete = !this.overflowDelete; NetworkHandler.CHANNEL.sendToServer(new SetBeaconOverflowMessage(beacon.getBlockPos(), this.overflowDelete)); this.init(); @@ -63,21 +63,12 @@ protected void init() { } private void addStorageAndTakeButton() { - this.addRenderableWidget(new Button(leftPos + 16, topPos + 48, 62, 20, new TranslatableComponent("gui.touhou_little_maid.maid_beacon.add_one"), b -> { + this.addRenderableWidget(new Button(leftPos + 118, topPos + 72, 76, 20, new TranslatableComponent("gui.touhou_little_maid.maid_beacon.add_one"), b -> { NetworkHandler.CHANNEL.sendToServer(new StorageAndTakePowerMessage(beacon.getBlockPos(), 1, true)); })); - this.addRenderableWidget(new Button(leftPos + 80, topPos + 48, 62, 20, new TranslatableComponent("gui.touhou_little_maid.maid_beacon.add_all"), b -> { - LocalPlayer player = Minecraft.getInstance().player; - if (player != null) { - player.getCapability(PowerCapabilityProvider.POWER_CAP).ifPresent(power -> NetworkHandler.CHANNEL.sendToServer(new StorageAndTakePowerMessage(beacon.getBlockPos(), power.get(), true))); - } - })); - this.addRenderableWidget(new Button(leftPos + 16, topPos + 70, 62, 20, new TranslatableComponent("gui.touhou_little_maid.maid_beacon.min_one"), b -> { + this.addRenderableWidget(new Button(leftPos + 196, topPos + 72, 76, 20, new TranslatableComponent("gui.touhou_little_maid.maid_beacon.min_one"), b -> { NetworkHandler.CHANNEL.sendToServer(new StorageAndTakePowerMessage(beacon.getBlockPos(), 1, false)); })); - this.addRenderableWidget(new Button(leftPos + 80, topPos + 70, 62, 20, new TranslatableComponent("gui.touhou_little_maid.maid_beacon.min_all"), b -> { - NetworkHandler.CHANNEL.sendToServer(new StorageAndTakePowerMessage(beacon.getBlockPos(), beacon.getStoragePower(), false)); - })); } private void addEffectButton(int start, int spacing, int y) { @@ -108,22 +99,27 @@ public void render(PoseStack poseStack, int mouseX, int mouseY, float partialTic this.renderBackground(poseStack); RenderSystem.setShader(GameRenderer::getPositionTexShader); RenderSystem.setShaderTexture(0, BG); - blit(poseStack, leftPos, topPos, 0, 0, imageWidth, imageHeight); + + blit(poseStack, leftPos, topPos + 2, 0, 0, 142, 111); + blit(poseStack, leftPos + 118, topPos + 1, 44, 111, 154, 15); + + blit(poseStack, leftPos + 224, topPos + 44, 44, 126, 12, 12); + blit(poseStack, leftPos + 224, topPos + 58, 44, 138, 12, 12); + + blit(poseStack, leftPos + 146, topPos + 46, 58, 128, 74, 9); + blit(poseStack, leftPos + 146, topPos + 59, 58, 128, 74, 9); + float percent = beacon.getStoragePower() / beacon.getMaxStorage(); + blit(poseStack, leftPos + 146, topPos + 48, 58, 138, (int) (74 * percent), 5); this.renderPlayerPower(poseStack); - drawCenteredString(poseStack, font, I18n.get("gui.touhou_little_maid.maid_beacon.storage_power", DECIMAL_FORMAT.format(beacon.getStoragePower())), leftPos + 198, topPos + 22, 0xffffff); + drawString(poseStack, font, DECIMAL_FORMAT.format(beacon.getStoragePower()), leftPos + 240, topPos + 46, 0xffffff); if (potionIndex == -1) { - drawCenteredString(poseStack, font, I18n.get("gui.touhou_little_maid.maid_beacon.cost_power", DECIMAL_FORMAT.format(0)), leftPos + 198, topPos + 32, 0xffffff); + this.drawCenteredStringNoShadow(poseStack, font, I18n.get("gui.touhou_little_maid.maid_beacon.cost_power", DECIMAL_FORMAT.format(0)), leftPos + 195, topPos + 5, ChatFormatting.DARK_GRAY.getColor()); } else { - drawCenteredString(poseStack, font, new TranslatableComponent("gui.touhou_little_maid.maid_beacon.cost_power", DECIMAL_FORMAT.format(beacon.getEffectCost() * 900)).withStyle(ChatFormatting.RED), leftPos + 198, topPos + 32, 0xffffff); + this.drawCenteredStringNoShadow(poseStack, font, new TranslatableComponent("gui.touhou_little_maid.maid_beacon.cost_power", DECIMAL_FORMAT.format(beacon.getEffectCost() * 900)).withStyle(ChatFormatting.RED), leftPos + 195, topPos + 5, 0xffffff); } - - RenderSystem.setShader(GameRenderer::getPositionColorShader); - ShapeDraw.drawCircle(leftPos + 180, topPos + 70, 20, 64, 0x22066b6d); - ShapeDraw.drawSector(leftPos + 180, topPos + 70, 22, 0, beacon.getStoragePower() / beacon.getMaxStorage() * 2 * Math.PI, 64, 0xff40e4e5); - - drawCenteredString(poseStack, font, String.format("%.2f%%", beacon.getStoragePower() / beacon.getMaxStorage() * 100), leftPos + 223, topPos + 66, 0xffffff); super.render(poseStack, mouseX, mouseY, partialTicks); + this.renderables.stream().filter(b -> b instanceof BeaconEffectButton).forEach(b -> ((BeaconEffectButton) b).renderToolTip(poseStack, this, mouseX, mouseY)); } @Override @@ -145,14 +141,23 @@ private void renderPlayerPower(PoseStack poseStack) { LocalPlayer player = Minecraft.getInstance().player; if (player != null) { player.getCapability(PowerCapabilityProvider.POWER_CAP).ifPresent(power -> { - drawCenteredString(poseStack, font, I18n.get("gui.touhou_little_maid.maid_beacon.player_power", - DECIMAL_FORMAT.format(power.get())), leftPos + 198, topPos + 10 + 2, 0xffffff); + float percent = power.get() / PowerCapability.MAX_POWER; + blit(poseStack, leftPos + 146, topPos + 61, 58, 143, (int) (74 * percent), 5); + drawString(poseStack, font, DECIMAL_FORMAT.format(power.get()), leftPos + 240, topPos + 60, 0xffffff); }); } } - private TranslatableComponent getOverflowDeleteButtonText(boolean overflowDelete) { + private MutableComponent getOverflowDeleteButtonText(boolean overflowDelete) { return overflowDelete ? new TranslatableComponent("gui.touhou_little_maid.maid_beacon.overflow_delete_true") : new TranslatableComponent("gui.touhou_little_maid.maid_beacon.overflow_delete_false"); } + + private void drawCenteredStringNoShadow(PoseStack matrixStack, Font font, String text, int pX, int pY, int color) { + font.draw(matrixStack, text, pX - font.width(text) / 2f, pY, color); + } + + private void drawCenteredStringNoShadow(PoseStack matrixStack, Font font, Component text, int pX, int pY, int color) { + font.draw(matrixStack, text, pX - font.width(text) / 2f, pY, color); + } } diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/entity/maid/AbstractMaidContainerGui.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/entity/maid/AbstractMaidContainerGui.java index 17a62ee4d..9734f3866 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/entity/maid/AbstractMaidContainerGui.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/entity/maid/AbstractMaidContainerGui.java @@ -11,6 +11,7 @@ import com.github.tartaricacid.touhoulittlemaid.client.gui.widget.button.ScheduleButton; import com.github.tartaricacid.touhoulittlemaid.client.gui.widget.button.TaskButton; import com.github.tartaricacid.touhoulittlemaid.client.resource.CustomPackLoader; +import com.github.tartaricacid.touhoulittlemaid.entity.favorability.FavorabilityManager; import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; import com.github.tartaricacid.touhoulittlemaid.entity.task.TaskManager; import com.github.tartaricacid.touhoulittlemaid.inventory.container.AbstractMaidContainer; @@ -190,11 +191,11 @@ protected void renderLabels(PoseStack poseStack, int x, int y) { private void addStateButton() { skin = new ImageButton(leftPos + 62, topPos + 14, 9, 9, 72, 43, 10, BUTTON, (b) -> getMinecraft().setScreen(new MaidModelGui(maid))); - info = new ImageButton(leftPos + 8, topPos + 14, 9, 9, 72, 65, 10, BUTTON, NO_ACTION); sound = new ImageButton(leftPos + 52, topPos + 14, 9, 9, 144, 43, 10, BUTTON, (b) -> getMinecraft().setScreen(new MaidSoundPackGui(maid))); + info = new ImageButton(leftPos + 8, topPos + 14, 9, 9, 72, 65, 10, BUTTON, NO_ACTION); this.addRenderableWidget(skin); - this.addRenderableWidget(info); this.addRenderableWidget(sound); + this.addRenderableWidget(info); } private void addTaskControlButton() { @@ -271,7 +272,7 @@ private List getTaskTooltips(IMaidTask maidTask) { } private void addScheduleButton() { - scheduleButton = new ScheduleButton<>(leftPos + 9, topPos + 177, this); + scheduleButton = new ScheduleButton<>(leftPos + 9, topPos + 187, this); this.addRenderableWidget(scheduleButton); } @@ -284,7 +285,7 @@ private void addTabsButton() { } private void addTaskSwitchButton() { - taskSwitch = new ImageButton(leftPos + 4, topPos + 149, 71, 21, 0, 42, 22, BUTTON, (b) -> { + taskSwitch = new ImageButton(leftPos + 4, topPos + 159, 71, 21, 0, 42, 22, BUTTON, (b) -> { taskListOpen = !taskListOpen; init(); }); @@ -292,7 +293,7 @@ private void addTaskSwitchButton() { } private void addRideButton() { - ride = new StateSwitchingButton(leftPos + 51, topPos + 196, 20, 20, maid.isRideable()) { + ride = new StateSwitchingButton(leftPos + 51, topPos + 206, 20, 20, maid.isRideable()) { @Override public void onClick(double mouseX, double mouseY) { this.isStateTriggered = !this.isStateTriggered; @@ -304,7 +305,7 @@ public void onClick(double mouseX, double mouseY) { } private void addPickButton() { - pick = new StateSwitchingButton(leftPos + 30, topPos + 196, 20, 20, maid.isPickup()) { + pick = new StateSwitchingButton(leftPos + 30, topPos + 206, 20, 20, maid.isPickup()) { @Override public void onClick(double mouseX, double mouseY) { this.isStateTriggered = !this.isStateTriggered; @@ -316,7 +317,7 @@ public void onClick(double mouseX, double mouseY) { } private void addHomeButton() { - home = new StateSwitchingButton(leftPos + 9, topPos + 196, 20, 20, maid.isHomeModeEnable()) { + home = new StateSwitchingButton(leftPos + 9, topPos + 206, 20, 20, maid.isHomeModeEnable()) { @Override public void onClick(double mouseX, double mouseY) { this.isStateTriggered = !this.isStateTriggered; @@ -328,7 +329,7 @@ public void onClick(double mouseX, double mouseY) { } private void addDownloadButton() { - modelDownload = new ImageButton(leftPos + 20, topPos + 217, 41, 20, 0, 86, 20, BUTTON, + modelDownload = new ImageButton(leftPos + 20, topPos + 230, 41, 20, 0, 86, 20, BUTTON, (b) -> { List downloadInfoList; int page = ModelDownloadGui.getCurrentPage(); @@ -352,10 +353,10 @@ private void drawTaskPageCount(PoseStack poseStack) { private void drawCurrentTaskText(PoseStack poseStack) { IMaidTask task = maid.getTask(); - itemRenderer.renderGuiItem(task.getIcon(), leftPos + 6, topPos + 151); + itemRenderer.renderGuiItem(task.getIcon(), leftPos + 6, topPos + 161); List splitTexts = font.split(task.getName(), 42); if (!splitTexts.isEmpty()) { - font.draw(poseStack, splitTexts.get(0), leftPos + 28, topPos + 155, 0x333333); + font.draw(poseStack, splitTexts.get(0), leftPos + 28, topPos + 165, 0x333333); } } @@ -455,11 +456,21 @@ private void drawBaseInfoGui(PoseStack poseStack) { blit(poseStack, leftPos + 29, topPos + 137, 2, 28, (int) (43 * percent), 5); getMinecraft().font.draw(poseStack, String.format("%d", count), leftPos + 15, topPos + 136, ChatFormatting.DARK_GRAY.getColor()); } + { + RenderSystem.setShader(GameRenderer::getPositionTexShader); + RenderSystem.setShaderTexture(0, SIDE); + blit(poseStack, leftPos + 5, topPos + 146, 27, 0, 9, 9); + blit(poseStack, leftPos + 27, topPos + 146, 0, 9, 47, 9); + FavorabilityManager manager = maid.getFavorabilityManager(); + double percent = manager.getLevelPercent(); + blit(poseStack, leftPos + 29, topPos + 148, 2, 33, (int) (43 * percent), 5); + getMinecraft().font.draw(poseStack, String.format("%d", manager.getLevel()), leftPos + 15, topPos + 147, ChatFormatting.DARK_GRAY.getColor()); + } RenderSystem.setShader(GameRenderer::getPositionTexShader); RenderSystem.setShaderTexture(0, SIDE); blit(poseStack, leftPos + 94, topPos + 7, 107, 0, 149, 21); - blit(poseStack, leftPos + 6, topPos + 168, 0, 47, 67, 25); + blit(poseStack, leftPos + 6, topPos + 178, 0, 47, 67, 25); } @Override diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/entity/maid/MaidMainContainerGui.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/entity/maid/MaidMainContainerGui.java deleted file mode 100644 index 0a6705339..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/entity/maid/MaidMainContainerGui.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.github.tartaricacid.touhoulittlemaid.client.gui.entity.maid; - -import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; -import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; -import com.github.tartaricacid.touhoulittlemaid.inventory.container.MaidMainContainer; -import com.github.tartaricacid.touhoulittlemaid.item.BackpackLevel; -import com.mojang.blaze3d.systems.RenderSystem; -import com.mojang.blaze3d.vertex.PoseStack; -import net.minecraft.client.renderer.GameRenderer; -import net.minecraft.network.chat.Component; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.entity.player.Inventory; - -public class MaidMainContainerGui extends AbstractMaidContainerGui { - private static final ResourceLocation BACKPACK = new ResourceLocation(TouhouLittleMaid.MOD_ID, "textures/gui/maid_gui_backpack.png"); - private final EntityMaid maid; - - public MaidMainContainerGui(MaidMainContainer screenContainer, Inventory inv, Component titleIn) { - super(screenContainer, inv, titleIn); - this.imageHeight = 256; - this.imageWidth = 256; - this.maid = menu.getMaid(); - } - - @Override - protected void renderBg(PoseStack matrixStack, float partialTicks, int x, int y) { - super.renderBg(matrixStack, partialTicks, x, y); - this.drawBackpackGui(matrixStack); - } - - private void drawBackpackGui(PoseStack matrixStack) { - RenderSystem.setShader(GameRenderer::getPositionTexShader); - RenderSystem.setShaderTexture(0, BACKPACK); - blit(matrixStack, leftPos + 85, topPos + 36, 0, 0, 165, 122); - - int level = maid.getBackpackLevel(); - if (level < BackpackLevel.SMALL) { - fill(matrixStack, leftPos + 142, topPos + 58, leftPos + 250, topPos + 76, 0xaa222222); - blit(matrixStack, leftPos + 190, topPos + 62, 165, 0, 11, 11); - } - if (level < BackpackLevel.MIDDLE) { - fill(matrixStack, leftPos + 142, topPos + 81, leftPos + 250, topPos + 117, 0xaa222222); - blit(matrixStack, leftPos + 190, topPos + 92, 165, 0, 11, 11); - } - if (level < BackpackLevel.BIG) { - fill(matrixStack, leftPos + 142, topPos + 122, leftPos + 250, topPos + 158, 0xaa222222); - blit(matrixStack, leftPos + 190, topPos + 133, 165, 0, 11, 11); - } - } -} \ No newline at end of file diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/entity/maid/MaidTabs.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/entity/maid/MaidTabs.java index c8f4e26dc..40b9a4eb2 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/entity/maid/MaidTabs.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/entity/maid/MaidTabs.java @@ -1,5 +1,6 @@ package com.github.tartaricacid.touhoulittlemaid.client.gui.entity.maid; +import com.github.tartaricacid.touhoulittlemaid.client.gui.entity.maid.backpack.IBackpackContainerScreen; import com.github.tartaricacid.touhoulittlemaid.client.gui.widget.button.MaidTabButton; import com.github.tartaricacid.touhoulittlemaid.entity.passive.TabIndex; import com.github.tartaricacid.touhoulittlemaid.inventory.container.AbstractMaidContainer; @@ -20,7 +21,7 @@ public MaidTabs(int entityId, int leftPos, int topPos) { public MaidTabButton[] getTabs(AbstractMaidContainerGui screen) { MaidTabButton main = new MaidTabButton(leftPos + 94, topPos + 5, 107, (b) -> NetworkHandler.CHANNEL.sendToServer(new ToggleTabMessage(entityId, TabIndex.MAIN))); - if (screen instanceof MaidMainContainerGui) { + if (screen instanceof IBackpackContainerScreen) { main.active = false; } diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/entity/maid/backpack/BigBackpackContainerScreen.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/entity/maid/backpack/BigBackpackContainerScreen.java new file mode 100644 index 000000000..1de3e72b9 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/entity/maid/backpack/BigBackpackContainerScreen.java @@ -0,0 +1,32 @@ +package com.github.tartaricacid.touhoulittlemaid.client.gui.entity.maid.backpack; + +import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; +import com.github.tartaricacid.touhoulittlemaid.client.gui.entity.maid.AbstractMaidContainerGui; +import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; +import com.github.tartaricacid.touhoulittlemaid.inventory.container.backpack.BigBackpackContainer; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.client.renderer.GameRenderer; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.player.Inventory; + +public class BigBackpackContainerScreen extends AbstractMaidContainerGui implements IBackpackContainerScreen { + private static final ResourceLocation BACKPACK = new ResourceLocation(TouhouLittleMaid.MOD_ID, "textures/gui/maid_gui_backpack.png"); + private final EntityMaid maid; + + public BigBackpackContainerScreen(BigBackpackContainer container, Inventory inv, Component titleIn) { + super(container, inv, titleIn); + this.imageHeight = 256; + this.imageWidth = 256; + this.maid = menu.getMaid(); + } + + @Override + protected void renderBg(PoseStack poseStack, float partialTicks, int x, int y) { + super.renderBg(poseStack, partialTicks, x, y); + RenderSystem.setShader(GameRenderer::getPositionTexShader); + RenderSystem.setShaderTexture(0, BACKPACK); + blit(poseStack, leftPos + 85, topPos + 36, 0, 0, 165, 128); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/entity/maid/backpack/CraftingTableBackpackContainerScreen.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/entity/maid/backpack/CraftingTableBackpackContainerScreen.java new file mode 100644 index 000000000..d1b4c9a2c --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/entity/maid/backpack/CraftingTableBackpackContainerScreen.java @@ -0,0 +1,32 @@ +package com.github.tartaricacid.touhoulittlemaid.client.gui.entity.maid.backpack; + +import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; +import com.github.tartaricacid.touhoulittlemaid.client.gui.entity.maid.AbstractMaidContainerGui; +import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; +import com.github.tartaricacid.touhoulittlemaid.inventory.container.backpack.CraftingTableBackpackContainer; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.client.renderer.GameRenderer; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.player.Inventory; + +public class CraftingTableBackpackContainerScreen extends AbstractMaidContainerGui implements IBackpackContainerScreen { + private static final ResourceLocation BACKPACK = new ResourceLocation(TouhouLittleMaid.MOD_ID, "textures/gui/maid_crafting_table.png"); + private final EntityMaid maid; + + public CraftingTableBackpackContainerScreen(CraftingTableBackpackContainer container, Inventory inv, Component titleIn) { + super(container, inv, titleIn); + this.imageHeight = 256; + this.imageWidth = 256; + this.maid = menu.getMaid(); + } + + @Override + protected void renderBg(PoseStack poseStack, float partialTicks, int x, int y) { + super.renderBg(poseStack, partialTicks, x, y); + RenderSystem.setShader(GameRenderer::getPositionTexShader); + RenderSystem.setShaderTexture(0, BACKPACK); + blit(poseStack, leftPos + 85, topPos + 36, 0, 0, 165, 128); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/entity/maid/backpack/EmptyBackpackContainerScreen.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/entity/maid/backpack/EmptyBackpackContainerScreen.java new file mode 100644 index 000000000..f13c0c9fc --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/entity/maid/backpack/EmptyBackpackContainerScreen.java @@ -0,0 +1,38 @@ +package com.github.tartaricacid.touhoulittlemaid.client.gui.entity.maid.backpack; + +import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; +import com.github.tartaricacid.touhoulittlemaid.client.gui.entity.maid.AbstractMaidContainerGui; +import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; +import com.github.tartaricacid.touhoulittlemaid.inventory.container.backpack.EmptyBackpackContainer; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.client.renderer.GameRenderer; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.player.Inventory; + +public class EmptyBackpackContainerScreen extends AbstractMaidContainerGui implements IBackpackContainerScreen { + private static final ResourceLocation BACKPACK = new ResourceLocation(TouhouLittleMaid.MOD_ID, "textures/gui/maid_gui_backpack.png"); + private final EntityMaid maid; + + public EmptyBackpackContainerScreen(EmptyBackpackContainer container, Inventory inv, Component titleIn) { + super(container, inv, titleIn); + this.imageHeight = 256; + this.imageWidth = 256; + this.maid = menu.getMaid(); + } + + @Override + protected void renderBg(PoseStack poseStack, float partialTicks, int x, int y) { + super.renderBg(poseStack, partialTicks, x, y); + RenderSystem.setShader(GameRenderer::getPositionTexShader); + RenderSystem.setShaderTexture(0, BACKPACK); + blit(poseStack, leftPos + 85, topPos + 36, 0, 0, 165, 128); + fill(poseStack, leftPos + 142, topPos + 58, leftPos + 250, topPos + 76, 0xaa222222); + blit(poseStack, leftPos + 190, topPos + 62, 165, 0, 11, 11); + fill(poseStack, leftPos + 142, topPos + 81, leftPos + 250, topPos + 117, 0xaa222222); + blit(poseStack, leftPos + 190, topPos + 92, 165, 0, 11, 11); + fill(poseStack, leftPos + 142, topPos + 122, leftPos + 250, topPos + 158, 0xaa222222); + blit(poseStack, leftPos + 190, topPos + 133, 165, 0, 11, 11); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/entity/maid/backpack/EnderChestBackpackContainerScreen.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/entity/maid/backpack/EnderChestBackpackContainerScreen.java new file mode 100644 index 000000000..48e5c043b --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/entity/maid/backpack/EnderChestBackpackContainerScreen.java @@ -0,0 +1,32 @@ +package com.github.tartaricacid.touhoulittlemaid.client.gui.entity.maid.backpack; + +import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; +import com.github.tartaricacid.touhoulittlemaid.client.gui.entity.maid.AbstractMaidContainerGui; +import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; +import com.github.tartaricacid.touhoulittlemaid.inventory.container.backpack.EnderChestBackpackContainer; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.client.renderer.GameRenderer; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.player.Inventory; + +public class EnderChestBackpackContainerScreen extends AbstractMaidContainerGui implements IBackpackContainerScreen { + private static final ResourceLocation BACKPACK = new ResourceLocation(TouhouLittleMaid.MOD_ID, "textures/gui/maid_ender_chest.png"); + private final EntityMaid maid; + + public EnderChestBackpackContainerScreen(EnderChestBackpackContainer container, Inventory inv, Component titleIn) { + super(container, inv, titleIn); + this.imageHeight = 256; + this.imageWidth = 256; + this.maid = menu.getMaid(); + } + + @Override + protected void renderBg(PoseStack poseStack, float partialTicks, int x, int y) { + super.renderBg(poseStack, partialTicks, x, y); + RenderSystem.setShader(GameRenderer::getPositionTexShader); + RenderSystem.setShaderTexture(0, BACKPACK); + blit(poseStack, leftPos + 85, topPos + 36, 0, 0, 165, 128); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/entity/maid/backpack/FurnaceBackpackContainerScreen.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/entity/maid/backpack/FurnaceBackpackContainerScreen.java new file mode 100644 index 000000000..8d3b51b98 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/entity/maid/backpack/FurnaceBackpackContainerScreen.java @@ -0,0 +1,35 @@ +package com.github.tartaricacid.touhoulittlemaid.client.gui.entity.maid.backpack; + +import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; +import com.github.tartaricacid.touhoulittlemaid.client.gui.entity.maid.AbstractMaidContainerGui; +import com.github.tartaricacid.touhoulittlemaid.inventory.container.backpack.FurnaceBackpackContainer; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.client.renderer.GameRenderer; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.player.Inventory; + +public class FurnaceBackpackContainerScreen extends AbstractMaidContainerGui implements IBackpackContainerScreen { + private static final ResourceLocation BACKPACK = new ResourceLocation(TouhouLittleMaid.MOD_ID, "textures/gui/maid_furnace.png"); + + public FurnaceBackpackContainerScreen(FurnaceBackpackContainer container, Inventory inv, Component titleIn) { + super(container, inv, titleIn); + this.imageHeight = 256; + this.imageWidth = 256; + } + + @Override + protected void renderBg(PoseStack poseStack, float partialTicks, int x, int y) { + super.renderBg(poseStack, partialTicks, x, y); + RenderSystem.setShader(GameRenderer::getPositionTexShader); + RenderSystem.setShaderTexture(0, BACKPACK); + blit(poseStack, leftPos + 85, topPos + 36, 0, 0, 165, 128); + if (this.menu.isLit()) { + int litProgress = this.menu.getLitProgress(); + blit(poseStack, leftPos + 161, topPos + 122 + 12 - litProgress, 165, 12 - litProgress, 14, litProgress + 1); + } + int burnProgress = this.menu.getBurnProgress(); + blit(poseStack, leftPos + 184, topPos + 120, 165, 14, burnProgress + 1, 16); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/entity/maid/backpack/IBackpackContainerScreen.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/entity/maid/backpack/IBackpackContainerScreen.java new file mode 100644 index 000000000..2d1e013ef --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/entity/maid/backpack/IBackpackContainerScreen.java @@ -0,0 +1,4 @@ +package com.github.tartaricacid.touhoulittlemaid.client.gui.entity.maid.backpack; + +public interface IBackpackContainerScreen { +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/entity/maid/backpack/MiddleBackpackContainerScreen.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/entity/maid/backpack/MiddleBackpackContainerScreen.java new file mode 100644 index 000000000..7775259fe --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/entity/maid/backpack/MiddleBackpackContainerScreen.java @@ -0,0 +1,34 @@ +package com.github.tartaricacid.touhoulittlemaid.client.gui.entity.maid.backpack; + +import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; +import com.github.tartaricacid.touhoulittlemaid.client.gui.entity.maid.AbstractMaidContainerGui; +import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; +import com.github.tartaricacid.touhoulittlemaid.inventory.container.backpack.MiddleBackpackContainer; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.client.renderer.GameRenderer; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.player.Inventory; + +public class MiddleBackpackContainerScreen extends AbstractMaidContainerGui implements IBackpackContainerScreen { + private static final ResourceLocation BACKPACK = new ResourceLocation(TouhouLittleMaid.MOD_ID, "textures/gui/maid_gui_backpack.png"); + private final EntityMaid maid; + + public MiddleBackpackContainerScreen(MiddleBackpackContainer container, Inventory inv, Component titleIn) { + super(container, inv, titleIn); + this.imageHeight = 256; + this.imageWidth = 256; + this.maid = menu.getMaid(); + } + + @Override + protected void renderBg(PoseStack poseStack, float partialTicks, int x, int y) { + super.renderBg(poseStack, partialTicks, x, y); + RenderSystem.setShader(GameRenderer::getPositionTexShader); + RenderSystem.setShaderTexture(0, BACKPACK); + blit(poseStack, leftPos + 85, topPos + 36, 0, 0, 165, 128); + fill(poseStack, leftPos + 142, topPos + 122, leftPos + 250, topPos + 158, 0xaa222222); + blit(poseStack, leftPos + 190, topPos + 133, 165, 0, 11, 11); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/entity/maid/backpack/SmallBackpackContainerScreen.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/entity/maid/backpack/SmallBackpackContainerScreen.java new file mode 100644 index 000000000..0b9bf8cea --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/entity/maid/backpack/SmallBackpackContainerScreen.java @@ -0,0 +1,36 @@ +package com.github.tartaricacid.touhoulittlemaid.client.gui.entity.maid.backpack; + +import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; +import com.github.tartaricacid.touhoulittlemaid.client.gui.entity.maid.AbstractMaidContainerGui; +import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; +import com.github.tartaricacid.touhoulittlemaid.inventory.container.backpack.SmallBackpackContainer; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.client.renderer.GameRenderer; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.player.Inventory; + +public class SmallBackpackContainerScreen extends AbstractMaidContainerGui implements IBackpackContainerScreen { + private static final ResourceLocation BACKPACK = new ResourceLocation(TouhouLittleMaid.MOD_ID, "textures/gui/maid_gui_backpack.png"); + private final EntityMaid maid; + + public SmallBackpackContainerScreen(SmallBackpackContainer container, Inventory inv, Component titleIn) { + super(container, inv, titleIn); + this.imageHeight = 256; + this.imageWidth = 256; + this.maid = menu.getMaid(); + } + + @Override + protected void renderBg(PoseStack poseStack, float partialTicks, int x, int y) { + super.renderBg(poseStack, partialTicks, x, y); + RenderSystem.setShader(GameRenderer::getPositionTexShader); + RenderSystem.setShaderTexture(0, BACKPACK); + blit(poseStack, leftPos + 85, topPos + 36, 0, 0, 165, 128); + fill(poseStack, leftPos + 142, topPos + 81, leftPos + 250, topPos + 117, 0xaa222222); + blit(poseStack, leftPos + 190, topPos + 92, 165, 0, 11, 11); + fill(poseStack, leftPos + 142, topPos + 122, leftPos + 250, topPos + 158, 0xaa222222); + blit(poseStack, leftPos + 190, topPos + 133, 165, 0, 11, 11); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/entity/maid/backpack/package-info.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/entity/maid/backpack/package-info.java new file mode 100644 index 000000000..150ce87f0 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/entity/maid/backpack/package-info.java @@ -0,0 +1,7 @@ +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +package com.github.tartaricacid.touhoulittlemaid.client.gui.entity.maid.backpack; + +import net.minecraft.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/item/FoxScrollScreen.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/item/FoxScrollScreen.java new file mode 100644 index 000000000..4cac224f3 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/item/FoxScrollScreen.java @@ -0,0 +1,156 @@ +package com.github.tartaricacid.touhoulittlemaid.client.gui.item; + +import com.github.tartaricacid.touhoulittlemaid.client.gui.widget.button.FlatColorButton; +import com.github.tartaricacid.touhoulittlemaid.network.NetworkHandler; +import com.github.tartaricacid.touhoulittlemaid.network.message.FoxScrollMessage; +import com.github.tartaricacid.touhoulittlemaid.network.message.SetScrollData; +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.ChatFormatting; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.network.chat.TranslatableComponent; +import org.apache.commons.lang3.StringUtils; + +import javax.annotation.Nullable; +import java.util.List; +import java.util.Map; + +public class FoxScrollScreen extends Screen { + private static final int PER_PAGE_COUNT = 5; + private final Map> data; + private int leftPos; + private int topPos; + private String selectDim; + private int page = 0; + + public FoxScrollScreen(Map> data) { + super(new TextComponent("Red Fox Scroll")); + this.data = data; + if (!this.data.isEmpty()) { + this.selectDim = this.data.keySet().stream().findFirst().get(); + } + } + + @Override + protected void init() { + this.clearWidgets(); + this.leftPos = (this.width - 400) / 2; + this.topPos = (this.height - 208) / 2; + this.addDimensionButtons(); + this.addPointButtons(); + } + + private void addPointButtons() { + if (StringUtils.isNotBlank(this.selectDim) && this.data.containsKey(this.selectDim)) { + List scrollData = this.data.get(this.selectDim); + if (scrollData.size() > PER_PAGE_COUNT) { + addRenderableWidget(new FlatColorButton(leftPos + 400 - 20, topPos, 20, 20, new TextComponent("↑"), b -> { + if (this.page > 0) { + this.page--; + this.init(); + } + })); + addRenderableWidget(new FlatColorButton(leftPos + 400 - 20, topPos + 208 - 20, 20, 20, new TextComponent("↓"), b -> { + if (this.page < (scrollData.size() - 1) / PER_PAGE_COUNT) { + this.page++; + this.init(); + } + })); + } + int offsetIn = this.topPos; + for (int i = this.page * PER_PAGE_COUNT; i < this.page * PER_PAGE_COUNT + PER_PAGE_COUNT; i++) { + if (i < scrollData.size()) { + FoxScrollMessage.FoxScrollData info = scrollData.get(i); + this.addRenderableWidget(new FlatColorButton(leftPos + 400 - 90, offsetIn + 11, 60, 20, new TranslatableComponent("gui.touhou_little_maid.fox_scroll.track"), b -> { + NetworkHandler.CHANNEL.sendToServer(new SetScrollData(this.selectDim, info.getPos())); + })); + offsetIn = offsetIn + 42; + } + } + } + } + + private void addDimensionButtons() { + int offset = this.topPos; + for (String dim : this.data.keySet()) { + Component name = new TextComponent(dim); + FlatColorButton dimButton = new FlatColorButton(leftPos, offset, 150, 19, name, b -> { + this.selectDim = dim; + this.page = 0; + this.init(); + }); + if (dim.equals(this.selectDim)) { + dimButton.setSelect(true); + } + this.addRenderableWidget(dimButton); + offset = offset + 21; + } + } + + @Override + public void render(PoseStack poseStack, int pMouseX, int pMouseY, float pPartialTick) { + this.renderBackground(poseStack); + if (this.data.isEmpty()) { + int x = this.width / 2; + int y = this.height / 2 - 5; + drawCenteredString(poseStack, font, new TranslatableComponent("gui.touhou_little_maid.fox_scroll.empty"), x, y, 0xFF0000); + return; + } + this.renderMain(poseStack); + super.render(poseStack, pMouseX, pMouseY, pPartialTick); + } + + private void renderMain(PoseStack poseStack) { + if (StringUtils.isNotBlank(this.selectDim) && this.data.containsKey(this.selectDim)) { + List scrollData = this.data.get(this.selectDim); + boolean inSameDim = this.selectDim.equals(this.getPlayerDimension()); + BlockPos playerPos = this.getPlayerPos(); + int offsetIn = this.topPos; + for (int i = this.page * PER_PAGE_COUNT; i < this.page * PER_PAGE_COUNT + PER_PAGE_COUNT; i++) { + if (i < scrollData.size()) { + FoxScrollMessage.FoxScrollData info = scrollData.get(i); + BlockPos pos = info.getPos(); + Component distanceText; + if (inSameDim) { + int distance = (int) Math.sqrt(playerPos.distSqr(pos)); + distanceText = new TranslatableComponent("gui.touhou_little_maid.fox_scroll.distance.same_dimension", distance); + } else { + distanceText = new TranslatableComponent("gui.touhou_little_maid.fox_scroll.distance.different_dimension"); + } + Component posText = new TranslatableComponent("gui.touhou_little_maid.fox_scroll.position", pos.toShortString()); + fill(poseStack, leftPos + 152, offsetIn, leftPos + 400 - 22, offsetIn + 40, 0xef58626b); + drawString(poseStack, font, info.getName(), leftPos + 160, offsetIn + 4, ChatFormatting.GOLD.getColor()); + font.draw(poseStack, posText, leftPos + 160, offsetIn + 16, ChatFormatting.GRAY.getColor()); + font.draw(poseStack, distanceText, leftPos + 160, offsetIn + 28, ChatFormatting.GRAY.getColor()); + offsetIn = offsetIn + 42; + } + } + if (scrollData.size() > PER_PAGE_COUNT) { + String pageText = String.format("%d/%d", this.page + 1, (scrollData.size() - 1) / PER_PAGE_COUNT + 1); + drawCenteredString(poseStack, font, pageText, leftPos + 400 - 8, topPos + 104 - 5, ChatFormatting.GRAY.getColor()); + } + } + } + + @Override + public boolean isPauseScreen() { + return false; + } + + private BlockPos getPlayerPos() { + if (this.getMinecraft().player != null) { + return this.getMinecraft().player.blockPosition(); + } + return BlockPos.ZERO; + } + + @Nullable + private String getPlayerDimension() { + if (this.getMinecraft().player != null) { + return this.getMinecraft().player.level.dimension().location().toString(); + } + return null; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/widget/button/BeaconEffectButton.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/widget/button/BeaconEffectButton.java index 7913fccd8..f97f5db94 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/widget/button/BeaconEffectButton.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/gui/widget/button/BeaconEffectButton.java @@ -8,9 +8,11 @@ import com.mojang.blaze3d.vertex.PoseStack; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.components.StateSwitchingButton; +import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.renderer.GameRenderer; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; import java.util.function.Consumer; @@ -18,6 +20,7 @@ public class BeaconEffectButton extends StateSwitchingButton { private static final ResourceLocation BG = new ResourceLocation(TouhouLittleMaid.MOD_ID, "textures/gui/maid_beacon.png"); private final TextureAtlasSprite sprite; + private final Component tooltips; private final int potionIndex; private final BlockPos pos; private final Consumer onClick; @@ -26,6 +29,7 @@ public BeaconEffectButton(TileEntityMaidBeacon.BeaconEffect effect, int xIn, int super(xIn, yIn, 22, 22, potionIndex == effect.ordinal()); this.initTextureValues(0, 111, 22, 22, BG); this.sprite = Minecraft.getInstance().getMobEffectTextures().get(effect.getEffect()); + this.tooltips = effect.getEffect().getDisplayName(); this.potionIndex = effect.ordinal(); this.pos = beacon.getBlockPos(); this.onClick = onClick; @@ -45,4 +49,10 @@ public void renderButton(PoseStack poseStack, int mouseX, int mouseY, float part RenderSystem.setShaderTexture(0, this.sprite.atlas().location()); blit(poseStack, this.x + 2, this.y + 2, this.getBlitOffset(), 18, 18, this.sprite); } + + public void renderToolTip(PoseStack poseStack, Screen screen, int pMouseX, int pMouseY) { + if (this.isHovered) { + screen.renderTooltip(poseStack, tooltips, pMouseX, pMouseY); + } + } } diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/init/InitContainerGui.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/init/InitContainerGui.java index 8ac4fc9da..56e2f2037 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/init/InitContainerGui.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/init/InitContainerGui.java @@ -1,11 +1,11 @@ package com.github.tartaricacid.touhoulittlemaid.client.init; import com.github.tartaricacid.touhoulittlemaid.client.gui.entity.maid.MaidConfigContainerGui; -import com.github.tartaricacid.touhoulittlemaid.client.gui.entity.maid.MaidMainContainerGui; +import com.github.tartaricacid.touhoulittlemaid.client.gui.entity.maid.backpack.*; import com.github.tartaricacid.touhoulittlemaid.client.gui.item.WirelessIOContainerGui; import com.github.tartaricacid.touhoulittlemaid.inventory.container.MaidConfigContainer; -import com.github.tartaricacid.touhoulittlemaid.inventory.container.MaidMainContainer; import com.github.tartaricacid.touhoulittlemaid.inventory.container.WirelessIOContainer; +import com.github.tartaricacid.touhoulittlemaid.inventory.container.backpack.*; import net.minecraft.client.gui.screens.MenuScreens; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.eventbus.api.SubscribeEvent; @@ -16,7 +16,14 @@ public final class InitContainerGui { @SubscribeEvent public static void clientSetup(FMLClientSetupEvent evt) { - evt.enqueueWork(() -> MenuScreens.register(MaidMainContainer.TYPE, MaidMainContainerGui::new)); + evt.enqueueWork(() -> MenuScreens.register(EmptyBackpackContainer.TYPE, EmptyBackpackContainerScreen::new)); + evt.enqueueWork(() -> MenuScreens.register(SmallBackpackContainer.TYPE, SmallBackpackContainerScreen::new)); + evt.enqueueWork(() -> MenuScreens.register(MiddleBackpackContainer.TYPE, MiddleBackpackContainerScreen::new)); + evt.enqueueWork(() -> MenuScreens.register(BigBackpackContainer.TYPE, BigBackpackContainerScreen::new)); + evt.enqueueWork(() -> MenuScreens.register(CraftingTableBackpackContainer.TYPE, CraftingTableBackpackContainerScreen::new)); + evt.enqueueWork(() -> MenuScreens.register(EnderChestBackpackContainer.TYPE, EnderChestBackpackContainerScreen::new)); + evt.enqueueWork(() -> MenuScreens.register(FurnaceBackpackContainer.TYPE, FurnaceBackpackContainerScreen::new)); + evt.enqueueWork(() -> MenuScreens.register(MaidConfigContainer.TYPE, MaidConfigContainerGui::new)); evt.enqueueWork(() -> MenuScreens.register(WirelessIOContainer.TYPE, WirelessIOContainerGui::new)); } diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/init/InitEntitiesRender.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/init/InitEntitiesRender.java index 2a5b06bc5..bc85799c7 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/init/InitEntitiesRender.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/init/InitEntitiesRender.java @@ -1,21 +1,15 @@ package com.github.tartaricacid.touhoulittlemaid.client.init; import com.github.tartaricacid.touhoulittlemaid.client.model.*; +import com.github.tartaricacid.touhoulittlemaid.client.model.backpack.*; import com.github.tartaricacid.touhoulittlemaid.client.renderer.entity.*; -import com.github.tartaricacid.touhoulittlemaid.client.renderer.tileentity.TileEntityAltarRenderer; -import com.github.tartaricacid.touhoulittlemaid.client.renderer.tileentity.TileEntityGarageKitRenderer; -import com.github.tartaricacid.touhoulittlemaid.client.renderer.tileentity.TileEntityStatueRenderer; -import com.github.tartaricacid.touhoulittlemaid.entity.item.EntityBox; -import com.github.tartaricacid.touhoulittlemaid.entity.item.EntityChair; -import com.github.tartaricacid.touhoulittlemaid.entity.item.EntityExtinguishingAgent; -import com.github.tartaricacid.touhoulittlemaid.entity.item.EntityPowerPoint; +import com.github.tartaricacid.touhoulittlemaid.client.renderer.tileentity.*; +import com.github.tartaricacid.touhoulittlemaid.entity.item.*; import com.github.tartaricacid.touhoulittlemaid.entity.monster.EntityFairy; import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; import com.github.tartaricacid.touhoulittlemaid.entity.projectile.EntityDanmaku; import com.github.tartaricacid.touhoulittlemaid.entity.projectile.EntityThrowPowerPoint; -import com.github.tartaricacid.touhoulittlemaid.tileentity.TileEntityAltar; -import com.github.tartaricacid.touhoulittlemaid.tileentity.TileEntityGarageKit; -import com.github.tartaricacid.touhoulittlemaid.tileentity.TileEntityStatue; +import com.github.tartaricacid.touhoulittlemaid.tileentity.*; import net.minecraft.client.renderer.blockentity.BlockEntityRenderers; import net.minecraft.client.renderer.entity.EntityRenderers; import net.minecraft.client.renderer.entity.ThrownItemRenderer; @@ -37,11 +31,20 @@ public static void onEntityRenderers(EntityRenderersEvent.RegisterRenderers evt) EntityRenderers.register(EntityExtinguishingAgent.TYPE, EntityExtinguishingAgentRenderer::new); EntityRenderers.register(EntityBox.TYPE, EntityBoxRender::new); EntityRenderers.register(EntityThrowPowerPoint.TYPE, ThrownItemRenderer::new); + EntityRenderers.register(EntityTombstone.TYPE, EntityTombstoneRenderer::new); + EntityRenderers.register(EntitySit.TYPE, EntitySitRenderer::new); + EntityRenderers.register(EntityType.SLIME, EntityYukkuriSlimeRender::new); EntityRenderers.register(EntityType.EXPERIENCE_ORB, ReplaceExperienceOrbRenderer::new); + BlockEntityRenderers.register(TileEntityAltar.TYPE, TileEntityAltarRenderer::new); BlockEntityRenderers.register(TileEntityStatue.TYPE, TileEntityStatueRenderer::new); BlockEntityRenderers.register(TileEntityGarageKit.TYPE, TileEntityGarageKitRenderer::new); + BlockEntityRenderers.register(TileEntityGomoku.TYPE, TileEntityGomokuRenderer::new); + BlockEntityRenderers.register(TileEntityKeyboard.TYPE, TileEntityKeyboardRenderer::new); + BlockEntityRenderers.register(TileEntityBookshelf.TYPE, TileEntityBookshelfRenderer::new); + BlockEntityRenderers.register(TileEntityComputer.TYPE, TileEntityComputerRenderer::new); + BlockEntityRenderers.register(TileEntityShrine.TYPE, TileEntityShrineRenderer::new); } @SubscribeEvent @@ -50,10 +53,20 @@ public static void onRegisterLayers(EntityRenderersEvent.RegisterLayerDefinition event.registerLayerDefinition(DebugFloorModel.LAYER, DebugFloorModel::createBodyLayer); event.registerLayerDefinition(EntityBoxModel.LAYER, EntityBoxModel::createBodyLayer); event.registerLayerDefinition(EntityFairyModel.LAYER, EntityFairyModel::createBodyLayer); - event.registerLayerDefinition(MaidBackpackBigModel.LAYER, MaidBackpackBigModel::createBodyLayer); - event.registerLayerDefinition(MaidBackpackMiddleModel.LAYER, MaidBackpackMiddleModel::createBodyLayer); - event.registerLayerDefinition(MaidBackpackSmallModel.LAYER, MaidBackpackSmallModel::createBodyLayer); + event.registerLayerDefinition(BigBackpackModel.LAYER, BigBackpackModel::createBodyLayer); + event.registerLayerDefinition(MiddleBackpackModel.LAYER, MiddleBackpackModel::createBodyLayer); + event.registerLayerDefinition(SmallBackpackModel.LAYER, SmallBackpackModel::createBodyLayer); event.registerLayerDefinition(StatueBaseModel.LAYER, StatueBaseModel::createBodyLayer); event.registerLayerDefinition(EntityYukkuriModel.LAYER, EntityYukkuriModel::createBodyLayer); + event.registerLayerDefinition(GomokuModel.LAYER, GomokuModel::createBodyLayer); + event.registerLayerDefinition(PieceModel.LAYER, PieceModel::createBodyLayer); + event.registerLayerDefinition(CraftingTableBackpackModel.LAYER, CraftingTableBackpackModel::createBodyLayer); + event.registerLayerDefinition(EnderChestBackpackModel.LAYER, EnderChestBackpackModel::createBodyLayer); + event.registerLayerDefinition(FurnaceBackpackModel.LAYER, FurnaceBackpackModel::createBodyLayer); + event.registerLayerDefinition(TombstoneModel.LAYER, TombstoneModel::createBodyLayer); + event.registerLayerDefinition(KeyboardModel.LAYER, KeyboardModel::createBodyLayer); + event.registerLayerDefinition(BookshelfModel.LAYER, BookshelfModel::createBodyLayer); + event.registerLayerDefinition(ComputerModel.LAYER, ComputerModel::createBodyLayer); + event.registerLayerDefinition(ShrineModel.LAYER, ShrineModel::createBodyLayer); } } diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/BookshelfModel.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/BookshelfModel.java new file mode 100644 index 000000000..950198a31 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/BookshelfModel.java @@ -0,0 +1,857 @@ +package com.github.tartaricacid.touhoulittlemaid.client.model; + +import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import net.minecraft.client.model.EntityModel; +import net.minecraft.client.model.geom.ModelLayerLocation; +import net.minecraft.client.model.geom.ModelPart; +import net.minecraft.client.model.geom.PartPose; +import net.minecraft.client.model.geom.builders.*; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.Entity; + +public class BookshelfModel extends EntityModel { + public static final ModelLayerLocation LAYER = new ModelLayerLocation(new ResourceLocation(TouhouLittleMaid.MOD_ID, "main"), "bookshelf"); + private final ModelPart main; + + public BookshelfModel(ModelPart root) { + this.main = root.getChild("main"); + } + + public static LayerDefinition createBodyLayer() { + MeshDefinition meshdefinition = new MeshDefinition(); + PartDefinition partdefinition = meshdefinition.getRoot(); + + PartDefinition main = partdefinition.addOrReplaceChild("main", CubeListBuilder.create(), PartPose.offset(0.0F, 24.0F, 0.0F)); + + PartDefinition bone = main.addOrReplaceChild("bone", CubeListBuilder.create().texOffs(0, 0).addBox(-8.0F, -42.0F, 23.0F, 32.0F, 41.0F, 1.0F, new CubeDeformation(0.0F)) + .texOffs(0, 58).addBox(-8.0F, -1.0F, 11.0F, 32.0F, 1.0F, 13.0F, new CubeDeformation(0.0F)) + .texOffs(67, 28).addBox(0.0F, -1.0F, -23.25F, 20.0F, 1.0F, 7.0F, new CubeDeformation(0.0F)) + .texOffs(67, 37).addBox(10.0F, -1.0F, -15.75F, 11.0F, 1.0F, 4.0F, new CubeDeformation(0.0F)) + .texOffs(120, 130).addBox(17.0F, -27.0F, -14.25F, 1.0F, 26.0F, 1.0F, new CubeDeformation(0.0F)) + .texOffs(122, 28).addBox(3.0F, -9.0F, -23.25F, 12.0F, 1.0F, 7.0F, new CubeDeformation(0.0F)) + .texOffs(98, 37).addBox(0.0F, -5.0F, -23.25F, 15.0F, 1.0F, 7.0F, new CubeDeformation(0.0F)) + .texOffs(67, 14).addBox(-7.0F, -29.0F, 11.0F, 30.0F, 1.0F, 12.0F, new CubeDeformation(0.0F)) + .texOffs(67, 0).addBox(-7.0F, -15.0F, 11.0F, 30.0F, 1.0F, 12.0F, new CubeDeformation(0.0F)) + .texOffs(27, 73).addBox(23.0F, -42.0F, 11.0F, 1.0F, 41.0F, 12.0F, new CubeDeformation(0.0F)) + .texOffs(0, 73).addBox(-8.0F, -42.0F, 11.0F, 1.0F, 41.0F, 12.0F, new CubeDeformation(0.0F)) + .texOffs(0, 43).addBox(-8.0F, -43.0F, 11.0F, 32.0F, 1.0F, 13.0F, new CubeDeformation(0.0F)), PartPose.offset(-6.0F, 0.0F, 3.0F)); + + PartDefinition bone2 = bone.addOrReplaceChild("bone2", CubeListBuilder.create().texOffs(42, 73).addBox(-12.2744F, -13.2846F, -11.5F, 1.0F, 1.0F, 7.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(12.9651F, 15.1173F, -11.75F, 0.0F, 0.0F, 1.1781F)); + + PartDefinition bone3 = bone2.addOrReplaceChild("bone3", CubeListBuilder.create().texOffs(42, 73).addBox(-17.5879F, 5.3324F, -11.5F, 1.0F, 1.0F, 7.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 1.1781F)); + + PartDefinition bone4 = bone3.addOrReplaceChild("bone4", CubeListBuilder.create().texOffs(42, 73).addBox(-1.8041F, 18.2897F, -11.5F, 1.0F, 1.0F, 7.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 1.1781F)); + + PartDefinition bone5 = bone4.addOrReplaceChild("bone5", CubeListBuilder.create().texOffs(42, 73).addBox(15.2832F, 7.2832F, -11.5F, 1.0F, 1.0F, 7.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(0.0F, 2.0F, 0.0F, 0.0F, 0.0F, 1.1781F)); + + PartDefinition bone9 = bone.addOrReplaceChild("bone9", CubeListBuilder.create(), PartPose.offset(-5.0F, 0.0F, 0.0F)); + + PartDefinition bone6 = bone9.addOrReplaceChild("bone6", CubeListBuilder.create().texOffs(42, 73).mirror().addBox(17.3974F, 1.4975F, -11.5F, 1.0F, 1.0F, 7.0F, new CubeDeformation(0.0F)).mirror(false), PartPose.offsetAndRotation(-3.9651F, 15.1173F, -11.75F, 0.0F, 0.0F, -1.1781F)); + + PartDefinition bone7 = bone6.addOrReplaceChild("bone7", CubeListBuilder.create().texOffs(42, 73).mirror().addBox(5.2742F, 16.6461F, -11.5F, 1.0F, 1.0F, 7.0F, new CubeDeformation(0.0F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.0F, -1.1781F)); + + PartDefinition bone8 = bone7.addOrReplaceChild("bone8", CubeListBuilder.create().texOffs(42, 73).mirror().addBox(-13.978F, 12.1668F, -11.5F, 1.0F, 1.0F, 7.0F, new CubeDeformation(0.0F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.0F, -1.1781F)); + + PartDefinition bone10 = bone9.addOrReplaceChild("bone10", CubeListBuilder.create().texOffs(42, 73).mirror().addBox(-12.1668F, -14.7434F, -11.5F, 1.0F, 1.0F, 7.0F, new CubeDeformation(0.0F)).mirror(false), PartPose.offsetAndRotation(-3.9651F, 11.8827F, -11.75F, 0.0F, 0.0F, 1.1781F)); + + PartDefinition bone11 = bone10.addOrReplaceChild("bone11", CubeListBuilder.create().texOffs(42, 73).mirror().addBox(-17.3532F, 4.9813F, -11.5F, 1.0F, 1.0F, 7.0F, new CubeDeformation(0.0F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 1.1781F)); + + PartDefinition bone12 = bone11.addOrReplaceChild("bone12", CubeListBuilder.create().texOffs(42, 73).mirror().addBox(-1.7321F, 16.3974F, -11.5F, 1.0F, 1.0F, 7.0F, new CubeDeformation(0.0F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 1.1781F)); + + PartDefinition bone13 = bone12.addOrReplaceChild("bone13", CubeListBuilder.create().texOffs(42, 73).mirror().addBox(15.7168F, 7.7168F, -11.5F, 1.0F, 1.0F, 7.0F, new CubeDeformation(0.0F)).mirror(false), PartPose.offsetAndRotation(0.0F, -2.0F, 0.0F, 0.0F, 0.0F, 1.1781F)); + + PartDefinition bone14 = bone.addOrReplaceChild("bone14", CubeListBuilder.create(), PartPose.offset(-1.6259F, -15.2706F, -5.75F)); + + PartDefinition bone15 = bone14.addOrReplaceChild("bone15", CubeListBuilder.create().texOffs(42, 73).addBox(-12.2744F, -13.2846F, -11.5F, 1.0F, 1.0F, 7.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(9.591F, 26.3879F, -6.0F, 0.0F, 0.0F, 1.1781F)); + + PartDefinition bone16 = bone15.addOrReplaceChild("bone16", CubeListBuilder.create().texOffs(42, 73).addBox(-17.5879F, 5.3324F, -11.5F, 1.0F, 1.0F, 7.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 1.1781F)); + + PartDefinition bone17 = bone16.addOrReplaceChild("bone17", CubeListBuilder.create().texOffs(42, 73).addBox(-1.8041F, 18.2897F, -11.5F, 1.0F, 1.0F, 7.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 1.1781F)); + + PartDefinition bone18 = bone14.addOrReplaceChild("bone18", CubeListBuilder.create().texOffs(42, 73).addBox(17.2897F, 0.0387F, -11.5F, 1.0F, 1.0F, 7.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(9.591F, 23.1533F, -6.0F, 0.0F, 0.0F, -1.1781F)); + + PartDefinition bone19 = bone18.addOrReplaceChild("bone19", CubeListBuilder.create().texOffs(42, 73).addBox(5.0395F, 16.295F, -11.5F, 1.0F, 1.0F, 7.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.0F, -1.1781F)); + + PartDefinition bone20 = bone19.addOrReplaceChild("bone20", CubeListBuilder.create().texOffs(42, 73).addBox(-14.0499F, 10.2744F, -11.5F, 1.0F, 1.0F, 7.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.0F, -1.1781F)); + + PartDefinition bone21 = bone20.addOrReplaceChild("bone21", CubeListBuilder.create().texOffs(42, 73).addBox(-16.7168F, -8.2832F, -11.5F, 1.0F, 1.0F, 7.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(0.0F, -2.0F, 0.0F, 0.0F, 0.0F, -1.1781F)); + + PartDefinition bone22 = bone.addOrReplaceChild("bone22", CubeListBuilder.create(), PartPose.offset(5.6259F, -15.2706F, -5.75F)); + + PartDefinition bone26 = bone22.addOrReplaceChild("bone26", CubeListBuilder.create().texOffs(42, 73).mirror().addBox(-12.1668F, -14.7434F, -11.5F, 1.0F, 1.0F, 7.0F, new CubeDeformation(0.0F)).mirror(false), PartPose.offsetAndRotation(-11.591F, 23.1533F, -6.0F, 0.0F, 0.0F, 1.1781F)); + + PartDefinition bone27 = bone26.addOrReplaceChild("bone27", CubeListBuilder.create().texOffs(42, 73).mirror().addBox(-17.3532F, 4.9813F, -11.5F, 1.0F, 1.0F, 7.0F, new CubeDeformation(0.0F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 1.1781F)); + + PartDefinition bone28 = bone27.addOrReplaceChild("bone28", CubeListBuilder.create().texOffs(42, 73).mirror().addBox(-1.7321F, 16.3974F, -11.5F, 1.0F, 1.0F, 7.0F, new CubeDeformation(0.0F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 1.1781F)); + + PartDefinition bone29 = bone28.addOrReplaceChild("bone29", CubeListBuilder.create().texOffs(42, 73).mirror().addBox(15.7168F, 7.7168F, -11.5F, 1.0F, 1.0F, 7.0F, new CubeDeformation(0.0F)).mirror(false), PartPose.offsetAndRotation(0.0F, -2.0F, 0.0F, 0.0F, 0.0F, 1.1781F)); + + PartDefinition bone23 = bone22.addOrReplaceChild("bone23", CubeListBuilder.create().texOffs(0, 43).addBox(18.2136F, -0.344F, -5.5F, 1.0F, 1.0F, 1.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(3.3391F, 4.1533F, -3.0F, 0.0F, 0.0F, -1.1781F)); + + PartDefinition bone24 = bone23.addOrReplaceChild("bone24", CubeListBuilder.create().texOffs(0, 43).addBox(5.7466F, 17.0021F, -5.5F, 1.0F, 1.0F, 1.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.0F, -1.1781F)); + + PartDefinition bone25 = bone24.addOrReplaceChild("bone25", CubeListBuilder.create().texOffs(0, 43).addBox(-14.4326F, 11.1983F, -5.5F, 1.0F, 1.0F, 1.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.0F, -1.1781F)); + + PartDefinition bone30 = bone25.addOrReplaceChild("bone30", CubeListBuilder.create(), PartPose.offsetAndRotation(0.0F, -2.0F, 0.0F, 0.0F, 0.0F, -1.1781F)); + + PartDefinition bone31 = bone30.addOrReplaceChild("bone31", CubeListBuilder.create().texOffs(0, 43).addBox(0.9241F, 18.3795F, -5.5F, 1.0F, 1.0F, 1.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(0.0F, 2.0F, 0.0F, 0.0F, 0.0F, 1.9635F)); + + PartDefinition bone32 = bone31.addOrReplaceChild("bone32", CubeListBuilder.create().texOffs(0, 43).addBox(-18.168F, 8.1939F, -5.5F, 1.0F, 1.0F, 1.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.0F, -1.1781F)); + + PartDefinition bone33 = bone32.addOrReplaceChild("bone33", CubeListBuilder.create().texOffs(0, 43).addBox(-15.4466F, -14.2667F, -5.5F, 1.0F, 1.0F, 1.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.0F, -1.1781F)); + + PartDefinition bone34 = bone33.addOrReplaceChild("bone34", CubeListBuilder.create().texOffs(0, 43).addBox(5.4218F, -18.9651F, -5.5F, 1.0F, 1.0F, 1.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(0.0F, -2.0F, 0.0F, 0.0F, 0.0F, -1.1781F)); + + PartDefinition bone35 = bone34.addOrReplaceChild("bone35", CubeListBuilder.create().texOffs(123, 102).addBox(-0.5768F, -19.7702F, -13.53F, 3.0F, 1.0F, 11.0F, new CubeDeformation(0.0F)) + .texOffs(15, 73).addBox(1.0181F, -19.4269F, -13.0F, 1.0F, 1.0F, 10.0F, new CubeDeformation(-0.2F)) + .texOffs(15, 73).addBox(-0.2319F, -19.4269F, -13.0F, 1.0F, 1.0F, 10.0F, new CubeDeformation(-0.2F)), PartPose.offsetAndRotation(-4.3391F, 0.9301F, 3.0F, 0.0F, 0.0F, 0.3927F)); + + PartDefinition bone202 = bone22.addOrReplaceChild("bone202", CubeListBuilder.create().texOffs(0, 127).addBox(15.0F, -11.4893F, -13.2676F, 2.0F, 10.0F, 10.0F, new CubeDeformation(-0.25F)) + .texOffs(141, 95).addBox(14.5F, -12.9893F, -14.7676F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(54, 90).addBox(15.5F, -12.9893F, -13.7676F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)) + .texOffs(54, 90).addBox(13.5F, -12.9893F, -13.7676F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(-4.1083F, 20.9945F, -6.7269F, 1.3963F, 0.0F, -1.5708F)); + + PartDefinition bone203 = bone202.addOrReplaceChild("bone203", CubeListBuilder.create().texOffs(141, 67).addBox(38.2479F, -23.9662F, -63.8165F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(141, 67).addBox(38.2479F, -23.9662F, -63.8165F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offset(-22.7479F, 10.9769F, 50.0489F)); + + PartDefinition bone204 = bone203.addOrReplaceChild("bone204", CubeListBuilder.create().texOffs(89, 137).addBox(-44.2474F, -23.9662F, -62.1464F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(89, 137).addBox(-44.2474F, -23.9662F, -62.1464F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -1.1781F, 0.0F)); + + PartDefinition bone205 = bone203.addOrReplaceChild("bone205", CubeListBuilder.create().texOffs(89, 137).addBox(-18.1704F, -23.9662F, -73.9654F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(89, 137).addBox(-18.1704F, -23.9662F, -73.9654F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -0.7854F, 0.0F)); + + PartDefinition bone206 = bone203.addOrReplaceChild("bone206", CubeListBuilder.create().texOffs(89, 137).addBox(10.4446F, -23.9662F, -74.9054F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(89, 137).addBox(10.4446F, -23.9662F, -74.9054F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -0.3927F, 0.0F)); + + PartDefinition bone207 = bone202.addOrReplaceChild("bone207", CubeListBuilder.create().texOffs(141, 67).mirror().addBox(40.7411F, -23.9662F, -63.8097F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(141, 67).mirror().addBox(40.7411F, -23.9662F, -63.8097F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offset(-27.2479F, 10.9769F, 50.0489F)); + + PartDefinition bone208 = bone207.addOrReplaceChild("bone208", CubeListBuilder.create().texOffs(89, 137).mirror().addBox(72.6169F, -23.9662F, 13.6041F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(89, 137).mirror().addBox(72.6169F, -23.9662F, 13.6041F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 1.1781F, 0.0F)); + + PartDefinition bone209 = bone207.addOrReplaceChild("bone209", CubeListBuilder.create().texOffs(89, 137).mirror().addBox(73.1405F, -23.9662F, -15.9855F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(89, 137).mirror().addBox(73.1405F, -23.9662F, -15.9855F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.7854F, 0.0F)); + + PartDefinition bone210 = bone207.addOrReplaceChild("bone210", CubeListBuilder.create().texOffs(89, 137).mirror().addBox(62.3008F, -23.9662F, -43.5232F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(89, 137).mirror().addBox(62.3008F, -23.9662F, -43.5232F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.3927F, 0.0F)); + + PartDefinition bone37 = bone.addOrReplaceChild("bone37", CubeListBuilder.create().texOffs(118, 46).addBox(8.9525F, -21.0F, -8.3296F, 4.0F, 10.0F, 10.0F, new CubeDeformation(-0.25F)) + .texOffs(99, 58).addBox(11.4525F, -22.5F, -8.8296F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)) + .texOffs(99, 58).addBox(7.4525F, -22.5F, -8.8296F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)) + .texOffs(25, 131).addBox(8.4525F, -22.5F, -9.8296F, 5.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(-21.25F, 11.0F, 15.75F, 0.0F, 0.3927F, 0.0F)); + + PartDefinition bone38 = bone37.addOrReplaceChild("bone38", CubeListBuilder.create().texOffs(0, 148).addBox(8.9525F, -22.5F, -10.3296F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offset(2.5F, 0.0F, 1.5F)); + + PartDefinition bone41 = bone38.addOrReplaceChild("bone41", CubeListBuilder.create().texOffs(102, 147).addBox(-6.0428F, -22.5F, -14.6124F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -1.1781F, 0.0F)); + + PartDefinition bone40 = bone38.addOrReplaceChild("bone40", CubeListBuilder.create().texOffs(102, 147).addBox(-1.0644F, -22.5F, -15.4294F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -0.7854F, 0.0F)); + + PartDefinition bone39 = bone38.addOrReplaceChild("bone39", CubeListBuilder.create().texOffs(102, 147).addBox(3.8477F, -22.5F, -14.279F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -0.3927F, 0.0F)); + + PartDefinition bone42 = bone37.addOrReplaceChild("bone42", CubeListBuilder.create().texOffs(0, 148).mirror().addBox(9.4457F, -22.5F, -10.3227F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offset(-2.0F, 0.0F, 1.5F)); + + PartDefinition bone43 = bone42.addOrReplaceChild("bone43", CubeListBuilder.create().texOffs(102, 147).mirror().addBox(11.2252F, -22.5F, 5.1595F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 1.1781F, 0.0F)); + + PartDefinition bone44 = bone42.addOrReplaceChild("bone44", CubeListBuilder.create().texOffs(102, 147).mirror().addBox(13.1903F, -22.5F, -0.2938F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.7854F, 0.0F)); + + PartDefinition bone45 = bone42.addOrReplaceChild("bone45", CubeListBuilder.create().texOffs(102, 147).mirror().addBox(12.919F, -22.5F, -6.084F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.3927F, 0.0F)); + + PartDefinition bone127 = bone.addOrReplaceChild("bone127", CubeListBuilder.create(), PartPose.offset(-13.0F, 11.0F, 24.5F)); + + PartDefinition bone100 = bone127.addOrReplaceChild("bone100", CubeListBuilder.create().texOffs(99, 58).addBox(9.0F, -23.5F, -12.5F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)) + .texOffs(99, 58).addBox(5.0F, -23.5F, -12.5F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)) + .texOffs(25, 131).addBox(6.0F, -23.5F, -13.5F, 5.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(118, 46).addBox(6.5F, -22.0F, -12.0F, 4.0F, 10.0F, 10.0F, new CubeDeformation(-0.25F)) + .texOffs(99, 58).addBox(9.0F, -23.5F, -12.5F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)) + .texOffs(99, 58).addBox(5.0F, -23.5F, -12.5F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)) + .texOffs(25, 131).addBox(6.0F, -23.5F, -13.5F, 5.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offset(22.0F, 0.0F, 0.0F)); + + PartDefinition bone101 = bone100.addOrReplaceChild("bone101", CubeListBuilder.create().texOffs(0, 148).addBox(6.5F, -23.5F, -14.0F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(0, 148).addBox(6.5F, -23.5F, -14.0F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offset(2.5F, 0.0F, 1.5F)); + + PartDefinition bone102 = bone101.addOrReplaceChild("bone102", CubeListBuilder.create().texOffs(102, 147).addBox(-10.3723F, -23.5F, -13.7512F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(102, 147).addBox(-10.3723F, -23.5F, -13.7512F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -1.1781F, 0.0F)); + + PartDefinition bone103 = bone101.addOrReplaceChild("bone103", CubeListBuilder.create().texOffs(102, 147).addBox(-5.3939F, -23.5F, -16.2906F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(102, 147).addBox(-5.3939F, -23.5F, -16.2906F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -0.7854F, 0.0F)); + + PartDefinition bone104 = bone101.addOrReplaceChild("bone104", CubeListBuilder.create().texOffs(102, 147).addBox(0.1773F, -23.5F, -16.7315F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(102, 147).addBox(0.1773F, -23.5F, -16.7315F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -0.3927F, 0.0F)); + + PartDefinition bone105 = bone100.addOrReplaceChild("bone105", CubeListBuilder.create().texOffs(0, 148).mirror().addBox(6.9932F, -23.5F, -13.9932F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(0, 148).mirror().addBox(6.9932F, -23.5F, -13.9932F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offset(-2.0F, 0.0F, 1.5F)); + + PartDefinition bone106 = bone105.addOrReplaceChild("bone106", CubeListBuilder.create().texOffs(102, 147).mirror().addBox(13.6777F, -23.5F, 1.4891F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(102, 147).mirror().addBox(13.6777F, -23.5F, 1.4891F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 1.1781F, 0.0F)); + + PartDefinition bone107 = bone105.addOrReplaceChild("bone107", CubeListBuilder.create().texOffs(102, 147).mirror().addBox(14.0515F, -23.5F, -4.6233F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(102, 147).mirror().addBox(14.0515F, -23.5F, -4.6233F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.7854F, 0.0F)); + + PartDefinition bone108 = bone105.addOrReplaceChild("bone108", CubeListBuilder.create().texOffs(102, 147).mirror().addBox(12.0578F, -23.5F, -10.4135F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(102, 147).mirror().addBox(12.0578F, -23.5F, -10.4135F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.3927F, 0.0F)); + + PartDefinition bone109 = bone127.addOrReplaceChild("bone109", CubeListBuilder.create().texOffs(54, 90).addBox(1.8854F, -25.2674F, -12.5F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)) + .texOffs(54, 90).addBox(-1.1146F, -25.2674F, -12.5F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)) + .texOffs(59, 134).addBox(-0.1146F, -25.2674F, -13.5F, 4.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(124, 74).addBox(0.3854F, -23.7674F, -12.0F, 3.0F, 10.0F, 10.0F, new CubeDeformation(-0.25F)) + .texOffs(54, 90).addBox(1.8854F, -25.2674F, -12.5F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)) + .texOffs(54, 90).addBox(-1.1146F, -25.2674F, -12.5F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)) + .texOffs(59, 134).addBox(-0.1146F, -25.2674F, -13.5F, 4.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(15.75F, -0.5F, 0.0F, 0.0F, 0.0F, 0.3927F)); + + PartDefinition bone110 = bone109.addOrReplaceChild("bone110", CubeListBuilder.create().texOffs(141, 67).addBox(-0.6146F, -25.2674F, -14.0F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(141, 67).addBox(-0.6146F, -25.2674F, -14.0F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offset(2.5F, 0.0F, 1.5F)); + + PartDefinition bone111 = bone110.addOrReplaceChild("bone111", CubeListBuilder.create().texOffs(89, 137).addBox(-13.095F, -25.2674F, -7.1782F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(89, 137).addBox(-13.095F, -25.2674F, -7.1782F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -1.1781F, 0.0F)); + + PartDefinition bone112 = bone110.addOrReplaceChild("bone112", CubeListBuilder.create().texOffs(89, 137).addBox(-10.4247F, -25.2674F, -11.2598F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(89, 137).addBox(-10.4247F, -25.2674F, -11.2598F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -0.7854F, 0.0F)); + + PartDefinition bone113 = bone110.addOrReplaceChild("bone113", CubeListBuilder.create().texOffs(89, 137).addBox(-6.3957F, -25.2674F, -14.0089F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(89, 137).addBox(-6.3957F, -25.2674F, -14.0089F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -0.3927F, 0.0F)); + + PartDefinition bone114 = bone109.addOrReplaceChild("bone114", CubeListBuilder.create().texOffs(141, 67).mirror().addBox(0.8786F, -25.2674F, -13.9932F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(141, 67).mirror().addBox(0.8786F, -25.2674F, -13.9932F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offset(-2.0F, 0.0F, 1.5F)); + + PartDefinition bone115 = bone114.addOrReplaceChild("bone115", CubeListBuilder.create().texOffs(89, 137).mirror().addBox(11.3377F, -25.2674F, -4.16F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(89, 137).mirror().addBox(11.3377F, -25.2674F, -4.16F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 1.1781F, 0.0F)); + + PartDefinition bone116 = bone114.addOrReplaceChild("bone116", CubeListBuilder.create().texOffs(89, 137).mirror().addBox(9.7279F, -25.2674F, -8.947F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(89, 137).mirror().addBox(9.7279F, -25.2674F, -8.947F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.7854F, 0.0F)); + + PartDefinition bone117 = bone114.addOrReplaceChild("bone117", CubeListBuilder.create().texOffs(89, 137).mirror().addBox(6.4086F, -25.2674F, -12.7535F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(89, 137).mirror().addBox(6.4086F, -25.2674F, -12.7535F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.3927F, 0.0F)); + + PartDefinition bone118 = bone127.addOrReplaceChild("bone118", CubeListBuilder.create().texOffs(54, 90).addBox(1.8854F, -25.2674F, -12.5F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)) + .texOffs(54, 90).addBox(-1.1146F, -25.2674F, -12.5F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)) + .texOffs(59, 134).addBox(-0.1146F, -25.2674F, -13.5F, 4.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(54, 90).addBox(1.8854F, -25.2674F, -12.5F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)) + .texOffs(54, 90).addBox(-1.1146F, -25.2674F, -12.5F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)) + .texOffs(59, 134).addBox(-0.1146F, -25.2674F, -13.5F, 4.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(15.75F, -0.5F, 0.0F, 0.0F, 0.0F, 0.3927F)); + + PartDefinition bone119 = bone118.addOrReplaceChild("bone119", CubeListBuilder.create().texOffs(141, 67).addBox(-0.6146F, -25.2674F, -14.0F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(141, 67).addBox(-0.6146F, -25.2674F, -14.0F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offset(2.5F, 0.0F, 1.5F)); + + PartDefinition bone120 = bone119.addOrReplaceChild("bone120", CubeListBuilder.create().texOffs(89, 137).addBox(-13.095F, -25.2674F, -7.1782F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(89, 137).addBox(-13.095F, -25.2674F, -7.1782F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -1.1781F, 0.0F)); + + PartDefinition bone121 = bone119.addOrReplaceChild("bone121", CubeListBuilder.create().texOffs(89, 137).addBox(-10.4247F, -25.2674F, -11.2598F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(89, 137).addBox(-10.4247F, -25.2674F, -11.2598F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -0.7854F, 0.0F)); + + PartDefinition bone122 = bone119.addOrReplaceChild("bone122", CubeListBuilder.create().texOffs(89, 137).addBox(-6.3957F, -25.2674F, -14.0089F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(89, 137).addBox(-6.3957F, -25.2674F, -14.0089F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -0.3927F, 0.0F)); + + PartDefinition bone123 = bone118.addOrReplaceChild("bone123", CubeListBuilder.create().texOffs(141, 67).mirror().addBox(0.8786F, -25.2674F, -13.9932F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(141, 67).mirror().addBox(0.8786F, -25.2674F, -13.9932F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offset(-2.0F, 0.0F, 1.5F)); + + PartDefinition bone124 = bone123.addOrReplaceChild("bone124", CubeListBuilder.create().texOffs(89, 137).mirror().addBox(11.3377F, -25.2674F, -4.16F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(89, 137).mirror().addBox(11.3377F, -25.2674F, -4.16F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 1.1781F, 0.0F)); + + PartDefinition bone125 = bone123.addOrReplaceChild("bone125", CubeListBuilder.create().texOffs(89, 137).mirror().addBox(9.7279F, -25.2674F, -8.947F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(89, 137).mirror().addBox(9.7279F, -25.2674F, -8.947F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.7854F, 0.0F)); + + PartDefinition bone126 = bone123.addOrReplaceChild("bone126", CubeListBuilder.create().texOffs(89, 137).mirror().addBox(6.4086F, -25.2674F, -12.7535F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(89, 137).mirror().addBox(6.4086F, -25.2674F, -12.7535F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.3927F, 0.0F)); + + PartDefinition bone91 = bone127.addOrReplaceChild("bone91", CubeListBuilder.create().texOffs(54, 90).addBox(9.0F, -23.5F, -12.5F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)) + .texOffs(54, 90).addBox(6.0F, -23.5F, -12.5F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)) + .texOffs(59, 134).addBox(7.0F, -23.5F, -13.5F, 4.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(124, 74).addBox(7.5F, -22.0F, -12.0F, 3.0F, 10.0F, 10.0F, new CubeDeformation(-0.25F)) + .texOffs(54, 90).addBox(9.0F, -23.5F, -12.5F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)) + .texOffs(54, 90).addBox(6.0F, -23.5F, -12.5F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)) + .texOffs(59, 134).addBox(7.0F, -23.5F, -13.5F, 4.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offset(25.5F, 0.0F, 0.0F)); + + PartDefinition bone92 = bone91.addOrReplaceChild("bone92", CubeListBuilder.create().texOffs(141, 67).addBox(6.5F, -23.5F, -14.0F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(141, 67).addBox(6.5F, -23.5F, -14.0F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offset(2.5F, 0.0F, 1.5F)); + + PartDefinition bone93 = bone92.addOrReplaceChild("bone93", CubeListBuilder.create().texOffs(89, 137).addBox(-10.3723F, -23.5F, -13.7512F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(89, 137).addBox(-10.3723F, -23.5F, -13.7512F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -1.1781F, 0.0F)); + + PartDefinition bone94 = bone92.addOrReplaceChild("bone94", CubeListBuilder.create().texOffs(89, 137).addBox(-5.3939F, -23.5F, -16.2906F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(89, 137).addBox(-5.3939F, -23.5F, -16.2906F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -0.7854F, 0.0F)); + + PartDefinition bone95 = bone92.addOrReplaceChild("bone95", CubeListBuilder.create().texOffs(89, 137).addBox(0.1773F, -23.5F, -16.7315F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(89, 137).addBox(0.1773F, -23.5F, -16.7315F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -0.3927F, 0.0F)); + + PartDefinition bone96 = bone91.addOrReplaceChild("bone96", CubeListBuilder.create().texOffs(141, 67).mirror().addBox(7.9932F, -23.5F, -13.9932F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(141, 67).mirror().addBox(7.9932F, -23.5F, -13.9932F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offset(-2.0F, 0.0F, 1.5F)); + + PartDefinition bone97 = bone96.addOrReplaceChild("bone97", CubeListBuilder.create().texOffs(89, 137).mirror().addBox(14.0604F, -23.5F, 2.413F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(89, 137).mirror().addBox(14.0604F, -23.5F, 2.413F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 1.1781F, 0.0F)); + + PartDefinition bone98 = bone96.addOrReplaceChild("bone98", CubeListBuilder.create().texOffs(89, 137).mirror().addBox(14.7586F, -23.5F, -3.9162F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(89, 137).mirror().addBox(14.7586F, -23.5F, -3.9162F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.7854F, 0.0F)); + + PartDefinition bone99 = bone96.addOrReplaceChild("bone99", CubeListBuilder.create().texOffs(89, 137).mirror().addBox(12.9817F, -23.5F, -10.0308F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(89, 137).mirror().addBox(12.9817F, -23.5F, -10.0308F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.3927F, 0.0F)); + + PartDefinition bone193 = bone127.addOrReplaceChild("bone193", CubeListBuilder.create().texOffs(0, 127).addBox(-17.0F, -15.2537F, 0.2814F, 2.0F, 10.0F, 10.0F, new CubeDeformation(-0.25F)) + .texOffs(54, 90).addBox(-16.5F, -16.7537F, -0.2186F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)) + .texOffs(54, 90).addBox(-18.5F, -16.7537F, -0.2186F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)) + .texOffs(141, 95).addBox(-17.5F, -16.7537F, -1.2186F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(54, 90).addBox(-16.5F, -16.7537F, -0.2186F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)) + .texOffs(54, 90).addBox(-18.5F, -16.7537F, -0.2186F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(5.3673F, 3.0761F, -2.5F, 1.2217F, 0.0F, 1.5708F)); + + PartDefinition bone194 = bone193.addOrReplaceChild("bone194", CubeListBuilder.create().texOffs(141, 67).addBox(9.4979F, -5.1576F, -11.8252F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(141, 67).addBox(9.4979F, -5.1576F, -11.8252F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offset(-25.9979F, -11.5961F, 11.6066F)); + + PartDefinition bone195 = bone194.addOrReplaceChild("bone195", CubeListBuilder.create().texOffs(89, 137).addBox(-7.2158F, -5.1576F, -15.6887F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(89, 137).addBox(-7.2158F, -5.1576F, -15.6887F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -1.1781F, 0.0F)); + + PartDefinition bone196 = bone194.addOrReplaceChild("bone196", CubeListBuilder.create().texOffs(89, 137).addBox(-1.7363F, -5.1576F, -16.8726F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(89, 137).addBox(-1.7363F, -5.1576F, -16.8726F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -0.7854F, 0.0F)); + + PartDefinition bone197 = bone194.addOrReplaceChild("bone197", CubeListBuilder.create().texOffs(89, 137).addBox(3.7793F, -5.1576F, -15.8695F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(89, 137).addBox(3.7793F, -5.1576F, -15.8695F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -0.3927F, 0.0F)); + + PartDefinition bone198 = bone193.addOrReplaceChild("bone198", CubeListBuilder.create().texOffs(141, 67).mirror().addBox(11.9911F, -5.1576F, -11.8184F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(141, 67).mirror().addBox(11.9911F, -5.1576F, -11.8184F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offset(-30.4979F, -11.5961F, 11.6066F)); + + PartDefinition bone199 = bone198.addOrReplaceChild("bone199", CubeListBuilder.create().texOffs(89, 137).mirror().addBox(13.5811F, -5.1576F, 6.9388F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(89, 137).mirror().addBox(13.5811F, -5.1576F, 6.9388F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 1.1781F, 0.0F)); + + PartDefinition bone200 = bone198.addOrReplaceChild("bone200", CubeListBuilder.create().texOffs(89, 137).mirror().addBox(16.0478F, -5.1576F, 0.4485F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(89, 137).mirror().addBox(16.0478F, -5.1576F, 0.4485F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.7854F, 0.0F)); + + PartDefinition bone201 = bone198.addOrReplaceChild("bone201", CubeListBuilder.create().texOffs(89, 137).mirror().addBox(15.843F, -5.1576F, -6.4917F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(89, 137).mirror().addBox(15.843F, -5.1576F, -6.4917F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.3927F, 0.0F)); + + PartDefinition bone73 = bone127.addOrReplaceChild("bone73", CubeListBuilder.create().texOffs(0, 127).addBox(-17.0F, -10.6569F, 5.298F, 2.0F, 10.0F, 10.0F, new CubeDeformation(-0.25F)) + .texOffs(54, 90).addBox(-16.5F, -12.1569F, 4.798F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)) + .texOffs(54, 90).addBox(-18.5F, -12.1569F, 4.798F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)) + .texOffs(141, 95).addBox(-17.5F, -12.1569F, 3.798F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(54, 90).addBox(-16.5F, -12.1569F, 4.798F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)) + .texOffs(54, 90).addBox(-18.5F, -12.1569F, 4.798F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(5.3673F, 0.5761F, -0.5F, 1.8326F, 0.0F, 1.5708F)); + + PartDefinition bone74 = bone73.addOrReplaceChild("bone74", CubeListBuilder.create().texOffs(141, 67).addBox(9.4979F, -0.5607F, -6.8086F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(141, 67).addBox(9.4979F, -0.5607F, -6.8086F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offset(-25.9979F, -11.5961F, 11.6066F)); + + PartDefinition bone75 = bone74.addOrReplaceChild("bone75", CubeListBuilder.create().texOffs(89, 137).addBox(-2.5811F, -0.5607F, -13.7689F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(89, 137).addBox(-2.5811F, -0.5607F, -13.7689F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -1.1781F, 0.0F)); + + PartDefinition bone76 = bone74.addOrReplaceChild("bone76", CubeListBuilder.create().texOffs(89, 137).addBox(1.811F, -0.5607F, -13.3254F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(89, 137).addBox(1.811F, -0.5607F, -13.3254F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -0.7854F, 0.0F)); + + PartDefinition bone77 = bone74.addOrReplaceChild("bone77", CubeListBuilder.create().texOffs(89, 137).addBox(5.699F, -0.5607F, -11.2348F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(89, 137).addBox(5.699F, -0.5607F, -11.2348F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -0.3927F, 0.0F)); + + PartDefinition bone78 = bone73.addOrReplaceChild("bone78", CubeListBuilder.create().texOffs(141, 67).mirror().addBox(11.9911F, -0.5607F, -6.8018F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(141, 67).mirror().addBox(11.9911F, -0.5607F, -6.8018F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offset(-30.4979F, -11.5961F, 11.6066F)); + + PartDefinition bone79 = bone78.addOrReplaceChild("bone79", CubeListBuilder.create().texOffs(89, 137).mirror().addBox(8.9463F, -0.5607F, 8.8586F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(89, 137).mirror().addBox(8.9463F, -0.5607F, 8.8586F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 1.1781F, 0.0F)); + + PartDefinition bone80 = bone78.addOrReplaceChild("bone80", CubeListBuilder.create().texOffs(89, 137).mirror().addBox(12.5005F, -0.5607F, 3.9958F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(89, 137).mirror().addBox(12.5005F, -0.5607F, 3.9958F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.7854F, 0.0F)); + + PartDefinition bone81 = bone78.addOrReplaceChild("bone81", CubeListBuilder.create().texOffs(89, 137).mirror().addBox(13.9232F, -0.5607F, -1.857F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(89, 137).mirror().addBox(13.9232F, -0.5607F, -1.857F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.3927F, 0.0F)); + + PartDefinition bone82 = bone127.addOrReplaceChild("bone82", CubeListBuilder.create().texOffs(0, 127).addBox(-17.0F, -10.2241F, -14.5354F, 2.0F, 10.0F, 10.0F, new CubeDeformation(-0.25F)) + .texOffs(54, 90).addBox(-16.5F, -11.7241F, -15.0354F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)) + .texOffs(54, 90).addBox(-18.5F, -11.7241F, -15.0354F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)) + .texOffs(141, 95).addBox(-17.5F, -11.7241F, -16.0354F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(54, 90).addBox(-16.5F, -11.7241F, -15.0354F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)) + .texOffs(54, 90).addBox(-18.5F, -11.7241F, -15.0354F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(5.3673F, -1.9239F, -0.5F, -0.3054F, 0.0F, 1.5708F)); + + PartDefinition bone83 = bone82.addOrReplaceChild("bone83", CubeListBuilder.create().texOffs(141, 67).addBox(9.4979F, -0.1279F, -26.642F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(141, 67).addBox(9.4979F, -0.1279F, -26.642F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offset(-25.9979F, -11.5961F, 11.6066F)); + + PartDefinition bone84 = bone83.addOrReplaceChild("bone84", CubeListBuilder.create().texOffs(89, 137).addBox(-20.9047F, -0.1279F, -21.3588F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(89, 137).addBox(-20.9047F, -0.1279F, -21.3588F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -1.1781F, 0.0F)); + + PartDefinition bone85 = bone83.addOrReplaceChild("bone85", CubeListBuilder.create().texOffs(89, 137).addBox(-12.2133F, -0.1279F, -27.3497F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(89, 137).addBox(-12.2133F, -0.1279F, -27.3497F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -0.7854F, 0.0F)); + + PartDefinition bone86 = bone83.addOrReplaceChild("bone86", CubeListBuilder.create().texOffs(89, 137).addBox(-1.8909F, -0.1279F, -29.5584F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(89, 137).addBox(-1.8909F, -0.1279F, -29.5584F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -0.3927F, 0.0F)); + + PartDefinition bone87 = bone82.addOrReplaceChild("bone87", CubeListBuilder.create().texOffs(141, 67).mirror().addBox(11.9911F, -0.1279F, -26.6352F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(141, 67).mirror().addBox(11.9911F, -0.1279F, -26.6352F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offset(-30.4979F, -11.5961F, 11.6066F)); + + PartDefinition bone88 = bone87.addOrReplaceChild("bone88", CubeListBuilder.create().texOffs(89, 137).mirror().addBox(27.27F, -0.1279F, 1.2687F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(89, 137).mirror().addBox(27.27F, -0.1279F, 1.2687F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 1.1781F, 0.0F)); + + PartDefinition bone89 = bone87.addOrReplaceChild("bone89", CubeListBuilder.create().texOffs(89, 137).mirror().addBox(26.5248F, -0.1279F, -10.0285F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(89, 137).mirror().addBox(26.5248F, -0.1279F, -10.0285F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.7854F, 0.0F)); + + PartDefinition bone90 = bone87.addOrReplaceChild("bone90", CubeListBuilder.create().texOffs(89, 137).mirror().addBox(21.5131F, -0.1279F, -20.1806F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(89, 137).mirror().addBox(21.5131F, -0.1279F, -20.1806F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.3927F, 0.0F)); + + PartDefinition bone128 = bone.addOrReplaceChild("bone128", CubeListBuilder.create(), PartPose.offset(0.0F, 0.0F, 0.0F)); + + PartDefinition bone129 = bone128.addOrReplaceChild("bone129", CubeListBuilder.create().texOffs(54, 90).mirror().addBox(4.0F, -23.5F, -12.5F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(54, 90).mirror().addBox(8.0F, -23.5F, -12.5F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(103, 130).mirror().addBox(5.0F, -23.5F, -13.5F, 5.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(74, 116).mirror().addBox(5.5F, -22.0F, -12.0F, 4.0F, 10.0F, 10.0F, new CubeDeformation(-0.25F)).mirror(false) + .texOffs(54, 90).mirror().addBox(4.0F, -23.5F, -12.5F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(54, 90).mirror().addBox(8.0F, -23.5F, -12.5F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(103, 130).mirror().addBox(5.0F, -23.5F, -13.5F, 5.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offset(13.0F, -3.0F, 24.5F)); + + PartDefinition bone130 = bone129.addOrReplaceChild("bone130", CubeListBuilder.create().texOffs(141, 67).mirror().addBox(6.5F, -23.5F, -14.0F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(141, 67).mirror().addBox(6.5F, -23.5F, -14.0F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offset(-2.5F, 0.0F, 1.5F)); + + PartDefinition bone131 = bone130.addOrReplaceChild("bone131", CubeListBuilder.create().texOffs(89, 137).mirror().addBox(13.4953F, -23.5F, 1.0309F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(89, 137).mirror().addBox(13.4953F, -23.5F, 1.0309F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 1.1781F, 0.0F)); + + PartDefinition bone132 = bone130.addOrReplaceChild("bone132", CubeListBuilder.create().texOffs(89, 137).mirror().addBox(13.7076F, -23.5F, -4.9769F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(89, 137).mirror().addBox(13.7076F, -23.5F, -4.9769F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.7854F, 0.0F)); + + PartDefinition bone133 = bone130.addOrReplaceChild("bone133", CubeListBuilder.create().texOffs(89, 137).mirror().addBox(11.6048F, -23.5F, -10.6086F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(89, 137).mirror().addBox(11.6048F, -23.5F, -10.6086F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.3927F, 0.0F)); + + PartDefinition bone134 = bone129.addOrReplaceChild("bone134", CubeListBuilder.create().texOffs(141, 67).addBox(6.0068F, -23.5F, -13.9932F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(141, 67).addBox(6.0068F, -23.5F, -13.9932F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offset(2.0F, 0.0F, 1.5F)); + + PartDefinition bone135 = bone134.addOrReplaceChild("bone135", CubeListBuilder.create().texOffs(89, 137).addBox(-10.5547F, -23.5F, -13.293F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(89, 137).addBox(-10.5547F, -23.5F, -13.293F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -1.1781F, 0.0F)); + + PartDefinition bone136 = bone134.addOrReplaceChild("bone136", CubeListBuilder.create().texOffs(89, 137).addBox(-5.7378F, -23.5F, -15.937F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(89, 137).addBox(-5.7378F, -23.5F, -15.937F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -0.7854F, 0.0F)); + + PartDefinition bone137 = bone134.addOrReplaceChild("bone137", CubeListBuilder.create().texOffs(89, 137).addBox(-0.2757F, -23.5F, -16.5365F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(89, 137).addBox(-0.2757F, -23.5F, -16.5365F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -0.3927F, 0.0F)); + + PartDefinition bone138 = bone128.addOrReplaceChild("bone138", CubeListBuilder.create().texOffs(118, 46).mirror().addBox(5.5F, -22.0F, -12.0F, 4.0F, 10.0F, 10.0F, new CubeDeformation(-0.25F)).mirror(false) + .texOffs(104, 104).addBox(4.0F, -23.5F, -12.5F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)) + .texOffs(104, 104).addBox(8.0F, -23.5F, -12.5F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)) + .texOffs(42, 134).addBox(5.0F, -23.5F, -13.5F, 5.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(104, 104).addBox(4.0F, -23.5F, -12.5F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)) + .texOffs(104, 104).addBox(8.0F, -23.5F, -12.5F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)) + .texOffs(42, 134).addBox(5.0F, -23.5F, -13.5F, 5.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offset(4.0F, -3.0F, 24.5F)); + + PartDefinition bone139 = bone138.addOrReplaceChild("bone139", CubeListBuilder.create().texOffs(13, 148).mirror().addBox(6.5F, -23.5F, -14.0F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(13, 148).mirror().addBox(6.5F, -23.5F, -14.0F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offset(-2.5F, 0.0F, 1.5F)); + + PartDefinition bone140 = bone139.addOrReplaceChild("bone140", CubeListBuilder.create().texOffs(125, 141).mirror().addBox(13.4953F, -23.5F, 1.0309F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(125, 141).mirror().addBox(13.4953F, -23.5F, 1.0309F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 1.1781F, 0.0F)); + + PartDefinition bone141 = bone139.addOrReplaceChild("bone141", CubeListBuilder.create().texOffs(125, 141).mirror().addBox(13.7076F, -23.5F, -4.9769F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(125, 141).mirror().addBox(13.7076F, -23.5F, -4.9769F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.7854F, 0.0F)); + + PartDefinition bone142 = bone139.addOrReplaceChild("bone142", CubeListBuilder.create().texOffs(125, 141).mirror().addBox(11.6048F, -23.5F, -10.6086F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(125, 141).mirror().addBox(11.6048F, -23.5F, -10.6086F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.3927F, 0.0F)); + + PartDefinition bone143 = bone138.addOrReplaceChild("bone143", CubeListBuilder.create().texOffs(13, 148).addBox(6.0068F, -23.5F, -13.9932F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(13, 148).addBox(6.0068F, -23.5F, -13.9932F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offset(2.0F, 0.0F, 1.5F)); + + PartDefinition bone144 = bone143.addOrReplaceChild("bone144", CubeListBuilder.create().texOffs(125, 141).addBox(-10.5547F, -23.5F, -13.293F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(125, 141).addBox(-10.5547F, -23.5F, -13.293F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -1.1781F, 0.0F)); + + PartDefinition bone145 = bone143.addOrReplaceChild("bone145", CubeListBuilder.create().texOffs(125, 141).addBox(-5.7378F, -23.5F, -15.937F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(125, 141).addBox(-5.7378F, -23.5F, -15.937F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -0.7854F, 0.0F)); + + PartDefinition bone146 = bone143.addOrReplaceChild("bone146", CubeListBuilder.create().texOffs(125, 141).addBox(-0.2757F, -23.5F, -16.5365F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(125, 141).addBox(-0.2757F, -23.5F, -16.5365F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -0.3927F, 0.0F)); + + PartDefinition bone147 = bone128.addOrReplaceChild("bone147", CubeListBuilder.create().texOffs(99, 58).mirror().addBox(4.0F, -23.5F, -12.5F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(99, 58).mirror().addBox(8.0F, -23.5F, -12.5F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(25, 131).mirror().addBox(5.0F, -23.5F, -13.5F, 5.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(118, 46).mirror().addBox(5.5F, -22.0F, -12.0F, 4.0F, 10.0F, 10.0F, new CubeDeformation(-0.25F)).mirror(false) + .texOffs(99, 58).mirror().addBox(4.0F, -23.5F, -12.5F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(99, 58).mirror().addBox(8.0F, -23.5F, -12.5F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(25, 131).mirror().addBox(5.0F, -23.5F, -13.5F, 5.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offset(-9.0F, -3.0F, 24.5F)); + + PartDefinition bone148 = bone147.addOrReplaceChild("bone148", CubeListBuilder.create().texOffs(0, 148).mirror().addBox(6.5F, -23.5F, -14.0F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(0, 148).mirror().addBox(6.5F, -23.5F, -14.0F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offset(-2.5F, 0.0F, 1.5F)); + + PartDefinition bone149 = bone148.addOrReplaceChild("bone149", CubeListBuilder.create().texOffs(102, 147).mirror().addBox(13.4953F, -23.5F, 1.0309F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(102, 147).mirror().addBox(13.4953F, -23.5F, 1.0309F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 1.1781F, 0.0F)); + + PartDefinition bone150 = bone148.addOrReplaceChild("bone150", CubeListBuilder.create().texOffs(102, 147).mirror().addBox(13.7076F, -23.5F, -4.9769F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(102, 147).mirror().addBox(13.7076F, -23.5F, -4.9769F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.7854F, 0.0F)); + + PartDefinition bone151 = bone148.addOrReplaceChild("bone151", CubeListBuilder.create().texOffs(102, 147).mirror().addBox(11.6048F, -23.5F, -10.6086F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(102, 147).mirror().addBox(11.6048F, -23.5F, -10.6086F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.3927F, 0.0F)); + + PartDefinition bone152 = bone147.addOrReplaceChild("bone152", CubeListBuilder.create().texOffs(0, 148).addBox(6.0068F, -23.5F, -13.9932F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(0, 148).addBox(6.0068F, -23.5F, -13.9932F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offset(2.0F, 0.0F, 1.5F)); + + PartDefinition bone153 = bone152.addOrReplaceChild("bone153", CubeListBuilder.create().texOffs(102, 147).addBox(-10.5547F, -23.5F, -13.293F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(102, 147).addBox(-10.5547F, -23.5F, -13.293F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -1.1781F, 0.0F)); + + PartDefinition bone154 = bone152.addOrReplaceChild("bone154", CubeListBuilder.create().texOffs(102, 147).addBox(-5.7378F, -23.5F, -15.937F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(102, 147).addBox(-5.7378F, -23.5F, -15.937F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -0.7854F, 0.0F)); + + PartDefinition bone155 = bone152.addOrReplaceChild("bone155", CubeListBuilder.create().texOffs(102, 147).addBox(-0.2757F, -23.5F, -16.5365F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(102, 147).addBox(-0.2757F, -23.5F, -16.5365F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -0.3927F, 0.0F)); + + PartDefinition bone156 = bone128.addOrReplaceChild("bone156", CubeListBuilder.create().texOffs(54, 90).mirror().addBox(4.0F, -23.5F, -12.5F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(54, 90).mirror().addBox(7.0F, -23.5F, -12.5F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(59, 134).mirror().addBox(5.0F, -23.5F, -13.5F, 4.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(54, 90).mirror().addBox(4.0F, -23.5F, -12.5F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(54, 90).mirror().addBox(7.0F, -23.5F, -12.5F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(59, 134).mirror().addBox(5.0F, -23.5F, -13.5F, 4.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(124, 74).mirror().addBox(5.5F, -22.0F, -12.0F, 3.0F, 10.0F, 10.0F, new CubeDeformation(-0.25F)).mirror(false), PartPose.offset(9.0F, -3.0F, 24.5F)); + + PartDefinition bone157 = bone156.addOrReplaceChild("bone157", CubeListBuilder.create().texOffs(141, 67).mirror().addBox(6.5F, -23.5F, -14.0F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(141, 67).mirror().addBox(6.5F, -23.5F, -14.0F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offset(-2.5F, 0.0F, 1.5F)); + + PartDefinition bone158 = bone157.addOrReplaceChild("bone158", CubeListBuilder.create().texOffs(89, 137).mirror().addBox(13.4953F, -23.5F, 1.0309F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(89, 137).mirror().addBox(13.4953F, -23.5F, 1.0309F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 1.1781F, 0.0F)); + + PartDefinition bone159 = bone157.addOrReplaceChild("bone159", CubeListBuilder.create().texOffs(89, 137).mirror().addBox(13.7076F, -23.5F, -4.9769F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(89, 137).mirror().addBox(13.7076F, -23.5F, -4.9769F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.7854F, 0.0F)); + + PartDefinition bone160 = bone157.addOrReplaceChild("bone160", CubeListBuilder.create().texOffs(89, 137).mirror().addBox(11.6048F, -23.5F, -10.6086F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(89, 137).mirror().addBox(11.6048F, -23.5F, -10.6086F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.3927F, 0.0F)); + + PartDefinition bone161 = bone156.addOrReplaceChild("bone161", CubeListBuilder.create().texOffs(141, 67).addBox(5.0068F, -23.5F, -13.9932F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(141, 67).addBox(5.0068F, -23.5F, -13.9932F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offset(2.0F, 0.0F, 1.5F)); + + PartDefinition bone162 = bone161.addOrReplaceChild("bone162", CubeListBuilder.create().texOffs(89, 137).addBox(-10.9374F, -23.5F, -12.3691F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(89, 137).addBox(-10.9374F, -23.5F, -12.3691F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -1.1781F, 0.0F)); + + PartDefinition bone163 = bone161.addOrReplaceChild("bone163", CubeListBuilder.create().texOffs(89, 137).addBox(-6.4449F, -23.5F, -15.2299F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(89, 137).addBox(-6.4449F, -23.5F, -15.2299F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -0.7854F, 0.0F)); + + PartDefinition bone164 = bone161.addOrReplaceChild("bone164", CubeListBuilder.create().texOffs(89, 137).addBox(-1.1996F, -23.5F, -16.1538F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(89, 137).addBox(-1.1996F, -23.5F, -16.1538F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -0.3927F, 0.0F)); + + PartDefinition bone165 = bone128.addOrReplaceChild("bone165", CubeListBuilder.create().texOffs(54, 90).mirror().addBox(9.8967F, -19.1445F, -12.5F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(54, 90).mirror().addBox(12.8967F, -19.1445F, -12.5F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(59, 134).mirror().addBox(10.8967F, -19.1445F, -13.5F, 4.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(54, 90).mirror().addBox(9.8967F, -19.1445F, -12.5F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(54, 90).mirror().addBox(12.8967F, -19.1445F, -12.5F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(59, 134).mirror().addBox(10.8967F, -19.1445F, -13.5F, 4.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(-2.75F, -3.5F, 24.5F, 0.0F, 0.0F, -0.3927F)); + + PartDefinition bone166 = bone165.addOrReplaceChild("bone166", CubeListBuilder.create().texOffs(141, 67).mirror().addBox(12.3967F, -19.1445F, -14.0F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(141, 67).mirror().addBox(12.3967F, -19.1445F, -14.0F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offset(-2.5F, 0.0F, 1.5F)); + + PartDefinition bone167 = bone166.addOrReplaceChild("bone167", CubeListBuilder.create().texOffs(89, 137).mirror().addBox(15.7518F, -19.1445F, 6.4787F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(89, 137).mirror().addBox(15.7518F, -19.1445F, 6.4787F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 1.1781F, 0.0F)); + + PartDefinition bone168 = bone166.addOrReplaceChild("bone168", CubeListBuilder.create().texOffs(89, 137).mirror().addBox(17.8772F, -19.1445F, -0.8073F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(89, 137).mirror().addBox(17.8772F, -19.1445F, -0.8073F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.7854F, 0.0F)); + + PartDefinition bone169 = bone166.addOrReplaceChild("bone169", CubeListBuilder.create().texOffs(89, 137).mirror().addBox(17.0526F, -19.1445F, -8.352F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(89, 137).mirror().addBox(17.0526F, -19.1445F, -8.352F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.3927F, 0.0F)); + + PartDefinition bone170 = bone165.addOrReplaceChild("bone170", CubeListBuilder.create().texOffs(141, 67).addBox(10.9035F, -19.1445F, -13.9932F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(141, 67).addBox(10.9035F, -19.1445F, -13.9932F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offset(2.0F, 0.0F, 1.5F)); + + PartDefinition bone171 = bone170.addOrReplaceChild("bone171", CubeListBuilder.create().texOffs(89, 137).addBox(-8.6809F, -19.1445F, -17.8169F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(89, 137).addBox(-8.6809F, -19.1445F, -17.8169F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -1.1781F, 0.0F)); + + PartDefinition bone172 = bone170.addOrReplaceChild("bone172", CubeListBuilder.create().texOffs(89, 137).addBox(-2.2754F, -19.1445F, -19.3995F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(89, 137).addBox(-2.2754F, -19.1445F, -19.3995F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -0.7854F, 0.0F)); + + PartDefinition bone173 = bone170.addOrReplaceChild("bone173", CubeListBuilder.create().texOffs(89, 137).addBox(4.2482F, -19.1445F, -18.4103F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(89, 137).addBox(4.2482F, -19.1445F, -18.4103F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -0.3927F, 0.0F)); + + PartDefinition bone174 = bone128.addOrReplaceChild("bone174", CubeListBuilder.create().texOffs(54, 90).mirror().addBox(9.8967F, -19.1445F, -12.5F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(54, 90).mirror().addBox(12.8967F, -19.1445F, -12.5F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(59, 134).mirror().addBox(10.8967F, -19.1445F, -13.5F, 4.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(124, 74).mirror().addBox(11.3967F, -17.6445F, -12.0F, 3.0F, 10.0F, 10.0F, new CubeDeformation(-0.25F)).mirror(false) + .texOffs(54, 90).mirror().addBox(9.8967F, -19.1445F, -12.5F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(54, 90).mirror().addBox(12.8967F, -19.1445F, -12.5F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(59, 134).mirror().addBox(10.8967F, -19.1445F, -13.5F, 4.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(-2.75F, -3.5F, 24.5F, 0.0F, 0.0F, -0.3927F)); + + PartDefinition bone175 = bone174.addOrReplaceChild("bone175", CubeListBuilder.create().texOffs(141, 67).mirror().addBox(12.3967F, -19.1445F, -14.0F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(141, 67).mirror().addBox(12.3967F, -19.1445F, -14.0F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offset(-2.5F, 0.0F, 1.5F)); + + PartDefinition bone176 = bone175.addOrReplaceChild("bone176", CubeListBuilder.create().texOffs(89, 137).mirror().addBox(15.7518F, -19.1445F, 6.4787F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(89, 137).mirror().addBox(15.7518F, -19.1445F, 6.4787F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 1.1781F, 0.0F)); + + PartDefinition bone177 = bone175.addOrReplaceChild("bone177", CubeListBuilder.create().texOffs(89, 137).mirror().addBox(17.8772F, -19.1445F, -0.8073F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(89, 137).mirror().addBox(17.8772F, -19.1445F, -0.8073F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.7854F, 0.0F)); + + PartDefinition bone178 = bone175.addOrReplaceChild("bone178", CubeListBuilder.create().texOffs(89, 137).mirror().addBox(17.0526F, -19.1445F, -8.352F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(89, 137).mirror().addBox(17.0526F, -19.1445F, -8.352F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.3927F, 0.0F)); + + PartDefinition bone179 = bone174.addOrReplaceChild("bone179", CubeListBuilder.create().texOffs(141, 67).addBox(10.9035F, -19.1445F, -13.9932F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(141, 67).addBox(10.9035F, -19.1445F, -13.9932F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offset(2.0F, 0.0F, 1.5F)); + + PartDefinition bone180 = bone179.addOrReplaceChild("bone180", CubeListBuilder.create().texOffs(89, 137).addBox(-8.6809F, -19.1445F, -17.8169F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(89, 137).addBox(-8.6809F, -19.1445F, -17.8169F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -1.1781F, 0.0F)); + + PartDefinition bone181 = bone179.addOrReplaceChild("bone181", CubeListBuilder.create().texOffs(89, 137).addBox(-2.2754F, -19.1445F, -19.3995F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(89, 137).addBox(-2.2754F, -19.1445F, -19.3995F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -0.7854F, 0.0F)); + + PartDefinition bone182 = bone179.addOrReplaceChild("bone182", CubeListBuilder.create().texOffs(89, 137).addBox(4.2482F, -19.1445F, -18.4103F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(89, 137).addBox(4.2482F, -19.1445F, -18.4103F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -0.3927F, 0.0F)); + + PartDefinition bone183 = bone128.addOrReplaceChild("bone183", CubeListBuilder.create().texOffs(54, 90).mirror().addBox(4.0F, -23.5F, -12.5F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(54, 90).mirror().addBox(7.0F, -23.5F, -12.5F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(59, 134).mirror().addBox(5.0F, -23.5F, -13.5F, 4.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(124, 74).mirror().addBox(5.5F, -22.0F, -12.0F, 3.0F, 10.0F, 10.0F, new CubeDeformation(-0.25F)).mirror(false) + .texOffs(54, 90).mirror().addBox(4.0F, -23.5F, -12.5F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(54, 90).mirror().addBox(7.0F, -23.5F, -12.5F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(59, 134).mirror().addBox(5.0F, -23.5F, -13.5F, 4.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offset(-12.5F, -3.0F, 24.5F)); + + PartDefinition bone184 = bone183.addOrReplaceChild("bone184", CubeListBuilder.create().texOffs(141, 67).mirror().addBox(6.5F, -23.5F, -14.0F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(141, 67).mirror().addBox(6.5F, -23.5F, -14.0F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offset(-2.5F, 0.0F, 1.5F)); + + PartDefinition bone185 = bone184.addOrReplaceChild("bone185", CubeListBuilder.create().texOffs(89, 137).mirror().addBox(13.4953F, -23.5F, 1.0309F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(89, 137).mirror().addBox(13.4953F, -23.5F, 1.0309F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 1.1781F, 0.0F)); + + PartDefinition bone186 = bone184.addOrReplaceChild("bone186", CubeListBuilder.create().texOffs(89, 137).mirror().addBox(13.7076F, -23.5F, -4.9769F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(89, 137).mirror().addBox(13.7076F, -23.5F, -4.9769F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.7854F, 0.0F)); + + PartDefinition bone187 = bone184.addOrReplaceChild("bone187", CubeListBuilder.create().texOffs(89, 137).mirror().addBox(11.6048F, -23.5F, -10.6086F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(89, 137).mirror().addBox(11.6048F, -23.5F, -10.6086F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.3927F, 0.0F)); + + PartDefinition bone188 = bone183.addOrReplaceChild("bone188", CubeListBuilder.create().texOffs(141, 67).addBox(5.0068F, -23.5F, -13.9932F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(141, 67).addBox(5.0068F, -23.5F, -13.9932F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offset(2.0F, 0.0F, 1.5F)); + + PartDefinition bone189 = bone188.addOrReplaceChild("bone189", CubeListBuilder.create().texOffs(89, 137).addBox(-10.9374F, -23.5F, -12.3691F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(89, 137).addBox(-10.9374F, -23.5F, -12.3691F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -1.1781F, 0.0F)); + + PartDefinition bone190 = bone188.addOrReplaceChild("bone190", CubeListBuilder.create().texOffs(89, 137).addBox(-6.4449F, -23.5F, -15.2299F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(89, 137).addBox(-6.4449F, -23.5F, -15.2299F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -0.7854F, 0.0F)); + + PartDefinition bone191 = bone188.addOrReplaceChild("bone191", CubeListBuilder.create().texOffs(89, 137).addBox(-1.1996F, -23.5F, -16.1538F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(89, 137).addBox(-1.1996F, -23.5F, -16.1538F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -0.3927F, 0.0F)); + + PartDefinition bone192 = bone.addOrReplaceChild("bone192", CubeListBuilder.create(), PartPose.offset(-22.0F, -17.0F, 24.5F)); + + PartDefinition bone211 = bone192.addOrReplaceChild("bone211", CubeListBuilder.create().texOffs(54, 90).addBox(4.0F, -23.5F, -12.5F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)) + .texOffs(54, 90).addBox(0.0F, -23.5F, -12.5F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)) + .texOffs(103, 130).addBox(1.0F, -23.5F, -13.5F, 5.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(74, 116).addBox(1.5F, -22.0F, -12.0F, 4.0F, 10.0F, 10.0F, new CubeDeformation(-0.25F)) + .texOffs(54, 90).addBox(4.0F, -23.5F, -12.5F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)) + .texOffs(54, 90).addBox(0.0F, -23.5F, -12.5F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)) + .texOffs(103, 130).addBox(1.0F, -23.5F, -13.5F, 5.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offset(20.9F, 0.0F, 0.0F)); + + PartDefinition bone212 = bone211.addOrReplaceChild("bone212", CubeListBuilder.create().texOffs(141, 67).addBox(1.5F, -23.5F, -14.0F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(141, 67).addBox(1.5F, -23.5F, -14.0F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offset(2.5F, 0.0F, 1.5F)); + + PartDefinition bone213 = bone212.addOrReplaceChild("bone213", CubeListBuilder.create().texOffs(89, 137).addBox(-12.2857F, -23.5F, -9.1318F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(89, 137).addBox(-12.2857F, -23.5F, -9.1318F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -1.1781F, 0.0F)); + + PartDefinition bone214 = bone212.addOrReplaceChild("bone214", CubeListBuilder.create().texOffs(89, 137).addBox(-8.9295F, -23.5F, -12.7551F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(89, 137).addBox(-8.9295F, -23.5F, -12.7551F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -0.7854F, 0.0F)); + + PartDefinition bone215 = bone212.addOrReplaceChild("bone215", CubeListBuilder.create().texOffs(89, 137).addBox(-4.4421F, -23.5F, -14.8181F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(89, 137).addBox(-4.4421F, -23.5F, -14.8181F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -0.3927F, 0.0F)); + + PartDefinition bone216 = bone211.addOrReplaceChild("bone216", CubeListBuilder.create().texOffs(141, 67).mirror().addBox(1.9932F, -23.5F, -13.9932F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(141, 67).mirror().addBox(1.9932F, -23.5F, -13.9932F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offset(-2.0F, 0.0F, 1.5F)); + + PartDefinition bone217 = bone216.addOrReplaceChild("bone217", CubeListBuilder.create().texOffs(89, 137).mirror().addBox(11.7643F, -23.5F, -3.1303F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(89, 137).mirror().addBox(11.7643F, -23.5F, -3.1303F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 1.1781F, 0.0F)); + + PartDefinition bone218 = bone216.addOrReplaceChild("bone218", CubeListBuilder.create().texOffs(89, 137).mirror().addBox(10.516F, -23.5F, -8.1589F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(89, 137).mirror().addBox(10.516F, -23.5F, -8.1589F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.7854F, 0.0F)); + + PartDefinition bone219 = bone216.addOrReplaceChild("bone219", CubeListBuilder.create().texOffs(89, 137).mirror().addBox(7.4384F, -23.5F, -12.3269F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(89, 137).mirror().addBox(7.4384F, -23.5F, -12.3269F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.3927F, 0.0F)); + + PartDefinition bone220 = bone192.addOrReplaceChild("bone220", CubeListBuilder.create().texOffs(85, 90).addBox(-15.0F, -10.9021F, -15.2901F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)) + .texOffs(85, 90).addBox(-18.0F, -10.9021F, -15.2901F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)) + .texOffs(74, 137).addBox(-17.0F, -10.9021F, -16.2901F, 4.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(85, 90).addBox(-15.0F, -10.9021F, -15.2901F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)) + .texOffs(85, 90).addBox(-18.0F, -10.9021F, -15.2901F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)) + .texOffs(74, 137).addBox(-17.0F, -10.9021F, -16.2901F, 4.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(125, 120).addBox(-16.5F, -9.4021F, -14.7901F, 3.0F, 10.0F, 10.0F, new CubeDeformation(-0.25F)), PartPose.offsetAndRotation(29.0F, 1.25F, -1.75F, -0.48F, 0.0F, 1.5708F)); + + PartDefinition bone221 = bone220.addOrReplaceChild("bone221", CubeListBuilder.create().texOffs(143, 37).addBox(-17.5F, -10.9021F, -16.7901F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(143, 37).addBox(-17.5F, -10.9021F, -16.7901F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offset(2.5F, 0.0F, 1.5F)); + + PartDefinition bone222 = bone221.addOrReplaceChild("bone222", CubeListBuilder.create().texOffs(138, 141).addBox(-22.1344F, -10.9021F, 7.3542F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(138, 141).addBox(-22.1344F, -10.9021F, 7.3542F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -1.1781F, 0.0F)); + + PartDefinition bone223 = bone221.addOrReplaceChild("bone223", CubeListBuilder.create().texOffs(138, 141).addBox(-24.3374F, -10.9021F, -1.2929F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(138, 141).addBox(-24.3374F, -10.9021F, -1.2929F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -0.7854F, 0.0F)); + + PartDefinition bone224 = bone221.addOrReplaceChild("bone224", CubeListBuilder.create().texOffs(138, 141).addBox(-23.0635F, -10.9021F, -10.1248F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(138, 141).addBox(-23.0635F, -10.9021F, -10.1248F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -0.3927F, 0.0F)); + + PartDefinition bone225 = bone220.addOrReplaceChild("bone225", CubeListBuilder.create().texOffs(143, 37).mirror().addBox(-16.0068F, -10.9021F, -16.7832F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(143, 37).mirror().addBox(-16.0068F, -10.9021F, -16.7832F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offset(-2.0F, 0.0F, 1.5F)); + + PartDefinition bone226 = bone225.addOrReplaceChild("bone226", CubeListBuilder.create().texOffs(138, 141).mirror().addBox(7.4536F, -10.9021F, -20.8279F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(138, 141).mirror().addBox(7.4536F, -10.9021F, -20.8279F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 1.1781F, 0.0F)); + + PartDefinition bone227 = bone225.addOrReplaceChild("bone227", CubeListBuilder.create().texOffs(138, 141).mirror().addBox(-0.2391F, -10.9021F, -22.8597F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(138, 141).mirror().addBox(-0.2391F, -10.9021F, -22.8597F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.7854F, 0.0F)); + + PartDefinition bone228 = bone225.addOrReplaceChild("bone228", CubeListBuilder.create().texOffs(138, 141).mirror().addBox(-8.1237F, -10.9021F, -21.7929F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(138, 141).mirror().addBox(-8.1237F, -10.9021F, -21.7929F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.3927F, 0.0F)); + + PartDefinition bone238 = bone192.addOrReplaceChild("bone238", CubeListBuilder.create().texOffs(0, 127).addBox(-4.3513F, -20.9301F, -12.5F, 2.0F, 10.0F, 10.0F, new CubeDeformation(-0.25F)) + .texOffs(54, 90).addBox(-3.8513F, -22.4301F, -13.0F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)) + .texOffs(54, 90).addBox(-5.8513F, -22.4301F, -13.0F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)) + .texOffs(141, 95).addBox(-4.8513F, -22.4301F, -14.0F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(54, 90).addBox(-3.8513F, -22.4301F, -13.0F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)) + .texOffs(54, 90).addBox(-5.8513F, -22.4301F, -13.0F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(15.8673F, -1.4239F, 0.5F, 0.0F, 0.0F, 0.3927F)); + + PartDefinition bone239 = bone238.addOrReplaceChild("bone239", CubeListBuilder.create().texOffs(141, 67).addBox(-5.234F, -23.354F, -14.0F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(141, 67).addBox(-5.234F, -23.354F, -14.0F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offset(1.3827F, 0.9239F, 1.0F)); + + PartDefinition bone240 = bone239.addOrReplaceChild("bone240", CubeListBuilder.create().texOffs(89, 137).addBox(-14.8627F, -23.354F, -2.9104F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(89, 137).addBox(-14.8627F, -23.354F, -2.9104F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -1.1781F, 0.0F)); + + PartDefinition bone241 = bone239.addOrReplaceChild("bone241", CubeListBuilder.create().texOffs(89, 137).addBox(-13.6911F, -23.354F, -7.9934F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(89, 137).addBox(-13.6911F, -23.354F, -7.9934F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -0.7854F, 0.0F)); + + PartDefinition bone242 = bone239.addOrReplaceChild("bone242", CubeListBuilder.create().texOffs(89, 137).addBox(-10.6635F, -23.354F, -12.2411F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(89, 137).addBox(-10.6635F, -23.354F, -12.2411F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -0.3927F, 0.0F)); + + PartDefinition bone243 = bone238.addOrReplaceChild("bone243", CubeListBuilder.create().texOffs(141, 67).mirror().addBox(-2.7408F, -23.354F, -13.9932F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(141, 67).mirror().addBox(-2.7408F, -23.354F, -13.9932F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offset(-3.1173F, 0.9239F, 1.0F)); + + PartDefinition bone244 = bone243.addOrReplaceChild("bone244", CubeListBuilder.create().texOffs(89, 137).mirror().addBox(9.9526F, -23.354F, -7.5039F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(89, 137).mirror().addBox(9.9526F, -23.354F, -7.5039F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 1.1781F, 0.0F)); + + PartDefinition bone245 = bone243.addOrReplaceChild("bone245", CubeListBuilder.create().texOffs(89, 137).mirror().addBox(7.1686F, -23.354F, -11.5063F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(89, 137).mirror().addBox(7.1686F, -23.354F, -11.5063F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.7854F, 0.0F)); + + PartDefinition bone246 = bone243.addOrReplaceChild("bone246", CubeListBuilder.create().texOffs(89, 137).mirror().addBox(3.0648F, -23.354F, -14.1386F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(89, 137).mirror().addBox(3.0648F, -23.354F, -14.1386F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.3927F, 0.0F)); + + PartDefinition bone46 = bone192.addOrReplaceChild("bone46", CubeListBuilder.create().texOffs(0, 127).addBox(-17.0F, -16.3137F, -4.5F, 2.0F, 10.0F, 10.0F, new CubeDeformation(-0.25F)) + .texOffs(54, 90).addBox(-16.5F, -17.8137F, -5.0F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)) + .texOffs(54, 90).addBox(-18.5F, -17.8137F, -5.0F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)) + .texOffs(141, 95).addBox(-17.5F, -17.8137F, -6.0F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(54, 90).addBox(-16.5F, -17.8137F, -5.0F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)) + .texOffs(54, 90).addBox(-18.5F, -17.8137F, -5.0F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(29.3673F, -0.9239F, -2.5F, 0.7854F, 0.0F, 1.5708F)); + + PartDefinition bone47 = bone46.addOrReplaceChild("bone47", CubeListBuilder.create().texOffs(141, 67).addBox(-22.5021F, -16.8242F, -6.0F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(141, 67).addBox(-22.5021F, -16.8242F, -6.0F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offset(6.0021F, -0.9895F, 1.0F)); + + PartDefinition bone48 = bone47.addOrReplaceChild("bone48", CubeListBuilder.create().texOffs(89, 137).addBox(-14.0799F, -16.8242F, 16.1047F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(89, 137).addBox(-14.0799F, -16.8242F, 16.1047F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -1.1781F, 0.0F)); + + PartDefinition bone49 = bone47.addOrReplaceChild("bone49", CubeListBuilder.create().texOffs(89, 137).addBox(-20.2446F, -16.8242F, 9.8738F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(89, 137).addBox(-20.2446F, -16.8242F, 9.8738F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -0.7854F, 0.0F)); + + PartDefinition bone50 = bone47.addOrReplaceChild("bone50", CubeListBuilder.create().texOffs(89, 137).addBox(-23.5557F, -16.8242F, 1.7582F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(89, 137).addBox(-23.5557F, -16.8242F, 1.7582F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -0.3927F, 0.0F)); + + PartDefinition bone51 = bone46.addOrReplaceChild("bone51", CubeListBuilder.create().texOffs(141, 67).mirror().addBox(-20.0089F, -16.8242F, -5.9932F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(141, 67).mirror().addBox(-20.0089F, -16.8242F, -5.9932F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offset(1.5021F, -0.9895F, 1.0F)); + + PartDefinition bone52 = bone51.addOrReplaceChild("bone52", CubeListBuilder.create().texOffs(89, 137).mirror().addBox(-4.0466F, -16.8242F, -20.3961F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(89, 137).mirror().addBox(-4.0466F, -16.8242F, -20.3961F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 1.1781F, 0.0F)); + + PartDefinition bone53 = bone51.addOrReplaceChild("bone53", CubeListBuilder.create().texOffs(89, 137).mirror().addBox(-10.6987F, -16.8242F, -18.0598F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(89, 137).mirror().addBox(-10.6987F, -16.8242F, -18.0598F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.7854F, 0.0F)); + + PartDefinition bone54 = bone51.addOrReplaceChild("bone54", CubeListBuilder.create().texOffs(89, 137).mirror().addBox(-15.9504F, -16.8242F, -13.3557F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(89, 137).mirror().addBox(-15.9504F, -16.8242F, -13.3557F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.3927F, 0.0F)); + + PartDefinition bone247 = bone192.addOrReplaceChild("bone247", CubeListBuilder.create().texOffs(54, 90).addBox(4.0F, -23.5F, -12.5F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)) + .texOffs(54, 90).addBox(1.0F, -23.5F, -12.5F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)) + .texOffs(59, 134).addBox(2.0F, -23.5F, -13.5F, 4.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(124, 74).addBox(2.5F, -22.0F, -12.0F, 3.0F, 10.0F, 10.0F, new CubeDeformation(-0.25F)) + .texOffs(54, 90).addBox(4.0F, -23.5F, -12.5F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)) + .texOffs(54, 90).addBox(1.0F, -23.5F, -12.5F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)) + .texOffs(59, 134).addBox(2.0F, -23.5F, -13.5F, 4.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offset(24.5F, 0.0F, 0.0F)); + + PartDefinition bone248 = bone247.addOrReplaceChild("bone248", CubeListBuilder.create().texOffs(141, 67).addBox(1.5F, -23.5F, -14.0F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(141, 67).addBox(1.5F, -23.5F, -14.0F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offset(2.5F, 0.0F, 1.5F)); + + PartDefinition bone249 = bone248.addOrReplaceChild("bone249", CubeListBuilder.create().texOffs(89, 137).addBox(-12.2857F, -23.5F, -9.1318F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(89, 137).addBox(-12.2857F, -23.5F, -9.1318F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -1.1781F, 0.0F)); + + PartDefinition bone250 = bone248.addOrReplaceChild("bone250", CubeListBuilder.create().texOffs(89, 137).addBox(-8.9295F, -23.5F, -12.7551F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(89, 137).addBox(-8.9295F, -23.5F, -12.7551F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -0.7854F, 0.0F)); + + PartDefinition bone251 = bone248.addOrReplaceChild("bone251", CubeListBuilder.create().texOffs(89, 137).addBox(-4.4421F, -23.5F, -14.8181F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(89, 137).addBox(-4.4421F, -23.5F, -14.8181F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -0.3927F, 0.0F)); + + PartDefinition bone252 = bone247.addOrReplaceChild("bone252", CubeListBuilder.create().texOffs(141, 67).mirror().addBox(2.9932F, -23.5F, -13.9932F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(141, 67).mirror().addBox(2.9932F, -23.5F, -13.9932F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offset(-2.0F, 0.0F, 1.5F)); + + PartDefinition bone253 = bone252.addOrReplaceChild("bone253", CubeListBuilder.create().texOffs(89, 137).mirror().addBox(12.1469F, -23.5F, -2.2064F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(89, 137).mirror().addBox(12.1469F, -23.5F, -2.2064F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 1.1781F, 0.0F)); + + PartDefinition bone254 = bone252.addOrReplaceChild("bone254", CubeListBuilder.create().texOffs(89, 137).mirror().addBox(11.2231F, -23.5F, -7.4518F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(89, 137).mirror().addBox(11.2231F, -23.5F, -7.4518F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.7854F, 0.0F)); + + PartDefinition bone255 = bone252.addOrReplaceChild("bone255", CubeListBuilder.create().texOffs(89, 137).mirror().addBox(8.3623F, -23.5F, -11.9443F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false) + .texOffs(89, 137).mirror().addBox(8.3623F, -23.5F, -11.9443F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.3927F, 0.0F)); + + PartDefinition bone55 = bone.addOrReplaceChild("bone55", CubeListBuilder.create().texOffs(74, 116).addBox(-17.5F, -16.2707F, -3.0139F, 4.0F, 10.0F, 10.0F, new CubeDeformation(-0.25F)) + .texOffs(54, 90).addBox(-15.0F, -17.7707F, -3.5139F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)) + .texOffs(54, 90).addBox(-19.0F, -17.7707F, -3.5139F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)) + .texOffs(103, 130).addBox(-18.0F, -17.7707F, -4.5139F, 5.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(-29.25F, 13.5F, 23.0F, 0.8727F, 0.0F, 1.5708F)); + + PartDefinition bone56 = bone55.addOrReplaceChild("bone56", CubeListBuilder.create().texOffs(141, 67).addBox(-17.5F, -17.7707F, -5.0139F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offset(2.5F, 0.0F, 1.5F)); + + PartDefinition bone57 = bone56.addOrReplaceChild("bone57", CubeListBuilder.create().texOffs(89, 137).addBox(-11.2547F, -17.7707F, 11.8607F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -1.1781F, 0.0F)); + + PartDefinition bone58 = bone56.addOrReplaceChild("bone58", CubeListBuilder.create().texOffs(89, 137).addBox(-16.0104F, -17.7707F, 7.0341F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -0.7854F, 0.0F)); + + PartDefinition bone59 = bone56.addOrReplaceChild("bone59", CubeListBuilder.create().texOffs(89, 137).addBox(-18.557F, -17.7707F, 0.7549F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -0.3927F, 0.0F)); + + PartDefinition bone60 = bone55.addOrReplaceChild("bone60", CubeListBuilder.create().texOffs(141, 67).mirror().addBox(-17.0068F, -17.7707F, -5.0071F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offset(-2.0F, 0.0F, 1.5F)); + + PartDefinition bone61 = bone60.addOrReplaceChild("bone61", CubeListBuilder.create().texOffs(89, 137).mirror().addBox(-3.8088F, -17.7707F, -17.2452F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 1.1781F, 0.0F)); + + PartDefinition bone62 = bone60.addOrReplaceChild("bone62", CubeListBuilder.create().texOffs(89, 137).mirror().addBox(-9.2731F, -17.7707F, -15.2398F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.7854F, 0.0F)); + + PartDefinition bone63 = bone60.addOrReplaceChild("bone63", CubeListBuilder.create().texOffs(89, 137).mirror().addBox(-13.5541F, -17.7707F, -11.2959F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.3927F, 0.0F)); + + PartDefinition bone64 = bone.addOrReplaceChild("bone64", CubeListBuilder.create().texOffs(0, 127).addBox(16.5F, 5.9282F, -6.9282F, 2.0F, 10.0F, 10.0F, new CubeDeformation(-0.25F)) + .texOffs(54, 90).addBox(17.0F, 4.4282F, -7.4282F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)) + .texOffs(54, 90).addBox(15.0F, 4.4282F, -7.4282F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)) + .texOffs(141, 95).addBox(16.0F, 4.4282F, -8.4282F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(-28.25F, 12.0F, 23.0F, -0.5236F, 0.0F, -1.5708F)); + + PartDefinition bone65 = bone64.addOrReplaceChild("bone65", CubeListBuilder.create().texOffs(141, 67).addBox(14.5F, 4.4282F, -8.9282F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offset(2.5F, 0.0F, 1.5F)); + + PartDefinition bone66 = bone65.addOrReplaceChild("bone66", CubeListBuilder.create().texOffs(89, 137).addBox(-2.6251F, 4.4282F, -19.2014F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -1.1781F, 0.0F)); + + PartDefinition bone67 = bone65.addOrReplaceChild("bone67", CubeListBuilder.create().texOffs(89, 137).addBox(3.8492F, 4.4282F, -18.3611F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -0.7854F, 0.0F)); + + PartDefinition bone68 = bone65.addOrReplaceChild("bone68", CubeListBuilder.create().texOffs(89, 137).addBox(9.5092F, 4.4282F, -15.1072F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -0.3927F, 0.0F)); + + PartDefinition bone69 = bone64.addOrReplaceChild("bone69", CubeListBuilder.create().texOffs(141, 67).mirror().addBox(16.9932F, 4.4282F, -8.9214F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offset(-2.0F, 0.0F, 1.5F)); + + PartDefinition bone70 = bone69.addOrReplaceChild("bone70", CubeListBuilder.create().texOffs(89, 137).mirror().addBox(12.8188F, 4.4282F, 12.6688F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 1.1781F, 0.0F)); + + PartDefinition bone71 = bone69.addOrReplaceChild("bone71", CubeListBuilder.create().texOffs(89, 137).mirror().addBox(17.5363F, 4.4282F, 6.034F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.7854F, 0.0F)); + + PartDefinition bone72 = bone69.addOrReplaceChild("bone72", CubeListBuilder.create().texOffs(89, 137).mirror().addBox(19.3557F, 4.4282F, -1.901F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)).mirror(false), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.3927F, 0.0F)); + + PartDefinition bone36 = main.addOrReplaceChild("bone36", CubeListBuilder.create().texOffs(54, 73).addBox(6.0F, -5.0F, -10.25F, 13.0F, 3.0F, 13.0F, new CubeDeformation(0.0F)) + .texOffs(104, 84).addBox(5.0F, -2.0F, -11.25F, 2.0F, 2.0F, 15.0F, new CubeDeformation(0.0F)) + .texOffs(98, 51).addBox(7.0F, -2.0F, -11.25F, 11.0F, 2.0F, 2.0F, new CubeDeformation(0.0F)) + .texOffs(98, 46).addBox(7.0F, -2.0F, 1.75F, 11.0F, 2.0F, 2.0F, new CubeDeformation(0.0F)) + .texOffs(39, 116).addBox(18.0F, -2.0F, -11.25F, 2.0F, 2.0F, 15.0F, new CubeDeformation(0.0F)), PartPose.offset(-12.5F, 0.0F, 3.75F)); + + PartDefinition bone229 = bone36.addOrReplaceChild("bone229", CubeListBuilder.create().texOffs(78, 45).addBox(3.5016F, -30.8187F, -13.675F, 3.0F, 11.0F, 13.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(5.4014F, 13.8604F, 3.425F, 0.0F, 0.0F, 0.3927F)); + + PartDefinition book = main.addOrReplaceChild("book", CubeListBuilder.create(), PartPose.offsetAndRotation(-13.25F, -13.25F, 2.0F, 0.2134F, -0.002F, 0.8411F)); + + PartDefinition bone230 = book.addOrReplaceChild("bone230", CubeListBuilder.create().texOffs(188, 27).addBox(-1.0F, -5.0F, -4.5F, 2.0F, 10.0F, 10.0F, new CubeDeformation(-0.25F)) + .texOffs(201, 65).addBox(-1.5F, -6.5F, -6.0F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)) + .texOffs(221, 0).addBox(-0.5F, -6.5F, -5.0F, 3.0F, 13.0F, 12.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(-0.6327F, 0.0761F, 2.0F, 0.0F, 0.0F, 1.5708F)); + + PartDefinition bone231 = bone230.addOrReplaceChild("bone231", CubeListBuilder.create(), PartPose.offset(-25.9979F, -11.5961F, 11.6066F)); + + PartDefinition bone232 = bone231.addOrReplaceChild("bone232", CubeListBuilder.create().texOffs(231, 48).addBox(-5.5103F, 5.0961F, -32.3005F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -1.1781F, 0.0F)); + + PartDefinition bone233 = bone231.addOrReplaceChild("bone233", CubeListBuilder.create().texOffs(214, 65).addBox(16.7316F, 5.0961F, -26.4099F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -0.3927F, 0.0F)); + + PartDefinition bone234 = book.addOrReplaceChild("bone234", CubeListBuilder.create(), PartPose.offsetAndRotation(-0.6327F, 0.0761F, 2.0F, 0.0F, 0.0F, 1.5708F)); + + PartDefinition bone235 = bone234.addOrReplaceChild("bone235", CubeListBuilder.create().texOffs(203, 48).addBox(-0.5F, -5.25F, -1.0F, 1.0F, 10.5F, 6.5F, new CubeDeformation(-0.49F)), PartPose.offsetAndRotation(-0.75F, 0.0F, -4.25F, 0.0F, -0.0654F, 0.0F)); + + PartDefinition bone236 = bone235.addOrReplaceChild("bone236", CubeListBuilder.create().texOffs(188, 82).addBox(-0.5F, -5.25F, -0.5F, 1.0F, 10.5F, 5.5F, new CubeDeformation(-0.49F)), PartPose.offsetAndRotation(0.0F, 0.0F, 5.0F, 0.0F, 0.0873F, 0.0F)); + + PartDefinition bone257 = bone234.addOrReplaceChild("bone257", CubeListBuilder.create().texOffs(213, 27).addBox(-0.7995F, -5.0F, 0.0001F, 1.0F, 10.0F, 10.0F, new CubeDeformation(-0.25F)), PartPose.offsetAndRotation(-0.7F, 0.0F, -4.0F, -3.1416F, -0.3927F, 3.1416F)); + + PartDefinition bone258 = bone234.addOrReplaceChild("bone258", CubeListBuilder.create().texOffs(188, 0).addBox(-2.5386F, -6.5F, -1.306F, 3.0F, 13.0F, 13.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(-1.0F, 0.0F, -4.0F, -3.1416F, -0.3927F, 3.1416F)); + + PartDefinition bone259 = bone234.addOrReplaceChild("bone259", CubeListBuilder.create(), PartPose.offset(-25.9979F, -11.5961F, 11.6066F)); + + PartDefinition bone260 = bone259.addOrReplaceChild("bone260", CubeListBuilder.create().texOffs(218, 48).addBox(6.1965F, 5.0961F, -31.5673F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -0.7854F, 0.0F)); + + PartDefinition bone261 = bone259.addOrReplaceChild("bone261", CubeListBuilder.create().texOffs(188, 65).addBox(16.7316F, 5.0961F, -26.4099F, 3.0F, 13.0F, 3.0F, new CubeDeformation(-1.25F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, -0.3927F, 0.0F)); + + return LayerDefinition.create(meshdefinition, 256, 256); + } + + @Override + public void setupAnim(Entity entity, float limbSwing, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch) { + } + + @Override + public void renderToBuffer(PoseStack poseStack, VertexConsumer vertexConsumer, int packedLight, int packedOverlay, float red, float green, float blue, float alpha) { + main.render(poseStack, vertexConsumer, packedLight, packedOverlay, red, green, blue, alpha); + } +} \ No newline at end of file diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/ComputerModel.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/ComputerModel.java new file mode 100644 index 000000000..7b1190399 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/ComputerModel.java @@ -0,0 +1,266 @@ +package com.github.tartaricacid.touhoulittlemaid.client.model; + +import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import net.minecraft.client.model.EntityModel; +import net.minecraft.client.model.geom.ModelLayerLocation; +import net.minecraft.client.model.geom.ModelPart; +import net.minecraft.client.model.geom.PartPose; +import net.minecraft.client.model.geom.builders.*; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.Entity; + +public class ComputerModel extends EntityModel { + public static final ModelLayerLocation LAYER = new ModelLayerLocation(new ResourceLocation(TouhouLittleMaid.MOD_ID, "main"), "computer"); + private final ModelPart main; + + public ComputerModel(ModelPart root) { + this.main = root.getChild("main"); + } + + public static LayerDefinition createBodyLayer() { + MeshDefinition meshdefinition = new MeshDefinition(); + PartDefinition partdefinition = meshdefinition.getRoot(); + + PartDefinition main = partdefinition.addOrReplaceChild("main", CubeListBuilder.create(), PartPose.offset(0.0F, 8.0F, -6.0F)); + + PartDefinition group = main.addOrReplaceChild("group", CubeListBuilder.create().texOffs(0, 0).addBox(-27.0F, -8.0F, -15.0F, 38.0F, 1.0F, 16.0F, new CubeDeformation(0.0F)) + .texOffs(0, 0).addBox(9.0F, -19.0F, -13.0F, 1.0F, 11.0F, 5.0F, new CubeDeformation(0.0F)) + .texOffs(100, 119).addBox(-1.0F, -19.0F, -14.0F, 11.0F, 11.0F, 1.0F, new CubeDeformation(0.0F)) + .texOffs(92, 6).addBox(-2.0F, -20.0F, -15.0F, 13.0F, 1.0F, 8.0F, new CubeDeformation(0.0F)) + .texOffs(112, 88).addBox(5.0F, -24.0F, -14.0F, 4.0F, 4.0F, 4.0F, new CubeDeformation(0.0F)) + .texOffs(16, 63).addBox(5.5F, -30.0F, -13.5F, 3.0F, 6.0F, 3.0F, new CubeDeformation(0.0F)) + .texOffs(136, 52).addBox(-17.0F, -8.5F, -14.5F, 6.0F, 1.0F, 5.0F, new CubeDeformation(0.0F)) + .texOffs(46, 63).addBox(-15.0F, -9.25F, -13.5F, 2.0F, 1.0F, 1.0F, new CubeDeformation(0.0F)) + .texOffs(124, 15).addBox(-20.0F, -17.25F, -13.5F, 12.0F, 8.0F, 1.0F, new CubeDeformation(0.0F)) + .texOffs(0, 56).addBox(-23.0F, 13.9F, -14.0F, 30.0F, 1.0F, 6.0F, new CubeDeformation(0.0F)) + .texOffs(46, 65).addBox(-23.0F, -4.0F, -11.0F, 30.0F, 1.0F, 1.0F, new CubeDeformation(0.0F)) + .texOffs(109, 17).addBox(6.0F, -2.0F, -14.0F, 1.0F, 1.0F, 13.0F, new CubeDeformation(0.0F)) + .texOffs(16, 63).addBox(-23.0F, -2.0F, -14.0F, 1.0F, 1.0F, 13.0F, new CubeDeformation(0.0F)) + .texOffs(30, 63).addBox(-24.0F, -7.0F, -14.0F, 1.0F, 23.0F, 14.0F, new CubeDeformation(0.0F)) + .texOffs(105, 33).addBox(-30.0F, 3.0F, -14.0F, 6.0F, 13.0F, 11.0F, new CubeDeformation(0.0F)) + .texOffs(0, 63).addBox(7.0F, -7.0F, -14.0F, 1.0F, 23.0F, 14.0F, new CubeDeformation(0.0F)) + .texOffs(0, 32).addBox(-24.0F, -7.0F, -15.0F, 32.0F, 23.0F, 1.0F, new CubeDeformation(0.0F)), PartPose.offset(8.0F, 0.0F, -8.0F)); + + PartDefinition cube_r1 = group.addOrReplaceChild("cube_r1", CubeListBuilder.create().texOffs(60, 120).addBox(-7.0F, -7.5F, 0.8F, 12.0F, 8.0F, 1.0F, new CubeDeformation(0.0F)) + .texOffs(52, 63).addBox(-2.0F, 0.5F, 0.8F, 2.0F, 1.0F, 1.0F, new CubeDeformation(0.0F)) + .texOffs(102, 135).addBox(-4.0F, 1.25F, -0.2F, 6.0F, 1.0F, 5.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(-1.0F, -9.75F, -11.5F, 0.0F, -0.3927F, 0.0F)); + + PartDefinition cube_r2 = group.addOrReplaceChild("cube_r2", CubeListBuilder.create().texOffs(112, 68).addBox(-1.3F, -4.0F, -4.3F, 1.0F, 8.0F, 12.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(6.8915F, -13.25F, -5.8238F, 0.0F, 0.7854F, 0.0F)); + + PartDefinition cube_r3 = group.addOrReplaceChild("cube_r3", CubeListBuilder.create().texOffs(37, 67).addBox(0.7F, -0.5F, 6.7F, 1.0F, 1.0F, 2.0F, new CubeDeformation(0.0F)) + .texOffs(128, 34).addBox(-2.3F, 0.25F, 4.7F, 5.0F, 1.0F, 6.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(1.2346F, -8.75F, -8.6522F, 0.0F, 0.7854F, 0.0F)); + + PartDefinition cube_r4 = group.addOrReplaceChild("cube_r4", CubeListBuilder.create().texOffs(110, 57).addBox(-3.75F, -0.0333F, -6.0833F, 8.0F, 1.0F, 10.0F, new CubeDeformation(0.0F)) + .texOffs(31, 63).addBox(-2.5F, -0.9833F, -2.8333F, 2.0F, 1.0F, 3.0F, new CubeDeformation(0.0F)) + .texOffs(0, 25).addBox(-3.25F, -0.4833F, 1.4167F, 5.0F, 1.0F, 2.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(-18.5733F, -8.2667F, -4.3254F, 0.0F, 0.3927F, 0.0F)); + + PartDefinition group8 = main.addOrReplaceChild("group8", CubeListBuilder.create().texOffs(0, 17).addBox(-15.2458F, 1.9736F, -14.0516F, 30.0F, 1.0F, 14.0F, new CubeDeformation(0.0F)), PartPose.offset(0.2458F, -4.9736F, -2.1984F)); + + PartDefinition cube_r5 = group8.addOrReplaceChild("cube_r5", CubeListBuilder.create().texOffs(0, 0).addBox(-2.9903F, -0.6223F, -0.9946F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.3F)) + .texOffs(0, 0).addBox(-2.9903F, -0.6223F, -0.5946F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.3F)) + .texOffs(0, 2).addBox(-2.8903F, -0.5223F, -0.0946F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(7.7597F, -0.5223F, 0.7054F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(7.7597F, -0.5223F, 1.5554F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(6.9597F, -0.5223F, 1.5554F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(6.1597F, -0.5223F, 1.5554F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 28).addBox(0.3597F, -0.5223F, 1.5554F, 6.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(-0.4903F, -0.5223F, 1.5554F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(-1.2903F, -0.5223F, 1.5554F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(-2.0653F, -0.5223F, 1.5554F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(-2.8653F, -0.5223F, 1.5554F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(7.3597F, -0.5223F, 0.7054F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(6.5597F, -0.5223F, 0.7054F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(5.7597F, -0.5223F, 0.7054F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(4.9597F, -0.5223F, 0.7054F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(4.1597F, -0.5223F, 0.7054F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(2.5597F, -0.5223F, 0.7054F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(1.7597F, -0.5223F, 0.7054F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(0.9597F, -0.5223F, 0.7054F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(3.3597F, -0.5223F, 0.7054F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(0.1597F, -0.5223F, 0.7054F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(-0.6403F, -0.5223F, 0.7054F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(-1.4403F, -0.5223F, 0.7054F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(-2.3153F, -0.5223F, 0.7054F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(-2.8903F, -0.5223F, 0.7054F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(7.3597F, -0.5223F, -0.0946F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(7.7597F, -0.5223F, -0.8946F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(7.7597F, -0.5223F, -0.0946F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(-2.6403F, -0.5223F, -0.0946F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(-1.8403F, -0.5223F, -0.0946F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(-1.0403F, -0.5223F, -0.0946F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(-0.2403F, -0.5223F, -0.0946F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(0.5597F, -0.5223F, -0.0946F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(1.3597F, -0.5223F, -0.0946F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(2.1597F, -0.5223F, -0.0946F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(2.9597F, -0.5223F, -0.0946F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(3.7597F, -0.5223F, -0.0946F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(4.5597F, -0.5223F, -0.0946F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(5.3597F, -0.5223F, -0.0946F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(6.1597F, -0.5223F, -0.0946F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(6.9597F, -0.5223F, -0.0946F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(-2.2403F, -0.5223F, -0.8946F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(-1.4403F, -0.5223F, -0.8946F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(-0.6403F, -0.5223F, -0.8946F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(0.1597F, -0.5223F, -0.8946F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(0.9597F, -0.5223F, -0.8946F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(1.7597F, -0.5223F, -0.8946F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(2.5597F, -0.5223F, -0.8946F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(3.3597F, -0.5223F, -0.8946F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(4.1597F, -0.5223F, -0.8946F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(4.9597F, -0.5223F, -0.8946F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(5.7597F, -0.5223F, -0.8946F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(6.5597F, -0.5223F, -0.8946F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(7.3597F, -0.5223F, -0.8946F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(-9.2403F, -0.5223F, 1.0554F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(12, 5).addBox(-9.2403F, -0.5223F, -0.4446F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(-7.2403F, -0.5223F, 1.5554F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(-9.2403F, -0.5223F, 1.5554F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(-6.8403F, -0.5223F, 1.5554F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(-7.6403F, -0.5223F, 1.5554F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(-8.4403F, -0.5223F, 1.5554F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(-9.2403F, -0.5223F, -1.6446F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(-6.8403F, -0.5223F, -1.6446F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(12, 7).addBox(-7.6403F, -0.5223F, -1.6446F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(12, 7).addBox(-8.4403F, -0.5223F, -1.6446F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(-9.2403F, -0.5223F, 0.7554F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(-6.8403F, -0.5223F, 0.7554F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(-7.6403F, -0.5223F, 0.7554F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(-8.4403F, -0.5223F, 0.7554F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(12, 5).addBox(-9.2403F, -0.5223F, -0.0446F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(-6.8403F, -0.5223F, -0.0446F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(-7.6403F, -0.5223F, -0.0446F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(-8.4403F, -0.5223F, -0.0446F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(12, 5).addBox(-9.2403F, -0.5223F, -0.8446F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(-6.8403F, -0.5223F, -0.8446F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(-7.6403F, -0.5223F, -0.8446F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(-8.4403F, -0.5223F, -0.8446F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(-4.9403F, -0.5223F, 0.7554F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(-4.1403F, -0.5223F, 1.5554F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(-4.9403F, -0.5223F, 1.5554F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(-5.7403F, -0.5223F, 1.5554F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(-4.1403F, -0.5223F, -0.6446F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(-4.9403F, -0.5223F, -0.6446F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(-5.7403F, -0.5223F, -0.6446F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(-4.1403F, -0.5223F, -1.4446F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(-4.9403F, -0.5223F, -1.4446F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(-5.7403F, -0.5223F, -1.4446F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(-2.9403F, -0.5223F, -1.6946F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(-2.6403F, -0.5223F, -1.6946F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(-1.8403F, -0.5223F, -1.6946F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(-1.0403F, -0.5223F, -1.6946F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(-0.2403F, -0.5223F, -1.6946F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(1.3597F, -0.5223F, -1.6946F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(0.5597F, -0.5223F, -1.6946F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(2.1597F, -0.5223F, -1.6946F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(2.9597F, -0.5223F, -1.6946F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(3.7597F, -0.5223F, -1.6946F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(4.5597F, -0.5223F, -1.6946F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(5.3597F, -0.5223F, -1.6946F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(6.1597F, -0.5223F, -1.6946F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(6.9597F, -0.5223F, -1.6946F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(7.7597F, -0.5223F, -1.6946F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(-5.7403F, -0.5223F, -2.5446F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(-4.9403F, -0.5223F, -2.5446F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(-4.1403F, -0.5223F, -2.5446F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(-2.9403F, -0.5223F, -2.5446F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(-2.1403F, -0.5223F, -2.5446F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(-1.3403F, -0.5223F, -2.5446F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(-0.5403F, -0.5223F, -2.5446F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(0.5597F, -0.5223F, -2.5446F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(1.3597F, -0.5223F, -2.5446F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(2.1597F, -0.5223F, -2.5446F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(2.9597F, -0.5223F, -2.5446F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(4.0597F, -0.5223F, -2.5446F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(4.8597F, -0.5223F, -2.5446F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(5.6597F, -0.5223F, -2.5446F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(6.4597F, -0.5223F, -2.5446F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 2).addBox(7.7597F, -0.5223F, -2.5446F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(126, 6).addBox(-7.2403F, 1.0777F, -1.9446F, 13.0F, 2.0F, 1.0F, new CubeDeformation(0.0F)) + .texOffs(92, 0).addBox(-9.2403F, 0.0777F, -2.4446F, 18.0F, 1.0F, 5.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(0.0F, 0.0F, -4.0F, -0.3927F, 0.0F, 0.0F)); + + PartDefinition group3 = main.addOrReplaceChild("group3", CubeListBuilder.create(), PartPose.offset(0.0F, -8.72F, 27.3872F)); + + PartDefinition cube_r6 = group3.addOrReplaceChild("cube_r6", CubeListBuilder.create().texOffs(104, 67).addBox(-1.0F, -24.0F, -1.1F, 2.0F, 48.0F, 2.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(0.0F, 0.72F, 0.8002F, 0.0F, 0.7854F, 0.0F)); + + PartDefinition bone = group3.addOrReplaceChild("bone", CubeListBuilder.create(), PartPose.offsetAndRotation(-0.2885F, 20.72F, 0.0499F, 0.0F, -0.3927F, 0.0F)); + + PartDefinition cube_r7 = bone.addOrReplaceChild("cube_r7", CubeListBuilder.create().texOffs(132, 88).addBox(-11.8876F, -24.5F, 7.424F, 10.0F, 2.0F, 2.0F, new CubeDeformation(-0.25F)) + .texOffs(132, 96).addBox(-11.8876F, -63.0F, 7.424F, 10.0F, 2.0F, 2.0F, new CubeDeformation(-0.25F)) + .texOffs(0, 100).addBox(-11.8876F, -62.0F, 7.924F, 10.0F, 38.0F, 1.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(-1.0F, 21.0F, -8.0F, 0.0F, 0.3927F, 0.0F)); + + PartDefinition cube_r8 = bone.addOrReplaceChild("cube_r8", CubeListBuilder.create().texOffs(132, 92).addBox(-5.0F, -24.5F, -1.0F, 10.0F, 2.0F, 2.0F, new CubeDeformation(-0.25F)) + .texOffs(133, 0).addBox(-5.0F, -63.0F, -1.0F, 10.0F, 2.0F, 2.0F, new CubeDeformation(-0.25F)) + .texOffs(22, 100).addBox(-5.0F, -62.0F, -0.5F, 10.0F, 38.0F, 1.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(-12.9957F, 21.0F, 2.4185F, 0.0F, -0.3927F, 0.0F)); + + PartDefinition cube_r9 = bone.addOrReplaceChild("cube_r9", CubeListBuilder.create().texOffs(44, 100).addBox(-1.0F, -24.0F, -1.5F, 2.0F, 48.0F, 2.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(-17.8064F, -20.0F, 0.9671F, 0.0F, -0.3927F, 0.0F)); + + PartDefinition cube_r10 = bone.addOrReplaceChild("cube_r10", CubeListBuilder.create().texOffs(52, 100).addBox(-15.3978F, -24.0F, 2.6955F, 2.0F, 48.0F, 2.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(-1.0F, -20.0F, -8.0F, 0.0F, 0.7854F, 0.0F)); + + PartDefinition bone2 = group3.addOrReplaceChild("bone2", CubeListBuilder.create(), PartPose.offsetAndRotation(0.2885F, 20.72F, 0.0499F, 0.0F, 0.3927F, 0.0F)); + + PartDefinition cube_r11 = bone2.addOrReplaceChild("cube_r11", CubeListBuilder.create().texOffs(132, 88).mirror().addBox(1.8876F, -24.5F, 7.424F, 10.0F, 2.0F, 2.0F, new CubeDeformation(-0.25F)).mirror(false) + .texOffs(132, 96).mirror().addBox(1.8876F, -63.0F, 7.424F, 10.0F, 2.0F, 2.0F, new CubeDeformation(-0.25F)).mirror(false) + .texOffs(60, 67).addBox(1.8876F, -62.0F, 7.924F, 10.0F, 38.0F, 1.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(1.0F, 21.0F, -8.0F, 0.0F, -0.3927F, 0.0F)); + + PartDefinition cube_r12 = bone2.addOrReplaceChild("cube_r12", CubeListBuilder.create().texOffs(132, 92).mirror().addBox(-5.0F, -24.5F, -1.0F, 10.0F, 2.0F, 2.0F, new CubeDeformation(-0.25F)).mirror(false) + .texOffs(133, 0).mirror().addBox(-5.0F, -63.0F, -1.0F, 10.0F, 2.0F, 2.0F, new CubeDeformation(-0.25F)).mirror(false) + .texOffs(82, 67).addBox(-5.0F, -62.0F, -0.5F, 10.0F, 38.0F, 1.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(12.9957F, 21.0F, 2.4185F, 0.0F, 0.3927F, 0.0F)); + + PartDefinition cube_r13 = bone2.addOrReplaceChild("cube_r13", CubeListBuilder.create().texOffs(44, 100).mirror().addBox(-1.0F, -24.0F, -1.5F, 2.0F, 48.0F, 2.0F, new CubeDeformation(0.0F)).mirror(false), PartPose.offsetAndRotation(17.8064F, -20.0F, 0.9671F, 0.0F, 0.3927F, 0.0F)); + + PartDefinition cube_r14 = bone2.addOrReplaceChild("cube_r14", CubeListBuilder.create().texOffs(52, 100).mirror().addBox(13.3978F, -24.0F, 2.6955F, 2.0F, 48.0F, 2.0F, new CubeDeformation(0.0F)).mirror(false), PartPose.offsetAndRotation(1.0F, -20.0F, -8.0F, 0.0F, -0.7854F, 0.0F)); + + PartDefinition group2 = main.addOrReplaceChild("group2", CubeListBuilder.create().texOffs(137, 114).addBox(6.0F, 11.9F, 12.0F, 4.0F, 3.0F, 4.0F, new CubeDeformation(0.0F)) + .texOffs(104, 57).addBox(6.0F, 6.9F, 12.0F, 4.0F, 2.0F, 4.0F, new CubeDeformation(0.0F)) + .texOffs(58, 49).addBox(0.0F, 1.9F, 6.0F, 16.0F, 2.0F, 14.0F, new CubeDeformation(0.0F)) + .texOffs(74, 17).addBox(1.0F, 3.9F, 9.0F, 14.0F, 3.0F, 10.0F, new CubeDeformation(0.0F)) + .texOffs(126, 9).addBox(4.0F, 10.363F, 1.3275F, 8.0F, 1.0F, 4.0F, new CubeDeformation(0.0F)) + .texOffs(66, 32).addBox(1.0F, 0.9F, 7.0F, 14.0F, 1.0F, 11.0F, new CubeDeformation(0.0F)) + .texOffs(84, 119).addBox(16.0F, -0.1F, 8.0F, 2.0F, 4.0F, 12.0F, new CubeDeformation(0.0F)) + .texOffs(116, 88).addBox(-2.0F, -0.1F, 8.0F, 2.0F, 4.0F, 12.0F, new CubeDeformation(0.0F)) + .texOffs(115, 122).addBox(-1.5F, -2.2075F, 9.6691F, 1.0F, 4.0F, 9.0F, new CubeDeformation(0.0F)) + .texOffs(128, 24).addBox(-2.0F, -3.2075F, 9.6691F, 2.0F, 1.0F, 9.0F, new CubeDeformation(0.0F)) + .texOffs(126, 127).addBox(16.0F, -3.2075F, 9.6691F, 2.0F, 1.0F, 9.0F, new CubeDeformation(0.0F)) + .texOffs(126, 114).addBox(16.5F, -2.2075F, 9.6691F, 1.0F, 4.0F, 9.0F, new CubeDeformation(0.0F)) + .texOffs(126, 68).addBox(3.0F, -14.1866F, 22.5922F, 10.0F, 7.0F, 2.0F, new CubeDeformation(0.0F)) + .texOffs(136, 58).addBox(4.0F, -13.1866F, 21.5922F, 8.0F, 5.0F, 1.0F, new CubeDeformation(0.0F)) + .texOffs(66, 44).addBox(0.0F, 12.9F, 13.0F, 16.0F, 2.0F, 2.0F, new CubeDeformation(0.0F)) + .texOffs(96, 101).addBox(7.0F, 12.9F, 6.0F, 2.0F, 2.0F, 16.0F, new CubeDeformation(0.0F)) + .texOffs(66, 37).addBox(7.0F, 8.9F, 13.0F, 2.0F, 3.0F, 2.0F, new CubeDeformation(0.0F)) + .texOffs(66, 58).addBox(7.5F, 13.9F, 21.0F, 1.0F, 2.0F, 2.0F, new CubeDeformation(0.0F)) + .texOffs(66, 54).addBox(7.5F, 13.9F, 5.0F, 1.0F, 2.0F, 2.0F, new CubeDeformation(0.0F)) + .texOffs(31, 67).addBox(-1.0F, 13.9F, 13.5F, 2.0F, 2.0F, 1.0F, new CubeDeformation(0.0F)) + .texOffs(38, 63).addBox(15.0F, 13.9F, 13.5F, 2.0F, 2.0F, 1.0F, new CubeDeformation(0.0F)), PartPose.offset(-8.0F, 0.0F, -8.0F)); + + PartDefinition cube_r15 = group2.addOrReplaceChild("cube_r15", CubeListBuilder.create().texOffs(80, 135).addBox(-4.0F, -2.0F, -1.3F, 8.0F, 3.0F, 3.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(8.0F, -14.1537F, 22.771F, -0.3927F, 0.0F, 0.0F)); + + PartDefinition cube_r16 = group2.addOrReplaceChild("cube_r16", CubeListBuilder.create().texOffs(0, 17).addBox(0.0F, -3.5F, -2.0F, 5.0F, 6.0F, 2.0F, new CubeDeformation(0.0F)) + .texOffs(7, 0).addBox(1.0F, -2.5F, -3.0F, 3.0F, 4.0F, 1.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(13.0F, -10.6866F, 24.5922F, 0.0F, 0.3927F, 0.0F)); + + PartDefinition cube_r17 = group2.addOrReplaceChild("cube_r17", CubeListBuilder.create().texOffs(66, 32).addBox(-4.0F, -2.5F, -3.0F, 3.0F, 4.0F, 1.0F, new CubeDeformation(0.0F)) + .texOffs(0, 63).addBox(-5.0F, -3.5F, -2.0F, 5.0F, 6.0F, 2.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(3.0F, -10.6866F, 24.5922F, 0.0F, -0.3927F, 0.0F)); + + PartDefinition cube_r18 = group2.addOrReplaceChild("cube_r18", CubeListBuilder.create().texOffs(116, 104).addBox(-7.0F, -11.5F, -3.0F, 14.0F, 9.0F, 1.0F, new CubeDeformation(0.0F)) + .texOffs(120, 137).addBox(-10.0F, -11.0F, -4.0F, 2.0F, 11.0F, 4.0F, new CubeDeformation(0.0F)) + .texOffs(132, 137).addBox(8.0F, -11.0F, -4.0F, 2.0F, 11.0F, 4.0F, new CubeDeformation(0.0F)) + .texOffs(60, 106).addBox(-8.0F, -12.0F, -2.0F, 16.0F, 12.0F, 2.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(8.0F, 3.9F, 20.0F, -0.3927F, 0.0F, 0.0F)); + + PartDefinition cube_r19 = group2.addOrReplaceChild("cube_r19", CubeListBuilder.create().texOffs(66, 48).addBox(8.5F, -4.0F, -9.7F, 1.0F, 4.0F, 2.0F, new CubeDeformation(0.0F)) + .texOffs(0, 56).addBox(-9.5F, -4.0F, -9.7F, 1.0F, 4.0F, 2.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(8.0F, 5.2F, 17.1F, -0.3927F, 0.0F, 0.0F)); + + PartDefinition cube_r20 = group2.addOrReplaceChild("cube_r20", CubeListBuilder.create().texOffs(60, 129).addBox(-4.0F, 0.0F, 1.0F, 8.0F, 9.0F, 2.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(8.0F, 1.9F, 6.0F, -0.3927F, 0.0F, 0.0F)); + + return LayerDefinition.create(meshdefinition, 256, 256); + } + + @Override + public void setupAnim(Entity entity, float limbSwing, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch) { + } + + @Override + public void renderToBuffer(PoseStack poseStack, VertexConsumer vertexConsumer, int packedLight, int packedOverlay, float red, float green, float blue, float alpha) { + main.render(poseStack, vertexConsumer, packedLight, packedOverlay, red, green, blue, alpha); + } +} \ No newline at end of file diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/GomokuModel.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/GomokuModel.java new file mode 100644 index 000000000..5cf744abc --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/GomokuModel.java @@ -0,0 +1,68 @@ +package com.github.tartaricacid.touhoulittlemaid.client.model; + +import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import net.minecraft.client.model.EntityModel; +import net.minecraft.client.model.geom.ModelLayerLocation; +import net.minecraft.client.model.geom.ModelPart; +import net.minecraft.client.model.geom.PartPose; +import net.minecraft.client.model.geom.builders.*; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.Entity; + +public class GomokuModel extends EntityModel { + public static final ModelLayerLocation LAYER = new ModelLayerLocation(new ResourceLocation(TouhouLittleMaid.MOD_ID, "main"), "gomoku"); + private final ModelPart main; + private final ModelPart blackBox; + private final ModelPart whiteBox; + + public GomokuModel(ModelPart root) { + this.main = root.getChild("main"); + this.blackBox = root.getChild("blackBox"); + this.whiteBox = root.getChild("whiteBox"); + } + + public static LayerDefinition createBodyLayer() { + MeshDefinition meshdefinition = new MeshDefinition(); + PartDefinition partdefinition = meshdefinition.getRoot(); + + PartDefinition main = partdefinition.addOrReplaceChild("main", CubeListBuilder.create().texOffs(0, 0).addBox(-75.5F, -61.51F, -75.5F, 151.0F, 151.0F, 151.0F, new CubeDeformation(-59.5F)) + .texOffs(0, 0).addBox(-16.0F, -2.0F, -16.0F, 32.0F, 2.0F, 32.0F, new CubeDeformation(0.0F)), PartPose.offset(0.0F, 24.0F, 0.0F)); + + PartDefinition blackBox = partdefinition.addOrReplaceChild("blackBox", CubeListBuilder.create().texOffs(69, 11).mirror().addBox(17.0F, -1.0F, 8.0F, 5.0F, 1.0F, 5.0F, new CubeDeformation(0.0F)).mirror(false) + .texOffs(12, 99).mirror().addBox(17.5F, -2.95F, 8.5F, 4.0F, 1.0F, 4.0F, new CubeDeformation(0.0F)).mirror(false) + .texOffs(12, 99).mirror().addBox(18.25F, -3.85F, 8.9F, 2.0F, 1.0F, 2.0F, new CubeDeformation(-0.25F)).mirror(false) + .texOffs(12, 99).mirror().addBox(19.0F, -3.85F, 10.4F, 2.0F, 1.0F, 2.0F, new CubeDeformation(-0.25F)).mirror(false) + .texOffs(69, 11).mirror().addBox(17.0F, -4.0F, 8.0F, 1.0F, 3.0F, 4.0F, new CubeDeformation(0.0F)).mirror(false) + .texOffs(69, 11).mirror().addBox(21.0F, -4.0F, 9.0F, 1.0F, 3.0F, 4.0F, new CubeDeformation(0.0F)).mirror(false) + .texOffs(69, 11).mirror().addBox(17.0F, -4.0F, 12.0F, 4.0F, 3.0F, 1.0F, new CubeDeformation(0.0F)).mirror(false) + .texOffs(69, 11).mirror().addBox(18.0F, -4.0F, 8.0F, 4.0F, 3.0F, 1.0F, new CubeDeformation(0.0F)).mirror(false), PartPose.offset(0.0F, 24.0F, 0.0F)); + + PartDefinition bone2 = blackBox.addOrReplaceChild("bone2", CubeListBuilder.create().texOffs(12, 99).mirror().addBox(-1.0F, -0.5F, -1.0F, 2.0F, 1.0F, 2.0F, new CubeDeformation(-0.25F)).mirror(false), PartPose.offsetAndRotation(18.5F, -4.15F, 9.9F, 0.0F, 0.0F, 0.3927F)); + + PartDefinition whiteBox = partdefinition.addOrReplaceChild("whiteBox", CubeListBuilder.create().texOffs(69, 11).addBox(-22.0F, -1.0F, -13.0F, 5.0F, 1.0F, 5.0F, new CubeDeformation(0.0F)) + .texOffs(44, 96).addBox(-21.5F, -2.95F, -12.5F, 4.0F, 1.0F, 4.0F, new CubeDeformation(0.0F)) + .texOffs(44, 96).addBox(-20.25F, -3.85F, -10.9F, 2.0F, 1.0F, 2.0F, new CubeDeformation(-0.25F)) + .texOffs(44, 96).addBox(-21.0F, -3.85F, -12.4F, 2.0F, 1.0F, 2.0F, new CubeDeformation(-0.25F)) + .texOffs(69, 11).addBox(-18.0F, -4.0F, -12.0F, 1.0F, 3.0F, 4.0F, new CubeDeformation(0.0F)) + .texOffs(69, 11).addBox(-22.0F, -4.0F, -13.0F, 1.0F, 3.0F, 4.0F, new CubeDeformation(0.0F)) + .texOffs(69, 11).addBox(-21.0F, -4.0F, -13.0F, 4.0F, 3.0F, 1.0F, new CubeDeformation(0.0F)) + .texOffs(69, 11).addBox(-22.0F, -4.0F, -9.0F, 4.0F, 3.0F, 1.0F, new CubeDeformation(0.0F)), PartPose.offset(0.0F, 24.0F, 0.0F)); + + PartDefinition bone4 = whiteBox.addOrReplaceChild("bone4", CubeListBuilder.create().texOffs(44, 96).addBox(-1.0F, -0.5F, -1.0F, 2.0F, 1.0F, 2.0F, new CubeDeformation(-0.25F)), PartPose.offsetAndRotation(-18.5F, -4.15F, -9.9F, 0.0F, 0.0F, -0.3927F)); + + return LayerDefinition.create(meshdefinition, 512, 512); + } + + @Override + public void setupAnim(Entity entity, float limbSwing, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch) { + } + + @Override + public void renderToBuffer(PoseStack poseStack, VertexConsumer vertexConsumer, int packedLight, int packedOverlay, float red, float green, float blue, float alpha) { + main.render(poseStack, vertexConsumer, packedLight, packedOverlay, red, green, blue, alpha); + blackBox.render(poseStack, vertexConsumer, packedLight, packedOverlay, red, green, blue, alpha); + whiteBox.render(poseStack, vertexConsumer, packedLight, packedOverlay, red, green, blue, alpha); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/KeyboardModel.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/KeyboardModel.java new file mode 100644 index 000000000..0d13ecaed --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/KeyboardModel.java @@ -0,0 +1,208 @@ +package com.github.tartaricacid.touhoulittlemaid.client.model; + +import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import net.minecraft.client.model.EntityModel; +import net.minecraft.client.model.geom.ModelLayerLocation; +import net.minecraft.client.model.geom.ModelPart; +import net.minecraft.client.model.geom.PartPose; +import net.minecraft.client.model.geom.builders.*; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.Entity; + +public class KeyboardModel extends EntityModel { + public static final ModelLayerLocation LAYER = new ModelLayerLocation(new ResourceLocation(TouhouLittleMaid.MOD_ID, "main"), "keyboard"); + private final ModelPart main; + + public KeyboardModel(ModelPart root) { + this.main = root.getChild("main"); + } + + public static LayerDefinition createBodyLayer() { + MeshDefinition meshdefinition = new MeshDefinition(); + PartDefinition partdefinition = meshdefinition.getRoot(); + + PartDefinition main = partdefinition.addOrReplaceChild("main", CubeListBuilder.create(), PartPose.offset(0.0F, 8.0F, 7.0F)); + + PartDefinition bone = main.addOrReplaceChild("bone", CubeListBuilder.create().texOffs(0, 0).addBox(-29.0F, 1.0F, 12.0F, 42.0F, 1.0F, 15.0F, new CubeDeformation(0.0F)) + .texOffs(0, 16).addBox(-29.0F, -1.0F, 18.0F, 42.0F, 2.0F, 9.0F, new CubeDeformation(0.0F)) + .texOffs(46, 71).addBox(-12.0F, -1.1F, 22.0F, 8.0F, 2.0F, 3.0F, new CubeDeformation(0.0F)) + .texOffs(29, 32).addBox(-9.0F, -1.1F, 20.5F, 2.0F, 2.0F, 1.0F, new CubeDeformation(0.0F)) + .texOffs(29, 29).addBox(-9.0F, -1.1F, 19.0F, 2.0F, 2.0F, 1.0F, new CubeDeformation(0.0F)) + .texOffs(18, 76).addBox(-29.0F, -0.4142F, 13.4142F, 4.0F, 2.0F, 5.0F, new CubeDeformation(0.0F)) + .texOffs(0, 75).addBox(9.0F, -0.4142F, 13.4142F, 4.0F, 2.0F, 5.0F, new CubeDeformation(0.0F)) + .texOffs(44, 47).addBox(-20.0F, 2.0F, 22.0F, 24.0F, 1.0F, 4.0F, new CubeDeformation(0.0F)) + .texOffs(0, 27).addBox(-19.0F, 2.0F, 17.0F, 2.0F, 1.0F, 5.0F, new CubeDeformation(0.0F)) + .texOffs(0, 27).mirror().addBox(1.0F, 2.0F, 17.0F, 2.0F, 1.0F, 5.0F, new CubeDeformation(0.0F)).mirror(false) + .texOffs(0, 11).addBox(10.0F, 15.0F, 22.0F, 3.0F, 1.0F, 3.0F, new CubeDeformation(0.0F)) + .texOffs(0, 11).addBox(-29.0F, 15.0F, 23.0F, 3.0F, 1.0F, 3.0F, new CubeDeformation(0.0F)) + .texOffs(44, 47).addBox(-20.0F, 2.0F, 13.0F, 24.0F, 1.0F, 4.0F, new CubeDeformation(0.0F)) + .texOffs(0, 11).addBox(-29.0F, 15.0F, 13.0F, 3.0F, 1.0F, 3.0F, new CubeDeformation(0.0F)) + .texOffs(0, 27).addBox(8.5F, -5.0F, -15.0F, 7.0F, 21.0F, 15.0F, new CubeDeformation(0.0F)) + .texOffs(44, 29).addBox(7.5F, -6.0F, -16.0F, 8.0F, 1.0F, 17.0F, new CubeDeformation(0.0F)) + .texOffs(60, 63).addBox(9.5F, -7.0F, -9.25F, 5.0F, 1.0F, 7.0F, new CubeDeformation(0.0F)) + .texOffs(0, 0).addBox(11.5F, -14.0F, -7.75F, 2.0F, 7.0F, 4.0F, new CubeDeformation(0.0F)) + .texOffs(9, 30).addBox(10.25F, -11.0F, -6.25F, 2.0F, 1.0F, 1.0F, new CubeDeformation(-0.3F)) + .texOffs(29, 35).addBox(10.75F, -11.0F, -6.25F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.2F)) + .texOffs(0, 11).addBox(10.0F, 15.0F, 14.0F, 3.0F, 1.0F, 3.0F, new CubeDeformation(0.0F)), PartPose.offset(8.0F, 0.0F, -11.0F)); + + PartDefinition cube_r1 = bone.addOrReplaceChild("cube_r1", CubeListBuilder.create().texOffs(0, 30).addBox(-0.5F, 1.7F, -0.5F, 1.0F, 1.0F, 1.0F, new CubeDeformation(-0.275F)) + .texOffs(0, 0).addBox(-0.5F, -4.5F, -0.5F, 1.0F, 2.0F, 1.0F, new CubeDeformation(-0.4F)) + .texOffs(8, 33).addBox(-0.5F, -5.5F, -0.5F, 1.0F, 8.0F, 1.0F, new CubeDeformation(-0.45F)), PartPose.offsetAndRotation(11.25F, -10.5F, -5.75F, 0.7854F, 0.0F, 0.0F)); + + PartDefinition cube_r2 = bone.addOrReplaceChild("cube_r2", CubeListBuilder.create().texOffs(48, 76).addBox(-7.0F, 4.5F, -4.5F, 1.0F, 1.0F, 7.0F, new CubeDeformation(0.0F)) + .texOffs(48, 76).addBox(17.0F, 4.5F, -4.5F, 1.0F, 1.0F, 7.0F, new CubeDeformation(0.0F)) + .texOffs(29, 27).addBox(-10.0F, 4.5F, -5.5F, 34.0F, 1.0F, 1.0F, new CubeDeformation(0.0F)) + .texOffs(29, 27).addBox(-10.0F, 4.5F, 2.5F, 34.0F, 1.0F, 1.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(-8.0F, 1.5F, 20.5F, 0.0F, 0.0F, 0.3927F)); + + PartDefinition cube_r3 = bone.addOrReplaceChild("cube_r3", CubeListBuilder.create().texOffs(35, 71).addBox(6.0F, 4.5F, -5.5F, 1.0F, 1.0F, 9.0F, new CubeDeformation(0.0F)) + .texOffs(35, 71).addBox(-18.0F, 4.5F, -5.5F, 1.0F, 1.0F, 9.0F, new CubeDeformation(0.0F)) + .texOffs(29, 27).addBox(-24.0F, 4.5F, -6.5F, 34.0F, 1.0F, 1.0F, new CubeDeformation(0.0F)) + .texOffs(29, 27).addBox(-24.0F, 4.5F, 3.5F, 34.0F, 1.0F, 1.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(-8.0F, 1.5F, 20.5F, 0.0F, 0.0F, -0.3927F)); + + PartDefinition cube_r4 = bone.addOrReplaceChild("cube_r4", CubeListBuilder.create().texOffs(18, 63).addBox(-0.5F, -0.5F, -6.0F, 1.0F, 1.0F, 12.0F, new CubeDeformation(-0.2F)), PartPose.offsetAndRotation(-8.0F, 6.9F, 19.5F, 0.0F, 0.0F, -0.7854F)); + + PartDefinition cube_r5 = bone.addOrReplaceChild("cube_r5", CubeListBuilder.create().texOffs(44, 42).addBox(-2.0F, -0.5F, -1.5F, 4.0F, 1.0F, 2.0F, new CubeDeformation(0.0F)) + .texOffs(32, 71).addBox(-40.0F, -0.5F, -1.5F, 4.0F, 1.0F, 2.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(11.0F, 0.2929F, 13.4142F, 0.7854F, 0.0F, 0.0F)); + + PartDefinition cube_r6 = bone.addOrReplaceChild("cube_r6", CubeListBuilder.create().texOffs(0, 63).addBox(-7.0F, -10.5F, -1.0F, 14.0F, 11.0F, 1.0F, new CubeDeformation(-0.475F)) + .texOffs(29, 39).addBox(-6.0F, -0.4F, -1.4F, 12.0F, 1.0F, 2.0F, new CubeDeformation(-0.4F)) + .texOffs(44, 52).addBox(-9.0F, -9.0F, -0.5F, 18.0F, 10.0F, 1.0F, new CubeDeformation(-0.3F)), PartPose.offsetAndRotation(-8.0F, -1.0F, 26.5F, -0.3927F, 0.0F, 0.0F)); + + PartDefinition bone3 = bone.addOrReplaceChild("bone3", CubeListBuilder.create().texOffs(8, 0).addBox(-5.7F, -2.85F, 1.0F, 2.0F, 2.0F, 1.0F, new CubeDeformation(-0.25F)) + .texOffs(8, 0).addBox(-3.7F, -2.85F, 1.0F, 2.0F, 2.0F, 1.0F, new CubeDeformation(-0.25F)) + .texOffs(8, 0).addBox(1.7F, -2.85F, 1.0F, 2.0F, 2.0F, 1.0F, new CubeDeformation(-0.25F)) + .texOffs(8, 0).addBox(3.7F, -2.85F, 1.0F, 2.0F, 2.0F, 1.0F, new CubeDeformation(-0.25F)), PartPose.offset(-8.0F, 1.5F, 19.5F)); + + PartDefinition bone4 = bone.addOrReplaceChild("bone4", CubeListBuilder.create().texOffs(9, 11).addBox(-5.7F, -2.85F, 0.25F, 2.0F, 2.0F, 1.0F, new CubeDeformation(-0.25F)) + .texOffs(9, 11).addBox(-3.7F, -2.85F, 0.25F, 2.0F, 2.0F, 1.0F, new CubeDeformation(-0.25F)) + .texOffs(9, 11).addBox(1.7F, -2.85F, 0.25F, 2.0F, 2.0F, 1.0F, new CubeDeformation(-0.25F)) + .texOffs(9, 11).addBox(3.7F, -2.85F, 0.25F, 2.0F, 2.0F, 1.0F, new CubeDeformation(-0.25F)), PartPose.offset(-8.0F, 1.5F, 19.5F)); + + PartDefinition bone5 = bone.addOrReplaceChild("bone5", CubeListBuilder.create().texOffs(9, 27).addBox(-5.7F, -2.85F, -0.5F, 2.0F, 2.0F, 1.0F, new CubeDeformation(-0.25F)) + .texOffs(9, 27).addBox(-3.7F, -2.85F, -0.5F, 2.0F, 2.0F, 1.0F, new CubeDeformation(-0.25F)) + .texOffs(9, 27).addBox(1.7F, -2.85F, -0.5F, 2.0F, 2.0F, 1.0F, new CubeDeformation(-0.25F)) + .texOffs(9, 27).addBox(3.7F, -2.85F, -0.5F, 2.0F, 2.0F, 1.0F, new CubeDeformation(-0.25F)), PartPose.offset(-8.0F, 1.5F, 19.5F)); + + PartDefinition bone2 = bone.addOrReplaceChild("bone2", CubeListBuilder.create().texOffs(0, 27).addBox(-7.7F, -2.85F, 4.0F, 1.0F, 2.0F, 1.0F, new CubeDeformation(-0.25F)) + .texOffs(0, 27).addBox(-7.7F, -2.85F, 3.25F, 1.0F, 2.0F, 1.0F, new CubeDeformation(-0.25F)) + .texOffs(0, 27).addBox(-7.7F, -2.85F, 2.5F, 1.0F, 2.0F, 1.0F, new CubeDeformation(-0.25F)) + .texOffs(0, 27).addBox(-6.7F, -2.85F, 4.0F, 1.0F, 2.0F, 1.0F, new CubeDeformation(-0.25F)) + .texOffs(0, 27).addBox(-6.7F, -2.85F, 3.25F, 1.0F, 2.0F, 1.0F, new CubeDeformation(-0.25F)) + .texOffs(0, 27).addBox(-6.7F, -2.85F, 2.5F, 1.0F, 2.0F, 1.0F, new CubeDeformation(-0.25F)) + .texOffs(0, 27).addBox(-5.7F, -2.85F, 4.0F, 1.0F, 2.0F, 1.0F, new CubeDeformation(-0.25F)) + .texOffs(0, 27).addBox(-5.7F, -2.85F, 3.25F, 1.0F, 2.0F, 1.0F, new CubeDeformation(-0.25F)) + .texOffs(0, 27).addBox(-5.7F, -2.85F, 2.5F, 1.0F, 2.0F, 1.0F, new CubeDeformation(-0.25F)) + .texOffs(0, 27).addBox(4.7F, -2.85F, 4.0F, 1.0F, 2.0F, 1.0F, new CubeDeformation(-0.25F)) + .texOffs(0, 27).addBox(-7.7F, -2.85F, 1.0F, 1.0F, 2.0F, 1.0F, new CubeDeformation(-0.25F)) + .texOffs(0, 27).addBox(-7.7F, -2.85F, 0.25F, 1.0F, 2.0F, 1.0F, new CubeDeformation(-0.25F)) + .texOffs(0, 27).addBox(-7.7F, -2.85F, -0.5F, 1.0F, 2.0F, 1.0F, new CubeDeformation(-0.25F)) + .texOffs(0, 27).addBox(-6.7F, -2.85F, 1.0F, 1.0F, 2.0F, 1.0F, new CubeDeformation(-0.25F)) + .texOffs(0, 27).addBox(-6.7F, -2.85F, 0.25F, 1.0F, 2.0F, 1.0F, new CubeDeformation(-0.25F)) + .texOffs(0, 27).addBox(-6.7F, -2.85F, -0.5F, 1.0F, 2.0F, 1.0F, new CubeDeformation(-0.25F)) + .texOffs(0, 27).addBox(4.7F, -2.85F, 3.25F, 1.0F, 2.0F, 1.0F, new CubeDeformation(-0.25F)) + .texOffs(0, 27).addBox(4.7F, -2.85F, 2.5F, 1.0F, 2.0F, 1.0F, new CubeDeformation(-0.25F)) + .texOffs(0, 27).addBox(5.7F, -2.85F, 4.0F, 1.0F, 2.0F, 1.0F, new CubeDeformation(-0.25F)) + .texOffs(0, 27).addBox(5.7F, -2.85F, 3.25F, 1.0F, 2.0F, 1.0F, new CubeDeformation(-0.25F)) + .texOffs(0, 27).addBox(5.7F, -2.85F, 2.5F, 1.0F, 2.0F, 1.0F, new CubeDeformation(-0.25F)) + .texOffs(0, 27).addBox(6.7F, -2.85F, 4.0F, 1.0F, 2.0F, 1.0F, new CubeDeformation(-0.25F)) + .texOffs(0, 27).addBox(6.7F, -2.85F, 3.25F, 1.0F, 2.0F, 1.0F, new CubeDeformation(-0.25F)) + .texOffs(0, 27).addBox(6.7F, -2.85F, 2.5F, 1.0F, 2.0F, 1.0F, new CubeDeformation(-0.25F)) + .texOffs(0, 27).addBox(5.7F, -2.85F, 1.0F, 1.0F, 2.0F, 1.0F, new CubeDeformation(-0.25F)) + .texOffs(0, 27).addBox(5.7F, -2.85F, 0.25F, 1.0F, 2.0F, 1.0F, new CubeDeformation(-0.25F)) + .texOffs(0, 27).addBox(5.7F, -2.85F, -0.5F, 1.0F, 2.0F, 1.0F, new CubeDeformation(-0.25F)) + .texOffs(0, 27).addBox(6.7F, -2.85F, -0.5F, 1.0F, 2.0F, 1.0F, new CubeDeformation(-0.25F)) + .texOffs(0, 27).addBox(6.7F, -2.85F, 0.25F, 1.0F, 2.0F, 1.0F, new CubeDeformation(-0.25F)) + .texOffs(0, 27).addBox(6.7F, -2.85F, 1.0F, 1.0F, 2.0F, 1.0F, new CubeDeformation(-0.25F)), PartPose.offset(-8.0F, 1.5F, 19.5F)); + + PartDefinition group = bone.addOrReplaceChild("group", CubeListBuilder.create().texOffs(60, 71).addBox(-14.0F, -1.1F, -6.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.175F)) + .texOffs(60, 71).addBox(-10.4F, -1.1F, -6.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.175F)) + .texOffs(60, 71).addBox(-13.1F, -1.1F, -6.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.175F)) + .texOffs(60, 71).addBox(-12.2F, -1.1F, -6.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.175F)) + .texOffs(60, 71).addBox(-4.1F, -1.1F, -6.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.175F)) + .texOffs(60, 71).addBox(-3.2F, -1.1F, -6.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.175F)) + .texOffs(60, 71).addBox(-5.9F, -1.1F, -6.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.175F)) + .texOffs(60, 71).addBox(-6.8F, -1.1F, -6.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.175F)) + .texOffs(60, 71).addBox(-7.7F, -1.1F, -6.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.175F)) + .texOffs(60, 71).addBox(-9.5F, -1.1F, -6.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.175F)) + .texOffs(60, 71).addBox(-1.4F, -1.1F, -6.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.175F)) + .texOffs(60, 71).addBox(0.4F, -1.1F, -6.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.175F)) + .texOffs(60, 71).addBox(-0.5F, -1.1F, -6.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.175F)) + .texOffs(60, 71).addBox(2.2F, -1.1F, -6.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.175F)) + .texOffs(60, 71).addBox(3.1F, -1.1F, -6.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.175F)) + .texOffs(60, 71).addBox(4.9F, -1.1F, -6.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.175F)) + .texOffs(60, 71).addBox(5.8F, -1.1F, -6.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.175F)) + .texOffs(60, 71).addBox(6.7F, -1.1F, -6.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.175F)) + .texOffs(60, 71).addBox(9.4F, -1.1F, -6.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.175F)) + .texOffs(60, 71).addBox(8.5F, -1.1F, -6.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.175F)) + .texOffs(60, 71).addBox(11.2F, -1.1F, -6.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.175F)) + .texOffs(60, 71).addBox(-16.7F, -1.1F, -6.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.175F)) + .texOffs(60, 71).addBox(-15.8F, -1.1F, -6.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.175F)) + .texOffs(60, 71).addBox(15.7F, -1.1F, -6.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.175F)) + .texOffs(60, 71).addBox(14.8F, -1.1F, -6.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.175F)) + .texOffs(60, 71).addBox(13.0F, -1.1F, -6.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.175F)) + .texOffs(60, 71).addBox(12.1F, -1.1F, -6.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.175F)), PartPose.offset(-8.0F, 1.2F, 21.4F)); + + PartDefinition group2 = bone.addOrReplaceChild("group2", CubeListBuilder.create().texOffs(70, 72).addBox(16.15F, -0.725F, -8.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.075F)) + .texOffs(70, 72).addBox(-14.45F, -0.725F, -8.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.075F)) + .texOffs(70, 72).addBox(-11.75F, -0.725F, -8.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.075F)) + .texOffs(70, 72).addBox(-10.85F, -0.725F, -8.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.075F)) + .texOffs(70, 72).addBox(-13.55F, -0.725F, -8.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.075F)) + .texOffs(70, 72).addBox(-12.65F, -0.725F, -8.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.075F)) + .texOffs(70, 72).addBox(-4.55F, -0.725F, -8.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.075F)) + .texOffs(70, 72).addBox(-3.65F, -0.725F, -8.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.075F)) + .texOffs(70, 72).addBox(-6.35F, -0.725F, -8.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.075F)) + .texOffs(70, 72).addBox(-5.45F, -0.725F, -8.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.075F)) + .texOffs(70, 72).addBox(-7.25F, -0.725F, -8.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.075F)) + .texOffs(70, 72).addBox(-8.15F, -0.725F, -8.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.075F)) + .texOffs(70, 72).addBox(-9.05F, -0.725F, -8.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.075F)) + .texOffs(70, 72).addBox(-9.95F, -0.725F, -8.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.075F)) + .texOffs(70, 72).addBox(-2.75F, -0.725F, -8.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.075F)) + .texOffs(70, 72).addBox(-1.85F, -0.725F, -8.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.075F)) + .texOffs(70, 72).addBox(-0.05F, -0.725F, -8.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.075F)) + .texOffs(70, 72).addBox(-0.95F, -0.725F, -8.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.075F)) + .texOffs(70, 72).addBox(1.75F, -0.725F, -8.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.075F)) + .texOffs(70, 72).addBox(0.85F, -0.725F, -8.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.075F)) + .texOffs(70, 72).addBox(3.55F, -0.725F, -8.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.075F)) + .texOffs(70, 72).addBox(2.65F, -0.725F, -8.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.075F)) + .texOffs(70, 72).addBox(4.45F, -0.725F, -8.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.075F)) + .texOffs(70, 72).addBox(5.35F, -0.725F, -8.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.075F)) + .texOffs(70, 72).addBox(6.25F, -0.725F, -8.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.075F)) + .texOffs(70, 72).addBox(7.15F, -0.725F, -8.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.075F)) + .texOffs(70, 72).addBox(8.95F, -0.725F, -8.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.075F)) + .texOffs(70, 72).addBox(8.05F, -0.725F, -8.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.075F)) + .texOffs(70, 72).addBox(10.75F, -0.725F, -8.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.075F)) + .texOffs(70, 72).addBox(9.85F, -0.725F, -8.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.075F)) + .texOffs(70, 72).addBox(-17.15F, -0.725F, -8.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.075F)) + .texOffs(70, 72).addBox(-16.25F, -0.725F, -8.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.075F)) + .texOffs(70, 72).addBox(-15.35F, -0.725F, -8.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.075F)) + .texOffs(70, 72).addBox(15.25F, -0.725F, -8.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.075F)) + .texOffs(70, 72).addBox(14.35F, -0.725F, -8.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.075F)) + .texOffs(70, 72).addBox(13.45F, -0.725F, -8.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.075F)) + .texOffs(70, 72).addBox(12.55F, -0.725F, -8.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.075F)) + .texOffs(70, 72).addBox(11.65F, -0.725F, -8.85F, 1.0F, 1.0F, 8.0F, new CubeDeformation(-0.075F)), PartPose.offset(-8.0F, 1.2F, 21.4F)); + + PartDefinition bone6 = main.addOrReplaceChild("bone6", CubeListBuilder.create().texOffs(32, 63).addBox(-3.5F, 6.0F, -10.5F, 7.0F, 1.0F, 7.0F, new CubeDeformation(0.0F)) + .texOffs(29, 29).addBox(-4.0F, 7.0F, -11.0F, 8.0F, 2.0F, 8.0F, new CubeDeformation(0.0F)) + .texOffs(0, 33).addBox(-4.0F, 9.0F, -11.0F, 2.0F, 7.0F, 2.0F, new CubeDeformation(0.0F)) + .texOffs(0, 16).addBox(-4.0F, 9.0F, -5.0F, 2.0F, 7.0F, 2.0F, new CubeDeformation(0.0F)) + .texOffs(77, 38).addBox(-3.5F, 11.0F, -9.0F, 1.0F, 2.0F, 4.0F, new CubeDeformation(0.0F)) + .texOffs(31, 76).addBox(-2.0F, 13.0F, -10.5F, 4.0F, 2.0F, 1.0F, new CubeDeformation(0.0F)) + .texOffs(80, 61).addBox(2.0F, 9.0F, -11.0F, 2.0F, 7.0F, 2.0F, new CubeDeformation(0.0F)) + .texOffs(13, 76).addBox(-2.0F, 13.0F, -4.5F, 4.0F, 2.0F, 1.0F, new CubeDeformation(0.0F)) + .texOffs(77, 29).addBox(2.0F, 9.0F, -5.0F, 2.0F, 7.0F, 2.0F, new CubeDeformation(0.0F)) + .texOffs(53, 63).addBox(2.5F, 11.0F, -9.0F, 1.0F, 2.0F, 4.0F, new CubeDeformation(0.0F)), PartPose.offset(0.0F, 0.0F, 0.0F)); + + return LayerDefinition.create(meshdefinition, 128, 128); + } + + @Override + public void setupAnim(Entity entity, float limbSwing, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch) { + } + + @Override + public void renderToBuffer(PoseStack poseStack, VertexConsumer vertexConsumer, int packedLight, int packedOverlay, float red, float green, float blue, float alpha) { + main.render(poseStack, vertexConsumer, packedLight, packedOverlay, red, green, blue, alpha); + } +} \ No newline at end of file diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/PieceModel.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/PieceModel.java new file mode 100644 index 000000000..ca228cbd4 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/PieceModel.java @@ -0,0 +1,37 @@ +package com.github.tartaricacid.touhoulittlemaid.client.model; + +import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import net.minecraft.client.model.EntityModel; +import net.minecraft.client.model.geom.ModelLayerLocation; +import net.minecraft.client.model.geom.ModelPart; +import net.minecraft.client.model.geom.PartPose; +import net.minecraft.client.model.geom.builders.*; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.Entity; + +public class PieceModel extends EntityModel { + public static final ModelLayerLocation LAYER = new ModelLayerLocation(new ResourceLocation(TouhouLittleMaid.MOD_ID, "main"), "piece"); + private final ModelPart main; + + public PieceModel(ModelPart root) { + this.main = root.getChild("main"); + } + + public static LayerDefinition createBodyLayer() { + MeshDefinition meshdefinition = new MeshDefinition(); + PartDefinition partdefinition = meshdefinition.getRoot(); + PartDefinition main = partdefinition.addOrReplaceChild("main", CubeListBuilder.create().texOffs(0, 0).addBox(-1.0F, -1.0F, -1.0F, 2.0F, 1.0F, 2.0F, new CubeDeformation(-0.25F)), PartPose.offset(0.0F, 24.0F, 0.0F)); + return LayerDefinition.create(meshdefinition, 16, 16); + } + + @Override + public void setupAnim(Entity entity, float limbSwing, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch) { + } + + @Override + public void renderToBuffer(PoseStack poseStack, VertexConsumer vertexConsumer, int packedLight, int packedOverlay, float red, float green, float blue, float alpha) { + main.render(poseStack, vertexConsumer, packedLight, packedOverlay, red, green, blue, alpha); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/ShrineModel.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/ShrineModel.java new file mode 100644 index 000000000..6c17f00c9 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/ShrineModel.java @@ -0,0 +1,177 @@ +package com.github.tartaricacid.touhoulittlemaid.client.model; + +import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import net.minecraft.client.model.EntityModel; +import net.minecraft.client.model.geom.ModelLayerLocation; +import net.minecraft.client.model.geom.ModelPart; +import net.minecraft.client.model.geom.PartPose; +import net.minecraft.client.model.geom.builders.*; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.Entity; + +public class ShrineModel extends EntityModel { + public static final ModelLayerLocation LAYER = new ModelLayerLocation(new ResourceLocation(TouhouLittleMaid.MOD_ID, "main"), "shrine"); + private final ModelPart main; + + public ShrineModel(ModelPart root) { + this.main = root.getChild("main"); + } + + public static LayerDefinition createBodyLayer() { + MeshDefinition meshdefinition = new MeshDefinition(); + PartDefinition partdefinition = meshdefinition.getRoot(); + + PartDefinition main = partdefinition.addOrReplaceChild("main", CubeListBuilder.create().texOffs(0, 0).addBox(-7.5F, -5.0F, -7.5F, 15.0F, 5.0F, 15.0F, new CubeDeformation(0.0F)) + .texOffs(0, 21).addBox(-6.0F, -10.0F, -6.0F, 12.0F, 1.0F, 12.0F, new CubeDeformation(0.0F)) + .texOffs(60, 26).mirror().addBox(-3.5F, -19.0F, -3.5F, 7.0F, 1.0F, 7.0F, new CubeDeformation(0.0F)).mirror(false) + .texOffs(73, 35).addBox(-3.5F, -17.5F, 2.5F, 7.0F, 6.0F, 1.0F, new CubeDeformation(0.0F)) + .texOffs(31, 39).addBox(-5.0F, -10.5F, -5.0F, 10.0F, 1.0F, 10.0F, new CubeDeformation(0.0F)) + .texOffs(42, 21).addBox(-0.5F, -23.75F, -3.75F, 1.0F, 4.0F, 1.0F, new CubeDeformation(0.0F)) + .texOffs(0, 37).addBox(-0.5F, -23.75F, 2.75F, 1.0F, 4.0F, 1.0F, new CubeDeformation(0.0F)) + .texOffs(98, 0).addBox(-0.5F, -24.5F, -7.0F, 1.0F, 1.0F, 14.0F, new CubeDeformation(0.0F)) + .texOffs(21, 68).mirror().addBox(2.5F, -17.5F, -3.5F, 1.0F, 6.0F, 7.0F, new CubeDeformation(0.0F)).mirror(false) + .texOffs(21, 68).addBox(-3.5F, -17.5F, -3.5F, 1.0F, 6.0F, 7.0F, new CubeDeformation(0.0F)) + .texOffs(60, 26).addBox(-3.5F, -11.5F, -3.5F, 7.0F, 1.0F, 7.0F, new CubeDeformation(0.0F)) + .texOffs(60, 26).addBox(-3.5F, -18.0F, -3.5F, 7.0F, 1.0F, 7.0F, new CubeDeformation(0.0F)), PartPose.offset(0.0F, 24.0F, 0.0F)); + + PartDefinition cube_r1 = main.addOrReplaceChild("cube_r1", CubeListBuilder.create().texOffs(0, 0).mirror().addBox(-2.5F, -2.5F, -0.5F, 5.0F, 5.0F, 1.0F, new CubeDeformation(0.0F)).mirror(false) + .texOffs(0, 8).addBox(-2.5F, -2.5F, -6.5F, 5.0F, 5.0F, 1.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(0.0F, -20.6005F, 3.0F, 0.0F, 0.0F, -0.7854F)); + + PartDefinition cube_r2 = main.addOrReplaceChild("cube_r2", CubeListBuilder.create().texOffs(98, 0).addBox(-0.5F, -5.034F, 9.2477F, 1.0F, 1.0F, 2.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(0.0F, -16.2342F, 0.0F, 0.3927F, 0.0F, 0.0F)); + + PartDefinition cube_r3 = main.addOrReplaceChild("cube_r3", CubeListBuilder.create().texOffs(98, 0).mirror().addBox(-0.5F, -5.034F, -11.2477F, 1.0F, 1.0F, 2.0F, new CubeDeformation(0.0F)).mirror(false), PartPose.offsetAndRotation(0.0F, -16.2342F, 0.0F, -0.3927F, 0.0F, 0.0F)); + + PartDefinition cube_r4 = main.addOrReplaceChild("cube_r4", CubeListBuilder.create().texOffs(98, 0).mirror().addBox(-6.9399F, -5.7711F, -4.0F, 5.25F, 0.5F, 1.0F, new CubeDeformation(0.0F)).mirror(false) + .texOffs(98, 0).addBox(-6.9399F, -5.7711F, 3.0F, 5.25F, 0.5F, 1.0F, new CubeDeformation(0.0F)) + .texOffs(35, 21).addBox(-6.4781F, -5.6293F, -7.0F, 5.0F, 0.5F, 14.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(0.0F, -16.2342F, 0.0F, 0.0F, 0.0F, -0.3927F)); + + PartDefinition cube_r5 = main.addOrReplaceChild("cube_r5", CubeListBuilder.create().texOffs(98, 0).mirror().addBox(-6.0166F, -5.4559F, -4.0F, 0.5F, 5.0F, 1.0F, new CubeDeformation(0.0F)).mirror(false) + .texOffs(98, 0).addBox(-6.0166F, -5.4559F, 3.0F, 0.5F, 5.0F, 1.0F, new CubeDeformation(0.0F)) + .texOffs(33, 51).addBox(-5.8044F, -5.5973F, -7.0F, 0.5F, 5.0F, 14.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(0.0F, -16.2342F, 0.0F, 0.0F, 0.0F, 0.7854F)); + + PartDefinition cube_r6 = main.addOrReplaceChild("cube_r6", CubeListBuilder.create().texOffs(98, 0).addBox(1.6899F, -5.7711F, 3.0F, 5.25F, 0.5F, 1.0F, new CubeDeformation(0.0F)) + .texOffs(98, 0).addBox(1.6899F, -5.7711F, -4.0F, 5.25F, 0.5F, 1.0F, new CubeDeformation(0.0F)) + .texOffs(35, 21).mirror().addBox(1.4781F, -5.6293F, -7.0F, 5.0F, 0.5F, 14.0F, new CubeDeformation(0.0F)).mirror(false), PartPose.offsetAndRotation(0.0F, -16.2342F, 0.0F, 0.0F, 0.0F, 0.3927F)); + + PartDefinition cube_r7 = main.addOrReplaceChild("cube_r7", CubeListBuilder.create().texOffs(98, 0).addBox(5.5166F, -5.4559F, 3.0F, 0.5F, 5.0F, 1.0F, new CubeDeformation(0.0F)) + .texOffs(98, 0).addBox(5.5166F, -5.4559F, -4.0F, 0.5F, 5.0F, 1.0F, new CubeDeformation(0.0F)) + .texOffs(61, 13).addBox(0.5771F, -5.5771F, -3.5F, 5.0F, 5.0F, 1.0F, new CubeDeformation(0.0F)) + .texOffs(61, 13).addBox(0.2235F, -5.2235F, 2.5F, 5.0F, 5.0F, 1.0F, new CubeDeformation(0.0F)) + .texOffs(33, 51).mirror().addBox(5.3044F, -5.5973F, -7.0F, 0.5F, 5.0F, 14.0F, new CubeDeformation(0.0F)).mirror(false), PartPose.offsetAndRotation(0.0F, -16.2342F, 0.0F, 0.0F, 0.0F, -0.7854F)); + + PartDefinition bone7 = main.addOrReplaceChild("bone7", CubeListBuilder.create().texOffs(110, 36).addBox(2.4833F, -1.7325F, -3.7F, 0.55F, 7.0F, 0.2F, new CubeDeformation(0.0F)) + .texOffs(110, 36).mirror().addBox(-3.5F, 4.2508F, -3.7F, 7.0F, 0.55F, 0.2F, new CubeDeformation(0.0F)).mirror(false) + .texOffs(110, 36).mirror().addBox(-3.0333F, -1.7325F, -3.7F, 0.55F, 7.0F, 0.2F, new CubeDeformation(0.0F)).mirror(false) + .texOffs(110, 36).mirror().addBox(-3.5F, -1.2658F, -3.7F, 7.0F, 0.55F, 0.2F, new CubeDeformation(0.0F)).mirror(false) + .texOffs(110, 36).addBox(3.5F, -0.3158F, -3.5F, 0.2F, 0.2F, 7.0F, new CubeDeformation(0.0F)) + .texOffs(110, 36).mirror().addBox(3.5F, -1.7325F, 2.4833F, 0.2F, 7.0F, 0.55F, new CubeDeformation(0.0F)).mirror(false) + .texOffs(110, 36).addBox(3.5F, 4.2508F, -3.5F, 0.2F, 0.55F, 7.0F, new CubeDeformation(0.0F)) + .texOffs(110, 36).addBox(3.5F, 3.6508F, -3.5F, 0.2F, 0.2F, 7.0F, new CubeDeformation(0.0F)) + .texOffs(110, 36).addBox(3.5F, -1.7325F, -3.0333F, 0.2F, 7.0F, 0.55F, new CubeDeformation(0.0F)) + .texOffs(110, 36).addBox(3.5F, -1.2658F, -3.5F, 0.2F, 0.55F, 7.0F, new CubeDeformation(0.0F)) + .texOffs(110, 36).addBox(3.5F, -1.7325F, -2.0833F, 0.2F, 7.0F, 0.2F, new CubeDeformation(0.0F)) + .texOffs(110, 36).addBox(3.5F, -1.7325F, 1.8833F, 0.2F, 7.0F, 0.2F, new CubeDeformation(0.0F)) + .texOffs(110, 36).addBox(-3.0333F, -1.7325F, 3.5F, 0.55F, 7.0F, 0.2F, new CubeDeformation(0.0F)) + .texOffs(110, 36).addBox(-3.5F, -1.2658F, 3.5F, 7.0F, 0.55F, 0.2F, new CubeDeformation(0.0F)) + .texOffs(110, 36).addBox(2.4833F, -1.7325F, 3.5F, 0.55F, 7.0F, 0.2F, new CubeDeformation(0.0F)) + .texOffs(110, 36).addBox(1.8833F, -1.7325F, 3.5F, 0.2F, 7.0F, 0.2F, new CubeDeformation(0.0F)) + .texOffs(110, 36).addBox(-3.5F, 4.2508F, 3.5F, 7.0F, 0.55F, 0.2F, new CubeDeformation(0.0F)) + .texOffs(110, 36).addBox(-3.5F, 3.6508F, 3.5F, 7.0F, 0.2F, 0.2F, new CubeDeformation(0.0F)) + .texOffs(110, 36).addBox(-3.5F, -0.3158F, 3.5F, 7.0F, 0.2F, 0.2F, new CubeDeformation(0.0F)) + .texOffs(110, 36).addBox(-2.0833F, -1.7325F, 3.5F, 0.2F, 7.0F, 0.2F, new CubeDeformation(0.0F)) + .texOffs(110, 36).addBox(-3.7F, -1.7325F, 2.4833F, 0.2F, 7.0F, 0.55F, new CubeDeformation(0.0F)) + .texOffs(110, 36).mirror().addBox(-3.7F, -1.2658F, -3.5F, 0.2F, 0.55F, 7.0F, new CubeDeformation(0.0F)).mirror(false) + .texOffs(110, 36).mirror().addBox(-3.7F, -0.3158F, -3.5F, 0.2F, 0.2F, 7.0F, new CubeDeformation(0.0F)).mirror(false) + .texOffs(110, 36).addBox(-3.7F, -1.7325F, -3.0333F, 0.2F, 7.0F, 0.55F, new CubeDeformation(0.0F)) + .texOffs(110, 36).mirror().addBox(-3.7F, 4.2508F, -3.5F, 0.2F, 0.55F, 7.0F, new CubeDeformation(0.0F)).mirror(false) + .texOffs(110, 36).mirror().addBox(-3.7F, 3.6508F, -3.5F, 0.2F, 0.2F, 7.0F, new CubeDeformation(0.0F)).mirror(false) + .texOffs(110, 36).addBox(-3.7F, -1.7325F, -2.0833F, 0.2F, 7.0F, 0.2F, new CubeDeformation(0.0F)) + .texOffs(110, 36).addBox(-3.7F, -1.7325F, 1.8833F, 0.2F, 7.0F, 0.2F, new CubeDeformation(0.0F)), PartPose.offset(0.0F, -16.2342F, 0.0F)); + + PartDefinition bone4 = main.addOrReplaceChild("bone4", CubeListBuilder.create().texOffs(32, 36).addBox(-6.75F, 7.9842F, 6.5F, 14.0F, 1.0F, 0.5F, new CubeDeformation(-0.1F)) + .texOffs(0, 35).addBox(3.25F, 7.9842F, -6.5F, 4.0F, 1.0F, 0.5F, new CubeDeformation(-0.1F)) + .texOffs(0, 35).addBox(-6.75F, 7.9842F, -6.5F, 4.0F, 1.0F, 0.5F, new CubeDeformation(-0.1F)) + .texOffs(60, 10).addBox(6.5F, 7.9842F, -6.75F, 0.5F, 1.0F, 14.0F, new CubeDeformation(-0.1F)) + .texOffs(58, 37).addBox(-6.5F, 7.9842F, -6.75F, 0.5F, 1.0F, 14.0F, new CubeDeformation(-0.1F)), PartPose.offset(-0.25F, -16.2342F, -0.25F)); + + PartDefinition bone5 = bone4.addOrReplaceChild("bone5", CubeListBuilder.create().texOffs(62, 0).addBox(-6.75F, 7.4842F, -6.75F, 0.5F, 7.0F, 0.5F, new CubeDeformation(0.1F)) + .texOffs(62, 0).addBox(-6.75F, 7.4842F, -5.75F, 0.5F, 7.0F, 0.5F, new CubeDeformation(0.0F)) + .texOffs(62, 0).addBox(-6.75F, 7.4842F, -4.75F, 0.5F, 7.0F, 0.5F, new CubeDeformation(0.0F)) + .texOffs(62, 0).addBox(-6.75F, 7.4842F, -3.75F, 0.5F, 7.0F, 0.5F, new CubeDeformation(0.0F)) + .texOffs(62, 0).addBox(-6.75F, 7.4842F, -2.75F, 0.5F, 7.0F, 0.5F, new CubeDeformation(0.0F)) + .texOffs(62, 0).addBox(-6.75F, 7.4842F, -1.75F, 0.5F, 7.0F, 0.5F, new CubeDeformation(0.0F)) + .texOffs(62, 0).addBox(-6.75F, 7.4842F, -0.75F, 0.5F, 7.0F, 0.5F, new CubeDeformation(0.0F)) + .texOffs(62, 0).addBox(-6.75F, 7.4842F, 0.25F, 0.5F, 7.0F, 0.5F, new CubeDeformation(0.0F)) + .texOffs(62, 0).addBox(-6.75F, 7.4842F, 1.25F, 0.5F, 7.0F, 0.5F, new CubeDeformation(0.0F)) + .texOffs(62, 0).addBox(-6.75F, 7.4842F, 2.25F, 0.5F, 7.0F, 0.5F, new CubeDeformation(0.0F)) + .texOffs(62, 0).addBox(-6.75F, 7.4842F, 3.25F, 0.5F, 7.0F, 0.5F, new CubeDeformation(0.0F)) + .texOffs(62, 0).addBox(-6.75F, 7.4842F, 4.25F, 0.5F, 7.0F, 0.5F, new CubeDeformation(0.0F)) + .texOffs(62, 0).addBox(-6.75F, 7.4842F, 5.25F, 0.5F, 7.0F, 0.5F, new CubeDeformation(0.0F)) + .texOffs(62, 0).addBox(6.25F, 7.4842F, 5.25F, 0.5F, 7.0F, 0.5F, new CubeDeformation(0.0F)) + .texOffs(62, 0).addBox(6.25F, 7.4842F, 4.25F, 0.5F, 7.0F, 0.5F, new CubeDeformation(0.0F)) + .texOffs(62, 0).addBox(6.25F, 7.4842F, 3.25F, 0.5F, 7.0F, 0.5F, new CubeDeformation(0.0F)) + .texOffs(62, 0).addBox(6.25F, 7.4842F, 2.25F, 0.5F, 7.0F, 0.5F, new CubeDeformation(0.0F)) + .texOffs(62, 0).addBox(6.25F, 7.4842F, 1.25F, 0.5F, 7.0F, 0.5F, new CubeDeformation(0.0F)) + .texOffs(62, 0).addBox(6.25F, 7.4842F, 0.25F, 0.5F, 7.0F, 0.5F, new CubeDeformation(0.0F)) + .texOffs(62, 0).addBox(6.25F, 7.4842F, -0.75F, 0.5F, 7.0F, 0.5F, new CubeDeformation(0.0F)) + .texOffs(62, 0).addBox(6.25F, 7.4842F, -1.75F, 0.5F, 7.0F, 0.5F, new CubeDeformation(0.0F)) + .texOffs(62, 0).addBox(6.25F, 7.4842F, -2.75F, 0.5F, 7.0F, 0.5F, new CubeDeformation(0.0F)) + .texOffs(62, 0).addBox(6.25F, 7.4842F, -3.75F, 0.5F, 7.0F, 0.5F, new CubeDeformation(0.0F)) + .texOffs(62, 0).addBox(6.25F, 7.4842F, -4.75F, 0.5F, 7.0F, 0.5F, new CubeDeformation(0.0F)) + .texOffs(62, 0).addBox(6.25F, 7.4842F, -5.75F, 0.5F, 7.0F, 0.5F, new CubeDeformation(0.0F)) + .texOffs(62, 0).addBox(6.25F, 7.4842F, -6.75F, 0.5F, 7.0F, 0.5F, new CubeDeformation(0.1F)) + .texOffs(62, 0).addBox(-6.75F, 7.4842F, 6.25F, 0.5F, 7.0F, 0.5F, new CubeDeformation(0.1F)) + .texOffs(62, 0).addBox(-5.75F, 7.4842F, 6.25F, 0.5F, 7.0F, 0.5F, new CubeDeformation(0.0F)) + .texOffs(62, 0).addBox(-4.75F, 7.4842F, 6.25F, 0.5F, 7.0F, 0.5F, new CubeDeformation(0.0F)) + .texOffs(62, 0).addBox(-3.75F, 7.4842F, 6.25F, 0.5F, 7.0F, 0.5F, new CubeDeformation(0.0F)) + .texOffs(62, 0).addBox(-2.75F, 7.4842F, 6.25F, 0.5F, 7.0F, 0.5F, new CubeDeformation(0.0F)) + .texOffs(62, 0).addBox(-1.75F, 7.4842F, 6.25F, 0.5F, 7.0F, 0.5F, new CubeDeformation(0.0F)) + .texOffs(62, 0).addBox(-0.75F, 7.4842F, 6.25F, 0.5F, 7.0F, 0.5F, new CubeDeformation(0.0F)) + .texOffs(62, 0).addBox(0.25F, 7.4842F, 6.25F, 0.5F, 7.0F, 0.5F, new CubeDeformation(0.0F)) + .texOffs(62, 0).addBox(1.25F, 7.4842F, 6.25F, 0.5F, 7.0F, 0.5F, new CubeDeformation(0.0F)) + .texOffs(62, 0).addBox(2.25F, 7.4842F, 6.25F, 0.5F, 7.0F, 0.5F, new CubeDeformation(0.0F)) + .texOffs(62, 0).addBox(3.25F, 7.4842F, 6.25F, 0.5F, 7.0F, 0.5F, new CubeDeformation(0.0F)) + .texOffs(62, 0).addBox(4.25F, 7.4842F, 6.25F, 0.5F, 7.0F, 0.5F, new CubeDeformation(0.0F)) + .texOffs(62, 0).addBox(5.25F, 7.4842F, 6.25F, 0.5F, 7.0F, 0.5F, new CubeDeformation(0.0F)) + .texOffs(62, 0).addBox(6.25F, 7.4842F, 6.25F, 0.5F, 7.0F, 0.5F, new CubeDeformation(0.1F)) + .texOffs(62, 0).addBox(-6.75F, 7.4842F, -6.75F, 0.5F, 7.0F, 0.5F, new CubeDeformation(0.0F)) + .texOffs(62, 0).addBox(-5.75F, 7.4842F, -6.75F, 0.5F, 7.0F, 0.5F, new CubeDeformation(0.0F)) + .texOffs(62, 0).addBox(-4.75F, 7.4842F, -6.75F, 0.5F, 7.0F, 0.5F, new CubeDeformation(0.0F)) + .texOffs(62, 0).addBox(-3.75F, 7.4842F, -6.75F, 0.5F, 7.0F, 0.5F, new CubeDeformation(0.0F)) + .texOffs(62, 0).addBox(3.25F, 7.4842F, -6.75F, 0.5F, 7.0F, 0.5F, new CubeDeformation(0.0F)) + .texOffs(62, 0).addBox(4.25F, 7.4842F, -6.75F, 0.5F, 7.0F, 0.5F, new CubeDeformation(0.0F)) + .texOffs(62, 0).addBox(5.25F, 7.4842F, -6.75F, 0.5F, 7.0F, 0.5F, new CubeDeformation(0.0F)) + .texOffs(62, 0).addBox(6.25F, 7.4842F, -6.75F, 0.5F, 7.0F, 0.5F, new CubeDeformation(0.0F)), PartPose.offset(0.25F, 0.0F, 0.25F)); + + PartDefinition bone3 = main.addOrReplaceChild("bone3", CubeListBuilder.create().texOffs(48, 56).addBox(-5.5F, -8.0F, -5.5F, 3.0F, 4.0F, 3.0F, new CubeDeformation(0.0F)) + .texOffs(33, 51).addBox(2.5F, -8.0F, -5.5F, 3.0F, 4.0F, 3.0F, new CubeDeformation(0.0F)) + .texOffs(0, 35).addBox(-5.5F, -8.0F, -3.5F, 11.0F, 4.0F, 9.0F, new CubeDeformation(0.0F)), PartPose.offset(0.0F, -1.0F, 0.0F)); + + PartDefinition bone6 = main.addOrReplaceChild("bone6", CubeListBuilder.create().texOffs(0, 68).addBox(3.0F, 4.7342F, -4.5F, 1.0F, 1.0F, 9.0F, new CubeDeformation(0.0F)) + .texOffs(48, 53).addBox(-4.5F, 4.7342F, -4.0F, 9.0F, 1.0F, 1.0F, new CubeDeformation(0.0F)) + .texOffs(37, 21).addBox(3.0F, -4.5158F, -4.0F, 1.0F, 10.0F, 1.0F, new CubeDeformation(0.0F)) + .texOffs(48, 53).addBox(-4.5F, 4.7342F, 3.0F, 9.0F, 1.0F, 1.0F, new CubeDeformation(0.0F)) + .texOffs(0, 68).addBox(-4.0F, 4.7342F, -4.5F, 1.0F, 1.0F, 9.0F, new CubeDeformation(0.0F)) + .texOffs(37, 21).addBox(-4.0F, -4.5158F, -4.0F, 1.0F, 10.0F, 1.0F, new CubeDeformation(0.0F)) + .texOffs(37, 21).addBox(3.0F, -4.5158F, 3.0F, 1.0F, 10.0F, 1.0F, new CubeDeformation(0.0F)) + .texOffs(37, 21).addBox(-4.0F, -4.5158F, 3.0F, 1.0F, 10.0F, 1.0F, new CubeDeformation(0.0F)) + .texOffs(46, 10).addBox(-4.5F, -3.7658F, 3.0F, 9.0F, 1.0F, 1.0F, new CubeDeformation(0.0F)) + .texOffs(65, 53).addBox(-4.0F, -3.7658F, -6.5F, 1.0F, 1.0F, 13.0F, new CubeDeformation(0.0F)) + .texOffs(65, 53).addBox(3.0F, -3.7658F, -6.5F, 1.0F, 1.0F, 13.0F, new CubeDeformation(0.0F)) + .texOffs(46, 10).addBox(-4.5F, -3.7658F, -4.0F, 9.0F, 1.0F, 1.0F, new CubeDeformation(0.0F)) + .texOffs(46, 0).addBox(-4.5F, -1.7658F, -4.5F, 9.0F, 0.5F, 9.0F, new CubeDeformation(0.0F)), PartPose.offset(0.0F, -16.2342F, 0.0F)); + + return LayerDefinition.create(meshdefinition, 128, 128); + } + + @Override + public void setupAnim(Entity entity, float limbSwing, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch) { + } + + @Override + public void renderToBuffer(PoseStack poseStack, VertexConsumer vertexConsumer, int packedLight, int packedOverlay, float red, float green, float blue, float alpha) { + main.render(poseStack, vertexConsumer, packedLight, packedOverlay, red, green, blue, alpha); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/TombstoneModel.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/TombstoneModel.java new file mode 100644 index 000000000..3b0164f99 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/TombstoneModel.java @@ -0,0 +1,66 @@ +package com.github.tartaricacid.touhoulittlemaid.client.model; + +import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import net.minecraft.client.model.EntityModel; +import net.minecraft.client.model.geom.ModelLayerLocation; +import net.minecraft.client.model.geom.ModelPart; +import net.minecraft.client.model.geom.PartPose; +import net.minecraft.client.model.geom.builders.*; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.Entity; + +public class TombstoneModel extends EntityModel { + public static ModelLayerLocation LAYER = new ModelLayerLocation(new ResourceLocation(TouhouLittleMaid.MOD_ID, "main"), "tombstone"); + private final ModelPart main; + + public TombstoneModel(ModelPart root) { + this.main = root.getChild("main"); + } + + public static LayerDefinition createBodyLayer() { + MeshDefinition meshdefinition = new MeshDefinition(); + PartDefinition partdefinition = meshdefinition.getRoot(); + + PartDefinition main = partdefinition.addOrReplaceChild("main", CubeListBuilder.create().texOffs(0, 29).addBox(-6.0F, -2.0F, -3.0F, 11.0F, 2.0F, 7.0F, new CubeDeformation(0.0F)) + .texOffs(0, 0).addBox(-7.0F, 0.0F, -6.0F, 13.0F, 1.0F, 13.0F, new CubeDeformation(0.0F)) + .texOffs(0, 15).addBox(-6.0F, 1.0F, -5.0F, 11.0F, 2.0F, 11.0F, new CubeDeformation(0.0F)) + .texOffs(34, 15).addBox(-4.0F, 4.3F, -3.0F, 7.0F, 1.0F, 7.0F, new CubeDeformation(0.0F)) + .texOffs(33, 33).addBox(-4.5F, -12.0F, -1.5F, 8.0F, 10.0F, 4.0F, new CubeDeformation(0.0F)) + .texOffs(16, 48).addBox(-8.5F, -16.0F, -7.025F, 16.0F, 16.0F, 0.0F, new CubeDeformation(-5.0F)) + .texOffs(40, 0).addBox(-3.0F, -14.0F, -2.0F, 5.0F, 2.0F, 5.0F, new CubeDeformation(0.0F)), PartPose.offset(0.5F, 17.1F, -0.5F)); + + PartDefinition bone8 = main.addOrReplaceChild("bone8", CubeListBuilder.create(), PartPose.offset(-0.5F, 4.5F, 0.5F)); + + PartDefinition bone6 = bone8.addOrReplaceChild("bone6", CubeListBuilder.create().texOffs(38, 23).addBox(2.5397F, -3.6003F, -3.5F, 1.0F, 3.0F, 7.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.3927F)); + + PartDefinition bone7 = bone8.addOrReplaceChild("bone7", CubeListBuilder.create().texOffs(38, 23).addBox(-3.5397F, -3.6003F, -3.5F, 1.0F, 3.0F, 7.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.0F, -0.3927F)); + + PartDefinition bone9 = main.addOrReplaceChild("bone9", CubeListBuilder.create(), PartPose.offsetAndRotation(-0.5F, 4.5F, 0.5F, 0.0F, -1.5708F, 0.0F)); + + PartDefinition bone10 = bone9.addOrReplaceChild("bone10", CubeListBuilder.create().texOffs(38, 23).addBox(2.5397F, -3.6003F, -3.5F, 1.0F, 3.0F, 7.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.3927F)); + + PartDefinition bone11 = bone9.addOrReplaceChild("bone11", CubeListBuilder.create().texOffs(38, 23).addBox(-3.5397F, -3.6003F, -3.5F, 1.0F, 3.0F, 7.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.0F, 0.0F, -0.3927F)); + + PartDefinition bone = main.addOrReplaceChild("bone", CubeListBuilder.create().texOffs(0, 38).addBox(-0.5642F, -4.9083F, -2.5F, 2.0F, 10.0F, 5.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(3.0F, -6.75F, 0.5F, 0.0F, 0.0F, 0.1309F)); + + PartDefinition bone2 = bone.addOrReplaceChild("bone2", CubeListBuilder.create().texOffs(15, 38).addBox(-0.872F, -1.5934F, -2.5F, 2.0F, 4.0F, 5.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(-1.2F, -5.25F, 0.0F, 0.0F, 0.0F, -1.0036F)); + + PartDefinition bone3 = main.addOrReplaceChild("bone3", CubeListBuilder.create().texOffs(0, 38).mirror().addBox(-1.4358F, -4.9083F, -2.5F, 2.0F, 10.0F, 5.0F, new CubeDeformation(0.0F)).mirror(false), PartPose.offsetAndRotation(-4.0F, -6.75F, 0.5F, 0.0F, 0.0F, -0.1309F)); + + PartDefinition bone4 = bone3.addOrReplaceChild("bone4", CubeListBuilder.create().texOffs(15, 38).mirror().addBox(-1.128F, -1.5934F, -2.5F, 2.0F, 4.0F, 5.0F, new CubeDeformation(0.0F)).mirror(false), PartPose.offsetAndRotation(1.2F, -5.25F, 0.0F, 0.0F, 0.0F, 1.0036F)); + + return LayerDefinition.create(meshdefinition, 64, 64); + } + + @Override + public void setupAnim(Entity entity, float limbSwing, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch) { + + } + + @Override + public void renderToBuffer(PoseStack poseStack, VertexConsumer vertexConsumer, int packedLight, int packedOverlay, float red, float green, float blue, float alpha) { + main.render(poseStack, vertexConsumer, packedLight, packedOverlay, red, green, blue, alpha); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/MaidBackpackBigModel.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/backpack/BigBackpackModel.java similarity index 95% rename from src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/MaidBackpackBigModel.java rename to src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/backpack/BigBackpackModel.java index 61b395de7..6a09ce0af 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/MaidBackpackBigModel.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/backpack/BigBackpackModel.java @@ -1,4 +1,4 @@ -package com.github.tartaricacid.touhoulittlemaid.client.model; +package com.github.tartaricacid.touhoulittlemaid.client.model.backpack; import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; @@ -13,11 +13,11 @@ import net.minecraft.resources.ResourceLocation; -public class MaidBackpackBigModel extends EntityModel { +public class BigBackpackModel extends EntityModel { public static ModelLayerLocation LAYER = new ModelLayerLocation(new ResourceLocation(TouhouLittleMaid.MOD_ID, "main"), "backpack_big"); private final ModelPart bone; - public MaidBackpackBigModel(ModelPart root) { + public BigBackpackModel(ModelPart root) { this.bone = root.getChild("bone"); } diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/backpack/CraftingTableBackpackModel.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/backpack/CraftingTableBackpackModel.java new file mode 100644 index 000000000..87031f8ad --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/backpack/CraftingTableBackpackModel.java @@ -0,0 +1,101 @@ +package com.github.tartaricacid.touhoulittlemaid.client.model.backpack; + +import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; +import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import net.minecraft.client.model.EntityModel; +import net.minecraft.client.model.geom.ModelLayerLocation; +import net.minecraft.client.model.geom.ModelPart; +import net.minecraft.client.model.geom.PartPose; +import net.minecraft.client.model.geom.builders.*; +import net.minecraft.resources.ResourceLocation; + +public class CraftingTableBackpackModel extends EntityModel { + public static ModelLayerLocation LAYER = new ModelLayerLocation(new ResourceLocation(TouhouLittleMaid.MOD_ID, "main"), "crafting_table_backpack"); + private final ModelPart main; + + public CraftingTableBackpackModel(ModelPart root) { + this.main = root.getChild("main"); + } + + public static LayerDefinition createBodyLayer() { + MeshDefinition meshdefinition = new MeshDefinition(); + PartDefinition partdefinition = meshdefinition.getRoot(); + + PartDefinition main = partdefinition.addOrReplaceChild("main", CubeListBuilder.create(), PartPose.offset(0.0F, 33.5F, 1.0F)); + + PartDefinition bone466 = main.addOrReplaceChild("bone466", CubeListBuilder.create().texOffs(0, 80).addBox(-5.0F, -25.0F, -2.0F, 1.0F, 14.0F, 1.0F, new CubeDeformation(0.0F)) + .texOffs(75, 65).addBox(4.0F, -25.0F, -2.0F, 1.0F, 14.0F, 1.0F, new CubeDeformation(0.0F)) + .texOffs(68, 41).addBox(-6.0F, -22.0F, -2.0F, 12.0F, 1.0F, 1.0F, new CubeDeformation(-0.1F)), PartPose.offset(0.0F, 0.0F, 0.0F)); + + PartDefinition bone36 = bone466.addOrReplaceChild("bone36", CubeListBuilder.create().texOffs(68, 35).addBox(-6.0F, -8.5F, -0.5F, 12.0F, 1.0F, 1.0F, new CubeDeformation(-0.1F)) + .texOffs(68, 32).addBox(-6.0F, -6.5F, -0.5F, 12.0F, 1.0F, 1.0F, new CubeDeformation(-0.1F)) + .texOffs(72, 0).addBox(-5.0F, -10.5F, -0.5F, 1.0F, 14.0F, 1.0F, new CubeDeformation(0.0F)) + .texOffs(70, 65).addBox(4.0F, -10.5F, -0.5F, 1.0F, 14.0F, 1.0F, new CubeDeformation(0.0F)), PartPose.offset(0.0F, -28.5F, -1.5F)); + + PartDefinition bone10 = bone36.addOrReplaceChild("bone10", CubeListBuilder.create().texOffs(68, 47).addBox(-5.5F, -6.9672F, 12.3212F, 11.0F, 1.0F, 1.0F, new CubeDeformation(-0.1F)) + .texOffs(35, 33).addBox(-5.0F, -6.9672F, -16.1788F, 1.0F, 1.0F, 30.0F, new CubeDeformation(-0.1F)) + .texOffs(0, 0).addBox(-4.5F, -6.9672F, -15.1788F, 9.0F, 1.0F, 28.0F, new CubeDeformation(-0.3F)) + .texOffs(49, 0).addBox(4.0F, -6.9672F, -16.1788F, 1.0F, 1.0F, 30.0F, new CubeDeformation(-0.1F)) + .texOffs(68, 50).addBox(-5.5F, -6.9672F, -15.6788F, 11.0F, 1.0F, 1.0F, new CubeDeformation(-0.1F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, -0.3927F, 0.0F, 0.0F)); + + PartDefinition bone26 = bone466.addOrReplaceChild("bone26", CubeListBuilder.create(), PartPose.offset(-4.0F, -19.1517F, -3.8731F)); + + PartDefinition bone6 = bone26.addOrReplaceChild("bone6", CubeListBuilder.create().texOffs(86, 15).addBox(0.0F, -1.0F, -2.0F, 1.0F, 1.0F, 4.0F, new CubeDeformation(0.025F)), PartPose.offset(0.0F, 0.0F, 0.0F)); + + PartDefinition bone8 = bone26.addOrReplaceChild("bone8", CubeListBuilder.create().texOffs(89, 18).addBox(0.0F, -1.0F, -3.0F, 1.0F, 6.0F, 1.0F, new CubeDeformation(0.0F)), PartPose.offset(0.0F, 0.0F, 0.0F)); + + PartDefinition bone7 = bone26.addOrReplaceChild("bone7", CubeListBuilder.create().texOffs(85, 14).addBox(-0.5F, -0.5F, -5.0F, 1.0F, 1.0F, 5.0F, new CubeDeformation(0.025F)), PartPose.offsetAndRotation(0.5F, 5.5F, 2.0F, -0.2443F, 0.0F, 0.0F)); + + PartDefinition bone9 = bone466.addOrReplaceChild("bone9", CubeListBuilder.create(), PartPose.offset(4.0F, -19.1517F, -3.8731F)); + + PartDefinition bone11 = bone9.addOrReplaceChild("bone11", CubeListBuilder.create().texOffs(86, 15).mirror().addBox(-1.0F, -1.0F, -2.0F, 1.0F, 1.0F, 4.0F, new CubeDeformation(0.025F)).mirror(false), PartPose.offset(0.0F, 0.0F, 0.0F)); + + PartDefinition bone12 = bone9.addOrReplaceChild("bone12", CubeListBuilder.create().texOffs(89, 18).mirror().addBox(-1.0F, -1.0F, -3.0F, 1.0F, 6.0F, 1.0F, new CubeDeformation(0.0F)).mirror(false), PartPose.offset(0.0F, 0.0F, 0.0F)); + + PartDefinition bone13 = bone9.addOrReplaceChild("bone13", CubeListBuilder.create().texOffs(85, 14).mirror().addBox(-0.5F, -0.5F, -5.0F, 1.0F, 1.0F, 5.0F, new CubeDeformation(0.025F)).mirror(false), PartPose.offsetAndRotation(-0.5F, 5.5F, 2.0F, -0.2443F, 0.0F, 0.0F)); + + PartDefinition bone = bone466.addOrReplaceChild("bone", CubeListBuilder.create().texOffs(88, 15).addBox(6.5F, -0.5F, -0.5F, 1.0F, 1.0F, 1.0F, new CubeDeformation(0.15F)) + .texOffs(88, 15).addBox(-0.5F, -0.5F, -0.5F, 1.0F, 1.0F, 1.0F, new CubeDeformation(0.15F)), PartPose.offsetAndRotation(-3.5F, -19.5F, -1.5F, 0.7854F, 0.0F, 0.0F)); + + PartDefinition bone2 = bone466.addOrReplaceChild("bone2", CubeListBuilder.create().texOffs(88, 15).addBox(6.5F, -0.5F, -0.5F, 1.0F, 1.0F, 1.0F, new CubeDeformation(0.15F)) + .texOffs(88, 15).addBox(-0.5F, -0.5F, -0.5F, 1.0F, 1.0F, 1.0F, new CubeDeformation(0.15F)), PartPose.offsetAndRotation(-3.5F, -13.5F, -1.5F, 0.7854F, 0.0F, 0.0F)); + + PartDefinition bone3 = bone466.addOrReplaceChild("bone3", CubeListBuilder.create().texOffs(3, 68).addBox(-5.5F, -2.3361F, -2.487F, 11.0F, 1.0F, 10.0F, new CubeDeformation(-0.25F)) + .texOffs(68, 44).addBox(-6.0F, -1.6861F, -3.237F, 12.0F, 1.0F, 1.0F, new CubeDeformation(-0.1F)) + .texOffs(68, 38).addBox(-6.0F, -1.6861F, 5.263F, 12.0F, 1.0F, 1.0F, new CubeDeformation(-0.1F)) + .texOffs(37, 68).addBox(-5.0F, -1.6861F, -3.737F, 1.0F, 1.0F, 12.0F, new CubeDeformation(-0.1F)) + .texOffs(55, 70).addBox(4.0F, -1.6861F, -3.737F, 1.0F, 1.0F, 12.0F, new CubeDeformation(-0.1F)), PartPose.offset(0.0F, -12.3139F, 1.237F)); + + PartDefinition bone467 = bone3.addOrReplaceChild("bone467", CubeListBuilder.create().texOffs(0, 30).addBox(4.0F, -0.5F, -3.0F, 1.0F, 1.0F, 6.0F, new CubeDeformation(-0.2F)) + .texOffs(12, 14).addBox(-5.0F, -0.5F, -3.0F, 1.0F, 1.0F, 6.0F, new CubeDeformation(-0.2F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.3927F, 0.0F, 0.0F)); + + PartDefinition bone4 = bone466.addOrReplaceChild("bone4", CubeListBuilder.create().texOffs(3, 68).addBox(-5.5F, -2.3361F, -2.487F, 11.0F, 1.0F, 10.0F, new CubeDeformation(-0.25F)) + .texOffs(68, 44).addBox(-6.0F, -1.6861F, -3.237F, 12.0F, 1.0F, 1.0F, new CubeDeformation(-0.1F)) + .texOffs(68, 38).addBox(-6.0F, -1.6861F, 5.263F, 12.0F, 1.0F, 1.0F, new CubeDeformation(-0.1F)) + .texOffs(37, 68).addBox(-5.0F, -1.6861F, -3.737F, 1.0F, 1.0F, 12.0F, new CubeDeformation(-0.1F)) + .texOffs(55, 70).addBox(4.0F, -1.6861F, -3.737F, 1.0F, 1.0F, 12.0F, new CubeDeformation(-0.1F)) + .texOffs(0, 30).addBox(-8.0F, -14.0861F, -5.737F, 16.0F, 16.0F, 16.0F, new CubeDeformation(-4.0F)), PartPose.offset(0.0F, -18.3139F, 1.237F)); + + PartDefinition bone5 = bone4.addOrReplaceChild("bone5", CubeListBuilder.create().texOffs(0, 30).addBox(4.0F, -0.5F, -3.0F, 1.0F, 1.0F, 6.0F, new CubeDeformation(-0.2F)) + .texOffs(12, 14).addBox(-5.0F, -0.5F, -3.0F, 1.0F, 1.0F, 6.0F, new CubeDeformation(-0.2F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.3927F, 0.0F, 0.0F)); + + PartDefinition bench = main.addOrReplaceChild("bench", CubeListBuilder.create().texOffs(0, 96).addBox(-10.0F, -8.0F, -10.05F, 16.0F, 16.0F, 16.0F, new CubeDeformation(-6.0F)) + .texOffs(64, 96).addBox(-6.0F, -8.0F, -10.05F, 16.0F, 16.0F, 16.0F, new CubeDeformation(-6.0F)), PartPose.offsetAndRotation(0.0F, -16.4F, 3.75F, 0.0F, -1.5708F, 0.0F)); + + PartDefinition bench2 = bench.addOrReplaceChild("bench2", CubeListBuilder.create().texOffs(0, 96).addBox(-10.0F, -8.0F, -8.05F, 16.0F, 16.0F, 16.0F, new CubeDeformation(-6.0F)) + .texOffs(64, 96).addBox(-6.0F, -8.0F, -8.05F, 16.0F, 16.0F, 16.0F, new CubeDeformation(-6.0F)), PartPose.offsetAndRotation(0.0F, 0.0F, 2.0F, -3.1416F, 0.0F, 3.1416F)); + + return LayerDefinition.create(meshdefinition, 128, 128); + } + + @Override + public void setupAnim(EntityMaid entity, float limbSwing, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch) { + } + + @Override + public void renderToBuffer(PoseStack poseStack, VertexConsumer vertexConsumer, int packedLight, int packedOverlay, float red, float green, float blue, float alpha) { + main.render(poseStack, vertexConsumer, packedLight, packedOverlay, red, green, blue, alpha); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/backpack/EnderChestBackpackModel.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/backpack/EnderChestBackpackModel.java new file mode 100644 index 000000000..e16ffc163 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/backpack/EnderChestBackpackModel.java @@ -0,0 +1,88 @@ +package com.github.tartaricacid.touhoulittlemaid.client.model.backpack; + + +import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; +import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import net.minecraft.client.model.EntityModel; +import net.minecraft.client.model.geom.ModelLayerLocation; +import net.minecraft.client.model.geom.ModelPart; +import net.minecraft.client.model.geom.PartPose; +import net.minecraft.client.model.geom.builders.*; +import net.minecraft.resources.ResourceLocation; + +public class EnderChestBackpackModel extends EntityModel { + public static ModelLayerLocation LAYER = new ModelLayerLocation(new ResourceLocation(TouhouLittleMaid.MOD_ID, "main"), "ender_chest_backpack"); + private final ModelPart main; + + public EnderChestBackpackModel(ModelPart root) { + this.main = root.getChild("main"); + } + + public static LayerDefinition createBodyLayer() { + MeshDefinition meshdefinition = new MeshDefinition(); + PartDefinition partdefinition = meshdefinition.getRoot(); + + PartDefinition main = partdefinition.addOrReplaceChild("main", CubeListBuilder.create(), PartPose.offset(0.0F, 35.0F, 0.0F)); + + PartDefinition bone466 = main.addOrReplaceChild("bone466", CubeListBuilder.create().texOffs(0, 80).addBox(-5.0F, -25.0F, -2.0F, 1.0F, 14.0F, 1.0F, new CubeDeformation(0.0F)) + .texOffs(75, 65).addBox(4.0F, -25.0F, -2.0F, 1.0F, 14.0F, 1.0F, new CubeDeformation(0.0F)) + .texOffs(68, 41).addBox(-6.0F, -22.0F, -2.0F, 12.0F, 1.0F, 1.0F, new CubeDeformation(-0.1F)) + .texOffs(4, 32).addBox(-7.0F, -24.9F, -3.65F, 14.0F, 14.0F, 14.0F, new CubeDeformation(-3.5F)) + .texOffs(49, 39).addBox(-1.0F, -20.4F, 5.85F, 2.0F, 3.0F, 2.0F, new CubeDeformation(-0.5F)), PartPose.offset(0.0F, 0.0F, 0.0F)); + + PartDefinition bone36 = bone466.addOrReplaceChild("bone36", CubeListBuilder.create().texOffs(68, 35).addBox(-6.0F, -8.5F, -0.5F, 12.0F, 1.0F, 1.0F, new CubeDeformation(-0.1F)) + .texOffs(68, 32).addBox(-6.0F, -6.5F, -0.5F, 12.0F, 1.0F, 1.0F, new CubeDeformation(-0.1F)) + .texOffs(72, 0).addBox(-5.0F, -10.5F, -0.5F, 1.0F, 14.0F, 1.0F, new CubeDeformation(0.0F)) + .texOffs(70, 65).addBox(4.0F, -10.5F, -0.5F, 1.0F, 14.0F, 1.0F, new CubeDeformation(0.0F)), PartPose.offset(0.0F, -28.5F, -1.5F)); + + PartDefinition bone10 = bone36.addOrReplaceChild("bone10", CubeListBuilder.create().texOffs(68, 47).addBox(-5.5F, -6.9672F, 12.3212F, 11.0F, 1.0F, 1.0F, new CubeDeformation(-0.1F)) + .texOffs(35, 33).addBox(-5.0F, -6.9672F, -16.1788F, 1.0F, 1.0F, 30.0F, new CubeDeformation(-0.1F)) + .texOffs(0, 0).addBox(-4.5F, -6.9672F, -15.1788F, 9.0F, 1.0F, 28.0F, new CubeDeformation(-0.3F)) + .texOffs(49, 0).addBox(4.0F, -6.9672F, -16.1788F, 1.0F, 1.0F, 30.0F, new CubeDeformation(-0.1F)) + .texOffs(68, 50).addBox(-5.5F, -6.9672F, -15.6788F, 11.0F, 1.0F, 1.0F, new CubeDeformation(-0.1F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, -0.3927F, 0.0F, 0.0F)); + + PartDefinition bone4 = bone466.addOrReplaceChild("bone4", CubeListBuilder.create(), PartPose.offset(-4.0F, -20.6517F, -2.8731F)); + + PartDefinition bone6 = bone4.addOrReplaceChild("bone6", CubeListBuilder.create().texOffs(86, 15).addBox(0.0F, -1.0F, -2.0F, 1.0F, 1.0F, 4.0F, new CubeDeformation(0.025F)), PartPose.offset(0.0F, 0.0F, 0.0F)); + + PartDefinition bone8 = bone4.addOrReplaceChild("bone8", CubeListBuilder.create().texOffs(89, 18).addBox(0.0F, -1.0F, -3.0F, 1.0F, 8.0F, 1.0F, new CubeDeformation(0.0F)), PartPose.offset(0.0F, 0.0F, 0.0F)); + + PartDefinition bone7 = bone4.addOrReplaceChild("bone7", CubeListBuilder.create().texOffs(85, 14).addBox(-0.5F, 1.4406F, -4.5162F, 1.0F, 1.0F, 5.0F, new CubeDeformation(0.025F)), PartPose.offsetAndRotation(0.5F, 5.5F, 2.0F, -0.2443F, 0.0F, 0.0F)); + + PartDefinition bone5 = bone466.addOrReplaceChild("bone5", CubeListBuilder.create(), PartPose.offset(4.0F, -20.6517F, -2.8731F)); + + PartDefinition bone9 = bone5.addOrReplaceChild("bone9", CubeListBuilder.create().texOffs(86, 15).mirror().addBox(-1.0F, -1.0F, -2.0F, 1.0F, 1.0F, 4.0F, new CubeDeformation(0.025F)).mirror(false), PartPose.offset(0.0F, 0.0F, 0.0F)); + + PartDefinition bone11 = bone5.addOrReplaceChild("bone11", CubeListBuilder.create().texOffs(89, 18).mirror().addBox(-1.0F, -1.0F, -3.0F, 1.0F, 8.0F, 1.0F, new CubeDeformation(0.0F)).mirror(false), PartPose.offset(0.0F, 0.0F, 0.0F)); + + PartDefinition bone12 = bone5.addOrReplaceChild("bone12", CubeListBuilder.create().texOffs(85, 14).mirror().addBox(-0.5F, 1.4406F, -4.5162F, 1.0F, 1.0F, 5.0F, new CubeDeformation(0.025F)).mirror(false), PartPose.offsetAndRotation(-0.5F, 5.5F, 2.0F, -0.2443F, 0.0F, 0.0F)); + + PartDefinition bone = bone466.addOrReplaceChild("bone", CubeListBuilder.create().texOffs(88, 15).addBox(6.5F, -0.5F, -0.5F, 1.0F, 1.0F, 1.0F, new CubeDeformation(0.15F)) + .texOffs(88, 15).addBox(-0.5F, -0.5F, -0.5F, 1.0F, 1.0F, 1.0F, new CubeDeformation(0.15F)), PartPose.offsetAndRotation(-3.5F, -21.5F, -1.5F, 0.7854F, 0.0F, 0.0F)); + + PartDefinition bone2 = bone466.addOrReplaceChild("bone2", CubeListBuilder.create().texOffs(88, 15).addBox(6.5F, -0.5F, -0.5F, 1.0F, 1.0F, 1.0F, new CubeDeformation(0.15F)) + .texOffs(88, 15).addBox(-0.5F, -0.5F, -0.5F, 1.0F, 1.0F, 1.0F, new CubeDeformation(0.15F)), PartPose.offsetAndRotation(-3.5F, -13.5F, -1.5F, 0.7854F, 0.0F, 0.0F)); + + PartDefinition bone3 = bone466.addOrReplaceChild("bone3", CubeListBuilder.create().texOffs(3, 68).addBox(-5.5F, -2.3361F, -2.487F, 11.0F, 1.0F, 10.0F, new CubeDeformation(-0.25F)) + .texOffs(68, 44).addBox(-6.0F, -1.6861F, -3.237F, 12.0F, 1.0F, 1.0F, new CubeDeformation(-0.1F)) + .texOffs(68, 38).addBox(-6.0F, -1.6861F, 5.263F, 12.0F, 1.0F, 1.0F, new CubeDeformation(-0.1F)) + .texOffs(37, 68).addBox(-5.0F, -1.6861F, -3.737F, 1.0F, 1.0F, 12.0F, new CubeDeformation(-0.1F)) + .texOffs(55, 70).addBox(4.0F, -1.6861F, -3.737F, 1.0F, 1.0F, 12.0F, new CubeDeformation(-0.1F)), PartPose.offset(0.0F, -12.3139F, 1.237F)); + + PartDefinition bone467 = bone3.addOrReplaceChild("bone467", CubeListBuilder.create().texOffs(0, 30).addBox(4.0F, -0.5F, -3.0F, 1.0F, 1.0F, 6.0F, new CubeDeformation(-0.2F)) + .texOffs(12, 14).addBox(-5.0F, -0.5F, -3.0F, 1.0F, 1.0F, 6.0F, new CubeDeformation(-0.2F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.3927F, 0.0F, 0.0F)); + + return LayerDefinition.create(meshdefinition, 128, 128); + } + + @Override + public void setupAnim(EntityMaid entity, float limbSwing, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch) { + } + + @Override + public void renderToBuffer(PoseStack poseStack, VertexConsumer vertexConsumer, int packedLight, int packedOverlay, float red, float green, float blue, float alpha) { + main.render(poseStack, vertexConsumer, packedLight, packedOverlay, red, green, blue, alpha); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/backpack/FurnaceBackpackModel.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/backpack/FurnaceBackpackModel.java new file mode 100644 index 000000000..633d10542 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/backpack/FurnaceBackpackModel.java @@ -0,0 +1,101 @@ +package com.github.tartaricacid.touhoulittlemaid.client.model.backpack; + +import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; +import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import net.minecraft.client.model.EntityModel; +import net.minecraft.client.model.geom.ModelLayerLocation; +import net.minecraft.client.model.geom.ModelPart; +import net.minecraft.client.model.geom.PartPose; +import net.minecraft.client.model.geom.builders.*; +import net.minecraft.resources.ResourceLocation; + +public class FurnaceBackpackModel extends EntityModel { + public static ModelLayerLocation LAYER = new ModelLayerLocation(new ResourceLocation(TouhouLittleMaid.MOD_ID, "main"), "furnace_backpack"); + private final ModelPart main; + + public FurnaceBackpackModel(ModelPart root) { + this.main = root.getChild("main"); + } + + public static LayerDefinition createBodyLayer() { + MeshDefinition meshdefinition = new MeshDefinition(); + PartDefinition partdefinition = meshdefinition.getRoot(); + + PartDefinition main = partdefinition.addOrReplaceChild("main", CubeListBuilder.create(), PartPose.offset(0.0F, 33.5F, 1.0F)); + + PartDefinition bone466 = main.addOrReplaceChild("bone466", CubeListBuilder.create().texOffs(0, 80).addBox(-5.0F, -25.0F, -2.0F, 1.0F, 14.0F, 1.0F, new CubeDeformation(0.0F)) + .texOffs(75, 65).addBox(4.0F, -25.0F, -2.0F, 1.0F, 14.0F, 1.0F, new CubeDeformation(0.0F)) + .texOffs(68, 41).addBox(-6.0F, -22.0F, -2.0F, 12.0F, 1.0F, 1.0F, new CubeDeformation(-0.1F)), PartPose.offset(0.0F, 0.0F, 0.0F)); + + PartDefinition bone36 = bone466.addOrReplaceChild("bone36", CubeListBuilder.create().texOffs(68, 35).addBox(-6.0F, -8.5F, -0.5F, 12.0F, 1.0F, 1.0F, new CubeDeformation(-0.1F)) + .texOffs(68, 32).addBox(-6.0F, -6.5F, -0.5F, 12.0F, 1.0F, 1.0F, new CubeDeformation(-0.1F)) + .texOffs(72, 0).addBox(-5.0F, -10.5F, -0.5F, 1.0F, 14.0F, 1.0F, new CubeDeformation(0.0F)) + .texOffs(70, 65).addBox(4.0F, -10.5F, -0.5F, 1.0F, 14.0F, 1.0F, new CubeDeformation(0.0F)), PartPose.offset(0.0F, -28.5F, -1.5F)); + + PartDefinition bone10 = bone36.addOrReplaceChild("bone10", CubeListBuilder.create().texOffs(68, 47).addBox(-5.5F, -6.9672F, 12.3212F, 11.0F, 1.0F, 1.0F, new CubeDeformation(-0.1F)) + .texOffs(35, 33).addBox(-5.0F, -6.9672F, -16.1788F, 1.0F, 1.0F, 30.0F, new CubeDeformation(-0.1F)) + .texOffs(0, 0).addBox(-4.5F, -6.9672F, -15.1788F, 9.0F, 1.0F, 28.0F, new CubeDeformation(-0.3F)) + .texOffs(49, 0).addBox(4.0F, -6.9672F, -16.1788F, 1.0F, 1.0F, 30.0F, new CubeDeformation(-0.1F)) + .texOffs(68, 50).addBox(-5.5F, -6.9672F, -15.6788F, 11.0F, 1.0F, 1.0F, new CubeDeformation(-0.1F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, -0.3927F, 0.0F, 0.0F)); + + PartDefinition bone26 = bone466.addOrReplaceChild("bone26", CubeListBuilder.create(), PartPose.offset(-4.0F, -19.1517F, -3.8731F)); + + PartDefinition bone6 = bone26.addOrReplaceChild("bone6", CubeListBuilder.create().texOffs(86, 15).addBox(0.0F, -1.0F, -2.0F, 1.0F, 1.0F, 4.0F, new CubeDeformation(0.025F)), PartPose.offset(0.0F, 0.0F, 0.0F)); + + PartDefinition bone8 = bone26.addOrReplaceChild("bone8", CubeListBuilder.create().texOffs(89, 18).addBox(0.0F, -1.0F, -3.0F, 1.0F, 6.0F, 1.0F, new CubeDeformation(0.0F)), PartPose.offset(0.0F, 0.0F, 0.0F)); + + PartDefinition bone7 = bone26.addOrReplaceChild("bone7", CubeListBuilder.create().texOffs(85, 14).addBox(-0.5F, -0.5F, -5.0F, 1.0F, 1.0F, 5.0F, new CubeDeformation(0.025F)), PartPose.offsetAndRotation(0.5F, 5.5F, 2.0F, -0.2443F, 0.0F, 0.0F)); + + PartDefinition bone9 = bone466.addOrReplaceChild("bone9", CubeListBuilder.create(), PartPose.offset(4.0F, -19.1517F, -3.8731F)); + + PartDefinition bone11 = bone9.addOrReplaceChild("bone11", CubeListBuilder.create().texOffs(86, 15).mirror().addBox(-1.0F, -1.0F, -2.0F, 1.0F, 1.0F, 4.0F, new CubeDeformation(0.025F)).mirror(false), PartPose.offset(0.0F, 0.0F, 0.0F)); + + PartDefinition bone12 = bone9.addOrReplaceChild("bone12", CubeListBuilder.create().texOffs(89, 18).mirror().addBox(-1.0F, -1.0F, -3.0F, 1.0F, 6.0F, 1.0F, new CubeDeformation(0.0F)).mirror(false), PartPose.offset(0.0F, 0.0F, 0.0F)); + + PartDefinition bone13 = bone9.addOrReplaceChild("bone13", CubeListBuilder.create().texOffs(85, 14).mirror().addBox(-0.5F, -0.5F, -5.0F, 1.0F, 1.0F, 5.0F, new CubeDeformation(0.025F)).mirror(false), PartPose.offsetAndRotation(-0.5F, 5.5F, 2.0F, -0.2443F, 0.0F, 0.0F)); + + PartDefinition bone = bone466.addOrReplaceChild("bone", CubeListBuilder.create().texOffs(88, 15).addBox(6.5F, -0.5F, -0.5F, 1.0F, 1.0F, 1.0F, new CubeDeformation(0.15F)) + .texOffs(88, 15).addBox(-0.5F, -0.5F, -0.5F, 1.0F, 1.0F, 1.0F, new CubeDeformation(0.15F)), PartPose.offsetAndRotation(-3.5F, -19.5F, -1.5F, 0.7854F, 0.0F, 0.0F)); + + PartDefinition bone2 = bone466.addOrReplaceChild("bone2", CubeListBuilder.create().texOffs(88, 15).addBox(6.5F, -0.5F, -0.5F, 1.0F, 1.0F, 1.0F, new CubeDeformation(0.15F)) + .texOffs(88, 15).addBox(-0.5F, -0.5F, -0.5F, 1.0F, 1.0F, 1.0F, new CubeDeformation(0.15F)), PartPose.offsetAndRotation(-3.5F, -13.5F, -1.5F, 0.7854F, 0.0F, 0.0F)); + + PartDefinition bone3 = bone466.addOrReplaceChild("bone3", CubeListBuilder.create().texOffs(3, 68).addBox(-5.5F, -2.3361F, -2.487F, 11.0F, 1.0F, 10.0F, new CubeDeformation(-0.25F)) + .texOffs(68, 44).addBox(-6.0F, -1.6861F, -3.237F, 12.0F, 1.0F, 1.0F, new CubeDeformation(-0.1F)) + .texOffs(68, 38).addBox(-6.0F, -1.6861F, 5.263F, 12.0F, 1.0F, 1.0F, new CubeDeformation(-0.1F)) + .texOffs(37, 68).addBox(-5.0F, -1.6861F, -3.737F, 1.0F, 1.0F, 12.0F, new CubeDeformation(-0.1F)) + .texOffs(55, 70).addBox(4.0F, -1.6861F, -3.737F, 1.0F, 1.0F, 12.0F, new CubeDeformation(-0.1F)), PartPose.offset(0.0F, -12.3139F, 1.237F)); + + PartDefinition bone467 = bone3.addOrReplaceChild("bone467", CubeListBuilder.create().texOffs(0, 30).addBox(4.0F, -0.5F, -3.0F, 1.0F, 1.0F, 6.0F, new CubeDeformation(-0.2F)) + .texOffs(12, 14).addBox(-5.0F, -0.5F, -3.0F, 1.0F, 1.0F, 6.0F, new CubeDeformation(-0.2F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.3927F, 0.0F, 0.0F)); + + PartDefinition bone4 = bone466.addOrReplaceChild("bone4", CubeListBuilder.create().texOffs(3, 68).addBox(-5.5F, -2.3361F, -2.487F, 11.0F, 1.0F, 10.0F, new CubeDeformation(-0.25F)) + .texOffs(68, 44).addBox(-6.0F, -1.6861F, -3.237F, 12.0F, 1.0F, 1.0F, new CubeDeformation(-0.1F)) + .texOffs(68, 38).addBox(-6.0F, -1.6861F, 5.263F, 12.0F, 1.0F, 1.0F, new CubeDeformation(-0.1F)) + .texOffs(37, 68).addBox(-5.0F, -1.6861F, -3.737F, 1.0F, 1.0F, 12.0F, new CubeDeformation(-0.1F)) + .texOffs(55, 70).addBox(4.0F, -1.6861F, -3.737F, 1.0F, 1.0F, 12.0F, new CubeDeformation(-0.1F)) + .texOffs(0, 30).addBox(-8.0F, -14.0861F, -5.737F, 16.0F, 16.0F, 16.0F, new CubeDeformation(-4.0F)), PartPose.offset(0.0F, -18.3139F, 1.237F)); + + PartDefinition bone5 = bone4.addOrReplaceChild("bone5", CubeListBuilder.create().texOffs(0, 30).addBox(4.0F, -0.5F, -3.0F, 1.0F, 1.0F, 6.0F, new CubeDeformation(-0.2F)) + .texOffs(12, 14).addBox(-5.0F, -0.5F, -3.0F, 1.0F, 1.0F, 6.0F, new CubeDeformation(-0.2F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, 0.3927F, 0.0F, 0.0F)); + + PartDefinition bench = main.addOrReplaceChild("bench", CubeListBuilder.create().texOffs(0, 96).addBox(-10.0F, -8.0F, -10.05F, 16.0F, 16.0F, 16.0F, new CubeDeformation(-6.0F)) + .texOffs(64, 96).addBox(-6.0F, -8.0F, -10.05F, 16.0F, 16.0F, 16.0F, new CubeDeformation(-6.0F)), PartPose.offsetAndRotation(0.0F, -16.4F, 3.75F, 0.0F, -1.5708F, 0.0F)); + + PartDefinition bench2 = bench.addOrReplaceChild("bench2", CubeListBuilder.create().texOffs(0, 96).addBox(-10.0F, -8.0F, -8.05F, 16.0F, 16.0F, 16.0F, new CubeDeformation(-6.0F)) + .texOffs(64, 96).addBox(-6.0F, -8.0F, -8.05F, 16.0F, 16.0F, 16.0F, new CubeDeformation(-6.0F)), PartPose.offsetAndRotation(0.0F, 0.0F, 2.0F, -3.1416F, 0.0F, 3.1416F)); + + return LayerDefinition.create(meshdefinition, 128, 128); + } + + @Override + public void setupAnim(EntityMaid entity, float limbSwing, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch) { + } + + @Override + public void renderToBuffer(PoseStack poseStack, VertexConsumer vertexConsumer, int packedLight, int packedOverlay, float red, float green, float blue, float alpha) { + main.render(poseStack, vertexConsumer, packedLight, packedOverlay, red, green, blue, alpha); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/MaidBackpackMiddleModel.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/backpack/MiddleBackpackModel.java similarity index 99% rename from src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/MaidBackpackMiddleModel.java rename to src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/backpack/MiddleBackpackModel.java index c6cbccdc7..8dce1d09a 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/MaidBackpackMiddleModel.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/backpack/MiddleBackpackModel.java @@ -1,4 +1,4 @@ -package com.github.tartaricacid.touhoulittlemaid.client.model; +package com.github.tartaricacid.touhoulittlemaid.client.model.backpack; import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; @@ -12,11 +12,11 @@ import net.minecraft.client.model.geom.builders.*; import net.minecraft.resources.ResourceLocation; -public class MaidBackpackMiddleModel extends EntityModel { +public class MiddleBackpackModel extends EntityModel { public static ModelLayerLocation LAYER = new ModelLayerLocation(new ResourceLocation(TouhouLittleMaid.MOD_ID, "main"), "backpack_middle"); private final ModelPart all; - public MaidBackpackMiddleModel(ModelPart root) { + public MiddleBackpackModel(ModelPart root) { this.all = root.getChild("all"); } diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/MaidBackpackSmallModel.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/backpack/SmallBackpackModel.java similarity index 99% rename from src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/MaidBackpackSmallModel.java rename to src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/backpack/SmallBackpackModel.java index b5be0af05..7f037a346 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/MaidBackpackSmallModel.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/backpack/SmallBackpackModel.java @@ -1,4 +1,4 @@ -package com.github.tartaricacid.touhoulittlemaid.client.model; +package com.github.tartaricacid.touhoulittlemaid.client.model.backpack; import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; @@ -12,11 +12,11 @@ import net.minecraft.client.model.geom.builders.*; import net.minecraft.resources.ResourceLocation; -public class MaidBackpackSmallModel extends EntityModel { +public class SmallBackpackModel extends EntityModel { public static ModelLayerLocation LAYER = new ModelLayerLocation(new ResourceLocation(TouhouLittleMaid.MOD_ID, "main"), "backpack_small"); private final ModelPart all; - public MaidBackpackSmallModel(ModelPart root) { + public SmallBackpackModel(ModelPart root) { this.all = root.getChild("all"); } diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/renderer/entity/EntitySitRenderer.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/renderer/entity/EntitySitRenderer.java new file mode 100644 index 000000000..71b7d840b --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/renderer/entity/EntitySitRenderer.java @@ -0,0 +1,26 @@ +package com.github.tartaricacid.touhoulittlemaid.client.renderer.entity; + +import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; +import com.github.tartaricacid.touhoulittlemaid.entity.item.EntitySit; +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.entity.EntityRenderer; +import net.minecraft.client.renderer.entity.EntityRendererProvider; +import net.minecraft.resources.ResourceLocation; + +public class EntitySitRenderer extends EntityRenderer { + private static final ResourceLocation EMPTY = new ResourceLocation(TouhouLittleMaid.MOD_ID, "textures/entity/empty.png"); + + public EntitySitRenderer(EntityRendererProvider.Context context) { + super(context); + } + + @Override + public void render(EntitySit entitySit, float entityYaw, float partialTicks, PoseStack poseStack, MultiBufferSource bufferIn, int packedLightIn) { + } + + @Override + public ResourceLocation getTextureLocation(EntitySit entitySit) { + return EMPTY; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/renderer/entity/EntityTombstoneRenderer.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/renderer/entity/EntityTombstoneRenderer.java new file mode 100644 index 000000000..480efd797 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/renderer/entity/EntityTombstoneRenderer.java @@ -0,0 +1,86 @@ +package com.github.tartaricacid.touhoulittlemaid.client.renderer.entity; + +import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; +import com.github.tartaricacid.touhoulittlemaid.client.model.TombstoneModel; +import com.github.tartaricacid.touhoulittlemaid.entity.item.EntityTombstone; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import com.mojang.math.Matrix4f; +import net.minecraft.ChatFormatting; +import net.minecraft.client.gui.Font; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.entity.EntityRenderer; +import net.minecraft.client.renderer.entity.EntityRendererProvider; +import net.minecraft.client.renderer.texture.OverlayTexture; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.Level; + +public class EntityTombstoneRenderer extends EntityRenderer { + private static final ResourceLocation DEFAULT_TEXTURE = new ResourceLocation(TouhouLittleMaid.MOD_ID, "textures/entity/tombstone.png"); + private static final ResourceLocation THE_NETHER_TEXTURE = new ResourceLocation(TouhouLittleMaid.MOD_ID, "textures/entity/tombstone_the_nether.png"); + private static final ResourceLocation THE_END_TEXTURE = new ResourceLocation(TouhouLittleMaid.MOD_ID, "textures/entity/tombstone_the_end.png"); + private static final ResourceLocation TWILIGHT_FOREST_TEXTURE = new ResourceLocation(TouhouLittleMaid.MOD_ID, "textures/entity/tombstone_twilight_forest.png"); + private static final ResourceLocation TWILIGHT_FOREST_NAME = new ResourceLocation("twilightforest", "twilight_forest"); + private static final int NAME_SHOW_DISTANCE = 64; + private final TombstoneModel tombstoneModel; + + public EntityTombstoneRenderer(EntityRendererProvider.Context manager) { + super(manager); + tombstoneModel = new TombstoneModel(manager.bakeLayer(TombstoneModel.LAYER)); + } + + @Override + public void render(EntityTombstone tombstone, float entityYaw, float partialTicks, PoseStack poseStack, MultiBufferSource bufferIn, int packedLight) { + poseStack.pushPose(); + poseStack.scale(-1.0F, -1.0F, 1.0F); + poseStack.translate(0.0, -1.501, 0.0); + tombstoneModel.setupAnim(tombstone, 0, 0, -0.1f, 0, 0); + RenderType renderType = RenderType.entityTranslucent(getTextureLocation(tombstone)); + VertexConsumer buffer = bufferIn.getBuffer(renderType); + tombstoneModel.renderToBuffer(poseStack, buffer, packedLight, OverlayTexture.NO_OVERLAY, 1.0F, 1.0F, 1.0F, 1.0F); + poseStack.popPose(); + if (this.shouldShowName(tombstone)) { + this.renderNameTag(tombstone, new TranslatableComponent("entity.touhou_little_maid.tombstone.display").withStyle(ChatFormatting.GOLD, ChatFormatting.UNDERLINE), 1.6f, poseStack, bufferIn, packedLight); + this.renderNameTag(tombstone, tombstone.getMaidName(), 1.85f, poseStack, bufferIn, packedLight); + } + } + + @Override + protected boolean shouldShowName(EntityTombstone tombstone) { + return !tombstone.getMaidName().equals(TextComponent.EMPTY); + } + + @Override + public ResourceLocation getTextureLocation(EntityTombstone entity) { + ResourceLocation dimension = entity.level.dimension().location(); + if (dimension.equals(Level.NETHER.location())) { + return THE_NETHER_TEXTURE; + } + if (dimension.equals(Level.END.location())) { + return THE_END_TEXTURE; + } + if (dimension.equals(TWILIGHT_FOREST_NAME)) { + return TWILIGHT_FOREST_TEXTURE; + } + return DEFAULT_TEXTURE; + } + + private void renderNameTag(EntityTombstone tombstone, Component component, float yOffset, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight) { + double distance = this.entityRenderDispatcher.distanceToSqr(tombstone); + if (distance < (NAME_SHOW_DISTANCE * NAME_SHOW_DISTANCE)) { + poseStack.pushPose(); + poseStack.translate(0.0F, yOffset, 0.0F); + poseStack.mulPose(this.entityRenderDispatcher.cameraOrientation()); + poseStack.scale(-0.025F, -0.025F, 0.025F); + Matrix4f matrix4f = poseStack.last().pose(); + Font font = this.getFont(); + float width = (float) (-font.width(component) / 2); + font.drawInBatch(component, width, 0, -1, false, matrix4f, bufferSource, false, 0, packedLight); + poseStack.popPose(); + } + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/renderer/entity/geckolayer/GeckoLayerMaidBackItem.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/renderer/entity/geckolayer/GeckoLayerMaidBackItem.java index ac8ffab85..e896dddc4 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/renderer/entity/geckolayer/GeckoLayerMaidBackItem.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/renderer/entity/geckolayer/GeckoLayerMaidBackItem.java @@ -7,7 +7,6 @@ import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.IAnimatable; import com.github.tartaricacid.touhoulittlemaid.geckolib3.geo.GeoLayerRenderer; -import com.github.tartaricacid.touhoulittlemaid.item.BackpackLevel; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.math.Vector3f; import net.minecraft.client.Minecraft; @@ -32,7 +31,7 @@ public void render(PoseStack matrixStack, MultiBufferSource bufferIn, int packed return; } if (entityLivingBaseIn instanceof EntityMaid maid) { - ItemStack stack = maid.getMaidInv().getStackInSlot(5); + ItemStack stack = maid.getBackpackShowItem(); if (stack.getItem() instanceof Vanishable) { if (!renderer.getMainInfo().isShowBackpack() || !InGameMaidConfig.INSTANCE.isShowBackpack() || maid.isSleeping() || maid.isInvisible()) { return; @@ -41,22 +40,7 @@ public void render(PoseStack matrixStack, MultiBufferSource bufferIn, int packed matrixStack.mulPose(Vector3f.ZP.rotationDegrees(180.0F)); matrixStack.mulPose(Vector3f.XP.rotationDegrees(180.0F)); matrixStack.translate(0, 0.5, -0.25); - switch (maid.getBackpackLevel()) { - default: - case BackpackLevel.EMPTY: - matrixStack.translate(0, 0.625, 0.2); - break; - case BackpackLevel.SMALL: - matrixStack.translate(0, 0.625, -0.05); - break; - case BackpackLevel.MIDDLE: - matrixStack.mulPose(Vector3f.XP.rotationDegrees(-7.5F)); - matrixStack.translate(0, 0.625, -0.25); - break; - case BackpackLevel.BIG: - matrixStack.translate(0, 0, -0.4); - break; - } + maid.getMaidBackpackType().offsetBackpackItem(matrixStack); if (SlashBladeCompat.isSlashBladeItem(stack)) { SlashBladeRender.renderGeckoMaidBackSlashBlade(matrixStack, bufferIn, packedLightIn, stack); } else { diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/renderer/entity/geckolayer/GeckoLayerMaidBackpack.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/renderer/entity/geckolayer/GeckoLayerMaidBackpack.java index 46fe98d0a..642a4fde7 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/renderer/entity/geckolayer/GeckoLayerMaidBackpack.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/renderer/entity/geckolayer/GeckoLayerMaidBackpack.java @@ -1,18 +1,15 @@ package com.github.tartaricacid.touhoulittlemaid.client.renderer.entity.geckolayer; -import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; -import com.github.tartaricacid.touhoulittlemaid.client.model.MaidBackpackBigModel; -import com.github.tartaricacid.touhoulittlemaid.client.model.MaidBackpackMiddleModel; -import com.github.tartaricacid.touhoulittlemaid.client.model.MaidBackpackSmallModel; +import com.github.tartaricacid.touhoulittlemaid.api.backpack.IMaidBackpack; import com.github.tartaricacid.touhoulittlemaid.client.renderer.entity.GeckoEntityMaidRenderer; import com.github.tartaricacid.touhoulittlemaid.config.subconfig.InGameMaidConfig; +import com.github.tartaricacid.touhoulittlemaid.entity.backpack.BackpackManager; import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; import com.github.tartaricacid.touhoulittlemaid.geckolib3.core.IAnimatable; import com.github.tartaricacid.touhoulittlemaid.geckolib3.geo.GeoLayerRenderer; import com.github.tartaricacid.touhoulittlemaid.geckolib3.geo.render.built.GeoBone; import com.github.tartaricacid.touhoulittlemaid.geckolib3.geo.render.built.GeoModel; import com.github.tartaricacid.touhoulittlemaid.geckolib3.util.RenderUtils; -import com.github.tartaricacid.touhoulittlemaid.item.BackpackLevel; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; import com.mojang.math.Vector3f; @@ -25,20 +22,11 @@ import net.minecraft.world.entity.LivingEntity; public class GeckoLayerMaidBackpack extends GeoLayerRenderer { - private static final ResourceLocation SMALL = new ResourceLocation(TouhouLittleMaid.MOD_ID, "textures/entity/maid_backpack_small.png"); - private static final ResourceLocation MIDDLE = new ResourceLocation(TouhouLittleMaid.MOD_ID, "textures/entity/maid_backpack_middle.png"); - private static final ResourceLocation BIG = new ResourceLocation(TouhouLittleMaid.MOD_ID, "textures/entity/maid_backpack_big.png"); private final GeckoEntityMaidRenderer renderer; - private final EntityModel smallModel; - private final EntityModel middleModel; - private final EntityModel bigModel; public GeckoLayerMaidBackpack(GeckoEntityMaidRenderer entityRendererIn, EntityModelSet modelSet) { super(entityRendererIn); this.renderer = entityRendererIn; - smallModel = new MaidBackpackSmallModel(modelSet.bakeLayer(MaidBackpackSmallModel.LAYER)); - middleModel = new MaidBackpackMiddleModel(modelSet.bakeLayer(MaidBackpackMiddleModel.LAYER)); - bigModel = new MaidBackpackBigModel(modelSet.bakeLayer(MaidBackpackBigModel.LAYER)); } protected static void renderColoredCutoutModel(EntityModel pModel, ResourceLocation pTextureLocation, PoseStack pPoseStack, MultiBufferSource pBuffer, int pPackedLight, T pEntity, float pRed, float pGreen, float pBlue) { @@ -57,18 +45,8 @@ public void render(PoseStack poseStack, MultiBufferSource bufferIn, int packedLi translateToBackpack(poseStack, geoModel); poseStack.translate(0, 1, 0.25); poseStack.mulPose(Vector3f.ZP.rotationDegrees(180)); - switch (maid.getBackpackLevel()) { - case BackpackLevel.SMALL: - renderColoredCutoutModel(smallModel, SMALL, poseStack, bufferIn, packedLightIn, maid, 1.0f, 1.0f, 1.0f); - return; - case BackpackLevel.MIDDLE: - renderColoredCutoutModel(middleModel, MIDDLE, poseStack, bufferIn, packedLightIn, maid, 1.0f, 1.0f, 1.0f); - return; - case BackpackLevel.BIG: - renderColoredCutoutModel(bigModel, BIG, poseStack, bufferIn, packedLightIn, maid, 1.0f, 1.0f, 1.0f); - case BackpackLevel.EMPTY: - default: - } + IMaidBackpack backpack = maid.getMaidBackpackType(); + BackpackManager.findBackpackModel(backpack.getId()).ifPresent(pair -> renderColoredCutoutModel(pair.getLeft(), pair.getRight(), poseStack, bufferIn, packedLightIn, maid, 1.0f, 1.0f, 1.0f)); } } } diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/renderer/entity/layer/LayerMaidBackItem.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/renderer/entity/layer/LayerMaidBackItem.java index 6e07aa34e..2844ac2d9 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/renderer/entity/layer/LayerMaidBackItem.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/renderer/entity/layer/LayerMaidBackItem.java @@ -6,13 +6,13 @@ import com.github.tartaricacid.touhoulittlemaid.compat.slashblade.SlashBladeRender; import com.github.tartaricacid.touhoulittlemaid.config.subconfig.InGameMaidConfig; import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; -import com.github.tartaricacid.touhoulittlemaid.item.BackpackLevel; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.math.Vector3f; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.block.model.ItemTransforms; import net.minecraft.client.renderer.entity.layers.RenderLayer; +import net.minecraft.client.renderer.texture.OverlayTexture; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Vanishable; @@ -26,7 +26,7 @@ public LayerMaidBackItem(EntityMaidRenderer renderer) { @Override public void render(PoseStack matrixStack, MultiBufferSource bufferIn, int packedLightIn, EntityMaid maid, float limbSwing, float limbSwingAmount, float partialTicks, float ageInTicks, float netHeadYaw, float headPitch) { - ItemStack stack = maid.getMaidInv().getStackInSlot(5); + ItemStack stack = maid.getBackpackShowItem(); if (stack.getItem() instanceof Vanishable) { if (!renderer.getMainInfo().isShowBackpack() || !InGameMaidConfig.INSTANCE.isShowBackpack() || maid.isSleeping() || maid.isInvisible()) { return; @@ -35,26 +35,11 @@ public void render(PoseStack matrixStack, MultiBufferSource bufferIn, int packed matrixStack.mulPose(Vector3f.ZP.rotationDegrees(180.0F)); matrixStack.mulPose(Vector3f.XP.rotationDegrees(180.0F)); matrixStack.translate(0, 0.5, -0.25); - switch (maid.getBackpackLevel()) { - default: - case BackpackLevel.EMPTY: - matrixStack.translate(0, 0.625, 0.2); - break; - case BackpackLevel.SMALL: - matrixStack.translate(0, 0.625, -0.05); - break; - case BackpackLevel.MIDDLE: - matrixStack.mulPose(Vector3f.XP.rotationDegrees(-7.5F)); - matrixStack.translate(0, 0.625, -0.25); - break; - case BackpackLevel.BIG: - matrixStack.translate(0, 0, -0.4); - break; - } + maid.getMaidBackpackType().offsetBackpackItem(matrixStack); if (SlashBladeCompat.isSlashBladeItem(stack)) { SlashBladeRender.renderMaidBackSlashBlade(matrixStack, bufferIn, packedLightIn, stack); } else { - Minecraft.getInstance().getItemInHandRenderer().renderItem(maid, stack, ItemTransforms.TransformType.FIXED, false, matrixStack, bufferIn, packedLightIn); + Minecraft.getInstance().getItemRenderer().renderStatic(maid, stack, ItemTransforms.TransformType.FIXED, false, matrixStack, bufferIn, maid.level, packedLightIn, OverlayTexture.NO_OVERLAY, maid.getId()); } matrixStack.popPose(); } diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/renderer/entity/layer/LayerMaidBackpack.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/renderer/entity/layer/LayerMaidBackpack.java index 22bb00f76..fdf049b9a 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/renderer/entity/layer/LayerMaidBackpack.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/renderer/entity/layer/LayerMaidBackpack.java @@ -1,37 +1,24 @@ package com.github.tartaricacid.touhoulittlemaid.client.renderer.entity.layer; -import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; -import com.github.tartaricacid.touhoulittlemaid.client.model.MaidBackpackBigModel; -import com.github.tartaricacid.touhoulittlemaid.client.model.MaidBackpackMiddleModel; -import com.github.tartaricacid.touhoulittlemaid.client.model.MaidBackpackSmallModel; +import com.github.tartaricacid.touhoulittlemaid.api.backpack.IMaidBackpack; import com.github.tartaricacid.touhoulittlemaid.client.model.bedrock.BedrockModel; import com.github.tartaricacid.touhoulittlemaid.client.model.bedrock.BedrockPart; import com.github.tartaricacid.touhoulittlemaid.client.renderer.entity.EntityMaidRenderer; import com.github.tartaricacid.touhoulittlemaid.config.subconfig.InGameMaidConfig; +import com.github.tartaricacid.touhoulittlemaid.entity.backpack.BackpackManager; import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; -import com.github.tartaricacid.touhoulittlemaid.item.BackpackLevel; import com.mojang.blaze3d.vertex.PoseStack; -import net.minecraft.client.model.EntityModel; import net.minecraft.client.model.geom.EntityModelSet; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.entity.layers.RenderLayer; -import net.minecraft.resources.ResourceLocation; public class LayerMaidBackpack extends RenderLayer> { - private static final ResourceLocation SMALL = new ResourceLocation(TouhouLittleMaid.MOD_ID, "textures/entity/maid_backpack_small.png"); - private static final ResourceLocation MIDDLE = new ResourceLocation(TouhouLittleMaid.MOD_ID, "textures/entity/maid_backpack_middle.png"); - private static final ResourceLocation BIG = new ResourceLocation(TouhouLittleMaid.MOD_ID, "textures/entity/maid_backpack_big.png"); private final EntityMaidRenderer renderer; - private final EntityModel smallModel; - private final EntityModel middleModel; - private final EntityModel bigModel; public LayerMaidBackpack(EntityMaidRenderer renderer, EntityModelSet modelSet) { super(renderer); this.renderer = renderer; - smallModel = new MaidBackpackSmallModel(modelSet.bakeLayer(MaidBackpackSmallModel.LAYER)); - middleModel = new MaidBackpackMiddleModel(modelSet.bakeLayer(MaidBackpackMiddleModel.LAYER)); - bigModel = new MaidBackpackBigModel(modelSet.bakeLayer(MaidBackpackBigModel.LAYER)); + BackpackManager.initClient(modelSet); } @Override @@ -39,7 +26,6 @@ public void render(PoseStack poseStack, MultiBufferSource bufferIn, int packedLi if (!renderer.getMainInfo().isShowBackpack() || !InGameMaidConfig.INSTANCE.isShowBackpack() || maid.isSleeping() || maid.isInvisible()) { return; } - // 稍微缩放,避免整数倍的 z-flight poseStack.scale(1.01f, 1.01f, 1.01f); // [-13, 41, 15] @@ -49,18 +35,7 @@ public void render(PoseStack poseStack, MultiBufferSource bufferIn, int packedLi } else { poseStack.translate(0, -0.5, 0.25); } - - switch (maid.getBackpackLevel()) { - case BackpackLevel.SMALL: - renderColoredCutoutModel(smallModel, SMALL, poseStack, bufferIn, packedLightIn, maid, 1.0f, 1.0f, 1.0f); - return; - case BackpackLevel.MIDDLE: - renderColoredCutoutModel(middleModel, MIDDLE, poseStack, bufferIn, packedLightIn, maid, 1.0f, 1.0f, 1.0f); - return; - case BackpackLevel.BIG: - renderColoredCutoutModel(bigModel, BIG, poseStack, bufferIn, packedLightIn, maid, 1.0f, 1.0f, 1.0f); - case BackpackLevel.EMPTY: - default: - } + IMaidBackpack type = maid.getMaidBackpackType(); + BackpackManager.findBackpackModel(type.getId()).ifPresent(pair -> renderColoredCutoutModel(pair.getLeft(), pair.getRight(), poseStack, bufferIn, packedLightIn, maid, 1.0f, 1.0f, 1.0f)); } } diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/renderer/tileentity/TileEntityBookshelfRenderer.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/renderer/tileentity/TileEntityBookshelfRenderer.java new file mode 100644 index 000000000..9bc153ca8 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/renderer/tileentity/TileEntityBookshelfRenderer.java @@ -0,0 +1,41 @@ +package com.github.tartaricacid.touhoulittlemaid.client.renderer.tileentity; + +import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; +import com.github.tartaricacid.touhoulittlemaid.block.BlockGomoku; +import com.github.tartaricacid.touhoulittlemaid.client.model.BookshelfModel; +import com.github.tartaricacid.touhoulittlemaid.tileentity.TileEntityBookshelf; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import com.mojang.math.Vector3f; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; +import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; +import net.minecraft.core.Direction; +import net.minecraft.resources.ResourceLocation; + +public class TileEntityBookshelfRenderer implements BlockEntityRenderer { + private static final ResourceLocation TEXTURE = new ResourceLocation(TouhouLittleMaid.MOD_ID, "textures/entity/bookshelf.png"); + private final BookshelfModel model; + + public TileEntityBookshelfRenderer(BlockEntityRendererProvider.Context context) { + model = new BookshelfModel(context.bakeLayer(BookshelfModel.LAYER)); + } + + @Override + public void render(TileEntityBookshelf bookshelf, float partialTick, PoseStack poseStack, MultiBufferSource bufferIn, int combinedLightIn, int combinedOverlayIn) { + Direction facing = bookshelf.getBlockState().getValue(BlockGomoku.FACING); + poseStack.pushPose(); + poseStack.translate(0.5, 1.5, 0.5); + poseStack.mulPose(Vector3f.ZN.rotationDegrees(180)); + poseStack.mulPose(Vector3f.YN.rotationDegrees(180 - facing.get2DDataValue() * 90)); + VertexConsumer buffer = bufferIn.getBuffer(RenderType.entityTranslucent(TEXTURE)); + model.renderToBuffer(poseStack, buffer, combinedLightIn, combinedOverlayIn, 1.0F, 1.0F, 1.0F, 1.0F); + poseStack.popPose(); + } + + @Override + public boolean shouldRenderOffScreen(TileEntityBookshelf te) { + return true; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/renderer/tileentity/TileEntityComputerRenderer.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/renderer/tileentity/TileEntityComputerRenderer.java new file mode 100644 index 000000000..6f6ac7953 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/renderer/tileentity/TileEntityComputerRenderer.java @@ -0,0 +1,41 @@ +package com.github.tartaricacid.touhoulittlemaid.client.renderer.tileentity; + +import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; +import com.github.tartaricacid.touhoulittlemaid.block.BlockGomoku; +import com.github.tartaricacid.touhoulittlemaid.client.model.ComputerModel; +import com.github.tartaricacid.touhoulittlemaid.tileentity.TileEntityComputer; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import com.mojang.math.Vector3f; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; +import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; +import net.minecraft.core.Direction; +import net.minecraft.resources.ResourceLocation; + +public class TileEntityComputerRenderer implements BlockEntityRenderer { + private static final ResourceLocation TEXTURE = new ResourceLocation(TouhouLittleMaid.MOD_ID, "textures/entity/computer.png"); + private final ComputerModel model; + + public TileEntityComputerRenderer(BlockEntityRendererProvider.Context context) { + model = new ComputerModel(context.bakeLayer(ComputerModel.LAYER)); + } + + @Override + public void render(TileEntityComputer computer, float partialTick, PoseStack poseStack, MultiBufferSource bufferIn, int combinedLightIn, int combinedOverlayIn) { + Direction facing = computer.getBlockState().getValue(BlockGomoku.FACING); + poseStack.pushPose(); + poseStack.translate(0.5, 1.5, 0.5); + poseStack.mulPose(Vector3f.ZN.rotationDegrees(180)); + poseStack.mulPose(Vector3f.YN.rotationDegrees(180 - facing.get2DDataValue() * 90)); + VertexConsumer buffer = bufferIn.getBuffer(RenderType.entityTranslucent(TEXTURE)); + model.renderToBuffer(poseStack, buffer, combinedLightIn, combinedOverlayIn, 1.0F, 1.0F, 1.0F, 1.0F); + poseStack.popPose(); + } + + @Override + public boolean shouldRenderOffScreen(TileEntityComputer te) { + return true; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/renderer/tileentity/TileEntityGomokuRenderer.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/renderer/tileentity/TileEntityGomokuRenderer.java new file mode 100644 index 000000000..6c2ef3afa --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/renderer/tileentity/TileEntityGomokuRenderer.java @@ -0,0 +1,146 @@ +package com.github.tartaricacid.touhoulittlemaid.client.renderer.tileentity; + +import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; +import com.github.tartaricacid.touhoulittlemaid.api.game.gomoku.Point; +import com.github.tartaricacid.touhoulittlemaid.block.BlockGomoku; +import com.github.tartaricacid.touhoulittlemaid.client.model.GomokuModel; +import com.github.tartaricacid.touhoulittlemaid.client.model.PieceModel; +import com.github.tartaricacid.touhoulittlemaid.tileentity.TileEntityGomoku; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import com.mojang.math.Vector3f; +import net.minecraft.ChatFormatting; +import net.minecraft.client.Camera; +import net.minecraft.client.gui.Font; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.blockentity.BlockEntityRenderDispatcher; +import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; +import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.resources.ResourceLocation; + +public class TileEntityGomokuRenderer implements BlockEntityRenderer { + private static final ResourceLocation CHECKER_BOARD_TEXTURE = new ResourceLocation(TouhouLittleMaid.MOD_ID, "textures/entity/gomoku.png"); + private static final ResourceLocation BLACK_PIECE_TEXTURE = new ResourceLocation(TouhouLittleMaid.MOD_ID, "textures/entity/black_piece.png"); + private static final ResourceLocation WHITE_PIECE_TEXTURE = new ResourceLocation(TouhouLittleMaid.MOD_ID, "textures/entity/white_piece.png"); + private static final int TIPS_RENDER_DISTANCE = 16; + private static final int PIECE_RENDER_DISTANCE = 24; + private final GomokuModel CHECKER_BOARD_MODEL; + private final PieceModel PIECE_MODEL; + private final Font font; + private final BlockEntityRenderDispatcher dispatcher; + + public TileEntityGomokuRenderer(BlockEntityRendererProvider.Context context) { + CHECKER_BOARD_MODEL = new GomokuModel(context.bakeLayer(GomokuModel.LAYER)); + PIECE_MODEL = new PieceModel(context.bakeLayer(PieceModel.LAYER)); + this.font = context.getFont(); + this.dispatcher = context.getBlockEntityRenderDispatcher(); + } + + @Override + public void render(TileEntityGomoku gomoku, float partialTick, PoseStack poseStack, MultiBufferSource bufferIn, int combinedLightIn, int combinedOverlayIn) { + this.renderChessboard(gomoku, poseStack, bufferIn, combinedLightIn, combinedOverlayIn); + this.renderPiece(gomoku, poseStack, bufferIn, combinedLightIn, combinedOverlayIn); + this.renderLatestChessTips(gomoku, poseStack, bufferIn, combinedLightIn); + this.renderTipsText(gomoku, poseStack, bufferIn, combinedLightIn); + } + + private void renderLatestChessTips(TileEntityGomoku gomoku, PoseStack poseStack, MultiBufferSource bufferIn, int combinedLightIn) { + if (!gomoku.getLatestChessPoint().equals(Point.NULL) && inRenderDistance(gomoku, PIECE_RENDER_DISTANCE)) { + Camera camera = this.dispatcher.camera; + Point point = gomoku.getLatestChessPoint(); + poseStack.pushPose(); + poseStack.translate(-0.42, 0.25, -0.42); + poseStack.translate(point.x * 0.1316, 0, point.y * 0.1316); + poseStack.mulPose(Vector3f.YN.rotationDegrees(180 + camera.getYRot())); + poseStack.scale(0.015625F, -0.015625F, 0.015625F); + float width = (float) (-this.font.width("▼") / 2) + 0.5f; + this.font.drawInBatch("▼", width, -1.5f, 0xFF0000, false, poseStack.last().pose(), bufferIn, true, 0, combinedLightIn); + poseStack.popPose(); + } + } + + private void renderChessboard(TileEntityGomoku gomoku, PoseStack poseStack, MultiBufferSource bufferIn, int combinedLightIn, int combinedOverlayIn) { + Direction facing = gomoku.getBlockState().getValue(BlockGomoku.FACING); + poseStack.pushPose(); + poseStack.translate(0.5, 1.5, 0.5); + poseStack.mulPose(Vector3f.ZN.rotationDegrees(180)); + poseStack.mulPose(Vector3f.YN.rotationDegrees(facing.get2DDataValue() * 90)); + if (facing == Direction.SOUTH || facing == Direction.NORTH) { + poseStack.mulPose(Vector3f.YN.rotationDegrees(180)); + } + VertexConsumer checkerBoardBuff = bufferIn.getBuffer(RenderType.entityTranslucent(CHECKER_BOARD_TEXTURE)); + CHECKER_BOARD_MODEL.renderToBuffer(poseStack, checkerBoardBuff, combinedLightIn, combinedOverlayIn, 1.0F, 1.0F, 1.0F, 1.0F); + poseStack.popPose(); + } + + private void renderPiece(TileEntityGomoku gomoku, PoseStack poseStack, MultiBufferSource bufferIn, int combinedLightIn, int combinedOverlayIn) { + if (inRenderDistance(gomoku, PIECE_RENDER_DISTANCE)) { + poseStack.pushPose(); + poseStack.translate(0.5, 1.5, 0.5); + poseStack.mulPose(Vector3f.ZN.rotationDegrees(180)); + poseStack.translate(0.92, -0.1, -1.055); + int[][] chessData = gomoku.getChessData(); + for (int[] row : chessData) { + for (int j = 0; j < chessData[0].length; j++) { + poseStack.translate(0, 0, 0.1316); + if (row[j] == Point.BLACK) { + VertexConsumer blackPieceBuff = bufferIn.getBuffer(RenderType.entityTranslucent(BLACK_PIECE_TEXTURE)); + PIECE_MODEL.renderToBuffer(poseStack, blackPieceBuff, combinedLightIn, combinedOverlayIn, 1.0F, 1.0F, 1.0F, 1.0F); + } + if (row[j] == Point.WHITE) { + VertexConsumer whitePieceBuff = bufferIn.getBuffer(RenderType.entityTranslucent(WHITE_PIECE_TEXTURE)); + PIECE_MODEL.renderToBuffer(poseStack, whitePieceBuff, combinedLightIn, combinedOverlayIn, 1.0F, 1.0F, 1.0F, 1.0F); + } + } + poseStack.translate(-0.1316, 0, -1.974); + } + poseStack.popPose(); + } + } + + private void renderTipsText(TileEntityGomoku gomoku, PoseStack poseStack, MultiBufferSource bufferIn, int combinedLightIn) { + if (!gomoku.isInProgress() && inRenderDistance(gomoku, TIPS_RENDER_DISTANCE)) { + Camera camera = this.dispatcher.camera; + MutableComponent loseTips; + MutableComponent resetTips = new TranslatableComponent("message.touhou_little_maid.gomoku.reset").withStyle(ChatFormatting.UNDERLINE).withStyle(ChatFormatting.AQUA); + MutableComponent roundText = new TranslatableComponent("message.touhou_little_maid.gomoku.round", gomoku.getChessCounter()).withStyle(ChatFormatting.WHITE); + MutableComponent preRoundIcon = new TextComponent("⏹ ").withStyle(ChatFormatting.GREEN); + MutableComponent postRoundIcon = new TextComponent(" ⏹").withStyle(ChatFormatting.GREEN); + MutableComponent roundTips = preRoundIcon.append(roundText).append(postRoundIcon); + if (gomoku.isPlayerTurn()) { + loseTips = new TranslatableComponent("message.touhou_little_maid.gomoku.win").withStyle(ChatFormatting.BOLD).withStyle(ChatFormatting.DARK_PURPLE); + } else { + loseTips = new TranslatableComponent("message.touhou_little_maid.gomoku.lose").withStyle(ChatFormatting.BOLD).withStyle(ChatFormatting.DARK_PURPLE); + } + float loseTipsWidth = (float) (-this.font.width(loseTips) / 2); + float resetTipsWidth = (float) (-this.font.width(resetTips) / 2); + float roundTipsWidth = (float) (-this.font.width(roundTips) / 2); + poseStack.pushPose(); + poseStack.translate(0.5, 0.75, 0.5); + poseStack.mulPose(Vector3f.YN.rotationDegrees(180 + camera.getYRot())); + poseStack.mulPose(Vector3f.XN.rotationDegrees(camera.getXRot())); + poseStack.scale(0.03F, -0.03F, 0.03F); + this.font.drawInBatch(loseTips, loseTipsWidth, -10, 0xFFFFFF, true, poseStack.last().pose(), bufferIn, true, 0, combinedLightIn); + poseStack.scale(0.5F, 0.5F, 0.5F); + this.font.drawInBatch(roundTips, roundTipsWidth, -30, 0xFFFFFF, true, poseStack.last().pose(), bufferIn, true, 0, combinedLightIn); + this.font.drawInBatch(resetTips, resetTipsWidth, 0, 0xFFFFFF, true, poseStack.last().pose(), bufferIn, true, 0, combinedLightIn); + poseStack.popPose(); + } + } + + private boolean inRenderDistance(TileEntityGomoku gomoku, int distance) { + BlockPos pos = gomoku.getBlockPos(); + return this.dispatcher.camera.getPosition().distanceToSqr(pos.getX(), pos.getY(), pos.getZ()) < distance * distance; + } + + @Override + public boolean shouldRenderOffScreen(TileEntityGomoku te) { + return true; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/renderer/tileentity/TileEntityKeyboardRenderer.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/renderer/tileentity/TileEntityKeyboardRenderer.java new file mode 100644 index 000000000..6eda662c2 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/renderer/tileentity/TileEntityKeyboardRenderer.java @@ -0,0 +1,41 @@ +package com.github.tartaricacid.touhoulittlemaid.client.renderer.tileentity; + +import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; +import com.github.tartaricacid.touhoulittlemaid.block.BlockGomoku; +import com.github.tartaricacid.touhoulittlemaid.client.model.KeyboardModel; +import com.github.tartaricacid.touhoulittlemaid.tileentity.TileEntityKeyboard; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import com.mojang.math.Vector3f; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; +import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; +import net.minecraft.core.Direction; +import net.minecraft.resources.ResourceLocation; + +public class TileEntityKeyboardRenderer implements BlockEntityRenderer { + private static final ResourceLocation TEXTURE = new ResourceLocation(TouhouLittleMaid.MOD_ID, "textures/entity/keyboard.png"); + private final KeyboardModel model; + + public TileEntityKeyboardRenderer(BlockEntityRendererProvider.Context context) { + model = new KeyboardModel(context.bakeLayer(KeyboardModel.LAYER)); + } + + @Override + public void render(TileEntityKeyboard keyboard, float partialTick, PoseStack poseStack, MultiBufferSource bufferIn, int combinedLightIn, int combinedOverlayIn) { + Direction facing = keyboard.getBlockState().getValue(BlockGomoku.FACING); + poseStack.pushPose(); + poseStack.translate(0.5, 1.5, 0.5); + poseStack.mulPose(Vector3f.ZN.rotationDegrees(180)); + poseStack.mulPose(Vector3f.YN.rotationDegrees(180 - facing.get2DDataValue() * 90)); + VertexConsumer buffer = bufferIn.getBuffer(RenderType.entityTranslucent(TEXTURE)); + model.renderToBuffer(poseStack, buffer, combinedLightIn, combinedOverlayIn, 1.0F, 1.0F, 1.0F, 1.0F); + poseStack.popPose(); + } + + @Override + public boolean shouldRenderOffScreen(TileEntityKeyboard te) { + return true; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/renderer/tileentity/TileEntityShrineRenderer.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/renderer/tileentity/TileEntityShrineRenderer.java new file mode 100644 index 000000000..f5e923d15 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/renderer/tileentity/TileEntityShrineRenderer.java @@ -0,0 +1,53 @@ +package com.github.tartaricacid.touhoulittlemaid.client.renderer.tileentity; + +import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; +import com.github.tartaricacid.touhoulittlemaid.block.BlockGomoku; +import com.github.tartaricacid.touhoulittlemaid.client.model.ShrineModel; +import com.github.tartaricacid.touhoulittlemaid.tileentity.TileEntityShrine; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import com.mojang.math.Vector3f; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.block.model.ItemTransforms; +import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; +import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; +import net.minecraft.core.Direction; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; + +public class TileEntityShrineRenderer implements BlockEntityRenderer { + private static final ResourceLocation TEXTURE = new ResourceLocation(TouhouLittleMaid.MOD_ID, "textures/entity/shrine.png"); + private final ShrineModel model; + + public TileEntityShrineRenderer(BlockEntityRendererProvider.Context context) { + model = new ShrineModel(context.bakeLayer(ShrineModel.LAYER)); + } + + @Override + public void render(TileEntityShrine shrine, float partialTick, PoseStack poseStack, MultiBufferSource bufferIn, int combinedLightIn, int combinedOverlayIn) { + Direction facing = shrine.getBlockState().getValue(BlockGomoku.FACING); + poseStack.pushPose(); + poseStack.translate(0.5, 1.5, 0.5); + poseStack.mulPose(Vector3f.ZN.rotationDegrees(180)); + poseStack.mulPose(Vector3f.YN.rotationDegrees(180 - facing.get2DDataValue() * 90)); + VertexConsumer buffer = bufferIn.getBuffer(RenderType.entityTranslucent(TEXTURE)); + model.renderToBuffer(poseStack, buffer, combinedLightIn, combinedOverlayIn, 1.0F, 1.0F, 1.0F, 1.0F); + poseStack.popPose(); + + Level level = shrine.getLevel(); + if (level == null) { + return; + } + ItemStack stack = shrine.getStorageItem(); + poseStack.pushPose(); + poseStack.translate(0.5, 0.85, 0.5); + poseStack.scale(0.5f, 0.5f, 0.5f); + float deg = (level.getGameTime() + partialTick) % 360; + poseStack.mulPose(Vector3f.YN.rotationDegrees(deg)); + Minecraft.getInstance().getItemRenderer().renderStatic(stack, ItemTransforms.TransformType.GROUND, combinedLightIn, combinedOverlayIn, poseStack, bufferIn, 0); + poseStack.popPose(); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/compat/cloth/MenuIntegration.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/compat/cloth/MenuIntegration.java index 3de0202ed..be324b15c 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/compat/cloth/MenuIntegration.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/compat/cloth/MenuIntegration.java @@ -118,12 +118,7 @@ private static void miscConfig(ConfigBuilder root, ConfigEntryBuilder entryBuild .setDefaultValue(70).setMin(0).setMax(Integer.MAX_VALUE) .setTooltip(new TranslatableComponent("config.touhou_little_maid.misc.maid_fairy_spawn_probability.desc")) .setSaveConsumer(d -> MiscConfig.MAID_FAIRY_SPAWN_PROBABILITY.set(d)).build()); - - - misc.addEntry(entryBuilder.startStrList(new TranslatableComponent("config.touhou_little_maid.misc.maid_fairy_blacklist_biome.name"), (List) MiscConfig.MAID_FAIRY_BLACKLIST_BIOME.get()) - .setDefaultValue(Lists.newArrayList(NETHER.getName(), THEEND.getName(), NONE.getName(), MUSHROOM.getName())) - .setTooltip(new TranslatableComponent("config.touhou_little_maid.misc.maid_fairy_blacklist_biome.desc")) - .setSaveConsumer(l -> MiscConfig.MAID_FAIRY_BLACKLIST_BIOME.set(l)).build()); + misc.addEntry(entryBuilder.startDoubleField(new TranslatableComponent("config.touhou_little_maid.misc.player_death_loss_power_point.name"), MiscConfig.PLAYER_DEATH_LOSS_POWER_POINT.get()) .setDefaultValue(1.0).setMin(0).setMax(5) diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/compat/domesticationinnovation/PetBedDrop.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/compat/domesticationinnovation/PetBedDrop.java new file mode 100644 index 000000000..7657321c1 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/compat/domesticationinnovation/PetBedDrop.java @@ -0,0 +1,16 @@ +package com.github.tartaricacid.touhoulittlemaid.compat.domesticationinnovation; + +import com.github.alexthe668.domesticationinnovation.server.entity.TameableUtils; +import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; +import net.minecraftforge.fml.ModList; + +public class PetBedDrop { + private static final String DOMESTICATION_INNOVATION = "domesticationinnovation"; + + public static boolean hasPetBedPos(EntityMaid maid) { + if (ModList.get().isLoaded(DOMESTICATION_INNOVATION)) { + return TameableUtils.isTamed(maid) && TameableUtils.getPetBedPos(maid) != null; + } + return false; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/compat/jade/provider/MaidProvider.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/compat/jade/provider/MaidProvider.java index c5e23227a..63b1dc4b6 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/compat/jade/provider/MaidProvider.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/compat/jade/provider/MaidProvider.java @@ -25,6 +25,8 @@ public void appendTooltip(ITooltip iTooltip, EntityAccessor entityAccessor, IPlu IMaidTask task = maid.getTask(); iTooltip.add(new TranslatableComponent("top.touhou_little_maid.entity_maid.task").append(task.getName())); iTooltip.add(new TranslatableComponent("top.touhou_little_maid.entity_maid.schedule").append(getActivityTransText(maid))); + iTooltip.add(new TranslatableComponent("top.touhou_little_maid.entity_maid.favorability", maid.getFavorabilityManager().getLevel())); + iTooltip.add(new TranslatableComponent("top.touhou_little_maid.entity_maid.nex_favorability_point", maid.getFavorabilityManager().nextLevelPoint())); if (maid.getIsInvulnerable()) { iTooltip.add(new TranslatableComponent("top.touhou_little_maid.entity_maid.invulnerable").withStyle(ChatFormatting.DARK_PURPLE)); } diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/compat/jei/MaidPlugin.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/compat/jei/MaidPlugin.java index 34cd621d2..e8ddf1fab 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/compat/jei/MaidPlugin.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/compat/jei/MaidPlugin.java @@ -1,17 +1,19 @@ package com.github.tartaricacid.touhoulittlemaid.compat.jei; import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; +import com.github.tartaricacid.touhoulittlemaid.client.gui.entity.maid.backpack.CraftingTableBackpackContainerScreen; +import com.github.tartaricacid.touhoulittlemaid.client.gui.entity.maid.backpack.FurnaceBackpackContainerScreen; import com.github.tartaricacid.touhoulittlemaid.compat.jei.altar.AltarRecipeCategory; import com.github.tartaricacid.touhoulittlemaid.compat.jei.altar.AltarRecipeMaker; import com.github.tartaricacid.touhoulittlemaid.compat.jei.altar.EntityPlaceholderSubtype; import com.github.tartaricacid.touhoulittlemaid.init.InitItems; +import com.github.tartaricacid.touhoulittlemaid.inventory.container.backpack.CraftingTableBackpackContainer; +import com.github.tartaricacid.touhoulittlemaid.inventory.container.backpack.FurnaceBackpackContainer; import mezz.jei.api.IModPlugin; import mezz.jei.api.JeiPlugin; +import mezz.jei.api.constants.RecipeTypes; import mezz.jei.api.constants.VanillaTypes; -import mezz.jei.api.registration.IRecipeCatalystRegistration; -import mezz.jei.api.registration.IRecipeCategoryRegistration; -import mezz.jei.api.registration.IRecipeRegistration; -import mezz.jei.api.registration.ISubtypeRegistration; +import mezz.jei.api.registration.*; import net.minecraft.network.chat.TranslatableComponent; import net.minecraft.resources.ResourceLocation; @@ -40,6 +42,19 @@ public void registerItemSubtypes(ISubtypeRegistration registration) { registration.registerSubtypeInterpreter(VanillaTypes.ITEM_STACK, InitItems.ENTITY_PLACEHOLDER.get(), new EntityPlaceholderSubtype()); } + @Override + public void registerRecipeTransferHandlers(IRecipeTransferRegistration registration) { + registration.addRecipeTransferHandler(CraftingTableBackpackContainer.class, RecipeTypes.CRAFTING, 71, 9, 0, 70); + registration.addRecipeTransferHandler(FurnaceBackpackContainer.class, RecipeTypes.SMELTING, 70, 1, 0, 70); + registration.addRecipeTransferHandler(FurnaceBackpackContainer.class, RecipeTypes.FUELING, 71, 1, 0, 70); + } + + @Override + public void registerGuiHandlers(IGuiHandlerRegistration registration) { + registration.addRecipeClickArea(CraftingTableBackpackContainerScreen.class, 213, 121, 13, 12, RecipeTypes.CRAFTING); + registration.addRecipeClickArea(FurnaceBackpackContainerScreen.class, 183, 118, 28, 24, RecipeTypes.SMELTING, RecipeTypes.FUELING); + } + @Override public ResourceLocation getPluginUid() { return UID; diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/compat/slashblade/SlashBladeCompat.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/compat/slashblade/SlashBladeCompat.java index f9b656519..09cfb4772 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/compat/slashblade/SlashBladeCompat.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/compat/slashblade/SlashBladeCompat.java @@ -1,6 +1,7 @@ package com.github.tartaricacid.touhoulittlemaid.compat.slashblade; import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; +import com.github.tartaricacid.touhoulittlemaid.entity.task.TaskAttack; import mods.flammpfeil.slashblade.capability.slashblade.CapabilitySlashBlade; import mods.flammpfeil.slashblade.util.AttackManager; import mods.flammpfeil.slashblade.util.KnockBacks; @@ -23,7 +24,7 @@ public static boolean isSlashBladeItem(ItemStack stack) { } public static void swingSlashBlade(EntityMaid maid, ItemStack itemInHand) { - if (SlashBladeCompat.isSlashBladeItem(itemInHand)) { + if (SlashBladeCompat.isSlashBladeItem(itemInHand) && maid.getTask().getUid().equals(TaskAttack.UID)) { int roll = maid.getRandom().nextInt(60) - 30; AttackManager.doSlash(maid, roll, Vec3.ZERO, false, false, 1.0, KnockBacks.smash); itemInHand.getCapability(CapabilitySlashBlade.BLADESTATE).ifPresent(bladeState -> bladeState.setLastActionTime(maid.level.getGameTime())); diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/compat/top/provider/MaidProvider.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/compat/top/provider/MaidProvider.java index 360d6888e..43b81b22b 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/compat/top/provider/MaidProvider.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/compat/top/provider/MaidProvider.java @@ -28,6 +28,12 @@ public void addProbeEntityInfo(ProbeMode probeMode, IProbeInfo probeInfo, Player MutableComponent scheduleTitle = new TranslatableComponent("top.touhou_little_maid.entity_maid.schedule").append(getActivityTransText(maid)); probeInfo.horizontal(probeInfo.defaultLayoutStyle().alignment(ElementAlignment.ALIGN_CENTER)).text(scheduleTitle); + + MutableComponent favorabilityTitle = new TranslatableComponent("top.touhou_little_maid.entity_maid.favorability", maid.getFavorabilityManager().getLevel()); + probeInfo.horizontal(probeInfo.defaultLayoutStyle().alignment(ElementAlignment.ALIGN_CENTER)).text(favorabilityTitle); + + MutableComponent nextFavorabilityPointTitle = new TranslatableComponent("top.touhou_little_maid.entity_maid.nex_favorability_point", maid.getFavorabilityManager().nextLevelPoint()); + probeInfo.horizontal(probeInfo.defaultLayoutStyle().alignment(ElementAlignment.ALIGN_CENTER)).text(nextFavorabilityPointTitle); } if (maid.getIsInvulnerable()) { MutableComponent text = new TranslatableComponent("top.touhou_little_maid.entity_maid.invulnerable").withStyle(ChatFormatting.DARK_PURPLE); diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/config/subconfig/MiscConfig.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/config/subconfig/MiscConfig.java index 70781b32b..fa40e89ca 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/config/subconfig/MiscConfig.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/config/subconfig/MiscConfig.java @@ -29,13 +29,6 @@ public static void init(ForgeConfigSpec.Builder builder) { builder.comment("Maid fairy's spawn probability (zombie is 100, enderman is 10)"); MAID_FAIRY_SPAWN_PROBABILITY = builder.defineInRange("MaidFairySpawnProbability", 70, 0, Integer.MAX_VALUE); - List biomes = Lists.newArrayList(); - Arrays.stream(values()).forEach(a -> biomes.add(a.getName())); - builder.comment("The following biome do not spawn maid fairy", - "Available names: " + StringUtils.join(biomes, ", ")); - MAID_FAIRY_BLACKLIST_BIOME = builder.defineList("MaidFairyBlacklistBiome", Lists.newArrayList(NETHER.getName(), THEEND.getName(), - NONE.getName(), MUSHROOM.getName()), MiscConfig::checkBiome); - builder.comment("Loss power point after player death"); PLAYER_DEATH_LOSS_POWER_POINT = builder.defineInRange("PlayerDeathLossPowerPoint", 1.0, 0, 5); diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/ai/brain/MaidBrain.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/ai/brain/MaidBrain.java index 52f257421..32218dec4 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/ai/brain/MaidBrain.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/ai/brain/MaidBrain.java @@ -1,6 +1,7 @@ package com.github.tartaricacid.touhoulittlemaid.entity.ai.brain; import com.github.tartaricacid.touhoulittlemaid.entity.ai.brain.task.*; +import com.github.tartaricacid.touhoulittlemaid.entity.item.EntitySit; import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; import com.github.tartaricacid.touhoulittlemaid.init.InitEntities; import com.google.common.collect.ImmutableList; @@ -97,13 +98,15 @@ private static void registerIdleGoals(Brain brain) { Pair, Integer> walkRandomly = Pair.of(new RandomStroll(0.3f, 5, 3), 1); Pair, Integer> noLook = Pair.of(new DoNothing(40, 80), 2); RunOne firstShuffledTask = new RunOne<>(ImmutableList.of(lookToPlayer, lookToMaid, lookToWolf, lookToCat, lookToParrot, walkRandomly, noLook)); - RunIf supplementedTask = new RunIf<>(e -> !e.isBegging(), firstShuffledTask); + RunIf supplementedTask = new RunIf<>(MaidBrain::lookAroundCondition, firstShuffledTask); Pair> beg = Pair.of(5, new MaidBegTask()); - Pair> supplemented = Pair.of(6, supplementedTask); + Pair> findJoy = Pair.of(6, new MaidFindJoyTask(0.6f, 12)); + Pair> sitJoy = Pair.of(7, new MaidSitJoyTask()); + Pair> supplemented = Pair.of(10, supplementedTask); Pair> updateActivity = Pair.of(99, new UpdateActivityFromSchedule()); - brain.addActivity(Activity.IDLE, ImmutableList.of(beg, supplemented, updateActivity)); + brain.addActivity(Activity.IDLE, ImmutableList.of(beg, findJoy, sitJoy, supplemented, updateActivity)); } private static void registerWorkGoals(Brain brain, EntityMaid maid) { @@ -142,10 +145,14 @@ private static void registerAwaitGoals(Brain brain) { Pair, Integer> lookToParrot = Pair.of(new SetEntityLookTarget(EntityType.PARROT, 5), 1); Pair, Integer> noLook = Pair.of(new DoNothing(30, 60), 2); - Pair> shuffled = Pair.of(5, new RunOne<>( - ImmutableList.of(lookToPlayer, lookToMaid, lookToWolf, lookToCat, lookToParrot, noLook))); + RunOne firstShuffledTask = new RunOne<>(ImmutableList.of(lookToPlayer, lookToMaid, lookToWolf, lookToCat, lookToParrot, noLook)); + Pair> shuffled = Pair.of(5, new RunIf<>(MaidBrain::lookAroundCondition, firstShuffledTask)); Pair> updateActivity = Pair.of(99, new UpdateActivityFromSchedule()); brain.addActivity(Activity.RIDE, ImmutableList.of(shuffled, updateActivity)); } + + public static boolean lookAroundCondition(EntityMaid maid) { + return !maid.isBegging() && !(maid.getVehicle() instanceof EntitySit); + } } diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/ai/brain/task/MaidFindGomokuTask.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/ai/brain/task/MaidFindGomokuTask.java new file mode 100644 index 000000000..f750307c7 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/ai/brain/task/MaidFindGomokuTask.java @@ -0,0 +1,36 @@ +package com.github.tartaricacid.touhoulittlemaid.entity.ai.brain.task; + +import com.github.tartaricacid.touhoulittlemaid.block.BlockJoy; +import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; +import com.github.tartaricacid.touhoulittlemaid.tileentity.TileEntityGomoku; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; + +public class MaidFindGomokuTask extends MaidMoveToBlockTask { + public MaidFindGomokuTask(float movementSpeed, int searchLength) { + super(movementSpeed, searchLength); + } + + @Override + protected boolean shouldMoveTo(ServerLevel worldIn, EntityMaid entityIn, BlockPos pos) { + BlockState blockstate = worldIn.getBlockState(pos); + return blockstate.getBlock() instanceof BlockJoy && !this.isOccupied(worldIn, pos); + } + + @Override + protected void start(ServerLevel worldIn, EntityMaid maid, long gameTimeIn) { + if (maid.getVehicle() == null && !maid.isInSittingPose()) { + this.searchForDestination(worldIn, maid); + } + } + + private boolean isOccupied(ServerLevel worldIn, BlockPos pos) { + BlockEntity te = worldIn.getBlockEntity(pos); + if (te instanceof TileEntityGomoku gomoku) { + return worldIn.getEntity(gomoku.getSitId()) != null; + } + return true; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/ai/brain/task/MaidFindJoyTask.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/ai/brain/task/MaidFindJoyTask.java new file mode 100644 index 000000000..9b589b15f --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/ai/brain/task/MaidFindJoyTask.java @@ -0,0 +1,36 @@ +package com.github.tartaricacid.touhoulittlemaid.entity.ai.brain.task; + +import com.github.tartaricacid.touhoulittlemaid.block.BlockJoy; +import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; +import com.github.tartaricacid.touhoulittlemaid.tileentity.TileEntityJoy; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; + +public class MaidFindJoyTask extends MaidMoveToBlockTask { + public MaidFindJoyTask(float movementSpeed, int searchLength) { + super(movementSpeed, searchLength); + } + + @Override + protected boolean shouldMoveTo(ServerLevel worldIn, EntityMaid entityIn, BlockPos pos) { + BlockState blockstate = worldIn.getBlockState(pos); + return blockstate.getBlock() instanceof BlockJoy && !this.isOccupied(worldIn, pos); + } + + @Override + protected void start(ServerLevel worldIn, EntityMaid maid, long gameTimeIn) { + if (maid.getVehicle() == null && !maid.isInSittingPose()) { + this.searchForDestination(worldIn, maid); + } + } + + private boolean isOccupied(ServerLevel worldIn, BlockPos pos) { + BlockEntity te = worldIn.getBlockEntity(pos); + if (te instanceof TileEntityJoy joy) { + return worldIn.getEntity(joy.getSitId()) != null; + } + return true; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/ai/brain/task/MaidSitJoyTask.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/ai/brain/task/MaidSitJoyTask.java new file mode 100644 index 000000000..e5303727d --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/ai/brain/task/MaidSitJoyTask.java @@ -0,0 +1,71 @@ +package com.github.tartaricacid.touhoulittlemaid.entity.ai.brain.task; + +import com.github.tartaricacid.touhoulittlemaid.block.BlockJoy; +import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; +import com.github.tartaricacid.touhoulittlemaid.init.InitEntities; +import com.github.tartaricacid.touhoulittlemaid.tileentity.TileEntityJoy; +import com.google.common.collect.ImmutableMap; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.ai.Brain; +import net.minecraft.world.entity.ai.behavior.Behavior; +import net.minecraft.world.entity.ai.memory.MemoryModuleType; +import net.minecraft.world.entity.ai.memory.MemoryStatus; +import net.minecraft.world.entity.ai.memory.WalkTarget; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; + +import java.util.Optional; + +public class MaidSitJoyTask extends Behavior { + private static final int CLOSED_DISTANCE = 2; + + public MaidSitJoyTask() { + super(ImmutableMap.of(InitEntities.TARGET_POS.get(), MemoryStatus.VALUE_PRESENT)); + } + + @Override + protected boolean checkExtraStartConditions(ServerLevel worldIn, EntityMaid owner) { + Brain brain = owner.getBrain(); + if (owner.getVehicle() != null && owner.isInSittingPose()) { + brain.eraseMemory(MemoryModuleType.PATH); + brain.eraseMemory(MemoryModuleType.WALK_TARGET); + brain.eraseMemory(MemoryModuleType.LOOK_TARGET); + return false; + } + return brain.getMemory(InitEntities.TARGET_POS.get()).map(targetPos -> { + BlockPos blockPos = targetPos.currentBlockPosition(); + if (!blockPos.closerThan(owner.blockPosition(), CLOSED_DISTANCE)) { + Optional walkTarget = brain.getMemory(MemoryModuleType.WALK_TARGET); + if (walkTarget.isEmpty() || !walkTarget.get().getTarget().currentBlockPosition().equals(blockPos)) { + brain.eraseMemory(InitEntities.TARGET_POS.get()); + } + return false; + } + BlockState blockstate = worldIn.getBlockState(blockPos); + return blockstate.getBlock() instanceof BlockJoy && !this.isOccupied(worldIn, blockPos); + }).orElse(false); + } + + @Override + protected void start(ServerLevel worldIn, EntityMaid maid, long gameTimeIn) { + maid.getBrain().getMemory(InitEntities.TARGET_POS.get()).ifPresent((targetPos) -> { + BlockPos pos = targetPos.currentBlockPosition(); + BlockState blockState = worldIn.getBlockState(pos); + if (blockState.getBlock() instanceof BlockJoy blockJoy) { + blockJoy.startMaidSit(maid, blockState, worldIn, pos); + maid.getBrain().eraseMemory(InitEntities.TARGET_POS.get()); + maid.getBrain().eraseMemory(MemoryModuleType.WALK_TARGET); + maid.getBrain().eraseMemory(MemoryModuleType.LOOK_TARGET); + } + }); + } + + private boolean isOccupied(ServerLevel worldIn, BlockPos pos) { + BlockEntity te = worldIn.getBlockEntity(pos); + if (te instanceof TileEntityJoy joy) { + return worldIn.getEntity(joy.getSitId()) != null; + } + return true; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/backpack/BackpackManager.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/backpack/BackpackManager.java new file mode 100644 index 000000000..239b68b2e --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/backpack/BackpackManager.java @@ -0,0 +1,95 @@ +package com.github.tartaricacid.touhoulittlemaid.entity.backpack; + +import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; +import com.github.tartaricacid.touhoulittlemaid.api.ILittleMaid; +import com.github.tartaricacid.touhoulittlemaid.api.backpack.IMaidBackpack; +import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; +import net.minecraft.client.model.EntityModel; +import net.minecraft.client.model.geom.EntityModelSet; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import org.apache.commons.lang3.tuple.Pair; + +import java.util.Map; +import java.util.Optional; + +public class BackpackManager { + private static Map BACKPACK_ID_MAP; + private static Map BACKPACK_ITEM_MAP; + @OnlyIn(Dist.CLIENT) + private static Map, ResourceLocation>> BACKPACK_MODEL_MAP; + private static IMaidBackpack EMPTY_BACKPACK; + + private BackpackManager() { + EMPTY_BACKPACK = new EmptyBackpack(); + BACKPACK_ID_MAP = Maps.newHashMap(); + } + + public static void init() { + BackpackManager manager = new BackpackManager(); + manager.add(EMPTY_BACKPACK); + manager.add(new SmallBackpack()); + manager.add(new MiddleBackpack()); + manager.add(new BigBackpack()); + manager.add(new CraftingTableBackpack()); + manager.add(new EnderChestBackpack()); + manager.add(new FurnaceBackpack()); + for (ILittleMaid littleMaid : TouhouLittleMaid.EXTENSIONS) { + littleMaid.addMaidBackpack(manager); + } + BACKPACK_ID_MAP = ImmutableMap.copyOf(BACKPACK_ID_MAP); + } + + public static void initItemIndex() { + BACKPACK_ITEM_MAP = Maps.newHashMap(); + BACKPACK_ID_MAP.forEach((id, backpack) -> BACKPACK_ITEM_MAP.put(backpack.getItem(), backpack)); + BACKPACK_ITEM_MAP = ImmutableMap.copyOf(BACKPACK_ITEM_MAP); + } + + @OnlyIn(Dist.CLIENT) + public static void initClient(EntityModelSet modelSet) { + BACKPACK_MODEL_MAP = Maps.newHashMap(); + BACKPACK_ID_MAP.forEach((id, backpack) -> BACKPACK_MODEL_MAP.put(id, Pair.of(backpack.getBackpackModel(modelSet), backpack.getBackpackTexture()))); + BACKPACK_MODEL_MAP = ImmutableMap.copyOf(BACKPACK_MODEL_MAP); + } + + public static IMaidBackpack getEmptyBackpack() { + return EMPTY_BACKPACK; + } + + public static Optional findBackpack(ResourceLocation id) { + return Optional.ofNullable(BACKPACK_ID_MAP.get(id)); + } + + public static Optional findBackpack(ItemStack stack) { + return Optional.ofNullable(BACKPACK_ITEM_MAP.get(stack.getItem())); + } + + public static void addBackpackCooldown(Player player) { + for (Item backpack : BACKPACK_ITEM_MAP.keySet()) { + player.getCooldowns().addCooldown(backpack, 20); + } + } + + @OnlyIn(Dist.CLIENT) + public static Optional, ResourceLocation>> findBackpackModel(ResourceLocation id) { + Pair, ResourceLocation> pair = BACKPACK_MODEL_MAP.get(id); + if (pair == null) { + return Optional.empty(); + } + if (pair.getLeft() == null) { + return Optional.empty(); + } + return Optional.of(pair); + } + + public void add(IMaidBackpack backpack) { + BACKPACK_ID_MAP.put(backpack.getId(), backpack); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/backpack/BigBackpack.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/backpack/BigBackpack.java new file mode 100644 index 000000000..00e7c9402 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/backpack/BigBackpack.java @@ -0,0 +1,105 @@ +package com.github.tartaricacid.touhoulittlemaid.entity.backpack; + +import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; +import com.github.tartaricacid.touhoulittlemaid.api.backpack.IMaidBackpack; +import com.github.tartaricacid.touhoulittlemaid.client.model.backpack.BigBackpackModel; +import com.github.tartaricacid.touhoulittlemaid.entity.item.EntityTombstone; +import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; +import com.github.tartaricacid.touhoulittlemaid.init.InitItems; +import com.github.tartaricacid.touhoulittlemaid.inventory.container.AbstractMaidContainer; +import com.github.tartaricacid.touhoulittlemaid.inventory.container.backpack.BigBackpackContainer; +import com.github.tartaricacid.touhoulittlemaid.item.BackpackLevel; +import com.github.tartaricacid.touhoulittlemaid.util.ItemsUtil; +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.client.model.EntityModel; +import net.minecraft.client.model.geom.EntityModelSet; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.MenuProvider; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +import javax.annotation.Nullable; + +public class BigBackpack extends IMaidBackpack { + public static final ResourceLocation ID = new ResourceLocation(TouhouLittleMaid.MOD_ID, "big_backpack"); + + @Override + public void onPutOn(ItemStack stack, Player player, EntityMaid maid) { + } + + @Override + public void onTakeOff(ItemStack stack, Player player, EntityMaid maid) { + Item item = stack.getItem(); + if (item == InitItems.MAID_BACKPACK_SMALL.get()) { + ItemsUtil.dropEntityItems(maid, maid.getMaidInv(), BackpackLevel.SMALL_CAPACITY); + return; + } + if (item == InitItems.MAID_BACKPACK_MIDDLE.get()) { + ItemsUtil.dropEntityItems(maid, maid.getMaidInv(), BackpackLevel.MIDDLE_CAPACITY); + return; + } + if (item == InitItems.MAID_BACKPACK_BIG.get()) { + return; + } + this.dropAllItems(maid); + } + + @Override + public void onSpawnTombstone(EntityMaid maid, EntityTombstone tombstone) { + } + + @Override + public MenuProvider getGuiProvider(int entityId) { + return new MenuProvider() { + @Override + public Component getDisplayName() { + return new TextComponent("Maid Big Container"); + } + + @Override + public AbstractMaidContainer createMenu(int index, Inventory playerInventory, Player player) { + return new BigBackpackContainer(index, playerInventory, entityId); + } + }; + } + + @Override + public int getAvailableMaxContainerIndex() { + return BackpackLevel.BIG_CAPACITY; + } + + @Nullable + @Override + @OnlyIn(Dist.CLIENT) + public EntityModel getBackpackModel(EntityModelSet modelSet) { + return new BigBackpackModel(modelSet.bakeLayer(BigBackpackModel.LAYER)); + } + + @Nullable + @Override + @OnlyIn(Dist.CLIENT) + public ResourceLocation getBackpackTexture() { + return new ResourceLocation(TouhouLittleMaid.MOD_ID, "textures/entity/maid_backpack_big.png"); + } + + @OnlyIn(Dist.CLIENT) + public void offsetBackpackItem(PoseStack poseStack) { + poseStack.translate(0, 0, -0.4); + } + + @Override + public ResourceLocation getId() { + return ID; + } + + @Override + public Item getItem() { + return InitItems.MAID_BACKPACK_BIG.get(); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/backpack/CraftingTableBackpack.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/backpack/CraftingTableBackpack.java new file mode 100644 index 000000000..922454cb4 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/backpack/CraftingTableBackpack.java @@ -0,0 +1,101 @@ +package com.github.tartaricacid.touhoulittlemaid.entity.backpack; + +import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; +import com.github.tartaricacid.touhoulittlemaid.api.backpack.IMaidBackpack; +import com.github.tartaricacid.touhoulittlemaid.client.model.backpack.CraftingTableBackpackModel; +import com.github.tartaricacid.touhoulittlemaid.entity.item.EntityTombstone; +import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; +import com.github.tartaricacid.touhoulittlemaid.init.InitItems; +import com.github.tartaricacid.touhoulittlemaid.inventory.container.AbstractMaidContainer; +import com.github.tartaricacid.touhoulittlemaid.inventory.container.backpack.CraftingTableBackpackContainer; +import com.github.tartaricacid.touhoulittlemaid.item.BackpackLevel; +import com.github.tartaricacid.touhoulittlemaid.util.ItemsUtil; +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.client.model.EntityModel; +import net.minecraft.client.model.geom.EntityModelSet; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.MenuProvider; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import org.jetbrains.annotations.Nullable; + +public class CraftingTableBackpack extends IMaidBackpack { + public static final ResourceLocation ID = new ResourceLocation(TouhouLittleMaid.MOD_ID, "crafting_table_backpack"); + + @Override + public void onPutOn(ItemStack stack, Player player, EntityMaid maid) { + } + + @Override + public void onTakeOff(ItemStack stack, Player player, EntityMaid maid) { + Item item = stack.getItem(); + if (item == InitItems.MAID_BACKPACK_SMALL.get()) { + ItemsUtil.dropEntityItems(maid, maid.getMaidInv(), BackpackLevel.SMALL_CAPACITY); + return; + } + if (item == InitItems.MAID_BACKPACK_MIDDLE.get() || item == InitItems.MAID_BACKPACK_BIG.get()) { + return; + } + this.dropAllItems(maid); + } + + @Override + public void onSpawnTombstone(EntityMaid maid, EntityTombstone tombstone) { + } + + @Override + public MenuProvider getGuiProvider(int entityId) { + return new MenuProvider() { + @Override + public Component getDisplayName() { + return new TextComponent("Maid Crafting Table Container"); + } + + @Override + public AbstractMaidContainer createMenu(int index, Inventory playerInventory, Player player) { + return new CraftingTableBackpackContainer(index, playerInventory, entityId); + } + }; + } + + @Override + public int getAvailableMaxContainerIndex() { + return BackpackLevel.CRAFTING_TABLE_CAPACITY; + } + + @Override + @OnlyIn(Dist.CLIENT) + public void offsetBackpackItem(PoseStack poseStack) { + poseStack.translate(0, 0.625, 0.25); + } + + @Nullable + @Override + @OnlyIn(Dist.CLIENT) + public EntityModel getBackpackModel(EntityModelSet modelSet) { + return new CraftingTableBackpackModel(modelSet.bakeLayer(CraftingTableBackpackModel.LAYER)); + } + + @Nullable + @Override + @OnlyIn(Dist.CLIENT) + public ResourceLocation getBackpackTexture() { + return new ResourceLocation(TouhouLittleMaid.MOD_ID, "textures/entity/crafting_table_backpack.png"); + } + + @Override + public ResourceLocation getId() { + return ID; + } + + @Override + public Item getItem() { + return InitItems.CRAFTING_TABLE_BACKPACK.get(); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/backpack/EmptyBackpack.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/backpack/EmptyBackpack.java new file mode 100644 index 000000000..bf84e18fa --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/backpack/EmptyBackpack.java @@ -0,0 +1,91 @@ +package com.github.tartaricacid.touhoulittlemaid.entity.backpack; + +import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; +import com.github.tartaricacid.touhoulittlemaid.api.backpack.IMaidBackpack; +import com.github.tartaricacid.touhoulittlemaid.entity.item.EntityTombstone; +import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; +import com.github.tartaricacid.touhoulittlemaid.inventory.container.AbstractMaidContainer; +import com.github.tartaricacid.touhoulittlemaid.inventory.container.backpack.EmptyBackpackContainer; +import com.github.tartaricacid.touhoulittlemaid.item.BackpackLevel; +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.client.model.EntityModel; +import net.minecraft.client.model.geom.EntityModelSet; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.MenuProvider; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +import javax.annotation.Nullable; + +public class EmptyBackpack extends IMaidBackpack { + public static final ResourceLocation ID = new ResourceLocation(TouhouLittleMaid.MOD_ID, "empty"); + + @Override + public ResourceLocation getId() { + return ID; + } + + @Override + public Item getItem() { + return Items.AIR; + } + + @Override + public void onPutOn(ItemStack stack, Player player, EntityMaid maid) { + } + + @Override + public void onTakeOff(ItemStack stack, Player player, EntityMaid maid) { + } + + @Override + public void onSpawnTombstone(EntityMaid maid, EntityTombstone tombstone) { + } + + @Override + public MenuProvider getGuiProvider(int entityId) { + return new MenuProvider() { + @Override + public Component getDisplayName() { + return new TextComponent("Maid Empty Container"); + } + + @Override + public AbstractMaidContainer createMenu(int index, Inventory playerInventory, Player player) { + return new EmptyBackpackContainer(index, playerInventory, entityId); + } + }; + } + + + @Override + public int getAvailableMaxContainerIndex() { + return BackpackLevel.EMPTY_CAPACITY; + } + + @Nullable + @Override + @OnlyIn(Dist.CLIENT) + public EntityModel getBackpackModel(EntityModelSet modelSet) { + return null; + } + + @Nullable + @Override + @OnlyIn(Dist.CLIENT) + public ResourceLocation getBackpackTexture() { + return null; + } + + @OnlyIn(Dist.CLIENT) + public void offsetBackpackItem(PoseStack poseStack) { + poseStack.translate(0, 0.625, 0.2); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/backpack/EnderChestBackpack.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/backpack/EnderChestBackpack.java new file mode 100644 index 000000000..de5edd8c5 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/backpack/EnderChestBackpack.java @@ -0,0 +1,92 @@ +package com.github.tartaricacid.touhoulittlemaid.entity.backpack; + +import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; +import com.github.tartaricacid.touhoulittlemaid.api.backpack.IMaidBackpack; +import com.github.tartaricacid.touhoulittlemaid.client.model.backpack.EnderChestBackpackModel; +import com.github.tartaricacid.touhoulittlemaid.entity.item.EntityTombstone; +import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; +import com.github.tartaricacid.touhoulittlemaid.init.InitItems; +import com.github.tartaricacid.touhoulittlemaid.inventory.container.AbstractMaidContainer; +import com.github.tartaricacid.touhoulittlemaid.inventory.container.backpack.EnderChestBackpackContainer; +import com.github.tartaricacid.touhoulittlemaid.item.BackpackLevel; +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.client.model.EntityModel; +import net.minecraft.client.model.geom.EntityModelSet; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.MenuProvider; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +import javax.annotation.Nullable; + +public class EnderChestBackpack extends IMaidBackpack { + public static final ResourceLocation ID = new ResourceLocation(TouhouLittleMaid.MOD_ID, "ender_chest_backpack"); + + @Override + public void onPutOn(ItemStack stack, Player player, EntityMaid maid) { + } + + @Override + public void onTakeOff(ItemStack stack, Player player, EntityMaid maid) { + } + + @Override + public void onSpawnTombstone(EntityMaid maid, EntityTombstone tombstone) { + } + + @Override + public MenuProvider getGuiProvider(int entityId) { + return new MenuProvider() { + @Override + public Component getDisplayName() { + return new TextComponent("Maid Ender Chest Container"); + } + + @Override + public AbstractMaidContainer createMenu(int index, Inventory playerInventory, Player player) { + return new EnderChestBackpackContainer(index, playerInventory, entityId); + } + }; + } + + @Override + public int getAvailableMaxContainerIndex() { + return BackpackLevel.EMPTY_CAPACITY; + } + + @Nullable + @Override + @OnlyIn(Dist.CLIENT) + public EntityModel getBackpackModel(EntityModelSet modelSet) { + return new EnderChestBackpackModel(modelSet.bakeLayer(EnderChestBackpackModel.LAYER)); + } + + @Nullable + @Override + @OnlyIn(Dist.CLIENT) + public ResourceLocation getBackpackTexture() { + return new ResourceLocation(TouhouLittleMaid.MOD_ID, "textures/entity/ender_chest_backpack.png"); + } + + @OnlyIn(Dist.CLIENT) + public void offsetBackpackItem(PoseStack poseStack) { + poseStack.translate(0, 0.625, 0.25); + } + + @Override + public ResourceLocation getId() { + return ID; + } + + @Override + public Item getItem() { + return InitItems.ENDER_CHEST_BACKPACK.get(); + } +} + diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/backpack/FurnaceBackpack.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/backpack/FurnaceBackpack.java new file mode 100644 index 000000000..bc3185839 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/backpack/FurnaceBackpack.java @@ -0,0 +1,131 @@ +package com.github.tartaricacid.touhoulittlemaid.entity.backpack; + +import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; +import com.github.tartaricacid.touhoulittlemaid.api.backpack.IBackpackData; +import com.github.tartaricacid.touhoulittlemaid.api.backpack.IMaidBackpack; +import com.github.tartaricacid.touhoulittlemaid.client.model.backpack.FurnaceBackpackModel; +import com.github.tartaricacid.touhoulittlemaid.entity.backpack.data.FurnaceBackpackData; +import com.github.tartaricacid.touhoulittlemaid.entity.item.EntityTombstone; +import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; +import com.github.tartaricacid.touhoulittlemaid.init.InitItems; +import com.github.tartaricacid.touhoulittlemaid.inventory.container.AbstractMaidContainer; +import com.github.tartaricacid.touhoulittlemaid.inventory.container.backpack.FurnaceBackpackContainer; +import com.github.tartaricacid.touhoulittlemaid.item.BackpackLevel; +import com.github.tartaricacid.touhoulittlemaid.util.ItemsUtil; +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.client.model.EntityModel; +import net.minecraft.client.model.geom.EntityModelSet; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.MenuProvider; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import net.minecraftforge.items.wrapper.InvWrapper; + +import javax.annotation.Nullable; + +public class FurnaceBackpack extends IMaidBackpack { + public static final ResourceLocation ID = new ResourceLocation(TouhouLittleMaid.MOD_ID, "furnace_backpack"); + + @Override + public void onPutOn(ItemStack stack, Player player, EntityMaid maid) { + } + + @Override + public void onTakeOff(ItemStack stack, Player player, EntityMaid maid) { + IBackpackData backpackData = maid.getBackpackData(); + if (backpackData instanceof FurnaceBackpackData furnaceBackpackData) { + InvWrapper inv = new InvWrapper(furnaceBackpackData); + ItemsUtil.dropEntityItems(maid, inv); + } + + Item item = stack.getItem(); + if (item == InitItems.MAID_BACKPACK_SMALL.get()) { + ItemsUtil.dropEntityItems(maid, maid.getMaidInv(), BackpackLevel.SMALL_CAPACITY); + return; + } + if (item == InitItems.MAID_BACKPACK_MIDDLE.get() || item == InitItems.MAID_BACKPACK_BIG.get()) { + return; + } + this.dropAllItems(maid); + } + + @Override + public void onSpawnTombstone(EntityMaid maid, EntityTombstone tombstone) { + IBackpackData backpackData = maid.getBackpackData(); + if (backpackData instanceof FurnaceBackpackData furnaceBackpackData) { + InvWrapper inv = new InvWrapper(furnaceBackpackData); + for (int i = 0; i < inv.getSlots(); i++) { + int size = inv.getSlotLimit(i); + tombstone.insertItem(inv.extractItem(i, size, false)); + } + } + } + + @Override + public MenuProvider getGuiProvider(int entityId) { + return new MenuProvider() { + @Override + public Component getDisplayName() { + return new TextComponent("Maid Furnace Container"); + } + + @Override + public AbstractMaidContainer createMenu(int index, Inventory playerInventory, Player player) { + return new FurnaceBackpackContainer(index, playerInventory, entityId); + } + }; + } + + @Override + public boolean hasBackpackData() { + return true; + } + + @Nullable + @Override + public IBackpackData getBackpackData(EntityMaid maid) { + return new FurnaceBackpackData(maid); + } + + @Override + public int getAvailableMaxContainerIndex() { + return BackpackLevel.FURNACE_CAPACITY; + } + + @Nullable + @Override + @OnlyIn(Dist.CLIENT) + public EntityModel getBackpackModel(EntityModelSet modelSet) { + return new FurnaceBackpackModel(modelSet.bakeLayer(FurnaceBackpackModel.LAYER)); + } + + @Nullable + @Override + @OnlyIn(Dist.CLIENT) + public ResourceLocation getBackpackTexture() { + return new ResourceLocation(TouhouLittleMaid.MOD_ID, "textures/entity/furnace_backpack.png"); + } + + @OnlyIn(Dist.CLIENT) + public void offsetBackpackItem(PoseStack poseStack) { + poseStack.translate(0, 0.625, 0.25); + } + + @Override + public ResourceLocation getId() { + return ID; + } + + @Override + public Item getItem() { + return InitItems.FURNACE_BACKPACK.get(); + } +} + + diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/backpack/MiddleBackpack.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/backpack/MiddleBackpack.java new file mode 100644 index 000000000..47b0995f1 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/backpack/MiddleBackpack.java @@ -0,0 +1,104 @@ +package com.github.tartaricacid.touhoulittlemaid.entity.backpack; + +import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; +import com.github.tartaricacid.touhoulittlemaid.api.backpack.IMaidBackpack; +import com.github.tartaricacid.touhoulittlemaid.client.model.backpack.MiddleBackpackModel; +import com.github.tartaricacid.touhoulittlemaid.entity.item.EntityTombstone; +import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; +import com.github.tartaricacid.touhoulittlemaid.init.InitItems; +import com.github.tartaricacid.touhoulittlemaid.inventory.container.AbstractMaidContainer; +import com.github.tartaricacid.touhoulittlemaid.inventory.container.backpack.MiddleBackpackContainer; +import com.github.tartaricacid.touhoulittlemaid.item.BackpackLevel; +import com.github.tartaricacid.touhoulittlemaid.item.ItemMaidBackpack; +import com.github.tartaricacid.touhoulittlemaid.util.ItemsUtil; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.math.Vector3f; +import net.minecraft.client.model.EntityModel; +import net.minecraft.client.model.geom.EntityModelSet; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.MenuProvider; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +import javax.annotation.Nullable; + +public class MiddleBackpack extends IMaidBackpack { + public static final ResourceLocation ID = new ResourceLocation(TouhouLittleMaid.MOD_ID, "middle_backpack"); + + @Override + public void onPutOn(ItemStack stack, Player player, EntityMaid maid) { + } + + @Override + public void onTakeOff(ItemStack stack, Player player, EntityMaid maid) { + Item item = stack.getItem(); + if (item == InitItems.MAID_BACKPACK_SMALL.get()) { + ItemsUtil.dropEntityItems(maid, maid.getMaidInv(), BackpackLevel.SMALL_CAPACITY); + return; + } + if (item == InitItems.MAID_BACKPACK_MIDDLE.get() || item == InitItems.MAID_BACKPACK_BIG.get()) { + return; + } + this.dropAllItems(maid); + } + + @Override + public void onSpawnTombstone(EntityMaid maid, EntityTombstone tombstone) { + } + + @Override + public MenuProvider getGuiProvider(int entityId) { + return new MenuProvider() { + @Override + public Component getDisplayName() { + return new TextComponent("Maid Middle Container"); + } + + @Override + public AbstractMaidContainer createMenu(int index, Inventory playerInventory, Player player) { + return new MiddleBackpackContainer(index, playerInventory, entityId); + } + }; + } + + @Override + public int getAvailableMaxContainerIndex() { + return BackpackLevel.MIDDLE_CAPACITY; + } + + @Nullable + @Override + @OnlyIn(Dist.CLIENT) + public EntityModel getBackpackModel(EntityModelSet modelSet) { + return new MiddleBackpackModel(modelSet.bakeLayer(MiddleBackpackModel.LAYER)); + } + + @Nullable + @Override + @OnlyIn(Dist.CLIENT) + public ResourceLocation getBackpackTexture() { + return new ResourceLocation(TouhouLittleMaid.MOD_ID, "textures/entity/maid_backpack_middle.png"); + } + + @OnlyIn(Dist.CLIENT) + public void offsetBackpackItem(PoseStack poseStack) { + poseStack.mulPose(Vector3f.XP.rotationDegrees(-7.5F)); + poseStack.translate(0, 0.625, -0.25); + } + + @Override + public ResourceLocation getId() { + return ID; + } + + @Override + public Item getItem() { + return InitItems.MAID_BACKPACK_MIDDLE.get(); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/backpack/SmallBackpack.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/backpack/SmallBackpack.java new file mode 100644 index 000000000..38d4eed51 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/backpack/SmallBackpack.java @@ -0,0 +1,96 @@ +package com.github.tartaricacid.touhoulittlemaid.entity.backpack; + +import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; +import com.github.tartaricacid.touhoulittlemaid.api.backpack.IMaidBackpack; +import com.github.tartaricacid.touhoulittlemaid.client.model.backpack.SmallBackpackModel; +import com.github.tartaricacid.touhoulittlemaid.entity.item.EntityTombstone; +import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; +import com.github.tartaricacid.touhoulittlemaid.init.InitItems; +import com.github.tartaricacid.touhoulittlemaid.inventory.container.AbstractMaidContainer; +import com.github.tartaricacid.touhoulittlemaid.inventory.container.backpack.SmallBackpackContainer; +import com.github.tartaricacid.touhoulittlemaid.item.BackpackLevel; +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.client.model.EntityModel; +import net.minecraft.client.model.geom.EntityModelSet; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.MenuProvider; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +import javax.annotation.Nullable; + +public class SmallBackpack extends IMaidBackpack { + public static final ResourceLocation ID = new ResourceLocation(TouhouLittleMaid.MOD_ID, "small_backpack"); + + @Override + public void onPutOn(ItemStack stack, Player player, EntityMaid maid) { + } + + @Override + public void onTakeOff(ItemStack stack, Player player, EntityMaid maid) { + Item item = stack.getItem(); + if (item == InitItems.MAID_BACKPACK_SMALL.get() || item == InitItems.MAID_BACKPACK_MIDDLE.get() || item == InitItems.MAID_BACKPACK_BIG.get()) { + return; + } + this.dropAllItems(maid); + } + + @Override + public void onSpawnTombstone(EntityMaid maid, EntityTombstone tombstone) { + } + + @Override + public MenuProvider getGuiProvider(int entityId) { + return new MenuProvider() { + @Override + public Component getDisplayName() { + return new TextComponent("Maid Small Container"); + } + + @Override + public AbstractMaidContainer createMenu(int index, Inventory playerInventory, Player player) { + return new SmallBackpackContainer(index, playerInventory, entityId); + } + }; + } + + @Override + public int getAvailableMaxContainerIndex() { + return BackpackLevel.SMALL_CAPACITY; + } + + @Nullable + @Override + @OnlyIn(Dist.CLIENT) + public EntityModel getBackpackModel(EntityModelSet modelSet) { + return new SmallBackpackModel(modelSet.bakeLayer(SmallBackpackModel.LAYER)); + } + + @Nullable + @Override + @OnlyIn(Dist.CLIENT) + public ResourceLocation getBackpackTexture() { + return new ResourceLocation(TouhouLittleMaid.MOD_ID, "textures/entity/maid_backpack_small.png"); + } + + @OnlyIn(Dist.CLIENT) + public void offsetBackpackItem(PoseStack poseStack) { + poseStack.translate(0, 0.625, -0.05); + } + + @Override + public ResourceLocation getId() { + return ID; + } + + @Override + public Item getItem() { + return InitItems.MAID_BACKPACK_SMALL.get(); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/backpack/data/FurnaceBackpackData.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/backpack/data/FurnaceBackpackData.java new file mode 100644 index 000000000..de8c1d7b5 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/backpack/data/FurnaceBackpackData.java @@ -0,0 +1,234 @@ +package com.github.tartaricacid.touhoulittlemaid.entity.backpack.data; + +import com.github.tartaricacid.touhoulittlemaid.api.backpack.IBackpackData; +import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.Tag; +import net.minecraft.util.Mth; +import net.minecraft.world.SimpleContainer; +import net.minecraft.world.inventory.ContainerData; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.crafting.AbstractCookingRecipe; +import net.minecraft.world.item.crafting.RecipeType; +import net.minecraft.world.item.crafting.SmeltingRecipe; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Blocks; +import net.minecraftforge.common.ForgeHooks; + +import javax.annotation.Nullable; + +public class FurnaceBackpackData extends SimpleContainer implements IBackpackData { + private static final int INPUT_INDEX = 0; + private static final int FUEL_INDEX = 1; + private static final int OUTPUT_INDEX = 2; + private final Level level; + private int litTime; + private int litDuration; + private int cookingProgress; + private int cookingTotalTime; + private final ContainerData dataAccess = new ContainerData() { + public int get(int index) { + return switch (index) { + case 0 -> FurnaceBackpackData.this.litTime; + case 1 -> FurnaceBackpackData.this.litDuration; + case 2 -> FurnaceBackpackData.this.cookingProgress; + case 3 -> FurnaceBackpackData.this.cookingTotalTime; + default -> 0; + }; + } + + public void set(int index, int value) { + switch (index) { + case 0 -> FurnaceBackpackData.this.litTime = value; + case 1 -> FurnaceBackpackData.this.litDuration = value; + case 2 -> FurnaceBackpackData.this.cookingProgress = value; + case 3 -> FurnaceBackpackData.this.cookingTotalTime = value; + } + } + + public int getCount() { + return 4; + } + }; + + public FurnaceBackpackData(EntityMaid maid) { + super(3); + this.level = maid.level; + } + + @Override + public ContainerData getDataAccess() { + return dataAccess; + } + + @Override + public void load(CompoundTag tag, EntityMaid maid) { + this.litTime = tag.getInt("BurnTime"); + this.cookingProgress = tag.getInt("CookTime"); + this.cookingTotalTime = tag.getInt("CookTimeTotal"); + this.litDuration = this.getBurnDuration(this.getItem(FUEL_INDEX)); + this.fromTag(tag.getList("Items", Tag.TAG_COMPOUND)); + } + + @Override + public void save(CompoundTag tag, EntityMaid maid) { + tag.putInt("BurnTime", this.litTime); + tag.putInt("CookTime", this.cookingProgress); + tag.putInt("CookTimeTotal", this.cookingTotalTime); + tag.put("Items", this.createTag()); + } + + @Override + public void serverTick(EntityMaid maid) { + Level level = maid.level; + // 如果是燃烧状态,继续燃烧 + if (this.isLit()) { + --this.litTime; + } + ItemStack fuelItem = this.getItem(FUEL_INDEX); + boolean inputNotEmpty = !this.getItem(INPUT_INDEX).isEmpty(); + boolean fuelNotEmpty = !fuelItem.isEmpty(); + boolean readyForLit = inputNotEmpty && fuelNotEmpty; + // 要么正在燃烧,要么具备燃烧条件 + if (this.isLit() || readyForLit) { + // 获取配方 + SmeltingRecipe recipe = this.level.getRecipeManager().getRecipeFor(RecipeType.SMELTING, this, this.level).orElse(null); + int maxStackSize = this.getMaxStackSize(); + // 没有燃烧,但是可以燃! + if (!this.isLit() && this.canBurn(recipe, this, maxStackSize)) { + this.litTime = this.getBurnDuration(fuelItem); + this.litDuration = this.litTime; + // 如果此时点燃了 + if (this.isLit()) { + // 如果燃料有残留物,比如熔岩桶燃烧后残留一个桶 + if (fuelItem.hasContainerItem()) { + this.setItem(FUEL_INDEX, fuelItem.getContainerItem()); + } else if (fuelNotEmpty) { + // 普通燃料减一 + fuelItem.shrink(1); + if (fuelItem.isEmpty()) { + this.setItem(FUEL_INDEX, fuelItem.getContainerItem()); + } + } + } + } + + // 点燃了,而且也能燃! + if (this.isLit() && this.canBurn(recipe, this, maxStackSize)) { + // 各种进度增加 + ++this.cookingProgress; + // 如果进度满了,重置,并给出产物 + if (this.cookingProgress == this.cookingTotalTime) { + this.cookingProgress = 0; + this.cookingTotalTime = getTotalCookTime(level); + // 如果烧制成功,把经验给女仆 + if (this.burn(recipe, this, maxStackSize)) { + int exp = this.createExperience(recipe.getExperience()); + maid.setExperience(maid.getExperience() + exp); + } + } + } else { + // 否则直接重置烧制进度 + this.cookingProgress = 0; + } + } else if (this.cookingProgress > 0) { + // 什么,燃料不足,那就逐 tick 递减烧制进度 + this.cookingProgress = Mth.clamp(this.cookingProgress - 2, 0, this.cookingTotalTime); + } + } + + @Override + public void setItem(int index, ItemStack stack) { + ItemStack slotItem = this.getItem(index); + boolean isSameItem = !stack.isEmpty() && ItemStack.tagMatches(slotItem, stack); + super.setItem(index, stack); + if (stack.getCount() > this.getMaxStackSize()) { + stack.setCount(this.getMaxStackSize()); + } + if (index == 0 && !isSameItem) { + this.cookingTotalTime = getTotalCookTime(this.level); + this.cookingProgress = 0; + this.setChanged(); + } + } + + private int createExperience(float recipeExp) { + int integer = Mth.floor(recipeExp); + float decimal = Mth.frac(recipeExp); + if (decimal != 0 && Math.random() < (double) decimal) { + ++integer; + } + return integer; + } + + private boolean isLit() { + return this.litTime > 0; + } + + private int getBurnDuration(ItemStack fuel) { + if (fuel.isEmpty()) { + return 0; + } else { + return ForgeHooks.getBurnTime(fuel, RecipeType.SMELTING); + } + } + + private boolean canBurn(@Nullable SmeltingRecipe recipe, SimpleContainer container, int maxStackSize) { + // 先检查输入物品和配方 + if (!container.getItem(INPUT_INDEX).isEmpty() && recipe != null) { + // 先检查配方结果 + ItemStack result = recipe.assemble(this); + // 没结果,不能燃烧 + if (result.isEmpty()) { + return false; + } else { + // 检查输出栏 + ItemStack output = container.getItem(OUTPUT_INDEX); + if (output.isEmpty()) { + // 空的,可以放 + return true; + } else if (!output.sameItem(result)) { + // 不同物品,不行 + return false; + } else if (output.getCount() + result.getCount() <= maxStackSize && output.getCount() + result.getCount() <= output.getMaxStackSize()) { + // Forge fix: make furnace respect stack sizes in furnace recipes + return true; + } else { + // Forge fix: make furnace respect stack sizes in furnace recipes + return output.getCount() + result.getCount() <= result.getMaxStackSize(); + } + } + } else { + return false; + } + } + + private boolean burn(@Nullable SmeltingRecipe recipe, SimpleContainer container, int maxStackSize) { + if (recipe != null && this.canBurn(recipe, container, maxStackSize)) { + ItemStack input = container.getItem(INPUT_INDEX); + ItemStack result = recipe.assemble(this); + ItemStack output = container.getItem(OUTPUT_INDEX); + // 如果输出栏为空 + if (output.isEmpty()) { + // 放东西 + container.setItem(OUTPUT_INDEX, result.copy()); + } else if (output.getItem() == result.getItem()) { + // 相同物品,增长数量即可 + output.grow(result.getCount()); + } + // 如果是海绵和桶 + if (input.getItem() == Blocks.WET_SPONGE.asItem() && !container.getItem(FUEL_INDEX).isEmpty() && container.getItem(FUEL_INDEX).getItem() == Items.BUCKET) { + container.setItem(FUEL_INDEX, new ItemStack(Items.WATER_BUCKET)); + } + input.shrink(1); + return true; + } else { + return false; + } + } + + private int getTotalCookTime(Level level) { + return level.getRecipeManager().getRecipeFor(RecipeType.SMELTING, this, level).map(AbstractCookingRecipe::getCookingTime).orElse(200); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/backpack/package-info.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/backpack/package-info.java new file mode 100644 index 000000000..fd6fd7894 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/backpack/package-info.java @@ -0,0 +1,7 @@ +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +package com.github.tartaricacid.touhoulittlemaid.entity.backpack; + +import net.minecraft.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/favorability/FavorabilityManager.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/favorability/FavorabilityManager.java new file mode 100644 index 000000000..92035497a --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/favorability/FavorabilityManager.java @@ -0,0 +1,274 @@ +package com.github.tartaricacid.touhoulittlemaid.entity.favorability; + +import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; +import com.github.tartaricacid.touhoulittlemaid.network.NetworkHandler; +import com.github.tartaricacid.touhoulittlemaid.network.message.SpawnParticleMessage; +import com.google.common.collect.Maps; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.Tag; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.util.Mth; +import net.minecraft.world.entity.ai.attributes.AttributeInstance; +import net.minecraft.world.entity.ai.attributes.Attributes; + +import java.util.Map; + +public class FavorabilityManager { + public static final Map TYPES = Maps.newHashMap(); + + private static final int LEVEL_0 = 0; + private static final int LEVEL_1 = 1; + private static final int LEVEL_2 = 2; + private static final int LEVEL_3 = 3; + + private static final int LEVEL_0_POINT = 0; + private static final int LEVEL_1_POINT = 64; + private static final int LEVEL_2_POINT = 192; + private static final int LEVEL_3_POINT = 384; + + private static final int LEVEL_0_HEALTH = 20; + private static final int LEVEL_1_HEALTH = 30; + private static final int LEVEL_2_HEALTH = 40; + private static final int LEVEL_3_HEALTH = 80; + + private static final int LEVEL_0_ATTACK_DAMAGE = 2; + private static final int LEVEL_1_ATTACK_DAMAGE = 2; + private static final int LEVEL_2_ATTACK_DAMAGE = 5; + private static final int LEVEL_3_ATTACK_DAMAGE = 10; + + private static final String TAG_NAME = "FavorabilityManagerCounter"; + + private final Map counter; + private final EntityMaid maid; + + public FavorabilityManager(EntityMaid maid) { + this.counter = Maps.newHashMap(); + this.maid = maid; + } + + public void tick() { + counter.values().forEach(Time::tick); + } + + public void apply(String type) { + Type typeInstance = TYPES.get(type); + if (typeInstance != null) { + this.apply(typeInstance); + } + } + + public void apply(Type type) { + if (this.canAdd(type.getTypeName())) { + if (type.isReduce()) { + this.reduce(type.getPoint()); + } else { + this.add(type.getPoint()); + } + this.addCooldown(type.getTypeName(), type.getCooldown()); + } + } + + private void addCooldown(String type, int tickCount) { + this.counter.put(type, new Time(tickCount)); + } + + private boolean canAdd(String type) { + if (this.counter.containsKey(type)) { + return this.counter.get(type).isZero(); + } + return true; + } + + public int getLevel() { + return this.getLevel(maid.getFavorability()); + } + + private int getLevel(int favorability) { + if (favorability < LEVEL_1_POINT) { + return LEVEL_0; + } else if (favorability < LEVEL_2_POINT) { + return LEVEL_1; + } else if (favorability < LEVEL_3_POINT) { + return LEVEL_2; + } else { + return LEVEL_3; + } + } + + public double getLevelPercent() { + int favorability = this.maid.getFavorability(); + if (favorability < LEVEL_1_POINT) { + return favorability / (double) LEVEL_1_POINT; + } else if (favorability < LEVEL_2_POINT) { + return (favorability - LEVEL_1_POINT) / (double) (LEVEL_2_POINT - LEVEL_1_POINT); + } else if (favorability < LEVEL_3_POINT) { + return (favorability - LEVEL_2_POINT) / (double) (LEVEL_3_POINT - LEVEL_2_POINT); + } else { + return 0; + } + } + + public int nextLevelPoint() { + int level = this.getLevel(); + if (level == LEVEL_3) { + return 0; + } + int pointByLevel = getPointByLevel(level + 1); + return pointByLevel - this.maid.getFavorability(); + } + + public int getHealthByLevel(int level) { + switch (level) { + case LEVEL_1 -> { + return LEVEL_1_HEALTH; + } + case LEVEL_2 -> { + return LEVEL_2_HEALTH; + } + case LEVEL_3 -> { + return LEVEL_3_HEALTH; + } + default -> { + return LEVEL_0_HEALTH; + } + } + } + + public int getAttackByLevel(int level) { + switch (level) { + case LEVEL_1 -> { + return LEVEL_1_ATTACK_DAMAGE; + } + case LEVEL_2 -> { + return LEVEL_2_ATTACK_DAMAGE; + } + case LEVEL_3 -> { + return LEVEL_3_ATTACK_DAMAGE; + } + default -> { + return LEVEL_0_ATTACK_DAMAGE; + } + } + } + + public int getPointByLevel(int level) { + switch (level) { + case LEVEL_1 -> { + return LEVEL_1_POINT; + } + case LEVEL_2 -> { + return LEVEL_2_POINT; + } + case LEVEL_3 -> { + return LEVEL_3_POINT; + } + default -> { + return LEVEL_0_POINT; + } + } + } + + public void add(int addPoint) { + int favorability = maid.getFavorability(); + int levelBefore = getLevel(); + int result = Mth.clamp(favorability + addPoint, 0, LEVEL_3_POINT); + maid.setFavorability(result); + int levelAfter = getLevel(); + if (levelBefore < levelAfter) { + AttributeInstance attack = maid.getAttribute(Attributes.ATTACK_DAMAGE); + AttributeInstance health = maid.getAttribute(Attributes.MAX_HEALTH); + if (attack != null) { + attack.setBaseValue(this.getAttackByLevel(levelAfter)); + } + if (health != null) { + if (maid.isStruckByLightning()) { + health.setBaseValue(this.getHealthByLevel(levelAfter) + 20); + } else { + health.setBaseValue(this.getHealthByLevel(levelAfter)); + } + if (maid.getHealth() > maid.getMaxHealth()) { + maid.setHealth(maid.getMaxHealth()); + } + } + maid.playSound(SoundEvents.PLAYER_LEVELUP, 0.5F, maid.getRandom().nextFloat() * 0.1F + 0.9F); + } + NetworkHandler.sendToNearby(maid, new SpawnParticleMessage(maid.getId(), SpawnParticleMessage.Type.HEART)); + } + + public void reduce(int reducePoint) { + int favorability = maid.getFavorability(); + int pointByLevel = getPointByLevel(getLevel()); + int result = Mth.clamp(favorability - reducePoint, pointByLevel, LEVEL_3_POINT); + maid.setFavorability(result); + } + + public void reduceWithoutLevel(int reducePoint) { + int favorability = maid.getFavorability(); + int levelBefore = getLevel(); + int result = Mth.clamp(favorability - reducePoint, 0, LEVEL_3_POINT); + maid.setFavorability(result); + int levelAfter = getLevel(); + if (levelBefore > levelAfter) { + AttributeInstance attack = maid.getAttribute(Attributes.ATTACK_DAMAGE); + AttributeInstance health = maid.getAttribute(Attributes.MAX_HEALTH); + if (attack != null) { + attack.setBaseValue(this.getAttackByLevel(levelAfter)); + } + if (health != null) { + if (maid.isStruckByLightning()) { + health.setBaseValue(this.getHealthByLevel(levelAfter) + 20); + } else { + health.setBaseValue(this.getHealthByLevel(levelAfter)); + } + if (maid.getHealth() > maid.getMaxHealth()) { + maid.setHealth(maid.getMaxHealth()); + } + } + } + } + + public void max() { + this.add(LEVEL_3_POINT); + } + + public void addAdditionalSaveData(CompoundTag compound) { + CompoundTag data = new CompoundTag(); + this.counter.forEach((name, time) -> data.putInt(name, time.getTickCount())); + compound.put(TAG_NAME, data); + } + + public void readAdditionalSaveData(CompoundTag compound) { + if (compound.contains(TAG_NAME, Tag.TAG_COMPOUND)) { + CompoundTag data = compound.getCompound(TAG_NAME); + for (String name : data.getAllKeys()) { + this.counter.put(name, new Time(data.getInt(name))); + } + } + } + + public static class Time { + private int tickCount; + + public Time(int tickCount) { + this.tickCount = tickCount; + } + + public int getTickCount() { + return tickCount; + } + + public void setTickCount(int tickCount) { + this.tickCount = tickCount; + } + + public void tick() { + if (tickCount > 0) { + tickCount--; + } + } + + public boolean isZero() { + return this.tickCount <= 0; + } + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/favorability/Type.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/favorability/Type.java new file mode 100644 index 000000000..11d58a9b6 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/favorability/Type.java @@ -0,0 +1,40 @@ +package com.github.tartaricacid.touhoulittlemaid.entity.favorability; + +public class Type { + public static final Type BOOKSHELF = new Type("BookShelf", 2, 24000); + public static final Type COMPUTER = new Type("Computer", 2, 24000); + public static final Type KEYBOARD = new Type("Keyboard", 2, 24000); + public static final Type GOMOKU = new Type("Gomoku", 2, 24000); + public static final Type SLEEP = new Type("Sleep", 2, 24000); + public static final Type GOMOKU_WIN = new Type("GomokuWin", 8, 12000); + public static final Type DEATH = new Type("Death", -2, 12000); + + private final String typeName; + private final int point; + private final int cooldown; + private final boolean isReduce; + + public Type(String typeName, int point, int cooldown) { + this.typeName = typeName; + this.point = Math.abs(point); + this.cooldown = cooldown; + this.isReduce = point < 0; + FavorabilityManager.TYPES.put(typeName, this); + } + + public String getTypeName() { + return typeName; + } + + public int getPoint() { + return point; + } + + public int getCooldown() { + return cooldown; + } + + public boolean isReduce() { + return isReduce; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/item/EntitySit.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/item/EntitySit.java new file mode 100644 index 000000000..a208b38a5 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/item/EntitySit.java @@ -0,0 +1,186 @@ +package com.github.tartaricacid.touhoulittlemaid.entity.item; + +import com.github.tartaricacid.touhoulittlemaid.entity.ai.brain.MaidSchedule; +import com.github.tartaricacid.touhoulittlemaid.entity.favorability.FavorabilityManager; +import com.github.tartaricacid.touhoulittlemaid.entity.favorability.Type; +import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; +import com.github.tartaricacid.touhoulittlemaid.entity.task.TaskBoardGames; +import com.github.tartaricacid.touhoulittlemaid.init.InitEntities; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.Tag; +import net.minecraft.network.protocol.Packet; +import net.minecraft.network.syncher.EntityDataAccessor; +import net.minecraft.network.syncher.EntityDataSerializers; +import net.minecraft.network.syncher.SynchedEntityData; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.entity.*; +import net.minecraft.world.entity.schedule.Activity; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.Vec3; +import net.minecraftforge.network.NetworkHooks; +import org.apache.commons.lang3.StringUtils; + +public class EntitySit extends Entity { + public static final EntityType TYPE = EntityType.Builder.of(EntitySit::new, MobCategory.MISC) + .sized(0.5f, 0.1f).clientTrackingRange(10).build("sit"); + private static final EntityDataAccessor SIT_TYPE = SynchedEntityData.defineId(EntitySit.class, EntityDataSerializers.STRING); + private int passengerTick = 0; + + public EntitySit(EntityType entityTypeIn, Level worldIn) { + super(entityTypeIn, worldIn); + } + + public EntitySit(Level worldIn, Vec3 pos, String joyType) { + this(TYPE, worldIn); + this.setPos(pos); + this.setJoyType(joyType); + } + + @Override + public double getPassengersRidingOffset() { + return -0.25; + } + + @Override + protected void defineSynchedData() { + this.entityData.define(SIT_TYPE, ""); + } + + @Override + protected void readAdditionalSaveData(CompoundTag tag) { + if (tag.contains("SitJoyType", Tag.TAG_STRING)) { + this.setJoyType(tag.getString("SitJoyType")); + } + } + + @Override + protected void addAdditionalSaveData(CompoundTag tag) { + if (StringUtils.isNotBlank(this.getJoyType())) { + tag.putString("SitJoyType", this.getJoyType()); + } + } + + @Override + public void tick() { + if (!this.level.isClientSide) { + this.checkOutOfWorld(); + this.checkPassengers(); + if (this.getFirstPassenger() instanceof EntityMaid maid) { + this.tickMaid(maid); + } + } + } + + private void tickMaid(EntityMaid maid) { + maid.setYRot(this.getYRot()); + maid.setYHeadRot(this.getYRot()); + if (tickCount % 20 == 0) { + FavorabilityManager manager = maid.getFavorabilityManager(); + manager.apply(this.getJoyType()); + if (!this.isIdleSchedule(maid) && !isGomokuTask(maid)) { + maid.stopRiding(); + } + } + } + + private void checkPassengers() { + if (this.getPassengers().isEmpty()) { + passengerTick++; + } else { + passengerTick = 0; + } + if (passengerTick > 10) { + this.discard(); + } + } + + private boolean isGomokuTask(EntityMaid maid) { + return Type.GOMOKU.getTypeName().equals(this.getJoyType()) && maid.getTask().getUid().equals(TaskBoardGames.UID) && isWorkSchedule(maid); + } + + private boolean isIdleSchedule(EntityMaid maid) { + MaidSchedule schedule = maid.getSchedule(); + int time = (int) (maid.level.getDayTime() % 24000L); + switch (schedule) { + case ALL -> { + return false; + } + case NIGHT -> { + return InitEntities.MAID_NIGHT_SHIFT_SCHEDULES.get().getActivityAt(time) == Activity.IDLE; + } + default -> { + return InitEntities.MAID_DAY_SHIFT_SCHEDULES.get().getActivityAt(time) == Activity.IDLE; + } + } + } + + private boolean isWorkSchedule(EntityMaid maid) { + MaidSchedule schedule = maid.getSchedule(); + int time = (int) (maid.level.getDayTime() % 24000L); + switch (schedule) { + case ALL -> { + return true; + } + case NIGHT -> { + return InitEntities.MAID_NIGHT_SHIFT_SCHEDULES.get().getActivityAt(time) == Activity.WORK; + } + default -> { + return InitEntities.MAID_DAY_SHIFT_SCHEDULES.get().getActivityAt(time) == Activity.WORK; + } + } + } + + @Override + public boolean skipAttackInteraction(Entity pEntity) { + return true; + } + + @Override + public boolean hurt(DamageSource pSource, float pAmount) { + return false; + } + + @Override + public void move(MoverType pType, Vec3 pPos) { + } + + @Override + public void push(Entity entity) { + } + + @Override + public void push(double pX, double pY, double pZ) { + } + + @Override + protected boolean repositionEntityAfterLoad() { + return false; + } + + @Override + public void thunderHit(ServerLevel pLevel, LightningBolt pLightning) { + } + + @Override + public void refreshDimensions() { + } + + @Override + public boolean canCollideWith(Entity entity) { + return false; + } + + @Override + public Packet getAddEntityPacket() { + return NetworkHooks.getEntitySpawningPacket(this); + } + + public String getJoyType() { + return this.entityData.get(SIT_TYPE); + } + + public void setJoyType(String type) { + this.entityData.set(SIT_TYPE, type); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/item/EntityTombstone.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/item/EntityTombstone.java new file mode 100644 index 000000000..ee80656ef --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/item/EntityTombstone.java @@ -0,0 +1,173 @@ +package com.github.tartaricacid.touhoulittlemaid.entity.item; + +import com.github.tartaricacid.touhoulittlemaid.world.data.MaidWorldData; +import net.minecraft.Util; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.network.protocol.Packet; +import net.minecraft.network.syncher.EntityDataAccessor; +import net.minecraft.network.syncher.EntityDataSerializers; +import net.minecraft.network.syncher.SynchedEntityData; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.entity.*; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.Vec3; +import net.minecraftforge.items.ItemHandlerHelper; +import net.minecraftforge.items.ItemStackHandler; +import net.minecraftforge.network.NetworkHooks; + +import javax.annotation.Nullable; +import java.util.UUID; + +public class EntityTombstone extends Entity { + public static final EntityType TYPE = EntityType.Builder.of(EntityTombstone::new, MobCategory.MISC) + .sized(0.8f, 1.2f).clientTrackingRange(10).build("tombstone"); + private static final String OWNER_ID_TAG = "OwnerId"; + private static final String TOMBSTONE_ITEMS_TAG = "TombstoneItems"; + private static final String MAID_NAME_TAG = "MaidName"; + private static final EntityDataAccessor MAID_NAME = SynchedEntityData.defineId(EntityTombstone.class, EntityDataSerializers.COMPONENT); + private final ItemStackHandler items = new ItemStackHandler(64); + private UUID ownerId = Util.NIL_UUID; + + public EntityTombstone(EntityType entityTypeIn, Level worldIn) { + super(entityTypeIn, worldIn); + } + + public EntityTombstone(Level worldIn, UUID ownerId, Vec3 pos) { + this(TYPE, worldIn); + this.ownerId = ownerId; + this.setPos(pos); + } + + public void insertItem(ItemStack item) { + ItemHandlerHelper.insertItemStacked(this.items, item, false); + } + + @Override + public InteractionResult interact(Player player, InteractionHand hand) { + if (player.getUUID().equals(this.ownerId)) { + for (int i = 0; i < this.items.getSlots(); i++) { + int size = this.items.getSlotLimit(i); + ItemStack extractItem = this.items.extractItem(i, size, false); + ItemHandlerHelper.giveItemToPlayer(player, extractItem); + } + this.discard(); + return InteractionResult.sidedSuccess(level.isClientSide); + } + return super.interact(player, hand); + } + + @Override + protected void defineSynchedData() { + this.entityData.define(MAID_NAME, TextComponent.EMPTY); + } + + @Override + protected void readAdditionalSaveData(CompoundTag tag) { + if (tag.contains(OWNER_ID_TAG)) { + this.ownerId = tag.getUUID(OWNER_ID_TAG); + } + if (tag.contains(TOMBSTONE_ITEMS_TAG)) { + items.deserializeNBT(tag.getCompound(TOMBSTONE_ITEMS_TAG)); + } + if (tag.contains(MAID_NAME_TAG)) { + String nameJson = tag.getString(MAID_NAME_TAG); + setMaidName(Component.Serializer.fromJson(nameJson)); + } + } + + @Override + protected void addAdditionalSaveData(CompoundTag tag) { + tag.putUUID(OWNER_ID_TAG, this.ownerId); + tag.put(TOMBSTONE_ITEMS_TAG, this.items.serializeNBT()); + tag.putString(MAID_NAME_TAG, Component.Serializer.toJson(this.getMaidName())); + } + + @Override + public void tick() { + if (!this.level.isClientSide) { + this.checkOutOfWorld(); + } + } + + @Override + public boolean skipAttackInteraction(Entity pEntity) { + return true; + } + + @Override + public boolean hurt(DamageSource pSource, float pAmount) { + return false; + } + + @Override + public void move(MoverType pType, Vec3 pPos) { + } + + @Override + public void push(Entity entity) { + } + + @Override + public void push(double pX, double pY, double pZ) { + } + + @Override + protected boolean repositionEntityAfterLoad() { + return false; + } + + @Override + public void thunderHit(ServerLevel pLevel, LightningBolt pLightning) { + } + + @Override + public void refreshDimensions() { + } + + @Override + public void remove(RemovalReason reason) { + if (reason.shouldDestroy()) { + MaidWorldData maidWorldData = MaidWorldData.get(level); + if (maidWorldData != null) { + maidWorldData.removeTombstones(this); + } + } + super.remove(reason); + } + + @Override + public boolean canCollideWith(Entity entity) { + return false; + } + + @Override + public boolean isPickable() { + return this.isAlive(); + } + + @Override + public Packet getAddEntityPacket() { + return NetworkHooks.getEntitySpawningPacket(this); + } + + public UUID getOwnerId() { + return ownerId; + } + + public Component getMaidName() { + return this.entityData.get(MAID_NAME); + } + + public void setMaidName(@Nullable Component name) { + if (name != null) { + this.entityData.set(MAID_NAME, name); + } + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/monster/EntityFairy.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/monster/EntityFairy.java index e5ab787df..990931108 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/monster/EntityFairy.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/monster/EntityFairy.java @@ -58,18 +58,13 @@ public static AttributeSupplier.Builder createFairyAttributes() { @Override protected void registerGoals() { goalSelector.addGoal(0, new FloatGoal(this)); - // TODO:待完成 - // goalSelector.addGoal(0, new AvoidEntityGoal<>(this, EntityScarecrow.class, 10.0F, 1.0D, 1.2D)); goalSelector.addGoal(1, new FairyAttackGoal(this, 6.0, 1.0)); goalSelector.addGoal(2, new MoveTowardsRestrictionGoal(this, 1.0)); goalSelector.addGoal(3, new WaterAvoidingRandomStrollGoal(this, 1.0)); goalSelector.addGoal(4, new LookAtPlayerGoal(this, Player.class, 8.0F)); goalSelector.addGoal(5, new LookAtPlayerGoal(this, EntityMaid.class, 8.0F)); goalSelector.addGoal(6, new RandomLookAroundGoal(this)); - targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, Player.class, true)); - targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, EntityMaid.class, 10, true, - false, e -> !e.isSleeping())); targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, IronGolem.class, true)); } diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/passive/EntityMaid.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/passive/EntityMaid.java index 5cc863618..47d1d2052 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/passive/EntityMaid.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/passive/EntityMaid.java @@ -1,5 +1,7 @@ package com.github.tartaricacid.touhoulittlemaid.entity.passive; +import com.github.tartaricacid.touhoulittlemaid.api.backpack.IBackpackData; +import com.github.tartaricacid.touhoulittlemaid.api.backpack.IMaidBackpack; import com.github.tartaricacid.touhoulittlemaid.api.event.*; import com.github.tartaricacid.touhoulittlemaid.api.task.IMaidTask; import com.github.tartaricacid.touhoulittlemaid.api.task.IRangedAttackTask; @@ -8,35 +10,38 @@ import com.github.tartaricacid.touhoulittlemaid.client.model.bedrock.BedrockPart; import com.github.tartaricacid.touhoulittlemaid.client.resource.CustomPackLoader; import com.github.tartaricacid.touhoulittlemaid.client.resource.pojo.MaidModelInfo; +import com.github.tartaricacid.touhoulittlemaid.compat.domesticationinnovation.PetBedDrop; import com.github.tartaricacid.touhoulittlemaid.compat.slashblade.SlashBladeCompat; import com.github.tartaricacid.touhoulittlemaid.config.subconfig.MaidConfig; import com.github.tartaricacid.touhoulittlemaid.entity.ai.brain.MaidBrain; import com.github.tartaricacid.touhoulittlemaid.entity.ai.brain.MaidSchedule; +import com.github.tartaricacid.touhoulittlemaid.entity.backpack.*; import com.github.tartaricacid.touhoulittlemaid.entity.chatbubble.ChatBubbleManger; import com.github.tartaricacid.touhoulittlemaid.entity.chatbubble.ChatText; import com.github.tartaricacid.touhoulittlemaid.entity.chatbubble.MaidChatBubbles; +import com.github.tartaricacid.touhoulittlemaid.entity.favorability.FavorabilityManager; +import com.github.tartaricacid.touhoulittlemaid.entity.favorability.Type; import com.github.tartaricacid.touhoulittlemaid.entity.info.ServerCustomPackLoader; import com.github.tartaricacid.touhoulittlemaid.entity.item.EntityPowerPoint; +import com.github.tartaricacid.touhoulittlemaid.entity.item.EntityTombstone; import com.github.tartaricacid.touhoulittlemaid.entity.task.TaskIdle; import com.github.tartaricacid.touhoulittlemaid.entity.task.TaskManager; import com.github.tartaricacid.touhoulittlemaid.init.InitItems; import com.github.tartaricacid.touhoulittlemaid.init.InitSounds; import com.github.tartaricacid.touhoulittlemaid.inventory.container.MaidConfigContainer; -import com.github.tartaricacid.touhoulittlemaid.inventory.container.MaidMainContainer; import com.github.tartaricacid.touhoulittlemaid.inventory.handler.BaubleItemHandler; import com.github.tartaricacid.touhoulittlemaid.inventory.handler.MaidBackpackHandler; import com.github.tartaricacid.touhoulittlemaid.inventory.handler.MaidHandsInvWrapper; -import com.github.tartaricacid.touhoulittlemaid.item.BackpackLevel; import com.github.tartaricacid.touhoulittlemaid.item.ItemFilm; -import com.github.tartaricacid.touhoulittlemaid.item.ItemMaidBackpack; import com.github.tartaricacid.touhoulittlemaid.mixin.MixinArrowEntity; import com.github.tartaricacid.touhoulittlemaid.network.NetworkHandler; import com.github.tartaricacid.touhoulittlemaid.network.message.ItemBreakMessage; import com.github.tartaricacid.touhoulittlemaid.network.message.PlayMaidSoundMessage; import com.github.tartaricacid.touhoulittlemaid.network.message.SendEffectMessage; import com.github.tartaricacid.touhoulittlemaid.util.BiomeCacheUtil; -import com.github.tartaricacid.touhoulittlemaid.util.ItemsUtil; import com.github.tartaricacid.touhoulittlemaid.util.ParseI18n; +import com.github.tartaricacid.touhoulittlemaid.util.TeleportHelper; +import com.github.tartaricacid.touhoulittlemaid.world.data.MaidWorldData; import com.google.common.collect.Lists; import com.mojang.serialization.Dynamic; import net.minecraft.Util; @@ -66,6 +71,8 @@ import net.minecraft.world.InteractionResult; import net.minecraft.world.MenuProvider; import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.effect.MobEffectInstance; +import net.minecraft.world.effect.MobEffects; import net.minecraft.world.entity.*; import net.minecraft.world.entity.ai.Brain; import net.minecraft.world.entity.ai.attributes.Attributes; @@ -96,6 +103,7 @@ import net.minecraftforge.common.ForgeHooks; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.util.ITeleporter; import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.event.ForgeEventFactory; import net.minecraftforge.items.CapabilityItemHandler; @@ -120,6 +128,7 @@ public class EntityMaid extends TamableAnimal implements CrossbowAttackMob { .sized(0.6f, 1.5f).clientTrackingRange(10).build("maid"); public static final String MODEL_ID_TAG = "ModelId"; public static final String SOUND_PACK_ID_TAG = "SoundPackId"; + public static final String MAID_BACKPACK_TYPE = "MaidBackpackType"; public static final String BACKPACK_LEVEL_TAG = "MaidBackpackLevel"; public static final String MAID_INVENTORY_TAG = "MaidInventory"; public static final String MAID_BAUBLE_INVENTORY_TAG = "MaidBaubleInventory"; @@ -132,7 +141,6 @@ public class EntityMaid extends TamableAnimal implements CrossbowAttackMob { private static final EntityDataAccessor DATA_HOME_MODE = SynchedEntityData.defineId(EntityMaid.class, EntityDataSerializers.BOOLEAN); private static final EntityDataAccessor DATA_RIDEABLE = SynchedEntityData.defineId(EntityMaid.class, EntityDataSerializers.BOOLEAN); private static final EntityDataAccessor DATA_INVULNERABLE = SynchedEntityData.defineId(EntityMaid.class, EntityDataSerializers.BOOLEAN); - private static final EntityDataAccessor DATA_BACKPACK_LEVEL = SynchedEntityData.defineId(EntityMaid.class, EntityDataSerializers.INT); private static final EntityDataAccessor DATA_HUNGER = SynchedEntityData.defineId(EntityMaid.class, EntityDataSerializers.INT); private static final EntityDataAccessor DATA_FAVORABILITY = SynchedEntityData.defineId(EntityMaid.class, EntityDataSerializers.INT); private static final EntityDataAccessor DATA_EXPERIENCE = SynchedEntityData.defineId(EntityMaid.class, EntityDataSerializers.INT); @@ -143,6 +151,8 @@ public class EntityMaid extends TamableAnimal implements CrossbowAttackMob { private static final EntityDataAccessor RESTRICT_CENTER = SynchedEntityData.defineId(EntityMaid.class, EntityDataSerializers.BLOCK_POS); private static final EntityDataAccessor RESTRICT_RADIUS = SynchedEntityData.defineId(EntityMaid.class, EntityDataSerializers.FLOAT); private static final EntityDataAccessor CHAT_BUBBLE = SynchedEntityData.defineId(EntityMaid.class, MaidChatBubbles.DATA); + private static final EntityDataAccessor BACKPACK_TYPE = SynchedEntityData.defineId(EntityMaid.class, EntityDataSerializers.STRING); + private static final EntityDataAccessor BACKPACK_ITEM_SHOW = SynchedEntityData.defineId(EntityMaid.class, EntityDataSerializers.ITEM_STACK); private static final String TASK_TAG = "MaidTask"; private static final String PICKUP_TAG = "MaidIsPickup"; private static final String HOME_TAG = "MaidIsHome"; @@ -153,28 +163,33 @@ public class EntityMaid extends TamableAnimal implements CrossbowAttackMob { private static final String FAVORABILITY_TAG = "MaidFavorability"; private static final String SCHEDULE_MODE_TAG = "MaidScheduleMode"; private static final String RESTRICT_CENTER_TAG = "MaidRestrictCenter"; + private static final String BACKPACK_DATA_TAG = "MaidBackpackData"; private static final String DEFAULT_MODEL_ID = "touhou_little_maid:hakurei_reimu"; private static final String DEFAULT_SOUND_PACK_ID = "touhou_little_maid"; private final EntityArmorInvWrapper armorInvWrapper = new EntityArmorInvWrapper(this); private final EntityHandsInvWrapper handsInvWrapper = new MaidHandsInvWrapper(this); - private final ItemStackHandler maidInv = new MaidBackpackHandler(36); + private final ItemStackHandler maidInv = new MaidBackpackHandler(36, this); private final BaubleItemHandler maidBauble = new BaubleItemHandler(9); + private final FavorabilityManager favorabilityManager; public boolean guiOpening = false; private List effects = Lists.newArrayList(); private IMaidTask task = TaskManager.getIdleTask(); + private IMaidBackpack backpack = BackpackManager.getEmptyBackpack(); private int playerHurtSoundCount = 120; private int pickupSoundCount = 5; private int backpackDelay = 0; + private IBackpackData backpackData = null; protected EntityMaid(EntityType type, Level world) { super(type, world); ((GroundPathNavigation) this.getNavigation()).setCanOpenDoors(true); this.getNavigation().setCanFloat(true); this.setPathfindingMalus(BlockPathTypes.COCOA, -1.0F); + this.favorabilityManager = new FavorabilityManager(this); } public EntityMaid(Level worldIn) { @@ -200,7 +215,6 @@ protected void defineSynchedData() { this.entityData.define(DATA_HOME_MODE, false); this.entityData.define(DATA_RIDEABLE, true); this.entityData.define(DATA_INVULNERABLE, false); - this.entityData.define(DATA_BACKPACK_LEVEL, 0); this.entityData.define(DATA_HUNGER, 0); this.entityData.define(DATA_FAVORABILITY, 0); this.entityData.define(DATA_EXPERIENCE, 0); @@ -211,6 +225,8 @@ protected void defineSynchedData() { this.entityData.define(RESTRICT_CENTER, BlockPos.ZERO); this.entityData.define(RESTRICT_RADIUS, MaidConfig.MAID_HOME_RANGE.get().floatValue()); this.entityData.define(CHAT_BUBBLE, MaidChatBubbles.DEFAULT); + this.entityData.define(BACKPACK_TYPE, EmptyBackpack.ID.toString()); + this.entityData.define(BACKPACK_ITEM_SHOW, ItemStack.EMPTY); } @Override @@ -293,6 +309,12 @@ public void aiStep() { this.updateSwingTime(); if (!level.isClientSide) { ChatBubbleManger.tick(this); + if (this.backpackData != null) { + this.level.getProfiler().push("maidBackpackData"); + this.backpackData.serverTick(this); + this.level.getProfiler().pop(); + } + this.favorabilityManager.tick(); } } @@ -571,6 +593,42 @@ protected void actuallyHurt(DamageSource damageSrc, float damageAmount) { } } + @Nullable + @Override + public Entity changeDimension(ServerLevel serverLevel, ITeleporter teleporter) { + if (this.level instanceof ServerLevel && !this.isRemoved()) { + final int MAX_RETRY = 16; + for (int i = 0; i < MAX_RETRY; ++i) { + if (TeleportHelper.teleport(this)) { + this.addEffect(new MobEffectInstance(MobEffects.GLOWING, 200, 1, true, false)); + } + } + } + return null; + } + + @Override + public void onAddedToWorld() { + super.onAddedToWorld(); + if (this.getOwnerUUID() != null) { + MaidWorldData data = MaidWorldData.get(this.level); + if (data != null) { + data.removeInfo(this); + } + } + } + + @Override + public void onRemovedFromWorld() { + super.onRemovedFromWorld(); + if (!this.level.isClientSide && this.isAlive() && this.getOwnerUUID() != null) { + MaidWorldData data = MaidWorldData.get(this.level); + if (data != null) { + data.addInfo(this); + } + } + } + @Override public void die(DamageSource cause) { if (!MinecraftForge.EVENT_BUS.post(new MaidDeathEvent(this, cause))) { @@ -766,6 +824,17 @@ public void spawnBubbleParticle() { } } + public void spawnHeartParticle() { + if (this.level.isClientSide) { + for (int i = 0; i < 8; ++i) { + double offsetX = this.random.nextGaussian() * 0.02; + double offsetY = this.random.nextGaussian() * 0.02; + double offsetZ = this.random.nextGaussian() * 0.02; + level.addParticle(ParticleTypes.HEART, this.getRandomX(1.0), this.getRandomY() + 0.5, this.getRandomZ(1.0), offsetX, offsetY, offsetZ); + } + } + } + @Override public void addAdditionalSaveData(CompoundTag compound) { super.addAdditionalSaveData(compound); @@ -775,7 +844,6 @@ public void addAdditionalSaveData(CompoundTag compound) { compound.putBoolean(PICKUP_TAG, isPickup()); compound.putBoolean(HOME_TAG, isHomeModeEnable()); compound.putBoolean(RIDEABLE_TAG, isRideable()); - compound.putInt(BACKPACK_LEVEL_TAG, getBackpackLevel()); compound.put(MAID_INVENTORY_TAG, maidInv.serializeNBT()); compound.put(MAID_BAUBLE_INVENTORY_TAG, maidBauble.serializeNBT()); compound.putBoolean(STRUCK_BY_LIGHTNING_TAG, isStruckByLightning()); @@ -785,6 +853,15 @@ public void addAdditionalSaveData(CompoundTag compound) { compound.putInt(EXPERIENCE_TAG, getExperience()); compound.putString(SCHEDULE_MODE_TAG, getSchedule().name()); compound.put(RESTRICT_CENTER_TAG, NbtUtils.writeBlockPos(getRestrictCenter())); + compound.putString(MAID_BACKPACK_TYPE, getMaidBackpackType().getId().toString()); + this.favorabilityManager.addAdditionalSaveData(compound); + if (this.backpackData != null) { + CompoundTag tag = new CompoundTag(); + this.backpackData.save(tag, this); + compound.put(BACKPACK_DATA_TAG, tag); + } else { + compound.put(BACKPACK_DATA_TAG, new CompoundTag()); + } } @Override @@ -811,7 +888,18 @@ public void readAdditionalSaveData(CompoundTag compound) { setRideable(compound.getBoolean(RIDEABLE_TAG)); } if (compound.contains(BACKPACK_LEVEL_TAG, Tag.TAG_INT)) { - setBackpackLevel(compound.getInt(BACKPACK_LEVEL_TAG)); + // 存档迁移 + int backpackLevel = compound.getInt(BACKPACK_LEVEL_TAG); + if (backpackLevel == 1) { + BackpackManager.findBackpack(SmallBackpack.ID).ifPresent(this::setMaidBackpackType); + } + if (backpackLevel == 2) { + BackpackManager.findBackpack(MiddleBackpack.ID).ifPresent(this::setMaidBackpackType); + } + if (backpackLevel == 3) { + BackpackManager.findBackpack(BigBackpack.ID).ifPresent(this::setMaidBackpackType); + } + compound.remove(BACKPACK_LEVEL_TAG); } if (compound.contains(MAID_INVENTORY_TAG, Tag.TAG_COMPOUND)) { maidInv.deserializeNBT(compound.getCompound(MAID_INVENTORY_TAG)); @@ -840,6 +928,16 @@ public void readAdditionalSaveData(CompoundTag compound) { if (compound.contains(RESTRICT_CENTER_TAG, Tag.TAG_COMPOUND)) { setRestrictCenter(NbtUtils.readBlockPos(compound.getCompound(RESTRICT_CENTER_TAG))); } + if (compound.contains(MAID_BACKPACK_TYPE, Tag.TAG_STRING)) { + ResourceLocation id = new ResourceLocation(compound.getString(MAID_BACKPACK_TYPE)); + IMaidBackpack backpack = BackpackManager.findBackpack(id).orElse(BackpackManager.getEmptyBackpack()); + setMaidBackpackType(backpack); + if (this.backpackData != null && compound.contains(BACKPACK_DATA_TAG, Tag.TAG_COMPOUND)) { + this.backpackData.load(compound.getCompound(BACKPACK_DATA_TAG), this); + } + } + this.favorabilityManager.readAdditionalSaveData(compound); + this.setBackpackShowItem(maidInv.getStackInSlot(MaidBackpackHandler.BACKPACK_ITEM_SLOT)); } public boolean openMaidGui(Player player) { @@ -860,7 +958,7 @@ private MenuProvider getGuiProvider(int tabIndex) { return MaidConfigContainer.create(getId()); case TabIndex.MAIN: default: - return MaidMainContainer.create(getId()); + return this.getMaidBackpackType().getGuiProvider(getId()); } } @@ -882,9 +980,40 @@ public LazyOptional getCapability(Capability capability, @Nullable Dir @Override protected void dropEquipment() { - ItemsUtil.dropEntityItems(this, new CombinedInvWrapper(armorInvWrapper, handsInvWrapper, maidInv, maidBauble)); - ItemMaidBackpack.getInstance(getBackpackLevel()).ifPresent(backpack -> spawnAtLocation(backpack.getDefaultInstance())); - spawnAtLocation(ItemFilm.maidToFilm(this), 0.2f); + if (this.getOwnerUUID() != null && !level.isClientSide && !PetBedDrop.hasPetBedPos(this)) { + // 掉出世界的判断 + Vec3 position = Vec3.atBottomCenterOf(blockPosition()); + // 防止卡在基岩里? + if (this.getY() < this.level.getMinBuildHeight() + 5) { + position = new Vec3(position.x, this.level.getMinBuildHeight() + 5, position.z); + } + if (this.getY() > this.level.getMaxBuildHeight()) { + position = new Vec3(position.x, this.level.getMaxBuildHeight(), position.z); + } + EntityTombstone tombstone = new EntityTombstone(level, this.getOwnerUUID(), position); + tombstone.setMaidName(this.getDisplayName()); + + // 女仆物品栏 + CombinedInvWrapper invWrapper = new CombinedInvWrapper(armorInvWrapper, handsInvWrapper, maidInv, maidBauble); + for (int i = 0; i < invWrapper.getSlots(); i++) { + int size = invWrapper.getSlotLimit(i); + tombstone.insertItem(invWrapper.extractItem(i, size, false)); + } + // 背包额外数据 + IMaidBackpack maidBackpack = this.getMaidBackpackType(); + tombstone.insertItem(maidBackpack.getTakeOffItemStack(ItemStack.EMPTY, null, this)); + maidBackpack.onSpawnTombstone(this, tombstone); + // 胶片 + ItemStack filmItem = ItemFilm.maidToFilm(this); + tombstone.insertItem(filmItem); + // 全局记录 + MaidWorldData maidWorldData = MaidWorldData.get(level); + if (maidWorldData != null) { + maidWorldData.addTombstones(this, tombstone); + } + + level.addFreshEntity(tombstone); + } } @Override @@ -1036,6 +1165,13 @@ public void swing(InteractionHand pHand) { super.swing(pHand); } + @Override + public void startSleeping(BlockPos pPos) { + super.startSleeping(pPos); + this.setHealth(this.getMaxHealth()); + this.favorabilityManager.apply(Type.SLEEP); + } + public void setBackpackDelay() { backpackDelay = 20; } @@ -1143,16 +1279,8 @@ public void setRideable(boolean rideable) { this.entityData.set(DATA_RIDEABLE, rideable); } - public int getBackpackLevel() { - return this.entityData.get(DATA_BACKPACK_LEVEL); - } - - public void setBackpackLevel(int level) { - this.entityData.set(DATA_BACKPACK_LEVEL, level); - } - public boolean hasBackpack() { - return getBackpackLevel() > 0; + return getMaidBackpackType() != BackpackManager.getEmptyBackpack(); } public int getHunger() { @@ -1195,6 +1323,36 @@ public void setSwingingArms(boolean swingingArms) { this.entityData.set(DATA_ARM_RISE, swingingArms); } + public ItemStack getBackpackShowItem() { + return this.entityData.get(BACKPACK_ITEM_SHOW); + } + + public void setBackpackShowItem(ItemStack stack) { + this.entityData.set(BACKPACK_ITEM_SHOW, stack); + } + + public IMaidBackpack getMaidBackpackType() { + ResourceLocation id = new ResourceLocation(entityData.get(BACKPACK_TYPE)); + return BackpackManager.findBackpack(id).orElse(BackpackManager.getEmptyBackpack()); + } + + public void setMaidBackpackType(IMaidBackpack backpack) { + if (backpack == this.backpack) { + return; + } + this.backpack = backpack; + if (this.backpack.hasBackpackData()) { + this.backpackData = this.backpack.getBackpackData(this); + } else { + this.backpackData = null; + } + this.entityData.set(BACKPACK_TYPE, backpack.getId().toString()); + } + + public IBackpackData getBackpackData() { + return backpackData; + } + public MaidSchedule getSchedule() { return this.entityData.get(SCHEDULE_MODE); } @@ -1211,7 +1369,7 @@ public ItemStackHandler getMaidInv() { } public CombinedInvWrapper getAvailableInv(boolean handsFirst) { - RangedWrapper combinedInvWrapper = new RangedWrapper(maidInv, 0, BackpackLevel.BACKPACK_CAPACITY_MAP.get(getBackpackLevel())); + RangedWrapper combinedInvWrapper = new RangedWrapper(maidInv, 0, getMaidBackpackType().getAvailableMaxContainerIndex()); return handsFirst ? new CombinedInvWrapper(handsInvWrapper, combinedInvWrapper) : new CombinedInvWrapper(combinedInvWrapper, handsInvWrapper); } @@ -1340,11 +1498,15 @@ public String getAtBiomeTemp() { } } + @Deprecated public boolean isSitInJoyBlock() { - // TODO:待完成 return false; } + public FavorabilityManager getFavorabilityManager() { + return favorabilityManager; + } + @Deprecated public int getDim() { ResourceKey dim = this.level.dimension(); diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/task/TaskBoardGames.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/task/TaskBoardGames.java new file mode 100644 index 000000000..e2bcd68fa --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/task/TaskBoardGames.java @@ -0,0 +1,46 @@ +package com.github.tartaricacid.touhoulittlemaid.entity.task; + +import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; +import com.github.tartaricacid.touhoulittlemaid.api.task.IMaidTask; +import com.github.tartaricacid.touhoulittlemaid.entity.ai.brain.task.MaidFindGomokuTask; +import com.github.tartaricacid.touhoulittlemaid.entity.ai.brain.task.MaidSitJoyTask; +import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; +import com.github.tartaricacid.touhoulittlemaid.init.InitItems; +import com.github.tartaricacid.touhoulittlemaid.init.InitSounds; +import com.github.tartaricacid.touhoulittlemaid.util.SoundUtil; +import com.google.common.collect.Lists; +import com.mojang.datafixers.util.Pair; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.world.entity.ai.behavior.Behavior; +import net.minecraft.world.item.ItemStack; + +import javax.annotation.Nullable; +import java.util.List; + +public class TaskBoardGames implements IMaidTask { + public static final ResourceLocation UID = new ResourceLocation(TouhouLittleMaid.MOD_ID, "board_games"); + + @Override + public ResourceLocation getUid() { + return UID; + } + + @Override + public ItemStack getIcon() { + return InitItems.GOMOKU.get().getDefaultInstance(); + } + + @Nullable + @Override + public SoundEvent getAmbientSound(EntityMaid maid) { + return SoundUtil.environmentSound(maid, InitSounds.MAID_IDLE.get(), 0.5f); + } + + @Override + public List>> createBrainTasks(EntityMaid maid) { + Pair> findGomoku = Pair.of(5, new MaidFindGomokuTask(0.6f, 12)); + Pair> sitGomoku = Pair.of(6, new MaidSitJoyTask()); + return Lists.newArrayList(findGomoku, sitGomoku); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/task/TaskCocoa.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/task/TaskCocoa.java index e70d3157a..1bfb1bbac 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/task/TaskCocoa.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/task/TaskCocoa.java @@ -11,6 +11,7 @@ import net.minecraft.core.Direction; import net.minecraft.resources.ResourceLocation; import net.minecraft.sounds.SoundSource; +import net.minecraft.tags.BlockTags; import net.minecraft.world.entity.ai.behavior.Behavior; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; @@ -73,7 +74,7 @@ public void harvest(EntityMaid maid, BlockPos cropPos, BlockState cropState) { public boolean canPlant(EntityMaid maid, BlockPos basePos, BlockState baseState, ItemStack seed) { basePos = basePos.above(); baseState = maid.level.getBlockState(basePos); - if (baseState.is(Blocks.JUNGLE_LOG) && seed.getItem() == Items.COCOA_BEANS) { + if (baseState.is(BlockTags.JUNGLE_LOGS) && seed.getItem() == Items.COCOA_BEANS) { for (Direction direction : Direction.Plane.HORIZONTAL) { BlockState state = maid.level.getBlockState(basePos.relative(direction)); if (state.getMaterial().isReplaceable()) { diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/task/TaskManager.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/task/TaskManager.java index a123f3e28..f81d23e01 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/task/TaskManager.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/task/TaskManager.java @@ -42,6 +42,7 @@ public static void init() { manager.add(new TaskTorch()); manager.add(new TaskFeedAnimal()); manager.add(new TaskExtinguishing()); + manager.add(new TaskBoardGames()); for (ILittleMaid littleMaid : TouhouLittleMaid.EXTENSIONS) { littleMaid.addMaidTask(manager); } diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/task/TaskSugarCane.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/task/TaskSugarCane.java index b91ff90f3..204f6fc40 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/task/TaskSugarCane.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/task/TaskSugarCane.java @@ -6,6 +6,7 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.BlockTags; import net.minecraft.tags.FluidTags; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; @@ -68,8 +69,7 @@ public double getCloseEnoughDist() { } private boolean canSustainSugarCane(BlockState state) { - return state.is(Blocks.GRASS_BLOCK) || state.is(Blocks.DIRT) || state.is(Blocks.COARSE_DIRT) - || state.is(Blocks.PODZOL) || state.is(Blocks.SAND) || state.is(Blocks.RED_SAND); + return state.is(BlockTags.DIRT) || state.is(BlockTags.SAND); } private boolean hasWaterSourceBlock(Level world, BlockPos basePos) { diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/event/maid/HandleBackpackEvent.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/event/maid/HandleBackpackEvent.java new file mode 100644 index 000000000..c06dc8c5a --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/event/maid/HandleBackpackEvent.java @@ -0,0 +1,51 @@ +package com.github.tartaricacid.touhoulittlemaid.event.maid; + +import com.github.tartaricacid.touhoulittlemaid.api.backpack.IMaidBackpack; +import com.github.tartaricacid.touhoulittlemaid.api.event.InteractMaidEvent; +import com.github.tartaricacid.touhoulittlemaid.entity.backpack.BackpackManager; +import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.common.Tags; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.items.ItemHandlerHelper; + +@Mod.EventBusSubscriber +public class HandleBackpackEvent { + @SubscribeEvent + public static void onInteractMaid(InteractMaidEvent event) { + ItemStack stack = event.getStack(); + Player player = event.getPlayer(); + EntityMaid maid = event.getMaid(); + IMaidBackpack maidBackpack = maid.getMaidBackpackType(); + if (stack.is(Tags.Items.SHEARS)) { + if (maid.isOwnedBy(player) && !maid.backpackHasDelay() && maidBackpack != BackpackManager.getEmptyBackpack()) { + maid.setBackpackDelay(); + player.getCooldowns().addCooldown(stack.getItem(), 20); + ItemHandlerHelper.giveItemToPlayer(player, maidBackpack.getTakeOffItemStack(stack, player, maid)); + maidBackpack.onTakeOff(stack, player, maid); + maid.setMaidBackpackType(BackpackManager.getEmptyBackpack()); + stack.hurtAndBreak(1, player, m -> m.broadcastBreakEvent(InteractionHand.MAIN_HAND)); + maid.playSound(SoundEvents.HORSE_SADDLE, 0.5F, 1.0F); + event.setCanceled(true); + } + } else { + BackpackManager.findBackpack(stack).ifPresent(backpack -> { + if (maid.isOwnedBy(player) && !maid.backpackHasDelay() && backpack != BackpackManager.getEmptyBackpack() && backpack != maidBackpack) { + maid.setBackpackDelay(); + BackpackManager.addBackpackCooldown(player); + ItemHandlerHelper.giveItemToPlayer(player, maidBackpack.getTakeOffItemStack(stack, player, maid)); + maidBackpack.onTakeOff(stack, player, maid); + maid.setMaidBackpackType(backpack); + stack.shrink(1); + backpack.onPutOn(stack, player, maid); + maid.playSound(SoundEvents.HORSE_SADDLE, 0.5F, 1.0F); + event.setCanceled(true); + } + }); + } + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/event/maid/MaidMountEvent.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/event/maid/MaidMountEvent.java index bc7f3d3c1..07f66f7f6 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/event/maid/MaidMountEvent.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/event/maid/MaidMountEvent.java @@ -1,5 +1,6 @@ package com.github.tartaricacid.touhoulittlemaid.event.maid; +import com.github.tartaricacid.touhoulittlemaid.entity.item.EntitySit; import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; import net.minecraftforge.event.entity.EntityMountEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; @@ -9,7 +10,7 @@ public final class MaidMountEvent { @SubscribeEvent public static void onMaidMount(EntityMountEvent event) { - if (event.isMounting() && event.getEntityMounting() instanceof EntityMaid) { + if (event.isMounting() && event.getEntityMounting() instanceof EntityMaid && !(event.getEntityBeingMounted() instanceof EntitySit)) { EntityMaid maid = (EntityMaid) event.getEntityMounting(); if (!maid.isRideable()) { event.setCanceled(true); diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/event/maid/SlabClickEvent.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/event/maid/SlabClickEvent.java index bc1c3e279..ba407ec29 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/event/maid/SlabClickEvent.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/event/maid/SlabClickEvent.java @@ -26,9 +26,6 @@ public static void onInteract(InteractMaidEvent event) { ItemStack output = maidSmartSlab.getDefaultInstance(); maid.setHomeModeEnable(false); ItemSmartSlab.storeMaidData(output, maid); - if (maid.hasCustomName()) { - output.setHoverName(maid.getCustomName()); - } maid.spawnExplosionParticle(); maid.discard(); maid.playSound(SoundEvents.PLAYER_SPLASH, 1.0F, maid.level.random.nextFloat() * 0.1F + 0.9F); diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/event/maid/TakeOffBackpackEvent.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/event/maid/TakeOffBackpackEvent.java deleted file mode 100644 index 71cbdfeb3..000000000 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/event/maid/TakeOffBackpackEvent.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.github.tartaricacid.touhoulittlemaid.event.maid; - -import com.github.tartaricacid.touhoulittlemaid.api.event.InteractMaidEvent; -import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; -import com.github.tartaricacid.touhoulittlemaid.item.BackpackLevel; -import com.github.tartaricacid.touhoulittlemaid.item.ItemMaidBackpack; -import com.github.tartaricacid.touhoulittlemaid.util.ItemsUtil; -import net.minecraft.sounds.SoundEvents; -import net.minecraft.world.InteractionHand; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.ItemStack; -import net.minecraftforge.common.Tags; -import net.minecraftforge.eventbus.api.SubscribeEvent; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.items.ItemHandlerHelper; - -@Mod.EventBusSubscriber -public class TakeOffBackpackEvent { - @SubscribeEvent - public static void onInteractMaid(InteractMaidEvent event) { - ItemStack stack = event.getStack(); - Player player = event.getPlayer(); - EntityMaid maid = event.getMaid(); - int backpackLevel = maid.getBackpackLevel(); - - if (backpackLevel != BackpackLevel.EMPTY && stack.is(Tags.Items.SHEARS)) { - if (maid.backpackHasDelay()) { - return; - } - maid.setBackpackLevel(BackpackLevel.EMPTY); - maid.setBackpackDelay(); - player.getCooldowns().addCooldown(stack.getItem(), 20); - stack.hurtAndBreak(1, player, m -> m.broadcastBreakEvent(InteractionHand.MAIN_HAND)); - maid.playSound(SoundEvents.HORSE_SADDLE, 0.5F, 1.0F); - ItemMaidBackpack.getInstance(backpackLevel).ifPresent(backpack -> ItemHandlerHelper.giveItemToPlayer(player, backpack.getDefaultInstance())); - int minIndex = BackpackLevel.BACKPACK_CAPACITY_MAP.get(BackpackLevel.EMPTY); - int maxIndex = BackpackLevel.BACKPACK_CAPACITY_MAP.get(BackpackLevel.BIG); - ItemsUtil.dropEntityItems(maid, maid.getMaidInv(), minIndex, maxIndex); - event.setCanceled(true); - } - } -} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/event/maid/UseFavorabilityToolEvent.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/event/maid/UseFavorabilityToolEvent.java new file mode 100644 index 000000000..7de2c74aa --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/event/maid/UseFavorabilityToolEvent.java @@ -0,0 +1,40 @@ +package com.github.tartaricacid.touhoulittlemaid.event.maid; + +import com.github.tartaricacid.touhoulittlemaid.api.event.InteractMaidEvent; +import com.github.tartaricacid.touhoulittlemaid.entity.favorability.FavorabilityManager; +import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; +import com.github.tartaricacid.touhoulittlemaid.init.InitItems; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; + +@Mod.EventBusSubscriber +public class UseFavorabilityToolEvent { + @SubscribeEvent + public static void onInteract(InteractMaidEvent event) { + EntityMaid maid = event.getMaid(); + FavorabilityManager manager = maid.getFavorabilityManager(); + ItemStack stack = event.getStack(); + Player player = event.getPlayer(); + int point = player.isShiftKeyDown() ? 1 : 64; + boolean success = false; + if (stack.getItem() == InitItems.FAVORABILITY_TOOL_ADD.get()) { + manager.add(point); + success = true; + } + if (stack.getItem() == InitItems.FAVORABILITY_TOOL_REDUCE.get()) { + manager.reduceWithoutLevel(point); + success = true; + } + if (stack.getItem() == InitItems.FAVORABILITY_TOOL_FULL.get()) { + manager.max(); + success = true; + } + if (success) { + maid.playSound(SoundEvents.PLAYER_LEVELUP, 0.5F, maid.getRandom().nextFloat() * 0.1F + 0.9F); + event.setCanceled(true); + } + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/init/InitBlocks.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/init/InitBlocks.java index 1b410f4c6..44e126ec9 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/init/InitBlocks.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/init/InitBlocks.java @@ -19,10 +19,20 @@ public final class InitBlocks { public static RegistryObject GARAGE_KIT = BLOCKS.register("garage_kit", BlockGarageKit::new); public static RegistryObject MAID_BEACON = BLOCKS.register("maid_beacon", BlockMaidBeacon::new); public static RegistryObject MODEL_SWITCHER = BLOCKS.register("model_switcher", BlockModelSwitcher::new); + public static RegistryObject GOMOKU = BLOCKS.register("gomoku", BlockGomoku::new); + public static RegistryObject KEYBOARD = BLOCKS.register("keyboard", BlockKeyboard::new); + public static RegistryObject BOOKSHELF = BLOCKS.register("bookshelf", BlockBookshelf::new); + public static RegistryObject COMPUTER = BLOCKS.register("computer", BlockComputer::new); + public static RegistryObject SHRINE = BLOCKS.register("shrine", BlockShrine::new); public static RegistryObject> ALTAR_TE = TILE_ENTITIES.register("altar", () -> TileEntityAltar.TYPE); public static RegistryObject> STATUE_TE = TILE_ENTITIES.register("statue", () -> TileEntityStatue.TYPE); public static RegistryObject> GARAGE_KIT_TE = TILE_ENTITIES.register("garage_kit", () -> TileEntityGarageKit.TYPE); public static RegistryObject> MAID_BEACON_TE = TILE_ENTITIES.register("maid_beacon", () -> TileEntityMaidBeacon.TYPE); public static RegistryObject> MODEL_SWITCHER_TE = TILE_ENTITIES.register("model_switcher", () -> TileEntityModelSwitcher.TYPE); + public static RegistryObject> GOMOKU_TE = TILE_ENTITIES.register("gomoku", () -> TileEntityGomoku.TYPE); + public static RegistryObject> KEYBOARD_TE = TILE_ENTITIES.register("keyboard", () -> TileEntityKeyboard.TYPE); + public static RegistryObject> BOOKSHELF_TE = TILE_ENTITIES.register("bookshelf", () -> TileEntityBookshelf.TYPE); + public static RegistryObject> COMPUTER_TE = TILE_ENTITIES.register("computer", () -> TileEntityComputer.TYPE); + public static RegistryObject> SHRINE_TE = TILE_ENTITIES.register("shrine", () -> TileEntityShrine.TYPE); } diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/init/InitContainer.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/init/InitContainer.java index 113c8ddbf..93f1429cb 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/init/InitContainer.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/init/InitContainer.java @@ -2,8 +2,8 @@ import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; import com.github.tartaricacid.touhoulittlemaid.inventory.container.MaidConfigContainer; -import com.github.tartaricacid.touhoulittlemaid.inventory.container.MaidMainContainer; import com.github.tartaricacid.touhoulittlemaid.inventory.container.WirelessIOContainer; +import com.github.tartaricacid.touhoulittlemaid.inventory.container.backpack.*; import net.minecraft.world.inventory.MenuType; import net.minecraftforge.registries.DeferredRegister; import net.minecraftforge.registries.ForgeRegistries; @@ -12,7 +12,14 @@ public final class InitContainer { public static final DeferredRegister> CONTAINER_TYPE = DeferredRegister.create(ForgeRegistries.CONTAINERS, TouhouLittleMaid.MOD_ID); - public static final RegistryObject> MAID_MAIN_CONTAINER = CONTAINER_TYPE.register("maid_main_container", () -> MaidMainContainer.TYPE); + public static final RegistryObject> MAID_EMPTY_BACKPACK_CONTAINER = CONTAINER_TYPE.register("maid_empty_backpack_container", () -> EmptyBackpackContainer.TYPE); + public static final RegistryObject> MAID_SMALL_BACKPACK_CONTAINER = CONTAINER_TYPE.register("maid_small_backpack_container", () -> SmallBackpackContainer.TYPE); + public static final RegistryObject> MAID_MIDDLE_BACKPACK_CONTAINER = CONTAINER_TYPE.register("maid_middle_backpack_container", () -> MiddleBackpackContainer.TYPE); + public static final RegistryObject> MAID_BIG_BACKPACK_CONTAINER = CONTAINER_TYPE.register("maid_big_backpack_container", () -> BigBackpackContainer.TYPE); + public static final RegistryObject> MAID_CRAFTING_TABLE_BACKPACK_CONTAINER = CONTAINER_TYPE.register("maid_crafting_table_backpack_container", () -> CraftingTableBackpackContainer.TYPE); + public static final RegistryObject> MAID_ENDER_CHEST_CONTAINER = CONTAINER_TYPE.register("maid_ender_chest_container", () -> EnderChestBackpackContainer.TYPE); + public static final RegistryObject> MAID_FURNACE_CONTAINER = CONTAINER_TYPE.register("maid_furnace_container", () -> FurnaceBackpackContainer.TYPE); + public static final RegistryObject> MAID_CONFIG_CONTAINER = CONTAINER_TYPE.register("maid_config_container", () -> MaidConfigContainer.TYPE); public static final RegistryObject> WIRELESS_IO_CONTAINER = CONTAINER_TYPE.register("wireless_io_container", () -> WirelessIOContainer.TYPE); } diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/init/InitEntities.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/init/InitEntities.java index fc6711251..1cd490f4d 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/init/InitEntities.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/init/InitEntities.java @@ -5,10 +5,7 @@ import com.github.tartaricacid.touhoulittlemaid.entity.ai.brain.sensor.MaidHostilesSensor; import com.github.tartaricacid.touhoulittlemaid.entity.ai.brain.sensor.MaidPickupEntitiesSensor; import com.github.tartaricacid.touhoulittlemaid.entity.chatbubble.MaidChatBubbles; -import com.github.tartaricacid.touhoulittlemaid.entity.item.EntityBox; -import com.github.tartaricacid.touhoulittlemaid.entity.item.EntityChair; -import com.github.tartaricacid.touhoulittlemaid.entity.item.EntityExtinguishingAgent; -import com.github.tartaricacid.touhoulittlemaid.entity.item.EntityPowerPoint; +import com.github.tartaricacid.touhoulittlemaid.entity.item.*; import com.github.tartaricacid.touhoulittlemaid.entity.monster.EntityFairy; import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; import com.github.tartaricacid.touhoulittlemaid.entity.projectile.EntityDanmaku; @@ -55,6 +52,8 @@ public final class InitEntities { public static RegistryObject> EXTINGUISHING_AGENT = ENTITY_TYPES.register("extinguishing_agent", () -> EntityExtinguishingAgent.TYPE); public static RegistryObject> BOX = ENTITY_TYPES.register("box", () -> EntityBox.TYPE); public static RegistryObject> THROW_POWER_POINT = ENTITY_TYPES.register("throw_power_point", () -> EntityThrowPowerPoint.TYPE); + public static RegistryObject> TOMBSTONE = ENTITY_TYPES.register("tombstone", () -> EntityTombstone.TYPE); + public static RegistryObject> SIT = ENTITY_TYPES.register("sit", () -> EntitySit.TYPE); public static RegistryObject>> VISIBLE_PICKUP_ENTITIES = MEMORY_MODULE_TYPES.register("visible_pickup_entities", () -> new MemoryModuleType<>(Optional.empty())); public static RegistryObject> TARGET_POS = MEMORY_MODULE_TYPES.register("target_pos", () -> new MemoryModuleType<>(Optional.empty())); diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/init/InitItems.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/init/InitItems.java index 2aa3a6b51..50eb557c0 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/init/InitItems.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/init/InitItems.java @@ -4,6 +4,7 @@ import com.github.tartaricacid.touhoulittlemaid.entity.monster.EntityFairy; import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; import com.github.tartaricacid.touhoulittlemaid.item.*; +import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.Item; import net.minecraftforge.common.ForgeSpawnEggItem; import net.minecraftforge.registries.DeferredRegister; @@ -15,9 +16,12 @@ public final class InitItems { public static final DeferredRegister ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, TouhouLittleMaid.MOD_ID); - public static RegistryObject MAID_BACKPACK_SMALL = ITEMS.register("maid_backpack_small", () -> new ItemMaidBackpack(BackpackLevel.SMALL)); - public static RegistryObject MAID_BACKPACK_MIDDLE = ITEMS.register("maid_backpack_middle", () -> new ItemMaidBackpack(BackpackLevel.MIDDLE)); - public static RegistryObject MAID_BACKPACK_BIG = ITEMS.register("maid_backpack_big", () -> new ItemMaidBackpack(BackpackLevel.BIG)); + public static RegistryObject MAID_BACKPACK_SMALL = ITEMS.register("maid_backpack_small", ItemMaidBackpack::new); + public static RegistryObject MAID_BACKPACK_MIDDLE = ITEMS.register("maid_backpack_middle", ItemMaidBackpack::new); + public static RegistryObject MAID_BACKPACK_BIG = ITEMS.register("maid_backpack_big", ItemMaidBackpack::new); + public static RegistryObject CRAFTING_TABLE_BACKPACK = ITEMS.register("crafting_table_backpack", ItemMaidBackpack::new); + public static RegistryObject ENDER_CHEST_BACKPACK = ITEMS.register("ender_chest_backpack", ItemMaidBackpack::new); + public static RegistryObject FURNACE_BACKPACK = ITEMS.register("furnace_backpack", ItemMaidBackpack::new); public static RegistryObject CHAIR = ITEMS.register("chair", ItemChair::new); public static RegistryObject HAKUREI_GOHEI = ITEMS.register("hakurei_gohei", ItemHakureiGohei::new); public static RegistryObject MAID_BED = ITEMS.register("maid_bed", ItemMaidBed::new); @@ -48,6 +52,16 @@ public final class InitItems { public static RegistryObject MAID_BEACON = ITEMS.register("maid_beacon", ItemMaidBeacon::new); public static RegistryObject MODEL_SWITCHER = ITEMS.register("model_switcher", ItemModelSwitcher::new); public static RegistryObject CHAIR_SHOW = ITEMS.register("chair_show", ItemChairShow::new); + public static RegistryObject RED_FOX_SCROLL = ITEMS.register("red_fox_scroll", ItemFoxScroll::new); + public static RegistryObject WHITE_FOX_SCROLL = ITEMS.register("white_fox_scroll", ItemFoxScroll::new); + public static RegistryObject GOMOKU = ITEMS.register("gomoku", () -> new BlockItem(InitBlocks.GOMOKU.get(), new Item.Properties().tab(MAIN_TAB))); + public static RegistryObject KEYBOARD = ITEMS.register("keyboard", () -> new BlockItem(InitBlocks.KEYBOARD.get(), new Item.Properties().tab(MAIN_TAB))); + public static RegistryObject BOOKSHELF = ITEMS.register("bookshelf", () -> new BlockItem(InitBlocks.BOOKSHELF.get(), new Item.Properties().tab(MAIN_TAB))); + public static RegistryObject COMPUTER = ITEMS.register("computer", () -> new BlockItem(InitBlocks.COMPUTER.get(), new Item.Properties().tab(MAIN_TAB))); + public static RegistryObject FAVORABILITY_TOOL_ADD = ITEMS.register("favorability_tool_add", () -> new ItemFavorabilityTool("add")); + public static RegistryObject FAVORABILITY_TOOL_REDUCE = ITEMS.register("favorability_tool_reduce", () -> new ItemFavorabilityTool("reduce")); + public static RegistryObject FAVORABILITY_TOOL_FULL = ITEMS.register("favorability_tool_full", () -> new ItemFavorabilityTool("full")); + public static RegistryObject SHRINE = ITEMS.register("shrine", () -> new BlockItem(InitBlocks.SHRINE.get(), new Item.Properties().tab(MAIN_TAB))); public static RegistryObject MAID_SPAWN_EGG = ITEMS.register("maid_spawn_egg", () -> new ForgeSpawnEggItem(() -> EntityMaid.TYPE, 0x4a6195, 0xffffff, (new Item.Properties()).tab(MAIN_TAB))); public static RegistryObject FAIRY_SPAWN_EGG = ITEMS.register("fairy_spawn_egg", () -> new ForgeSpawnEggItem(() -> EntityFairy.TYPE, 0x171c20, 0xffffff, (new Item.Properties()).tab(MAIN_TAB))); diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/init/InitSounds.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/init/InitSounds.java index bcde68304..0de2a68a4 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/init/InitSounds.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/init/InitSounds.java @@ -44,6 +44,8 @@ public final class InitSounds { public static final RegistryObject ALTAR_CRAFT = registerSound("block.altar_craft"); public static final RegistryObject BOX_OPEN = registerSound("entity.box"); public static final RegistryObject COMPASS_POINT = registerSound("item.compass"); + public static final RegistryObject GOMOKU = registerSound("block.gomoku"); + public static final RegistryObject GOMOKU_RESET = registerSound("block.gomoku_reset"); private static RegistryObject registerSound(String name) { return SOUNDS.register(name, () -> new SoundEvent(new ResourceLocation(TouhouLittleMaid.MOD_ID, name))); diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/init/registry/CommonRegistry.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/init/registry/CommonRegistry.java index e5c8a7378..fd6f348c0 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/init/registry/CommonRegistry.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/init/registry/CommonRegistry.java @@ -1,5 +1,6 @@ package com.github.tartaricacid.touhoulittlemaid.init.registry; +import com.github.tartaricacid.touhoulittlemaid.entity.backpack.BackpackManager; import com.github.tartaricacid.touhoulittlemaid.entity.info.ServerCustomPackLoader; import com.github.tartaricacid.touhoulittlemaid.network.NetworkHandler; import net.minecraftforge.eventbus.api.SubscribeEvent; @@ -13,5 +14,6 @@ public static void onSetupEvent(FMLCommonSetupEvent event) { event.enqueueWork(ServerCustomPackLoader::reloadPacks); event.enqueueWork(CommandRegistry::registerArgumentTypes); event.enqueueWork(NetworkHandler::init); + event.enqueueWork(BackpackManager::initItemIndex); } } \ No newline at end of file diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/init/registry/MobSpawnInfoRegistry.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/init/registry/MobSpawnInfoRegistry.java index b89718f94..e6b4f2799 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/init/registry/MobSpawnInfoRegistry.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/init/registry/MobSpawnInfoRegistry.java @@ -2,26 +2,28 @@ import com.github.tartaricacid.touhoulittlemaid.config.subconfig.MiscConfig; import com.github.tartaricacid.touhoulittlemaid.init.InitEntities; +import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.MobCategory; -import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.biome.MobSpawnSettings; import net.minecraftforge.event.world.BiomeLoadingEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; -import static com.github.tartaricacid.touhoulittlemaid.config.subconfig.MiscConfig.MAID_FAIRY_BLACKLIST_BIOME; - +import java.util.List; @Mod.EventBusSubscriber public final class MobSpawnInfoRegistry { + private static MobSpawnSettings.SpawnerData SPAWNER_DATA; + @SubscribeEvent public static void addMobSpawnInfo(BiomeLoadingEvent event) { - if (biomeIsOkay(event.getCategory())) { - event.getSpawns().addSpawn(MobCategory.MONSTER, new MobSpawnSettings.SpawnerData(InitEntities.FAIRY.get(), MiscConfig.MAID_FAIRY_SPAWN_PROBABILITY.get(), 2, 6)); + List spawnerData = event.getSpawns().getSpawner(MobCategory.MONSTER); + boolean canZombieSpawn = spawnerData.stream().anyMatch(data -> data.type.equals(EntityType.ZOMBIE)); + if (SPAWNER_DATA == null) { + SPAWNER_DATA = new MobSpawnSettings.SpawnerData(InitEntities.FAIRY.get(), MiscConfig.MAID_FAIRY_SPAWN_PROBABILITY.get(), 2, 4); + } + if (canZombieSpawn) { + event.getSpawns().addSpawn(MobCategory.MONSTER, SPAWNER_DATA); } - } - - private static boolean biomeIsOkay(Biome.BiomeCategory category) { - return !MAID_FAIRY_BLACKLIST_BIOME.get().contains(category.getName()); } } diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/inventory/container/MaidMainContainer.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/inventory/container/MaidMainContainer.java index d5313a741..8c3eb5bd2 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/inventory/container/MaidMainContainer.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/inventory/container/MaidMainContainer.java @@ -2,13 +2,9 @@ import com.github.tartaricacid.touhoulittlemaid.client.event.ReloadResourceEvent; import com.github.tartaricacid.touhoulittlemaid.inventory.handler.BaubleItemHandler; -import com.github.tartaricacid.touhoulittlemaid.item.BackpackLevel; import com.mojang.datafixers.util.Pair; import net.minecraft.core.Direction; -import net.minecraft.network.chat.Component; -import net.minecraft.network.chat.TextComponent; import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.MenuProvider; import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Player; @@ -18,7 +14,6 @@ import net.minecraft.world.item.enchantment.EnchantmentHelper; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; -import net.minecraftforge.common.extensions.IForgeMenuType; import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.items.CapabilityItemHandler; import net.minecraftforge.items.IItemHandler; @@ -30,46 +25,32 @@ import static net.minecraft.world.inventory.InventoryMenu.*; -public class MaidMainContainer extends AbstractMaidContainer { - public static final MenuType TYPE = IForgeMenuType.create((windowId, inv, data) -> new MaidMainContainer(windowId, inv, data.readInt())); +public abstract class MaidMainContainer extends AbstractMaidContainer { + protected static final int PLAYER_INVENTORY_SIZE = 36; private static final ResourceLocation[] TEXTURE_EMPTY_SLOTS = new ResourceLocation[]{EMPTY_ARMOR_SLOT_BOOTS, EMPTY_ARMOR_SLOT_LEGGINGS, EMPTY_ARMOR_SLOT_CHESTPLATE, EMPTY_ARMOR_SLOT_HELMET}; private static final EquipmentSlot[] SLOT_IDS = new EquipmentSlot[]{EquipmentSlot.HEAD, EquipmentSlot.CHEST, EquipmentSlot.LEGS, EquipmentSlot.FEET}; - private static final int PLAYER_INVENTORY_SIZE = 36; - public MaidMainContainer(int id, Inventory inventory, int entityId) { - super(TYPE, id, inventory, entityId); + public MaidMainContainer(MenuType type, int id, Inventory inventory, int entityId) { + super(type, id, inventory, entityId); if (maid != null) { this.addMaidArmorInv(); this.addMaidBauble(); this.addMaidHandInv(); - this.addMainInv(); + this.addMainDefaultInv(); + this.addBackpackInv(inventory); } } - public static MenuProvider create(int entityId) { - return new MenuProvider() { - @Override - public Component getDisplayName() { - return new TextComponent("Maid Main Container"); - } - - @Override - public AbstractMaidContainer createMenu(int index, Inventory playerInventory, Player player) { - return new MaidMainContainer(index, playerInventory, entityId); - } - }; - } - private void addMaidHandInv() { LazyOptional hand = maid.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, Direction.DOWN); - hand.ifPresent((handler) -> addSlot(new SlotItemHandler(handler, 0, 87, 79) { + hand.ifPresent((handler) -> addSlot(new SlotItemHandler(handler, 0, 87, 77) { @Override @OnlyIn(Dist.CLIENT) public Pair getNoItemIcon() { return Pair.of(BLOCK_ATLAS, ReloadResourceEvent.EMPTY_MAINHAND_SLOT); } })); - hand.ifPresent((handler) -> addSlot(new SlotItemHandler(handler, 1, 121, 79) { + hand.ifPresent((handler) -> addSlot(new SlotItemHandler(handler, 1, 121, 77) { @Override @OnlyIn(Dist.CLIENT) public Pair getNoItemIcon() { @@ -113,10 +94,8 @@ public Pair getNoItemIcon() { })); } - private void addMainInv() { + private void addMainDefaultInv() { ItemStackHandler inv = maid.getMaidInv(); - int level = maid.getBackpackLevel(); - // 默认背包 for (int i = 0; i < 6; i++) { addSlot(new SlotItemHandler(inv, i, 143 + 18 * i, 37)); @@ -130,43 +109,15 @@ public Pair getNoItemIcon() { }); } } - - if (level > BackpackLevel.EMPTY) { - for (int i = 0; i < 6; i++) { - addSlot(new SlotItemHandler(inv, 6 + i, 143 + 18 * i, 59)); - } - } - - if (level > BackpackLevel.SMALL) { - for (int i = 0; i < 6; i++) { - addSlot(new SlotItemHandler(inv, 12 + i, 143 + 18 * i, 82)); - } - for (int i = 0; i < 6; i++) { - addSlot(new SlotItemHandler(inv, 18 + i, 143 + 18 * i, 100)); - } - } - - if (level > BackpackLevel.MIDDLE) { - for (int i = 0; i < 6; i++) { - addSlot(new SlotItemHandler(inv, 24 + i, 143 + 18 * i, 123)); - } - for (int i = 0; i < 6; i++) { - addSlot(new SlotItemHandler(inv, 30 + i, 143 + 18 * i, 141)); - } - } } + protected abstract void addBackpackInv(Inventory inventory); + private void addMaidBauble() { BaubleItemHandler maidBauble = maid.getMaidBauble(); for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { - addSlot(new SlotItemHandler(maidBauble, i * 3 + j, 86 + 18 * j, 105 + 18 * i) { - @Override - @OnlyIn(Dist.CLIENT) - public Pair getNoItemIcon() { - return Pair.of(BLOCK_ATLAS, ReloadResourceEvent.EMPTY_BAUBLE_SLOT); - } - }); + addSlot(new SlotItemHandler(maidBauble, i * 3 + j, 86 + 18 * j, 99 + 18 * i)); } } } diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/inventory/container/backpack/BigBackpackContainer.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/inventory/container/backpack/BigBackpackContainer.java new file mode 100644 index 000000000..45b78d374 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/inventory/container/backpack/BigBackpackContainer.java @@ -0,0 +1,36 @@ +package com.github.tartaricacid.touhoulittlemaid.inventory.container.backpack; + +import com.github.tartaricacid.touhoulittlemaid.inventory.container.MaidMainContainer; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.inventory.MenuType; +import net.minecraftforge.common.extensions.IForgeMenuType; +import net.minecraftforge.items.IItemHandler; +import net.minecraftforge.items.SlotItemHandler; + +public class BigBackpackContainer extends MaidMainContainer { + public static final MenuType TYPE = IForgeMenuType.create((windowId, inv, data) -> new BigBackpackContainer(windowId, inv, data.readInt())); + + public BigBackpackContainer(int id, Inventory inventory, int entityId) { + super(TYPE, id, inventory, entityId); + } + + @Override + protected void addBackpackInv(Inventory inventory) { + IItemHandler itemHandler = maid.getMaidInv(); + for (int i = 0; i < 6; i++) { + addSlot(new SlotItemHandler(itemHandler, 6 + i, 143 + 18 * i, 59)); + } + for (int i = 0; i < 6; i++) { + addSlot(new SlotItemHandler(itemHandler, 12 + i, 143 + 18 * i, 82)); + } + for (int i = 0; i < 6; i++) { + addSlot(new SlotItemHandler(itemHandler, 18 + i, 143 + 18 * i, 100)); + } + for (int i = 0; i < 6; i++) { + addSlot(new SlotItemHandler(itemHandler, 24 + i, 143 + 18 * i, 123)); + } + for (int i = 0; i < 6; i++) { + addSlot(new SlotItemHandler(itemHandler, 30 + i, 143 + 18 * i, 141)); + } + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/inventory/container/backpack/CraftingTableBackpackContainer.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/inventory/container/backpack/CraftingTableBackpackContainer.java new file mode 100644 index 000000000..3b69360b8 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/inventory/container/backpack/CraftingTableBackpackContainer.java @@ -0,0 +1,124 @@ +package com.github.tartaricacid.touhoulittlemaid.inventory.container.backpack; + +import com.github.tartaricacid.touhoulittlemaid.inventory.container.MaidMainContainer; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.Container; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.*; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.CraftingRecipe; +import net.minecraft.world.item.crafting.RecipeType; +import net.minecraft.world.level.Level; +import net.minecraftforge.common.extensions.IForgeMenuType; +import net.minecraftforge.items.IItemHandler; +import net.minecraftforge.items.SlotItemHandler; + +import java.util.Optional; + +public class CraftingTableBackpackContainer extends MaidMainContainer { + public static final MenuType TYPE = IForgeMenuType.create((windowId, inv, data) -> new CraftingTableBackpackContainer(windowId, inv, data.readInt())); + private final CraftingContainer craftSlots = new CraftingContainer(this, 3, 3); + private final ResultContainer resultSlots = new ResultContainer(); + private final ContainerLevelAccess access; + private final ResultSlot resultSlot; + private final Player player; + + public CraftingTableBackpackContainer(int id, Inventory inventory, int entityId) { + super(TYPE, id, inventory, entityId); + this.player = inventory.player; + this.access = ContainerLevelAccess.create(this.getMaid().level, this.getMaid().blockPosition()); + this.resultSlot = new ResultSlot(this.player, this.craftSlots, this.resultSlots, 0, 229, 119); + this.addSlot(this.resultSlot); + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 3; ++j) { + this.addSlot(new Slot(this.craftSlots, j + i * 3, 152 + j * 18, 101 + i * 18)); + } + } + } + + @Override + public void slotsChanged(Container container) { + this.access.execute((level, blockPos) -> slotChangedCraftingGrid(this, level, this.player, this.craftSlots, this.resultSlots)); + } + + @Override + public void removed(Player player) { + super.removed(player); + this.access.execute((level, blockPos) -> this.clearContainer(player, this.craftSlots)); + } + + @Override + public boolean canTakeItemForPickAll(ItemStack stack, Slot slot) { + return slot.container != this.resultSlots && super.canTakeItemForPickAll(stack, slot); + } + + @Override + public ItemStack quickMoveStack(Player player, int index) { + ItemStack stack1 = ItemStack.EMPTY; + Slot slot = this.slots.get(index); + if (slot != null && slot.hasItem()) { + ItemStack stack2 = slot.getItem(); + stack1 = stack2.copy(); + if (index == resultSlot.index) { + this.access.execute((level, blockPos) -> stack2.getItem().onCraftedBy(stack2, level, player)); + if (!this.moveItemStackTo(stack2, 0, PLAYER_INVENTORY_SIZE, true)) { + return ItemStack.EMPTY; + } + slot.onQuickCraft(stack2, stack1); + } else if (index < resultSlot.index) { + if (index < PLAYER_INVENTORY_SIZE) { + if (!this.moveItemStackTo(stack2, PLAYER_INVENTORY_SIZE, resultSlot.index, false)) { + return ItemStack.EMPTY; + } + } else if (!this.moveItemStackTo(stack2, 0, PLAYER_INVENTORY_SIZE, true)) { + return ItemStack.EMPTY; + } + } else if (!this.moveItemStackTo(stack2, 0, PLAYER_INVENTORY_SIZE, false)) { + return ItemStack.EMPTY; + } + if (stack2.isEmpty()) { + slot.set(ItemStack.EMPTY); + } else { + slot.setChanged(); + } + if (stack2.getCount() == stack1.getCount()) { + return ItemStack.EMPTY; + } + slot.onTake(player, stack2); + if (index == resultSlot.index) { + player.drop(stack2, false); + } + } + return stack1; + } + + @Override + protected void addBackpackInv(Inventory inventory) { + IItemHandler itemHandler = maid.getMaidInv(); + for (int i = 0; i < 6; i++) { + addSlot(new SlotItemHandler(itemHandler, 6 + i, 143 + 18 * i, 57)); + } + for (int i = 0; i < 6; i++) { + addSlot(new SlotItemHandler(itemHandler, 12 + i, 143 + 18 * i, 75)); + } + } + + private void slotChangedCraftingGrid(AbstractContainerMenu menu, Level level, Player player, CraftingContainer container, ResultContainer result) { + if (!level.isClientSide && level.getServer() != null) { + ServerPlayer serverPlayer = (ServerPlayer) player; + ItemStack itemStack = ItemStack.EMPTY; + Optional optional = level.getServer().getRecipeManager().getRecipeFor(RecipeType.CRAFTING, container, level); + if (optional.isPresent()) { + CraftingRecipe recipe = optional.get(); + if (result.setRecipeUsed(level, serverPlayer, recipe)) { + itemStack = recipe.assemble(container); + } + } + result.setItem(0, itemStack); + menu.setRemoteSlot(0, itemStack); + serverPlayer.connection.send(new ClientboundContainerSetSlotPacket(menu.containerId, menu.incrementStateId(), resultSlot.index, itemStack)); + } + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/inventory/container/backpack/EmptyBackpackContainer.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/inventory/container/backpack/EmptyBackpackContainer.java new file mode 100644 index 000000000..9f4b2ea90 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/inventory/container/backpack/EmptyBackpackContainer.java @@ -0,0 +1,18 @@ +package com.github.tartaricacid.touhoulittlemaid.inventory.container.backpack; + +import com.github.tartaricacid.touhoulittlemaid.inventory.container.MaidMainContainer; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.inventory.MenuType; +import net.minecraftforge.common.extensions.IForgeMenuType; + +public class EmptyBackpackContainer extends MaidMainContainer { + public static final MenuType TYPE = IForgeMenuType.create((windowId, inv, data) -> new EmptyBackpackContainer(windowId, inv, data.readInt())); + + public EmptyBackpackContainer(int id, Inventory inventory, int entityId) { + super(TYPE, id, inventory, entityId); + } + + @Override + protected void addBackpackInv(Inventory inventory) { + } +} \ No newline at end of file diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/inventory/container/backpack/EnderChestBackpackContainer.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/inventory/container/backpack/EnderChestBackpackContainer.java new file mode 100644 index 000000000..7949e2d75 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/inventory/container/backpack/EnderChestBackpackContainer.java @@ -0,0 +1,31 @@ +package com.github.tartaricacid.touhoulittlemaid.inventory.container.backpack; + +import com.github.tartaricacid.touhoulittlemaid.inventory.container.MaidMainContainer; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.PlayerEnderChestContainer; +import net.minecraft.world.inventory.Slot; +import net.minecraftforge.common.extensions.IForgeMenuType; + +public class EnderChestBackpackContainer extends MaidMainContainer { + public static final MenuType TYPE = IForgeMenuType.create((windowId, inv, data) -> new EnderChestBackpackContainer(windowId, inv, data.readInt())); + + public EnderChestBackpackContainer(int id, Inventory inventory, int entityId) { + super(TYPE, id, inventory, entityId); + } + + @Override + protected void addBackpackInv(Inventory inventory) { + PlayerEnderChestContainer enderChestContainer = inventory.player.getEnderChestInventory(); + for (int i = 0; i < 6; i++) { + addSlot(new Slot(enderChestContainer, i, 143 + 18 * i, 61)); + } + for (int j = 0; j < 3; j++) { + for (int i = 0; i < 5; i++) { + addSlot(new Slot(enderChestContainer, 6 + 5 * j + i, 161 + 18 * i, 79 + j * 18)); } + } + for (int i = 0; i < 6; i++) { + addSlot(new Slot(enderChestContainer, 21 + i, 143 + 18 * i, 133)); + } + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/inventory/container/backpack/FurnaceBackpackContainer.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/inventory/container/backpack/FurnaceBackpackContainer.java new file mode 100644 index 000000000..41b50f79a --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/inventory/container/backpack/FurnaceBackpackContainer.java @@ -0,0 +1,91 @@ +package com.github.tartaricacid.touhoulittlemaid.inventory.container.backpack; + +import com.github.tartaricacid.touhoulittlemaid.entity.backpack.data.FurnaceBackpackData; +import com.github.tartaricacid.touhoulittlemaid.inventory.container.MaidMainContainer; +import net.minecraft.world.Container; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.inventory.ContainerData; +import net.minecraft.world.inventory.FurnaceResultSlot; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.crafting.RecipeType; +import net.minecraftforge.common.ForgeHooks; +import net.minecraftforge.common.extensions.IForgeMenuType; +import net.minecraftforge.items.IItemHandler; +import net.minecraftforge.items.SlotItemHandler; + +public class FurnaceBackpackContainer extends MaidMainContainer { + public static final MenuType TYPE = IForgeMenuType.create((windowId, inv, data) -> new FurnaceBackpackContainer(windowId, inv, data.readInt())); + private final ContainerData data; + + public FurnaceBackpackContainer(int id, Inventory inventory, int entityId) { + super(TYPE, id, inventory, entityId); + FurnaceBackpackData furnaceData; + if (this.getMaid().getBackpackData() instanceof FurnaceBackpackData) { + furnaceData = (FurnaceBackpackData) this.getMaid().getBackpackData(); + } else { + furnaceData = new FurnaceBackpackData(this.getMaid()); + } + this.data = furnaceData.getDataAccess(); + this.addSlot(new Slot(furnaceData, 0, 161, 101)); + this.addSlot(new FurnaceBackpackFuelSlot(this, furnaceData, 1, 161, 142)); + this.addSlot(new FurnaceResultSlot(inventory.player, furnaceData, 2, 221, 121)); + this.addDataSlots(this.data); + } + + @Override + protected void addBackpackInv(Inventory inventory) { + IItemHandler itemHandler = maid.getMaidInv(); + for (int i = 0; i < 6; i++) { + addSlot(new SlotItemHandler(itemHandler, 6 + i, 143 + 18 * i, 57)); + } + for (int i = 0; i < 6; i++) { + addSlot(new SlotItemHandler(itemHandler, 12 + i, 143 + 18 * i, 75)); + } + } + + private boolean isFuel(ItemStack stack) { + return ForgeHooks.getBurnTime(stack, RecipeType.SMELTING) > 0; + } + + public int getBurnProgress() { + int cookingProgress = this.data.get(2); + int cookingTotalTime = this.data.get(3); + return cookingTotalTime != 0 && cookingProgress != 0 ? cookingProgress * 24 / cookingTotalTime : 0; + } + + public int getLitProgress() { + int litDuration = this.data.get(1); + if (litDuration == 0) { + litDuration = 200; + } + return this.data.get(0) * 13 / litDuration; + } + + public boolean isLit() { + return this.data.get(0) > 0; + } + + public static class FurnaceBackpackFuelSlot extends Slot { + private final FurnaceBackpackContainer furnaceBackpackContainer; + + public FurnaceBackpackFuelSlot(FurnaceBackpackContainer furnaceBackpackContainer, Container container, int slot, int pX, int pY) { + super(container, slot, pX, pY); + this.furnaceBackpackContainer = furnaceBackpackContainer; + } + + public boolean mayPlace(ItemStack stack) { + return this.furnaceBackpackContainer.isFuel(stack) || isBucket(stack); + } + + public int getMaxStackSize(ItemStack stack) { + return isBucket(stack) ? 1 : super.getMaxStackSize(stack); + } + + public static boolean isBucket(ItemStack stack) { + return stack.is(Items.BUCKET); + } + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/inventory/container/backpack/MiddleBackpackContainer.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/inventory/container/backpack/MiddleBackpackContainer.java new file mode 100644 index 000000000..6b49b0afc --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/inventory/container/backpack/MiddleBackpackContainer.java @@ -0,0 +1,30 @@ +package com.github.tartaricacid.touhoulittlemaid.inventory.container.backpack; + +import com.github.tartaricacid.touhoulittlemaid.inventory.container.MaidMainContainer; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.inventory.MenuType; +import net.minecraftforge.common.extensions.IForgeMenuType; +import net.minecraftforge.items.IItemHandler; +import net.minecraftforge.items.SlotItemHandler; + +public class MiddleBackpackContainer extends MaidMainContainer { + public static final MenuType TYPE = IForgeMenuType.create((windowId, inv, data) -> new MiddleBackpackContainer(windowId, inv, data.readInt())); + + public MiddleBackpackContainer(int id, Inventory inventory, int entityId) { + super(TYPE, id, inventory, entityId); + } + + @Override + protected void addBackpackInv(Inventory inventory) { + IItemHandler itemHandler = maid.getMaidInv(); + for (int i = 0; i < 6; i++) { + addSlot(new SlotItemHandler(itemHandler, 6 + i, 143 + 18 * i, 59)); + } + for (int i = 0; i < 6; i++) { + addSlot(new SlotItemHandler(itemHandler, 12 + i, 143 + 18 * i, 82)); + } + for (int i = 0; i < 6; i++) { + addSlot(new SlotItemHandler(itemHandler, 18 + i, 143 + 18 * i, 100)); + } + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/inventory/container/backpack/SmallBackpackContainer.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/inventory/container/backpack/SmallBackpackContainer.java new file mode 100644 index 000000000..5b4d861d9 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/inventory/container/backpack/SmallBackpackContainer.java @@ -0,0 +1,22 @@ +package com.github.tartaricacid.touhoulittlemaid.inventory.container.backpack; + +import com.github.tartaricacid.touhoulittlemaid.inventory.container.MaidMainContainer; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.inventory.MenuType; +import net.minecraftforge.common.extensions.IForgeMenuType; +import net.minecraftforge.items.SlotItemHandler; + +public class SmallBackpackContainer extends MaidMainContainer { + public static final MenuType TYPE = IForgeMenuType.create((windowId, inv, data) -> new SmallBackpackContainer(windowId, inv, data.readInt())); + + public SmallBackpackContainer(int id, Inventory inventory, int entityId) { + super(TYPE, id, inventory, entityId); + } + + @Override + protected void addBackpackInv(Inventory inventory) { + for (int i = 0; i < 6; i++) { + addSlot(new SlotItemHandler(maid.getMaidInv(), 6 + i, 143 + 18 * i, 59)); + } + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/inventory/handler/MaidBackpackHandler.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/inventory/handler/MaidBackpackHandler.java index c7f6ffb23..4d44efc4d 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/inventory/handler/MaidBackpackHandler.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/inventory/handler/MaidBackpackHandler.java @@ -7,12 +7,23 @@ import javax.annotation.Nonnull; public class MaidBackpackHandler extends ItemStackHandler { - public MaidBackpackHandler(int size) { + public static final int BACKPACK_ITEM_SLOT = 5; + private final EntityMaid maid; + + public MaidBackpackHandler(int size, EntityMaid maid) { super(size); + this.maid = maid; } @Override public boolean isItemValid(int slot, @Nonnull ItemStack stack) { return EntityMaid.canInsertItem(stack); } + + @Override + protected void onContentsChanged(int slot) { + if (slot == BACKPACK_ITEM_SLOT) { + maid.setBackpackShowItem(this.getStackInSlot(slot)); + } + } } diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/item/BackpackLevel.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/item/BackpackLevel.java index 2ec1af4ac..1a83cd1ed 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/item/BackpackLevel.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/item/BackpackLevel.java @@ -1,26 +1,10 @@ package com.github.tartaricacid.touhoulittlemaid.item; -import com.google.common.collect.Maps; - -import java.util.Map; - public final class BackpackLevel { - public static final int EMPTY = 0; - public static final int SMALL = 1; - public static final int MIDDLE = 2; - public static final int BIG = 3; - public static final int EMPTY_CAPACITY = 6; public static final int SMALL_CAPACITY = 12; public static final int MIDDLE_CAPACITY = 24; public static final int BIG_CAPACITY = 36; - - public static final Map BACKPACK_CAPACITY_MAP = Maps.newHashMap(); - - static { - BACKPACK_CAPACITY_MAP.put(EMPTY, EMPTY_CAPACITY); - BACKPACK_CAPACITY_MAP.put(SMALL, SMALL_CAPACITY); - BACKPACK_CAPACITY_MAP.put(MIDDLE, MIDDLE_CAPACITY); - BACKPACK_CAPACITY_MAP.put(BIG, BIG_CAPACITY); - } + public static final int CRAFTING_TABLE_CAPACITY = 18; + public static final int FURNACE_CAPACITY = 18; } diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/item/ItemCamera.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/item/ItemCamera.java index 16cf3bcb6..cc7a54767 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/item/ItemCamera.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/item/ItemCamera.java @@ -61,13 +61,10 @@ private void spawnMaidPhoto(Level worldIn, EntityMaid maid, Player playerIn) { CompoundTag photoTag = new CompoundTag(); CompoundTag maidTag = new CompoundTag(); maid.setHomeModeEnable(false); - maid.addAdditionalSaveData(maidTag); + maid.saveWithoutId(maidTag); maidTag.putString("id", Objects.requireNonNull(InitEntities.MAID.get().getRegistryName()).toString()); photoTag.put(MAID_INFO, maidTag); photo.setTag(photoTag); - if (maid.hasCustomName()) { - photo.setHoverName(maid.getCustomName()); - } Containers.dropItemStack(worldIn, playerIn.getX(), playerIn.getY(), playerIn.getZ(), photo); } diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/item/ItemFavorabilityTool.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/item/ItemFavorabilityTool.java new file mode 100644 index 000000000..f4254e50d --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/item/ItemFavorabilityTool.java @@ -0,0 +1,33 @@ +package com.github.tartaricacid.touhoulittlemaid.item; + +import net.minecraft.ChatFormatting; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.level.Level; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +import static com.github.tartaricacid.touhoulittlemaid.item.MaidGroup.MAIN_TAB; + +public class ItemFavorabilityTool extends Item { + private final String type; + + public ItemFavorabilityTool(String type) { + super(new Properties().stacksTo(1).tab(MAIN_TAB)); + this.type = type; + } + + @Override + public boolean isFoil(ItemStack pStack) { + return true; + } + + @Override + public void appendHoverText(ItemStack pStack, @Nullable Level pLevel, List components, TooltipFlag pIsAdvanced) { + components.add(new TranslatableComponent("tooltips.touhou_little_maid.favorability_tool." + this.type).withStyle(ChatFormatting.GRAY)); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/item/ItemFilm.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/item/ItemFilm.java index 864b716dd..752f1f7da 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/item/ItemFilm.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/item/ItemFilm.java @@ -4,23 +4,34 @@ import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; import com.github.tartaricacid.touhoulittlemaid.init.InitEntities; import com.github.tartaricacid.touhoulittlemaid.init.InitItems; +import com.github.tartaricacid.touhoulittlemaid.init.InitSounds; +import com.github.tartaricacid.touhoulittlemaid.network.NetworkHandler; +import com.github.tartaricacid.touhoulittlemaid.network.message.SpawnParticleMessage; import com.github.tartaricacid.touhoulittlemaid.util.ParseI18n; import net.minecraft.ChatFormatting; +import net.minecraft.Util; import net.minecraft.client.resources.language.I18n; +import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.Tag; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.item.ItemEntity; +import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.TooltipFlag; import net.minecraft.world.level.Level; +import net.minecraft.world.phys.Vec3; import org.apache.commons.lang3.StringUtils; import javax.annotation.Nullable; import java.util.List; import java.util.Objects; +import java.util.Optional; import static com.github.tartaricacid.touhoulittlemaid.item.MaidGroup.MAIN_TAB; @@ -28,7 +39,7 @@ public class ItemFilm extends Item { public static final String MAID_INFO = "MaidInfo"; public ItemFilm() { - super((new Item.Properties()).tab(MAIN_TAB).stacksTo(1)); + super((new Properties()).tab(MAIN_TAB).stacksTo(1)); } public static ItemStack maidToFilm(EntityMaid maid) { @@ -36,17 +47,32 @@ public static ItemStack maidToFilm(EntityMaid maid) { CompoundTag filmTag = new CompoundTag(); CompoundTag maidTag = new CompoundTag(); maid.setHomeModeEnable(false); - maid.addAdditionalSaveData(maidTag); + maid.saveWithoutId(maidTag); removeMaidSomeData(maidTag); maidTag.putString("id", Objects.requireNonNull(InitEntities.MAID.get().getRegistryName()).toString()); filmTag.put(MAID_INFO, maidTag); film.setTag(filmTag); - if (maid.hasCustomName()) { - film.setHoverName(maid.getCustomName()); - } return film; } + public static void filmToMaid(ItemStack film, Level worldIn, BlockPos pos, Player player) { + Optional entityOptional = EntityType.create(getMaidData(film), worldIn); + if (entityOptional.isPresent() && entityOptional.get() instanceof EntityMaid) { + EntityMaid maid = (EntityMaid) entityOptional.get(); + maid.setPos(pos.getX(), pos.getY(), pos.getZ()); + // 实体生成必须在服务端应用 + if (!worldIn.isClientSide) { + worldIn.addFreshEntity(maid); + NetworkHandler.sendToNearby(maid, new SpawnParticleMessage(maid.getId(), SpawnParticleMessage.Type.EXPLOSION)); + worldIn.playSound(null, pos, InitSounds.ALTAR_CRAFT.get(), SoundSource.VOICE, 1.0f, 1.0f); + } + film.shrink(1); + } + if (!worldIn.isClientSide) { + player.sendMessage(new TranslatableComponent("tooltips.touhou_little_maid.film.no_data.desc"), Util.NIL_UUID); + } + } + private static boolean hasMaidData(ItemStack stack) { return stack.hasTag() && !Objects.requireNonNull(stack.getTag()).getCompound(MAID_INFO).isEmpty(); } @@ -59,7 +85,7 @@ private static CompoundTag getMaidData(ItemStack stack) { } private static void removeMaidSomeData(CompoundTag nbt) { - nbt.remove(EntityMaid.BACKPACK_LEVEL_TAG); + nbt.remove(EntityMaid.MAID_BACKPACK_TYPE); nbt.remove(EntityMaid.MAID_INVENTORY_TAG); nbt.remove(EntityMaid.MAID_BAUBLE_INVENTORY_TAG); nbt.remove(EntityMaid.EXPERIENCE_TAG); @@ -70,6 +96,14 @@ private static void removeMaidSomeData(CompoundTag nbt) { nbt.remove("HurtTime"); nbt.remove("DeathTime"); nbt.remove("HurtByTimestamp"); + nbt.remove("Pos"); + nbt.remove("Motion"); + nbt.remove("FallDistance"); + nbt.remove("Fire"); + nbt.remove("Air"); + nbt.remove("TicksFrozen"); + nbt.remove("HasVisualFire"); + nbt.remove("Passengers"); } @Override @@ -77,6 +111,13 @@ public boolean onEntityItemUpdate(ItemStack stack, ItemEntity entity) { if (!entity.isInvulnerable()) { entity.setInvulnerable(true); } + Vec3 position = entity.position(); + int minY = entity.level.getMinBuildHeight(); + if (position.y < minY) { + entity.setNoGravity(true); + entity.setDeltaMovement(Vec3.ZERO); + entity.setPos(position.x, minY, position.z); + } return super.onEntityItemUpdate(stack, entity); } diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/item/ItemFoxScroll.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/item/ItemFoxScroll.java new file mode 100644 index 000000000..bccdf4d8f --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/item/ItemFoxScroll.java @@ -0,0 +1,103 @@ +package com.github.tartaricacid.touhoulittlemaid.item; + +import com.github.tartaricacid.touhoulittlemaid.init.InitItems; +import com.github.tartaricacid.touhoulittlemaid.network.NetworkHandler; +import com.github.tartaricacid.touhoulittlemaid.network.message.FoxScrollMessage; +import com.github.tartaricacid.touhoulittlemaid.world.data.MaidInfo; +import com.github.tartaricacid.touhoulittlemaid.world.data.MaidWorldData; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import net.minecraft.ChatFormatting; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtUtils; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResultHolder; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.level.Level; +import org.apache.commons.lang3.tuple.Pair; + +import javax.annotation.Nullable; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import static com.github.tartaricacid.touhoulittlemaid.item.MaidGroup.MAIN_TAB; + +public class ItemFoxScroll extends Item { + private static final String TRACK_INFO = "TrackInfo"; + + public ItemFoxScroll() { + super((new Properties()).stacksTo(1).tab(MAIN_TAB)); + } + + public static boolean hasTrackInfo(ItemStack scroll) { + return scroll.hasTag() && !Objects.requireNonNull(scroll.getTag()).getCompound(TRACK_INFO).isEmpty(); + } + + public static void setTrackInfo(ItemStack scroll, String dimension, BlockPos pos) { + CompoundTag tag = scroll.getOrCreateTagElement(TRACK_INFO); + tag.putString("Dimension", dimension); + tag.put("Position", NbtUtils.writeBlockPos(pos)); + } + + @Nullable + public static Pair getTrackInfo(ItemStack scroll) { + if (hasTrackInfo(scroll)) { + CompoundTag tag = Objects.requireNonNull(scroll.getTag()).getCompound(TRACK_INFO); + String dimension = tag.getString("Dimension"); + BlockPos position = NbtUtils.readBlockPos(tag.getCompound("Position")); + return Pair.of(dimension, position); + } + return null; + } + + @Override + public InteractionResultHolder use(Level level, Player player, InteractionHand hand) { + if (!level.isClientSide && hand == InteractionHand.MAIN_HAND) { + ItemStack item = player.getMainHandItem(); + MaidWorldData maidWorldData = MaidWorldData.get(level); + if (maidWorldData == null) { + return super.use(level, player, hand); + } + Map> data = Maps.newHashMap(); + List maidInfos = null; + if (item.getItem() == InitItems.RED_FOX_SCROLL.get()) { + maidInfos = maidWorldData.getPlayerMaidInfos(player); + } else if (item.getItem() == InitItems.WHITE_FOX_SCROLL.get()) { + maidInfos = maidWorldData.getPlayerMaidTombstones(player); + } + if (maidInfos == null) { + maidInfos = Collections.emptyList(); + } + maidInfos.forEach(info -> { + List scrollData = data.computeIfAbsent(info.getDimension(), dim -> Lists.newArrayList()); + scrollData.add(new FoxScrollMessage.FoxScrollData(info.getChunkPos(), info.getName(), info.getTimestamp())); + }); + NetworkHandler.sendToClientPlayer(new FoxScrollMessage(data), player); + return InteractionResultHolder.success(item); + } + return super.use(level, player, hand); + } + + @Override + public void appendHoverText(ItemStack stack, @Nullable Level pLevel, List components, TooltipFlag pIsAdvanced) { + if (stack.getItem() == InitItems.RED_FOX_SCROLL.get()) { + components.add(new TranslatableComponent("tooltips.touhou_little_maid.fox_scroll.red").withStyle(ChatFormatting.GRAY)); + } else if (stack.getItem() == InitItems.WHITE_FOX_SCROLL.get()) { + components.add(new TranslatableComponent("tooltips.touhou_little_maid.fox_scroll.white").withStyle(ChatFormatting.GRAY)); + } + Pair info = getTrackInfo(stack); + if (info != null) { + components.add(new TranslatableComponent("tooltips.touhou_little_maid.fox_scroll.dimension", info.getLeft()).withStyle(ChatFormatting.GRAY)); + components.add(new TranslatableComponent("tooltips.touhou_little_maid.fox_scroll.position", info.getRight().toShortString()).withStyle(ChatFormatting.GRAY)); + } + super.appendHoverText(stack, pLevel, components, pIsAdvanced); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/item/ItemMaidBackpack.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/item/ItemMaidBackpack.java index 931168304..839b4a36a 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/item/ItemMaidBackpack.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/item/ItemMaidBackpack.java @@ -1,67 +1,11 @@ package com.github.tartaricacid.touhoulittlemaid.item; -import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; -import com.github.tartaricacid.touhoulittlemaid.util.ItemsUtil; -import com.google.common.collect.Maps; -import net.minecraft.sounds.SoundEvents; -import net.minecraft.world.InteractionHand; -import net.minecraft.world.InteractionResult; -import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; -import net.minecraftforge.items.ItemHandlerHelper; -import net.minecraftforge.items.ItemStackHandler; - -import java.util.Map; -import java.util.Optional; import static com.github.tartaricacid.touhoulittlemaid.item.MaidGroup.MAIN_TAB; public class ItemMaidBackpack extends Item { - private static final Map ITEM_BY_LEVEL = Maps.newHashMap(); - private final int level; - - public ItemMaidBackpack(int level) { - super((new Properties()).tab(MAIN_TAB).stacksTo(1)); - this.level = level; - ITEM_BY_LEVEL.put(level, this); - } - - public static Optional getInstance(int level) { - return Optional.ofNullable(ITEM_BY_LEVEL.get(level)); - } - - public int getLevel() { - return level; - } - - @Override - public InteractionResult interactLivingEntity(ItemStack stack, Player playerIn, LivingEntity target, InteractionHand hand) { - if (target instanceof EntityMaid) { - EntityMaid maid = (EntityMaid) target; - int maidBackpackLevel = maid.getBackpackLevel(); - if (maid.isOwnedBy(playerIn) && level != maidBackpackLevel) { - if (maid.backpackHasDelay()) { - return InteractionResult.SUCCESS; - } - maid.setBackpackLevel(level); - maid.setBackpackDelay(); - for (Item backpack : ITEM_BY_LEVEL.values()) { - playerIn.getCooldowns().addCooldown(backpack, 20); - } - stack.shrink(1); - target.playSound(SoundEvents.HORSE_SADDLE, 0.5F, 1.0F); - getInstance(maidBackpackLevel).ifPresent(backpack -> ItemHandlerHelper.giveItemToPlayer(playerIn, backpack.getDefaultInstance())); - if (level < maidBackpackLevel) { - ItemStackHandler maidInv = maid.getMaidInv(); - int startIndex = BackpackLevel.BACKPACK_CAPACITY_MAP.get(level); - int endIndex = BackpackLevel.BACKPACK_CAPACITY_MAP.get(maidBackpackLevel); - ItemsUtil.dropEntityItems(maid, maidInv, startIndex, endIndex); - } - return InteractionResult.SUCCESS; - } - } - return super.interactLivingEntity(stack, playerIn, target, hand); + public ItemMaidBackpack() { + super((new Properties()).stacksTo(1).tab(MAIN_TAB)); } } diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/item/ItemPhoto.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/item/ItemPhoto.java index 7efc6fd82..0457017a0 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/item/ItemPhoto.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/item/ItemPhoto.java @@ -16,6 +16,7 @@ import net.minecraft.world.InteractionResult; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; @@ -50,6 +51,21 @@ public static CompoundTag getMaidData(ItemStack stack) { return new CompoundTag(); } + @Override + public boolean onEntityItemUpdate(ItemStack stack, ItemEntity entity) { + if (!entity.isInvulnerable()) { + entity.setInvulnerable(true); + } + Vec3 position = entity.position(); + int minY = entity.level.getMinBuildHeight(); + if (position.y < minY) { + entity.setNoGravity(true); + entity.setDeltaMovement(Vec3.ZERO); + entity.setPos(position.x, minY, position.z); + } + return super.onEntityItemUpdate(stack, entity); + } + @Override public InteractionResult useOn(UseOnContext context) { Direction facing = context.getClickedFace(); @@ -84,9 +100,6 @@ public InteractionResult useOn(UseOnContext context) { if (entityOptional.isPresent() && entityOptional.get() instanceof EntityMaid) { EntityMaid maid = (EntityMaid) entityOptional.get(); maid.setPos(clickLocation.x, clickLocation.y, clickLocation.z); - if (photo.hasCustomHoverName()) { - maid.setCustomName(photo.getHoverName()); - } // 实体生成必须在服务端应用 if (!worldIn.isClientSide) { worldIn.addFreshEntity(maid); diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/item/ItemSmartSlab.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/item/ItemSmartSlab.java index 22d791046..e015d99a7 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/item/ItemSmartSlab.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/item/ItemSmartSlab.java @@ -16,11 +16,13 @@ import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.Tag; import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; import net.minecraft.network.chat.TranslatableComponent; import net.minecraft.server.level.ServerLevel; import net.minecraft.sounds.SoundEvents; import net.minecraft.world.InteractionResult; import net.minecraft.world.entity.MobSpawnType; +import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.CreativeModeTab; import net.minecraft.world.item.Item; @@ -28,6 +30,7 @@ import net.minecraft.world.item.TooltipFlag; import net.minecraft.world.item.context.UseOnContext; import net.minecraft.world.level.Level; +import net.minecraft.world.phys.Vec3; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import org.apache.commons.lang3.StringUtils; @@ -50,7 +53,7 @@ public ItemSmartSlab(Type type) { } public static void storeMaidData(ItemStack stack, EntityMaid maid) { - maid.addAdditionalSaveData(stack.getOrCreateTagElement(MAID_INFO)); + maid.saveWithoutId(stack.getOrCreateTagElement(MAID_INFO)); } public static boolean hasMaidData(ItemStack stack) { @@ -64,6 +67,21 @@ public static CompoundTag getMaidData(ItemStack stack) { return new CompoundTag(); } + @Override + public boolean onEntityItemUpdate(ItemStack stack, ItemEntity entity) { + if (!entity.isInvulnerable()) { + entity.setInvulnerable(true); + } + Vec3 position = entity.position(); + int minY = entity.level.getMinBuildHeight(); + if (position.y < minY) { + entity.setNoGravity(true); + entity.setDeltaMovement(Vec3.ZERO); + entity.setPos(position.x, minY, position.z); + } + return super.onEntityItemUpdate(stack, entity); + } + @Override public void fillItemCategory(CreativeModeTab group, NonNullList items) { if (this.allowdedIn(group) && this.type == Type.INIT) { @@ -90,7 +108,6 @@ public InteractionResult useOn(UseOnContext context) { if (maid == null) { return super.useOn(context); } - maid.moveTo(clickedPos.above(), 0, 0); if (this.type == Type.INIT) { return spawnNewMaid(context, player, worldIn, maid); } @@ -113,10 +130,8 @@ private InteractionResult spawnFromStore(UseOnContext context, Player player, Le if (!player.getUUID().equals(ownerUid)) { return InteractionResult.FAIL; } - maid.readAdditionalSaveData(maidData); - if (stack.hasCustomHoverName()) { - maid.setCustomName(stack.getHoverName()); - } + maid.load(maidData); + maid.moveTo(context.getClickedPos().above(), 0, 0); if (worldIn instanceof ServerLevel) { worldIn.addFreshEntity(maid); } @@ -139,6 +154,7 @@ private InteractionResult spawnNewMaid(UseOnContext context, Player player, Leve if (worldIn instanceof ServerLevel) { maid.finalizeSpawn((ServerLevel) worldIn, worldIn.getCurrentDifficultyAt(context.getClickedPos()), MobSpawnType.SPAWN_EGG, null, null); + maid.moveTo(context.getClickedPos().above(), 0, 0); worldIn.addFreshEntity(maid); } maid.spawnExplosionParticle(); @@ -165,7 +181,7 @@ public boolean isFoil(ItemStack stack) { @OnlyIn(Dist.CLIENT) public void appendHoverText(ItemStack stack, @Nullable Level worldIn, List tooltip, TooltipFlag flagIn) { if (this.type == Type.INIT) { - TranslatableComponent text = new TranslatableComponent("tooltips.touhou_little_maid.smart_slab.maid_name", + MutableComponent text = new TranslatableComponent("tooltips.touhou_little_maid.smart_slab.maid_name", I18n.get("tooltips.touhou_little_maid.smart_slab.maid_name.unknown")); tooltip.add(text.withStyle(ChatFormatting.GRAY)); } diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/item/ItemTrumpet.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/item/ItemTrumpet.java index f26799457..db7f795b6 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/item/ItemTrumpet.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/item/ItemTrumpet.java @@ -1,7 +1,10 @@ package com.github.tartaricacid.touhoulittlemaid.item; import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; +import com.github.tartaricacid.touhoulittlemaid.world.data.MaidInfo; +import com.github.tartaricacid.touhoulittlemaid.world.data.MaidWorldData; import net.minecraft.ChatFormatting; +import net.minecraft.Util; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.TranslatableComponent; import net.minecraft.server.level.ServerLevel; @@ -36,6 +39,13 @@ public void releaseUsing(ItemStack stack, Level worldIn, LivingEntity entityLivi ((ServerLevel) worldIn).getEntities(EntityMaid.TYPE, Entity::isAlive).stream() .filter(maid -> maid.isOwnedBy(player)) .forEach(maid -> teleportToOwner(maid, player)); + MaidWorldData data = MaidWorldData.get(worldIn); + if (data != null) { + List infos = data.getPlayerMaidInfos(player); + if (infos != null && !infos.isEmpty()) { + player.sendMessage(new TranslatableComponent("message.touhou_little_maid.trumpet.unloaded_maid", infos.size()).withStyle(ChatFormatting.DARK_RED), Util.NIL_UUID); + } + } } player.getCooldowns().addCooldown(this, 200); } diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/item/bauble/ExtraLifeBauble.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/item/bauble/ExtraLifeBauble.java index e3878965c..41186626c 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/item/bauble/ExtraLifeBauble.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/item/bauble/ExtraLifeBauble.java @@ -30,7 +30,7 @@ public void onLivingDamage(MaidDeathEvent event) { stack.hurtAndBreak(1, maid, m -> maid.sendItemBreakMessage(stack)); maid.getMaidBauble().setStackInSlot(slot, stack); maid.setHealth(maid.getMaxHealth()); - NetworkHandler.sendToNearby(maid, new SpawnParticleMessage(maid.getId(), SpawnParticleMessage.Type.EXPLOSION)); + NetworkHandler.sendToNearby(maid, new SpawnParticleMessage(maid.getId(), SpawnParticleMessage.Type.HEART)); maid.playSound(SoundEvents.GLASS_BREAK, 1.0f, 1.0f); } } diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/item/bauble/NimbleFabricBauble.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/item/bauble/NimbleFabricBauble.java index a8d7639fa..4d881084c 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/item/bauble/NimbleFabricBauble.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/item/bauble/NimbleFabricBauble.java @@ -4,14 +4,10 @@ import com.github.tartaricacid.touhoulittlemaid.api.event.MaidAttackEvent; import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; import com.github.tartaricacid.touhoulittlemaid.util.ItemsUtil; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.sounds.SoundEvents; -import net.minecraft.tags.FluidTags; +import com.github.tartaricacid.touhoulittlemaid.util.TeleportHelper; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.damagesource.IndirectEntityDamageSource; import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.state.BlockState; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.eventbus.api.EventPriority; import net.minecraftforge.eventbus.api.SubscribeEvent; @@ -35,42 +31,11 @@ public void onLivingDamage(MaidAttackEvent event) { stack.hurtAndBreak(1, maid, m -> maid.sendItemBreakMessage(stack)); maid.getMaidBauble().setStackInSlot(slot, stack); for (int i = 0; i < MAX_RETRY; ++i) { - if (teleport(maid)) { + if (TeleportHelper.teleport(maid)) { return; } } } } } - - protected boolean teleport(EntityMaid maid) { - if (!maid.level.isClientSide() && maid.isAlive()) { - double x = maid.getX() + (maid.getRandom().nextDouble() - 0.5) * 16; - double y = maid.getY() + maid.getRandom().nextInt(16) - 8; - double z = maid.getZ() + (maid.getRandom().nextDouble() - 0.5) * 16; - return teleport(maid, x, y, z); - } else { - return false; - } - } - - private boolean teleport(EntityMaid maid, double x, double y, double z) { - BlockPos.MutableBlockPos blockPos = new BlockPos.MutableBlockPos(x, y, z); - while (blockPos.getY() > maid.level.getMinBuildHeight() && !maid.level.getBlockState(blockPos).getMaterial().blocksMotion()) { - blockPos.move(Direction.DOWN); - } - BlockState blockState = maid.level.getBlockState(blockPos); - boolean isMotion = blockState.getMaterial().blocksMotion(); - boolean isWater = blockState.getFluidState().is(FluidTags.WATER); - if (isMotion && !isWater) { - boolean teleportIsSuccess = maid.randomTeleport(x, y, z, true); - if (teleportIsSuccess && !maid.isSilent()) { - maid.level.playSound(null, maid.xo, maid.yo, maid.zo, SoundEvents.ENDERMAN_TELEPORT, maid.getSoundSource(), 1.0F, 1.0F); - maid.playSound(SoundEvents.ENDERMAN_TELEPORT, 1.0F, 1.0F); - } - return teleportIsSuccess; - } else { - return false; - } - } } diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/network/NetworkHandler.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/network/NetworkHandler.java index 94a85183c..9bcc29a71 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/network/NetworkHandler.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/network/NetworkHandler.java @@ -70,6 +70,14 @@ public static void init() { Optional.of(NetworkDirection.PLAY_TO_CLIENT)); CHANNEL.registerMessage(22, SetMaidSoundIdMessage.class, SetMaidSoundIdMessage::encode, SetMaidSoundIdMessage::decode, SetMaidSoundIdMessage::handle, Optional.of(NetworkDirection.PLAY_TO_SERVER)); + CHANNEL.registerMessage(23, ChessDataToClientMessage.class, ChessDataToClientMessage::encode, ChessDataToClientMessage::decode, ChessDataToClientMessage::handle, + Optional.of(NetworkDirection.PLAY_TO_CLIENT)); + CHANNEL.registerMessage(24, ChessDataToServerMessage.class, ChessDataToServerMessage::encode, ChessDataToServerMessage::decode, ChessDataToServerMessage::handle, + Optional.of(NetworkDirection.PLAY_TO_SERVER)); + CHANNEL.registerMessage(25, FoxScrollMessage.class, FoxScrollMessage::encode, FoxScrollMessage::decode, FoxScrollMessage::handle, + Optional.of(NetworkDirection.PLAY_TO_CLIENT)); + CHANNEL.registerMessage(26, SetScrollData.class, SetScrollData::encode, SetScrollData::decode, SetScrollData::handle, + Optional.of(NetworkDirection.PLAY_TO_SERVER)); } public static void sendToClientPlayer(Object message, Player player) { diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/network/message/ChessDataToClientMessage.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/network/message/ChessDataToClientMessage.java new file mode 100644 index 000000000..849cf1ffd --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/network/message/ChessDataToClientMessage.java @@ -0,0 +1,69 @@ +package com.github.tartaricacid.touhoulittlemaid.network.message; + +import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; +import com.github.tartaricacid.touhoulittlemaid.api.game.gomoku.Point; +import com.github.tartaricacid.touhoulittlemaid.network.NetworkHandler; +import net.minecraft.Util; +import net.minecraft.client.Minecraft; +import net.minecraft.core.BlockPos; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import net.minecraftforge.network.NetworkEvent; + +import java.util.concurrent.CompletableFuture; +import java.util.function.Supplier; + +public class ChessDataToClientMessage { + private final BlockPos pos; + private final int[][] chessData; + private final Point point; + + public ChessDataToClientMessage(BlockPos pos, int[][] chessData, Point point) { + this.pos = pos; + this.chessData = chessData; + this.point = point; + } + + public static void encode(ChessDataToClientMessage message, FriendlyByteBuf buf) { + buf.writeBlockPos(message.pos); + buf.writeVarInt(message.chessData.length); + for (int[] row : message.chessData) { + buf.writeVarIntArray(row); + } + buf.writeVarInt(message.point.x); + buf.writeVarInt(message.point.y); + buf.writeVarInt(message.point.type); + } + + public static ChessDataToClientMessage decode(FriendlyByteBuf buf) { + BlockPos blockPos = buf.readBlockPos(); + int length = buf.readVarInt(); + int[][] chessData = new int[length][length]; + for (int i = 0; i < length; i++) { + chessData[i] = buf.readVarIntArray(); + } + Point pointIn = new Point(buf.readVarInt(), buf.readVarInt(), buf.readVarInt()); + return new ChessDataToClientMessage(blockPos, chessData, pointIn); + } + + public static void handle(ChessDataToClientMessage message, Supplier contextSupplier) { + NetworkEvent.Context context = contextSupplier.get(); + if (context.getDirection().getReceptionSide().isClient()) { + context.enqueueWork(() -> CompletableFuture.runAsync(() -> onHandle(message), Util.backgroundExecutor())); + } + context.setPacketHandled(true); + } + + @OnlyIn(Dist.CLIENT) + private static void onHandle(ChessDataToClientMessage message) { + Point aiPoint = TouhouLittleMaid.SERVICE.getPoint(message.chessData, message.point); + int time = (int) (Math.random() * 1250) + 250; + try { + Thread.sleep(time); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + Minecraft.getInstance().submitAsync(() -> NetworkHandler.CHANNEL.sendToServer(new ChessDataToServerMessage(message.pos, aiPoint))); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/network/message/ChessDataToServerMessage.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/network/message/ChessDataToServerMessage.java new file mode 100644 index 000000000..32c878816 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/network/message/ChessDataToServerMessage.java @@ -0,0 +1,75 @@ +package com.github.tartaricacid.touhoulittlemaid.network.message; + +import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; +import com.github.tartaricacid.touhoulittlemaid.api.game.gomoku.Point; +import com.github.tartaricacid.touhoulittlemaid.api.game.gomoku.Statue; +import com.github.tartaricacid.touhoulittlemaid.entity.item.EntitySit; +import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; +import com.github.tartaricacid.touhoulittlemaid.init.InitSounds; +import com.github.tartaricacid.touhoulittlemaid.tileentity.TileEntityGomoku; +import net.minecraft.core.BlockPos; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.level.Level; +import net.minecraftforge.network.NetworkEvent; + +import java.util.function.Supplier; + +public class ChessDataToServerMessage { + private final BlockPos pos; + private final Point point; + + public ChessDataToServerMessage(BlockPos pos, Point point) { + this.pos = pos; + this.point = point; + } + + public static void encode(ChessDataToServerMessage message, FriendlyByteBuf buf) { + buf.writeBlockPos(message.pos); + buf.writeVarInt(message.point.x); + buf.writeVarInt(message.point.y); + buf.writeVarInt(message.point.type); + } + + public static ChessDataToServerMessage decode(FriendlyByteBuf buf) { + BlockPos blockPos = buf.readBlockPos(); + Point pointIn = new Point(buf.readVarInt(), buf.readVarInt(), buf.readVarInt()); + return new ChessDataToServerMessage(blockPos, pointIn); + } + + public static void handle(ChessDataToServerMessage message, Supplier contextSupplier) { + NetworkEvent.Context context = contextSupplier.get(); + if (context.getDirection().getReceptionSide().isServer()) { + context.enqueueWork(() -> { + ServerPlayer sender = context.getSender(); + if (sender == null) { + return; + } + Level level = sender.level; + if (!level.isLoaded(message.pos)) { + return; + } + if (level.getBlockEntity(message.pos) instanceof TileEntityGomoku gomoku) { + if (!gomoku.isInProgress() || gomoku.isPlayerTurn() || gomoku.getChessCounter() <= 0) { + return; + } + Point aiPoint = message.point; + gomoku.setChessData(aiPoint.x, aiPoint.y, aiPoint.type); + if (level instanceof ServerLevel serverLevel && serverLevel.getEntity(gomoku.getSitId()) instanceof EntitySit sit && sit.getFirstPassenger() instanceof EntityMaid maid) { + maid.swing(InteractionHand.MAIN_HAND); + } + gomoku.setInProgress(TouhouLittleMaid.SERVICE.getStatue(gomoku.getChessData(), aiPoint) == Statue.IN_PROGRESS); + level.playSound(null, message.pos, InitSounds.GOMOKU.get(), SoundSource.BLOCKS, 1.0f, 0.8F + level.random.nextFloat() * 0.4F); + if (gomoku.isInProgress()) { + gomoku.setPlayerTurn(true); + } + gomoku.refresh(); + } + }); + } + context.setPacketHandled(true); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/network/message/FoxScrollMessage.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/network/message/FoxScrollMessage.java new file mode 100644 index 000000000..c842e3029 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/network/message/FoxScrollMessage.java @@ -0,0 +1,95 @@ +package com.github.tartaricacid.touhoulittlemaid.network.message; + +import com.github.tartaricacid.touhoulittlemaid.client.gui.item.FoxScrollScreen; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import net.minecraft.client.Minecraft; +import net.minecraft.core.BlockPos; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.chat.Component; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import net.minecraftforge.network.NetworkEvent; + +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; + +public class FoxScrollMessage { + private final Map> data; + + public FoxScrollMessage(Map> data) { + this.data = data; + } + + public static void encode(FoxScrollMessage message, FriendlyByteBuf buf) { + buf.writeVarInt(message.data.size()); + message.data.forEach((dim, scrollData) -> { + buf.writeVarInt(scrollData.size()); + buf.writeUtf(dim); + scrollData.forEach(data -> FoxScrollData.encode(data, buf)); + }); + } + + public static FoxScrollMessage decode(FriendlyByteBuf buf) { + Map> data = Maps.newHashMap(); + int dimLength = buf.readVarInt(); + for (int i = 0; i < dimLength; i++) { + List scrollData = Lists.newArrayList(); + int dataLength = buf.readVarInt(); + String dim = buf.readUtf(); + for (int j = 0; j < dataLength; j++) { + scrollData.add(FoxScrollData.decode(buf)); + } + data.put(dim, scrollData); + } + return new FoxScrollMessage(data); + } + + public static void handle(FoxScrollMessage message, Supplier contextSupplier) { + NetworkEvent.Context context = contextSupplier.get(); + if (context.getDirection().getReceptionSide().isClient()) { + context.enqueueWork(() -> onHandle(message)); + } + context.setPacketHandled(true); + } + + @OnlyIn(Dist.CLIENT) + private static void onHandle(FoxScrollMessage message) { + Minecraft.getInstance().setScreen(new FoxScrollScreen(message.data)); + } + + public static class FoxScrollData { + private final BlockPos pos; + private final Component name; + private final long timestamp; + + public FoxScrollData(BlockPos pos, Component name, long timestamp) { + this.pos = pos; + this.name = name; + this.timestamp = timestamp; + } + + public static void encode(FoxScrollData data, FriendlyByteBuf buf) { + buf.writeBlockPos(data.pos); + buf.writeComponent(data.name); + buf.writeLong(data.timestamp); + } + + public static FoxScrollData decode(FriendlyByteBuf buf) { + return new FoxScrollData(buf.readBlockPos(), buf.readComponent(), buf.readLong()); + } + + public BlockPos getPos() { + return pos; + } + + public Component getName() { + return name; + } + + public long getTimestamp() { + return timestamp; + } + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/network/message/MaidConfigMessage.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/network/message/MaidConfigMessage.java index c064855e9..6d323d406 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/network/message/MaidConfigMessage.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/network/message/MaidConfigMessage.java @@ -1,6 +1,7 @@ package com.github.tartaricacid.touhoulittlemaid.network.message; import com.github.tartaricacid.touhoulittlemaid.entity.ai.brain.MaidSchedule; +import com.github.tartaricacid.touhoulittlemaid.entity.item.EntitySit; import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.server.level.ServerPlayer; @@ -47,10 +48,16 @@ public static void handle(MaidConfigMessage message, Supplier contextSupplier) { + NetworkEvent.Context context = contextSupplier.get(); + if (context.getDirection().getReceptionSide().isServer()) { + context.enqueueWork(() -> { + ServerPlayer sender = context.getSender(); + if (sender == null) { + return; + } + ItemStack item = sender.getMainHandItem(); + if (item.getItem() instanceof ItemFoxScroll) { + ItemFoxScroll.setTrackInfo(item, message.dimension, message.pos); + } + }); + } + context.setPacketHandled(true); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/network/message/SpawnParticleMessage.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/network/message/SpawnParticleMessage.java index 3955ade2d..af64a8760 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/network/message/SpawnParticleMessage.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/network/message/SpawnParticleMessage.java @@ -53,6 +53,9 @@ private static void handleSpawnParticle(SpawnParticleMessage message) { case BUBBLE: maid.spawnBubbleParticle(); return; + case HEART: + maid.spawnHeartParticle(); + return; default: } } @@ -66,6 +69,6 @@ public enum Type { /** * 粒子类型 */ - EXPLOSION, BUBBLE + EXPLOSION, BUBBLE, HEART } } diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/tileentity/TileEntityBookshelf.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/tileentity/TileEntityBookshelf.java new file mode 100644 index 000000000..97f7ab9b4 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/tileentity/TileEntityBookshelf.java @@ -0,0 +1,14 @@ +package com.github.tartaricacid.touhoulittlemaid.tileentity; + +import com.github.tartaricacid.touhoulittlemaid.init.InitBlocks; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockState; + +public class TileEntityBookshelf extends TileEntityJoy { + public static final BlockEntityType TYPE = BlockEntityType.Builder.of(TileEntityBookshelf::new, InitBlocks.BOOKSHELF.get()).build(null); + + public TileEntityBookshelf(BlockPos pos, BlockState blockState) { + super(TYPE, pos, blockState); + } +} \ No newline at end of file diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/tileentity/TileEntityComputer.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/tileentity/TileEntityComputer.java new file mode 100644 index 000000000..214821bc7 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/tileentity/TileEntityComputer.java @@ -0,0 +1,14 @@ +package com.github.tartaricacid.touhoulittlemaid.tileentity; + +import com.github.tartaricacid.touhoulittlemaid.init.InitBlocks; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockState; + +public class TileEntityComputer extends TileEntityJoy { + public static final BlockEntityType TYPE = BlockEntityType.Builder.of(TileEntityComputer::new, InitBlocks.COMPUTER.get()).build(null); + + public TileEntityComputer(BlockPos pos, BlockState blockState) { + super(TYPE, pos, blockState); + } +} \ No newline at end of file diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/tileentity/TileEntityGomoku.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/tileentity/TileEntityGomoku.java new file mode 100644 index 000000000..b289112d1 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/tileentity/TileEntityGomoku.java @@ -0,0 +1,101 @@ +package com.github.tartaricacid.touhoulittlemaid.tileentity; + +import com.github.tartaricacid.touhoulittlemaid.api.game.gomoku.Point; +import com.github.tartaricacid.touhoulittlemaid.init.InitBlocks; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.IntArrayTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockState; + +public class TileEntityGomoku extends TileEntityJoy { + public static final BlockEntityType TYPE = BlockEntityType.Builder.of(TileEntityGomoku::new, InitBlocks.GOMOKU.get()).build(null); + + private static final String CHESS_DATA = "ChessData"; + private static final String IN_PROGRESS = "InProgress"; + private static final String PLAYER_TURN = "PlayerTurn"; + private static final String CHESS_COUNTER = "ChessCounter"; + private static final String LATEST_CHESS_POINT = "LatestChessPoint"; + + private int[][] chessData = new int[15][15]; + private boolean inProgress = true; + private boolean playerTurn = true; + private int chessCounter = 0; + private Point latestChessPoint = Point.NULL; + + public TileEntityGomoku(BlockPos pos, BlockState blockState) { + super(TYPE, pos, blockState); + } + + @Override + protected void saveAdditional(CompoundTag tag) { + ListTag listTag = new ListTag(); + for (int[] chessRow : chessData) { + listTag.add(new IntArrayTag(chessRow)); + } + getTileData().put(CHESS_DATA, listTag); + getTileData().putBoolean(IN_PROGRESS, this.inProgress); + getTileData().putBoolean(PLAYER_TURN, this.playerTurn); + getTileData().putInt(CHESS_COUNTER, this.chessCounter); + getTileData().put(LATEST_CHESS_POINT, Point.toTag(this.latestChessPoint)); + super.saveAdditional(tag); + } + + @Override + public void load(CompoundTag nbt) { + super.load(nbt); + ListTag listTag = getTileData().getList(CHESS_DATA, Tag.TAG_INT_ARRAY); + for (int i = 0; i < listTag.size(); i++) { + int[] intArray = listTag.getIntArray(i); + this.chessData[i] = intArray; + } + this.inProgress = getTileData().getBoolean(IN_PROGRESS); + this.playerTurn = getTileData().getBoolean(PLAYER_TURN); + this.chessCounter = getTileData().getInt(CHESS_COUNTER); + this.latestChessPoint = Point.fromTag(getTileData().getCompound(LATEST_CHESS_POINT)); + } + + public void reset() { + this.chessData = new int[15][15]; + this.inProgress = true; + this.playerTurn = true; + this.chessCounter = 0; + this.latestChessPoint = Point.NULL; + } + + public int[][] getChessData() { + return chessData; + } + + public void setChessData(int x, int y, int type) { + this.chessData[x][y] = type; + this.latestChessPoint = new Point(x, y, type); + this.chessCounter += 1; + } + + public boolean isPlayerTurn() { + return playerTurn; + } + + public void setPlayerTurn(boolean playerTurn) { + this.playerTurn = playerTurn; + } + + public boolean isInProgress() { + return inProgress; + } + + public void setInProgress(boolean inProgress) { + this.inProgress = inProgress; + } + + public int getChessCounter() { + return chessCounter; + } + + public Point getLatestChessPoint() { + return latestChessPoint; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/tileentity/TileEntityJoy.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/tileentity/TileEntityJoy.java new file mode 100644 index 000000000..ef2e8ddfe --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/tileentity/TileEntityJoy.java @@ -0,0 +1,72 @@ +package com.github.tartaricacid.touhoulittlemaid.tileentity; + +import net.minecraft.Util; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.game.ClientGamePacketListener; +import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.AABB; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +import javax.annotation.Nullable; +import java.util.UUID; + +public abstract class TileEntityJoy extends BlockEntity { + private static final String SIT_ID = "SitId"; + private UUID sitId = Util.NIL_UUID; + + public TileEntityJoy(BlockEntityType type, BlockPos pos, BlockState blockState) { + super(type, pos, blockState); + } + + @Override + protected void saveAdditional(CompoundTag tag) { + getTileData().putUUID(SIT_ID, this.sitId); + super.saveAdditional(tag); + } + + @Override + public void load(CompoundTag nbt) { + super.load(nbt); + this.sitId = getTileData().getUUID(SIT_ID); + } + + @Override + public CompoundTag getUpdateTag() { + return saveWithoutMetadata(); + } + + @Nullable + @Override + public Packet getUpdatePacket() { + return ClientboundBlockEntityDataPacket.create(this); + } + + public void refresh() { + this.setChanged(); + if (level != null) { + BlockState state = level.getBlockState(worldPosition); + level.sendBlockUpdated(worldPosition, state, state, Block.UPDATE_ALL); + } + } + + @Override + @OnlyIn(Dist.CLIENT) + public AABB getRenderBoundingBox() { + return new AABB(worldPosition.offset(-2, 0, -2), worldPosition.offset(2, 1, 2)); + } + + public void setSitId(UUID sitId) { + this.sitId = sitId; + } + + public UUID getSitId() { + return this.sitId; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/tileentity/TileEntityKeyboard.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/tileentity/TileEntityKeyboard.java new file mode 100644 index 000000000..6a7f2fb61 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/tileentity/TileEntityKeyboard.java @@ -0,0 +1,14 @@ +package com.github.tartaricacid.touhoulittlemaid.tileentity; + +import com.github.tartaricacid.touhoulittlemaid.init.InitBlocks; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockState; + +public class TileEntityKeyboard extends TileEntityJoy { + public static final BlockEntityType TYPE = BlockEntityType.Builder.of(TileEntityKeyboard::new, InitBlocks.KEYBOARD.get()).build(null); + + public TileEntityKeyboard(BlockPos pos, BlockState blockState) { + super(TYPE, pos, blockState); + } +} \ No newline at end of file diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/tileentity/TileEntityShrine.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/tileentity/TileEntityShrine.java new file mode 100644 index 000000000..00137f282 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/tileentity/TileEntityShrine.java @@ -0,0 +1,89 @@ +package com.github.tartaricacid.touhoulittlemaid.tileentity; + +import com.github.tartaricacid.touhoulittlemaid.init.InitBlocks; +import com.github.tartaricacid.touhoulittlemaid.init.InitItems; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.game.ClientGamePacketListener; +import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraftforge.items.ItemStackHandler; +import org.jetbrains.annotations.NotNull; + +import javax.annotation.Nullable; + +public class TileEntityShrine extends BlockEntity { + public static final BlockEntityType TYPE = BlockEntityType.Builder.of(TileEntityShrine::new, InitBlocks.SHRINE.get()).build(null); + private static final String STORAGE_ITEM = "StorageItem"; + private final ItemStackHandler handler = new ItemStackHandler() { + @Override + public boolean isItemValid(int slot, @NotNull ItemStack stack) { + return stack.getItem() == InitItems.FILM.get(); + } + + @Override + public int getSlotLimit(int slot) { + return 1; + } + }; + + public TileEntityShrine(BlockPos pos, BlockState blockState) { + super(TYPE, pos, blockState); + } + + @Override + protected void saveAdditional(CompoundTag tag) { + getTileData().put(STORAGE_ITEM, handler.serializeNBT()); + super.saveAdditional(tag); + } + + @Override + public void load(CompoundTag nbt) { + super.load(nbt); + handler.deserializeNBT(getTileData().getCompound(STORAGE_ITEM)); + } + + @Override + public CompoundTag getUpdateTag() { + return saveWithoutMetadata(); + } + + @Nullable + @Override + public Packet getUpdatePacket() { + return ClientboundBlockEntityDataPacket.create(this); + } + + public void refresh() { + this.setChanged(); + if (level != null) { + BlockState state = level.getBlockState(worldPosition); + level.sendBlockUpdated(worldPosition, state, state, Block.UPDATE_ALL); + } + } + + public ItemStack getStorageItem() { + return handler.getStackInSlot(0); + } + + public void insertStorageItem(ItemStack stack) { + handler.insertItem(0, stack, false); + } + + public ItemStack extractStorageItem() { + return handler.extractItem(0, 1, false); + } + + public boolean isEmpty() { + return handler.getStackInSlot(0).isEmpty(); + } + + public boolean canInsert(ItemStack stack) { + return handler.isItemValid(0, stack); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/util/EntityCacheUtil.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/util/EntityCacheUtil.java index 50e4a44c9..ed11b0363 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/util/EntityCacheUtil.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/util/EntityCacheUtil.java @@ -1,6 +1,7 @@ package com.github.tartaricacid.touhoulittlemaid.util; import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; +import com.github.tartaricacid.touhoulittlemaid.entity.backpack.BackpackManager; import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; import com.github.tartaricacid.touhoulittlemaid.item.BackpackLevel; import com.google.common.cache.Cache; @@ -36,7 +37,7 @@ public static void clearMaidDataResidue(EntityMaid maid, boolean clearEquipmentD maid.deathTime = 0; maid.setOnGround(true); maid.setInSittingPose(false); - maid.setBackpackLevel(BackpackLevel.EMPTY); + maid.setMaidBackpackType(BackpackManager.getEmptyBackpack()); maid.setCustomName(TextComponent.EMPTY); if (clearEquipmentData) { for (EquipmentSlot slot : EquipmentSlot.values()) { diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/util/ItemsUtil.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/util/ItemsUtil.java index 39076e771..0641b07fe 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/util/ItemsUtil.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/util/ItemsUtil.java @@ -29,6 +29,13 @@ public static void dropEntityItems(Entity entity, IItemHandler itemHandler, int } } + /** + * 掉落指定起始的物品 + */ + public static void dropEntityItems(Entity entity, IItemHandler itemHandler, int startIndex) { + dropEntityItems(entity, itemHandler, startIndex, itemHandler.getSlots()); + } + /** * 掉落全部物品 */ diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/util/RenderHelper.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/util/RenderHelper.java index 8bdbf742a..3958c08c6 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/util/RenderHelper.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/util/RenderHelper.java @@ -1,26 +1,38 @@ package com.github.tartaricacid.touhoulittlemaid.util; +import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.math.Matrix4f; +import net.minecraft.client.Camera; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.world.phys.Vec3; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; -import net.minecraftforge.client.gui.GuiUtils; @OnlyIn(Dist.CLIENT) public final class RenderHelper { - public static void renderBackground(Matrix4f mat, int x, int y, int width, int height) { - x = x - width; - y = y - height; - int backgroundColor = 0xF0100010; - int borderColorStart = 0x505000FF; - int borderColorEnd = 0x5028007f; - GuiUtils.drawGradientRect(mat, 300, x - 3, y - 4, x + width + 3, y - 3, backgroundColor, backgroundColor); - GuiUtils.drawGradientRect(mat, 300, x - 3, y + height + 3, x + width + 3, y + height + 4, backgroundColor, backgroundColor); - GuiUtils.drawGradientRect(mat, 300, x - 3, y - 3, x + width + 3, y + height + 3, backgroundColor, backgroundColor); - GuiUtils.drawGradientRect(mat, 300, x - 4, y - 3, x - 3, y + height + 3, backgroundColor, backgroundColor); - GuiUtils.drawGradientRect(mat, 300, x + width + 3, y - 3, x + width + 4, y + height + 3, backgroundColor, backgroundColor); - GuiUtils.drawGradientRect(mat, 300, x - 3, y - 3 + 1, x - 3 + 1, y + height + 3 - 1, borderColorStart, borderColorEnd); - GuiUtils.drawGradientRect(mat, 300, x + width + 2, y - 3 + 1, x + width + 3, y + height + 3 - 1, borderColorStart, borderColorEnd); - GuiUtils.drawGradientRect(mat, 300, x - 3, y - 3, x + width + 3, y - 3 + 1, borderColorStart, borderColorStart); - GuiUtils.drawGradientRect(mat, 300, x - 3, y + height + 2, x + width + 3, y + height + 3, borderColorEnd, borderColorEnd); + public static void renderFloatingText(PoseStack poseStack, String text, Vec3 vec, int color, float scale, float yOffset) { + renderFloatingText(poseStack, text, vec.x + 0.5, vec.y + 1.5, vec.z + 0.5, color, scale, true, yOffset, true); + } + + public static void renderFloatingText(PoseStack poseStack, String text, double x, double y, double z, int color, float scale, boolean center, float yOffset, boolean seeThrough) { + Minecraft minecraft = Minecraft.getInstance(); + MultiBufferSource.BufferSource buffer = minecraft.renderBuffers().bufferSource(); + Camera camera = minecraft.gameRenderer.getMainCamera(); + if (camera.isInitialized()) { + Font font = minecraft.font; + double viewX = camera.getPosition().x; + double viewY = camera.getPosition().y; + double viewZ = camera.getPosition().z; + poseStack.pushPose(); + poseStack.translate((float) (x - viewX), (float) (y - viewY) + 0.07F, (float) (z - viewZ)); + poseStack.mulPoseMatrix(new Matrix4f(camera.rotation())); + poseStack.scale(-scale, -scale, scale); + float fontX = center ? (float) (-font.width(text)) / 2.0F : 0.0F; + font.drawInBatch(text, fontX, yOffset, color, false, poseStack.last().pose(), buffer, seeThrough, 0, 0xf000f0); + poseStack.popPose(); + buffer.endBatch(); + } } } diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/util/TeleportHelper.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/util/TeleportHelper.java new file mode 100644 index 000000000..f6338a6b7 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/util/TeleportHelper.java @@ -0,0 +1,41 @@ +package com.github.tartaricacid.touhoulittlemaid.util; + +import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.tags.FluidTags; +import net.minecraft.world.level.block.state.BlockState; + +public final class TeleportHelper { + public static boolean teleport(EntityMaid maid) { + if (!maid.level.isClientSide() && maid.isAlive()) { + double x = maid.getX() + (maid.getRandom().nextDouble() - 0.5) * 16; + double y = maid.getY() + maid.getRandom().nextInt(16) - 8; + double z = maid.getZ() + (maid.getRandom().nextDouble() - 0.5) * 16; + return teleport(maid, x, y, z); + } else { + return false; + } + } + + public static boolean teleport(EntityMaid maid, double x, double y, double z) { + BlockPos.MutableBlockPos blockPos = new BlockPos.MutableBlockPos(x, y, z); + while (blockPos.getY() > maid.level.getMinBuildHeight() && !maid.level.getBlockState(blockPos).getMaterial().blocksMotion()) { + blockPos.move(Direction.DOWN); + } + BlockState blockState = maid.level.getBlockState(blockPos); + boolean isMotion = blockState.getMaterial().blocksMotion(); + boolean isWater = blockState.getFluidState().is(FluidTags.WATER); + if (isMotion && !isWater) { + boolean teleportIsSuccess = maid.randomTeleport(x, y, z, true); + if (teleportIsSuccess && !maid.isSilent()) { + maid.level.playSound(null, maid.xo, maid.yo, maid.zo, SoundEvents.ENDERMAN_TELEPORT, maid.getSoundSource(), 1.0F, 1.0F); + maid.playSound(SoundEvents.ENDERMAN_TELEPORT, 1.0F, 1.0F); + } + return teleportIsSuccess; + } else { + return false; + } + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/world/data/MaidInfo.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/world/data/MaidInfo.java new file mode 100644 index 000000000..c35b9cf30 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/world/data/MaidInfo.java @@ -0,0 +1,48 @@ +package com.github.tartaricacid.touhoulittlemaid.world.data; + +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; + +import java.util.UUID; + +public final class MaidInfo { + private final String dimension; + private final BlockPos chunkPos; + private final UUID ownerId; + private final UUID entityId; + private final long timestamp; + private final Component name; + + public MaidInfo(String dimension, BlockPos chunkPos, UUID ownerId, UUID entityId, long timestamp, Component name) { + this.dimension = dimension; + this.chunkPos = chunkPos; + this.ownerId = ownerId; + this.entityId = entityId; + this.timestamp = timestamp; + this.name = name; + } + + public String getDimension() { + return dimension; + } + + public BlockPos getChunkPos() { + return chunkPos; + } + + public UUID getOwnerId() { + return ownerId; + } + + public UUID getEntityId() { + return entityId; + } + + public long getTimestamp() { + return timestamp; + } + + public Component getName() { + return name; + } +} \ No newline at end of file diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/world/data/MaidWorldData.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/world/data/MaidWorldData.java new file mode 100644 index 000000000..22371d2c4 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/world/data/MaidWorldData.java @@ -0,0 +1,195 @@ +package com.github.tartaricacid.touhoulittlemaid.world.data; + +import com.github.tartaricacid.touhoulittlemaid.entity.item.EntityTombstone; +import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.NbtUtils; +import net.minecraft.nbt.Tag; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.saveddata.SavedData; +import net.minecraft.world.level.storage.DimensionDataStorage; + +import javax.annotation.Nullable; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +public class MaidWorldData extends SavedData { + private static final String IDENTIFIER = "touhou_little_maid_world_data"; + private static final String MAID_INFOS_TAG = "MaidInfos"; + private static final String MAID_TOMBSTONES_TAG = "MaidTombstones"; + private final Map> infos = Maps.newHashMap(); + private final Map> tombstones = Maps.newHashMap(); + + @Nullable + public static MaidWorldData get(Level level) { + if (level instanceof ServerLevel) { + ServerLevel overWorld = level.getServer().getLevel(Level.OVERWORLD); + if (overWorld == null) { + return null; + } + DimensionDataStorage storage = overWorld.getDataStorage(); + MaidWorldData data = storage.computeIfAbsent(MaidWorldData::load, MaidWorldData::new, IDENTIFIER); + data.setDirty(); + return data; + } + return null; + } + + public static MaidWorldData load(CompoundTag tag) { + MaidWorldData data = new MaidWorldData(); + if (tag.contains(MAID_INFOS_TAG, Tag.TAG_COMPOUND)) { + CompoundTag infosTag = tag.getCompound(MAID_INFOS_TAG); + for (String key : infosTag.getAllKeys()) { + ListTag listTag = infosTag.getList(key, Tag.TAG_COMPOUND); + for (int i = 0; i < listTag.size(); i++) { + CompoundTag infoTag = listTag.getCompound(i); + String dimension = infoTag.getString("Dimension"); + BlockPos chunkPos = NbtUtils.readBlockPos(infoTag.getCompound("ChunkPos")); + UUID ownerId = infoTag.getUUID("OwnerId"); + UUID maidId = infoTag.getUUID("MaidId"); + long timestamp = infoTag.getLong("Timestamp"); + MutableComponent name = Component.Serializer.fromJson(infoTag.getString("Name")); + List maidInfos = data.infos.computeIfAbsent(ownerId, uuid -> Lists.newArrayList()); + maidInfos.add(new MaidInfo(dimension, chunkPos, ownerId, maidId, timestamp, name)); + } + } + } + if (tag.contains(MAID_TOMBSTONES_TAG, Tag.TAG_COMPOUND)) { + CompoundTag tombstonesTag = tag.getCompound(MAID_TOMBSTONES_TAG); + for (String key : tombstonesTag.getAllKeys()) { + ListTag listTag = tombstonesTag.getList(key, Tag.TAG_COMPOUND); + for (int i = 0; i < listTag.size(); i++) { + CompoundTag infoTag = listTag.getCompound(i); + String dimension = infoTag.getString("Dimension"); + BlockPos chunkPos = NbtUtils.readBlockPos(infoTag.getCompound("ChunkPos")); + UUID ownerId = infoTag.getUUID("OwnerId"); + UUID tombstoneId = infoTag.getUUID("TombstoneId"); + long timestamp = infoTag.getLong("Timestamp"); + MutableComponent name = Component.Serializer.fromJson(infoTag.getString("Name")); + List tombstoneInfos = data.tombstones.computeIfAbsent(ownerId, uuid -> Lists.newArrayList()); + tombstoneInfos.add(new MaidInfo(dimension, chunkPos, ownerId, tombstoneId, timestamp, name)); + } + } + } + return data; + } + + @Override + public CompoundTag save(CompoundTag tag) { + CompoundTag infosTag = new CompoundTag(); + infos.forEach((id, data) -> { + ListTag listTag = new ListTag(); + data.forEach(info -> { + CompoundTag infoTag = new CompoundTag(); + infoTag.putString("Dimension", info.getDimension()); + infoTag.put("ChunkPos", NbtUtils.writeBlockPos(info.getChunkPos())); + infoTag.putUUID("OwnerId", info.getOwnerId()); + infoTag.putUUID("MaidId", info.getEntityId()); + infoTag.putLong("Timestamp", info.getTimestamp()); + infoTag.putString("Name", Component.Serializer.toJson(info.getName())); + listTag.add(infoTag); + }); + infosTag.put(id.toString(), listTag); + }); + + CompoundTag tombstonesTag = new CompoundTag(); + tombstones.forEach((id, data) -> { + ListTag listTag = new ListTag(); + data.forEach(info -> { + CompoundTag infoTag = new CompoundTag(); + infoTag.putString("Dimension", info.getDimension()); + infoTag.put("ChunkPos", NbtUtils.writeBlockPos(info.getChunkPos())); + infoTag.putUUID("OwnerId", info.getOwnerId()); + infoTag.putUUID("TombstoneId", info.getEntityId()); + infoTag.putLong("Timestamp", info.getTimestamp()); + infoTag.putString("Name", Component.Serializer.toJson(info.getName())); + listTag.add(infoTag); + }); + tombstonesTag.put(id.toString(), listTag); + }); + tag.put(MAID_INFOS_TAG, infosTag); + tag.put(MAID_TOMBSTONES_TAG, tombstonesTag); + return tag; + } + + public void addInfo(MaidInfo info) { + UUID ownerId = info.getOwnerId(); + List maidInfos = this.infos.computeIfAbsent(ownerId, uuid -> Lists.newArrayList()); + maidInfos.add(info); + this.setDirty(); + } + + public void addInfo(EntityMaid maid) { + String dimension = maid.level.dimension().location().toString(); + BlockPos chunkPos = maid.blockPosition(); + UUID ownerId = maid.getOwnerUUID(); + UUID maidId = maid.getUUID(); + long timestamp = System.currentTimeMillis(); + Component name = maid.getDisplayName(); + this.addInfo(new MaidInfo(dimension, chunkPos, ownerId, maidId, timestamp, name)); + } + + public void removeInfo(EntityMaid maid) { + UUID ownerId = maid.getOwnerUUID(); + if (this.infos.containsKey(ownerId)) { + UUID maidId = maid.getUUID(); + this.infos.get(ownerId).removeIf(info -> info.getEntityId().equals(maidId)); + this.setDirty(); + } + } + + @Nullable + public List getInfos(UUID owner) { + return infos.get(owner); + } + + @Nullable + public List getPlayerMaidInfos(Player player) { + return this.infos.get(player.getUUID()); + } + + public void addTombstones(MaidInfo info) { + UUID ownerId = info.getOwnerId(); + List tombstoneInfos = this.tombstones.computeIfAbsent(ownerId, uuid -> Lists.newArrayList()); + tombstoneInfos.add(info); + this.setDirty(); + } + + public void addTombstones(EntityMaid maid, EntityTombstone tombstone) { + String dimension = maid.level.dimension().location().toString(); + BlockPos chunkPos = maid.blockPosition(); + UUID ownerId = maid.getOwnerUUID(); + UUID tombstoneId = tombstone.getUUID(); + long timestamp = System.currentTimeMillis(); + Component name = maid.getDisplayName(); + this.addTombstones(new MaidInfo(dimension, chunkPos, ownerId, tombstoneId, timestamp, name)); + } + + public void removeTombstones(EntityTombstone tombstone) { + UUID ownerId = tombstone.getOwnerId(); + if (this.tombstones.containsKey(ownerId)) { + UUID tombstoneId = tombstone.getUUID(); + this.tombstones.get(ownerId).removeIf(info -> info.getEntityId().equals(tombstoneId)); + this.setDirty(); + } + } + + @Nullable + public List getTombstones(UUID owner) { + return tombstones.get(owner); + } + + @Nullable + public List getPlayerMaidTombstones(Player player) { + return this.tombstones.get(player.getUUID()); + } +} diff --git a/src/main/resources/assets/touhou_little_maid/animation/maid.animation.json b/src/main/resources/assets/touhou_little_maid/animation/maid.animation.json index 23f421a4f..cdf3dc5fd 100644 --- a/src/main/resources/assets/touhou_little_maid/animation/maid.animation.json +++ b/src/main/resources/assets/touhou_little_maid/animation/maid.animation.json @@ -2202,6 +2202,302 @@ } } }, + "gomoku": { + "animation_length": 0.375, + "bones": { + "Root": { + "rotation": [0, 0, 0], + "position": [0, -5, 0], + "scale": 1 + }, + "UpBody": { + "rotation": [15, 0, 0], + "position": [0, 0, 0] + }, + "LeftArm": { + "rotation": [-68.659, 23.92746, -39.32269], + "position": [0, 0, 0] + }, + "LeftForeArm": { + "rotation": [-60.94031, 19.61291, -8.71337], + "position": [0, 0, 0] + }, + "RightArm": { + "rotation": [-87.59409, -11.75726, 65.47122], + "position": [0, 0, 0] + }, + "RightForeArm": { + "rotation": [-47.5, 0, 0], + "position": [0, 0, 0] + }, + "LeftLeg": { + "rotation": [-142.5, 0, 0], + "position": [0, 0, 0] + }, + "LeftLowerLeg": { + "rotation": [105, 0, 0], + "position": [0, 0, 0] + }, + "LeftFoot": { + "rotation": [37.5, 0, 0], + "position": [0, 0, 0] + }, + "RightLeg": { + "rotation": [-127.5, 0, 0], + "position": [0, 0, 0] + }, + "RightLowerLeg": { + "rotation": [75, 0, 0], + "position": [0, 0, 0] + }, + "RightFoot": { + "rotation": [55, 0, 0], + "position": [0, 0, 0] + }, + "AllBody": { + "rotation": [0, 0, 0], + "position": [0, -10.5, 0], + "scale": 1 + }, + "AllHead": { + "rotation": [0, 0, 0], + "position": [0, 0, 0] + }, + "Head": { + "rotation": [-15, 0, 0], + "position": [0, 0, 0] + }, + "LongHair": { + "rotation": [15, 0, 0] + }, + "Skirt": { + "rotation": [-123.71393, -4.07256, -8.03263], + "position": [0.3, -3.45, 1.8] + } + } + }, + "bookshelf": { + "animation_length": 0.375, + "bones": { + "Root": { + "rotation": [0, 0, 0], + "position": [0, -5, 0], + "scale": 1 + }, + "UpBody": { + "rotation": [15, 0, 0], + "position": [0, 0, 0] + }, + "LeftArm": { + "rotation": [-68.659, 23.92746, -39.32269], + "position": [0, 0, 0] + }, + "LeftForeArm": { + "rotation": [-60.94031, 19.61291, -8.71337], + "position": [0, 0, 0] + }, + "RightArm": { + "rotation": [-87.59409, -11.75726, 65.47122], + "position": [0, 0, 0] + }, + "RightForeArm": { + "rotation": [-47.5, 0, 0], + "position": [0, 0, 0] + }, + "LeftLeg": { + "rotation": [-142.5, 0, 0], + "position": [0, 0, 0] + }, + "LeftLowerLeg": { + "rotation": [105, 0, 0], + "position": [0, 0, 0] + }, + "LeftFoot": { + "rotation": [37.5, 0, 0], + "position": [0, 0, 0] + }, + "RightLeg": { + "rotation": [-127.5, 0, 0], + "position": [0, 0, 0] + }, + "RightLowerLeg": { + "rotation": [75, 0, 0], + "position": [0, 0, 0] + }, + "RightFoot": { + "rotation": [55, 0, 0], + "position": [0, 0, 0] + }, + "AllBody": { + "rotation": [0, 0, 0], + "position": [0, -10.5, 0], + "scale": 1 + }, + "AllHead": { + "rotation": [0, 0, 0], + "position": [0, 0, 0] + }, + "Head": { + "rotation": [-15, 0, 0], + "position": [0, 0, 0] + }, + "LongHair": { + "rotation": [15, 0, 0] + }, + "Skirt": { + "rotation": [-123.71393, -4.07256, -8.03263], + "position": [0.3, -3.45, 1.8] + } + } + }, + "computer": { + "animation_length": 0.375, + "bones": { + "Root": { + "rotation": [0, 0, 0], + "position": [0, -5, 0], + "scale": 1 + }, + "UpBody": { + "rotation": [15, 0, 0], + "position": [0, 0, 0] + }, + "LeftArm": { + "rotation": [-68.659, 23.92746, -39.32269], + "position": [0, 0, 0] + }, + "LeftForeArm": { + "rotation": [-60.94031, 19.61291, -8.71337], + "position": [0, 0, 0] + }, + "RightArm": { + "rotation": [-87.59409, -11.75726, 65.47122], + "position": [0, 0, 0] + }, + "RightForeArm": { + "rotation": [-47.5, 0, 0], + "position": [0, 0, 0] + }, + "LeftLeg": { + "rotation": [-142.5, 0, 0], + "position": [0, 0, 0] + }, + "LeftLowerLeg": { + "rotation": [105, 0, 0], + "position": [0, 0, 0] + }, + "LeftFoot": { + "rotation": [37.5, 0, 0], + "position": [0, 0, 0] + }, + "RightLeg": { + "rotation": [-127.5, 0, 0], + "position": [0, 0, 0] + }, + "RightLowerLeg": { + "rotation": [75, 0, 0], + "position": [0, 0, 0] + }, + "RightFoot": { + "rotation": [55, 0, 0], + "position": [0, 0, 0] + }, + "AllBody": { + "rotation": [0, 0, 0], + "position": [0, -10.5, 0], + "scale": 1 + }, + "AllHead": { + "rotation": [0, 0, 0], + "position": [0, 0, 0] + }, + "Head": { + "rotation": [-15, 0, 0], + "position": [0, 0, 0] + }, + "LongHair": { + "rotation": [15, 0, 0] + }, + "Skirt": { + "rotation": [-123.71393, -4.07256, -8.03263], + "position": [0.3, -3.45, 1.8] + } + } + }, + "keyboard": { + "animation_length": 0.375, + "bones": { + "Root": { + "rotation": [0, 0, 0], + "position": [0, -5, 0], + "scale": 1 + }, + "UpBody": { + "rotation": [15, 0, 0], + "position": [0, 0, 0] + }, + "LeftArm": { + "rotation": [-68.659, 23.92746, -39.32269], + "position": [0, 0, 0] + }, + "LeftForeArm": { + "rotation": [-60.94031, 19.61291, -8.71337], + "position": [0, 0, 0] + }, + "RightArm": { + "rotation": [-87.59409, -11.75726, 65.47122], + "position": [0, 0, 0] + }, + "RightForeArm": { + "rotation": [-47.5, 0, 0], + "position": [0, 0, 0] + }, + "LeftLeg": { + "rotation": [-142.5, 0, 0], + "position": [0, 0, 0] + }, + "LeftLowerLeg": { + "rotation": [105, 0, 0], + "position": [0, 0, 0] + }, + "LeftFoot": { + "rotation": [37.5, 0, 0], + "position": [0, 0, 0] + }, + "RightLeg": { + "rotation": [-127.5, 0, 0], + "position": [0, 0, 0] + }, + "RightLowerLeg": { + "rotation": [75, 0, 0], + "position": [0, 0, 0] + }, + "RightFoot": { + "rotation": [55, 0, 0], + "position": [0, 0, 0] + }, + "AllBody": { + "rotation": [0, 0, 0], + "position": [0, -10.5, 0], + "scale": 1 + }, + "AllHead": { + "rotation": [0, 0, 0], + "position": [0, 0, 0] + }, + "Head": { + "rotation": [-15, 0, 0], + "position": [0, 0, 0] + }, + "LongHair": { + "rotation": [15, 0, 0] + }, + "Skirt": { + "rotation": [-123.71393, -4.07256, -8.03263], + "position": [0.3, -3.45, 1.8] + } + } + }, "sleep": { "animation_length": 0.375, "bones": { diff --git a/src/main/resources/assets/touhou_little_maid/blockstates/bookshelf.json b/src/main/resources/assets/touhou_little_maid/blockstates/bookshelf.json new file mode 100644 index 000000000..6a0feaf80 --- /dev/null +++ b/src/main/resources/assets/touhou_little_maid/blockstates/bookshelf.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "touhou_little_maid:block/bookshelf" + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/touhou_little_maid/blockstates/computer.json b/src/main/resources/assets/touhou_little_maid/blockstates/computer.json new file mode 100644 index 000000000..c32ecf26d --- /dev/null +++ b/src/main/resources/assets/touhou_little_maid/blockstates/computer.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "touhou_little_maid:block/computer" + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/touhou_little_maid/blockstates/gomoku.json b/src/main/resources/assets/touhou_little_maid/blockstates/gomoku.json new file mode 100644 index 000000000..ed4e84f45 --- /dev/null +++ b/src/main/resources/assets/touhou_little_maid/blockstates/gomoku.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "touhou_little_maid:block/gomoku" + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/touhou_little_maid/blockstates/keyboard.json b/src/main/resources/assets/touhou_little_maid/blockstates/keyboard.json new file mode 100644 index 000000000..ef300beaa --- /dev/null +++ b/src/main/resources/assets/touhou_little_maid/blockstates/keyboard.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "touhou_little_maid:block/keyboard" + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/touhou_little_maid/blockstates/shrine.json b/src/main/resources/assets/touhou_little_maid/blockstates/shrine.json new file mode 100644 index 000000000..e0b01befc --- /dev/null +++ b/src/main/resources/assets/touhou_little_maid/blockstates/shrine.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "touhou_little_maid:block/shrine" + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/touhou_little_maid/lang/en_us.json b/src/main/resources/assets/touhou_little_maid/lang/en_us.json index 298b1c779..123096b88 100644 --- a/src/main/resources/assets/touhou_little_maid/lang/en_us.json +++ b/src/main/resources/assets/touhou_little_maid/lang/en_us.json @@ -5,14 +5,19 @@ "entity.touhou_little_maid.danmaku": "Danmaku", "entity.touhou_little_maid.box": "Cake Box", "entity.touhou_little_maid.throw_power_point": "Power Point", + "entity.touhou_little_maid.tombstone": "Tombstone", + "entity.touhou_little_maid.tombstone.display": "Laid to rest here", "item_group.touhou_little_maid.main": "Touhou Little Maid: Main", "item_group.touhou_little_maid.chair": "Touhou Little Maid: Chair", "item_group.touhou_little_maid.garage_kit": "Touhou Little Maid: Garage Kit", "item.touhou_little_maid.maid_spawn_egg": "Maid Spawn Egg", "item.touhou_little_maid.fairy_spawn_egg": "Fairy Maid Spawn Egg", - "item.touhou_little_maid.maid_backpack_small": "Small BackPack", - "item.touhou_little_maid.maid_backpack_middle": "Middle BackPack", - "item.touhou_little_maid.maid_backpack_big": "Big BackPack", + "item.touhou_little_maid.maid_backpack_small": "Small Backpack", + "item.touhou_little_maid.maid_backpack_middle": "Middle Backpack", + "item.touhou_little_maid.maid_backpack_big": "Big Backpack", + "item.touhou_little_maid.crafting_table_backpack": "Crafting Table Backpack", + "item.touhou_little_maid.ender_chest_backpack": "Ender Chest Backpack", + "item.touhou_little_maid.furnace_backpack": "Furnace Backpack", "item.touhou_little_maid.chair": "Chair", "item.touhou_little_maid.hakurei_gohei": "Hakurei's Gohei", "item.touhou_little_maid.extinguisher": "Extinguisher", @@ -37,10 +42,20 @@ "item.touhou_little_maid.trumpet": "Trumpet", "item.touhou_little_maid.wireless_io": "§6Gap", "item.touhou_little_maid.chair_show": "Chair Display", + "item.touhou_little_maid.red_fox_scroll": "Red Fox Scroll", + "item.touhou_little_maid.white_fox_scroll": "White Fox Scroll", + "item.touhou_little_maid.favorability_tool_add": "Favorability Tool", + "item.touhou_little_maid.favorability_tool_full": "Favorability Tool", + "item.touhou_little_maid.favorability_tool_reduce": "Favorability Tool", "block.touhou_little_maid.maid_bed": "Maid Bed", "block.touhou_little_maid.garage_kit": "Garage Kit", "block.touhou_little_maid.maid_beacon": "Shrine Lamp", "block.touhou_little_maid.model_switcher": "Model Switcher", + "block.touhou_little_maid.gomoku": "Gomoku", + "block.touhou_little_maid.keyboard": "Keyboard", + "block.touhou_little_maid.bookshelf": "Bookshelf", + "block.touhou_little_maid.computer": "Computer", + "block.touhou_little_maid.shrine": "Shrine", "tooltips.touhou_little_maid.chair.place.desc": "Right click on the top of the block to place it.", "tooltips.touhou_little_maid.chair.destroy.desc": "Shift left hit can destroy it.", "tooltips.touhou_little_maid.chair.gui.desc": "Shift right click can open skin gui.", @@ -52,6 +67,8 @@ "tooltips.touhou_little_maid.info.favorability": "Favorability", "tooltips.touhou_little_maid.bauble.desc": "§a[Maid Bauble]", "tooltips.touhou_little_maid.bauble.usage": "Need to be placed in the maid's bauble inventory", + "tooltips.touhou_little_maid.backpack.desc": "§6[Maid Backpack]", + "tooltips.touhou_little_maid.backpack.usage": "Use this item to right-click the maid", "tooltips.touhou_little_maid.substitute_jizo.desc": "Right click on a maid to make it invulnerable, creative only", "tooltips.touhou_little_maid.photo.no_data.desc": "This Photo Is Missing Maid Data", "tooltips.touhou_little_maid.film.no_data.desc": "This Film Is Missing Maid Data", @@ -76,6 +93,13 @@ "tooltips.touhou_little_maid.custom_sound.credit": "Play a credit sound", "tooltips.touhou_little_maid.custom_sound.open_url": "Open the sound pack's url", "tooltips.touhou_little_maid.custom_sound.play_sound": "Click to play the sound", + "tooltips.touhou_little_maid.fox_scroll.dimension": "Dimension: %s", + "tooltips.touhou_little_maid.fox_scroll.position": "Position: [%s]", + "tooltips.touhou_little_maid.fox_scroll.red": "Used to find maids on unloaded chunks", + "tooltips.touhou_little_maid.fox_scroll.white": "Used to find maid's tombstone", + "tooltips.touhou_little_maid.favorability_tool.add": "Right click maid to add 64 (shift is 1) point favorability,", + "tooltips.touhou_little_maid.favorability_tool.reduce": "Right click maid to reduce 64 (shift is 1) point favorability", + "tooltips.touhou_little_maid.favorability_tool.full": "Right click maid to add full favorability", "gui.touhou_little_maid.schedule.day": "Day Shift", "gui.touhou_little_maid.schedule.night": "Night Shift", "gui.touhou_little_maid.schedule.all": "All Day", @@ -162,6 +186,11 @@ "gui.touhou_little_maid.custom_sound.pack.title": "§l§nMaid Sound Pack", "gui.touhou_little_maid.custom_sound.pack.apply": "Apply Sound Pack", "gui.touhou_little_maid.custom_sound.sounds.preview": "§l§nSound Preview", + "gui.touhou_little_maid.fox_scroll.position": "Pos: [%s]", + "gui.touhou_little_maid.fox_scroll.distance.same_dimension": "Distance: %dm", + "gui.touhou_little_maid.fox_scroll.distance.different_dimension": "Distance: ???m", + "gui.touhou_little_maid.fox_scroll.track": "Track", + "gui.touhou_little_maid.fox_scroll.empty": "There are no any maid coordinates", "message.touhou_little_maid.change_model.disabled": "Model switch is disabled.", "message.touhou_little_maid.extinguisher.player_cannot_use": "Your lack of knowledge does not know how to use this item", "message.touhou_little_maid.altar.not_enough_power": "You don't have enough power.", @@ -170,6 +199,14 @@ "message.touhou_little_maid.photo.have_no_nbt_data": "This damnable photo don't have NBT data.", "message.touhou_little_maid.chisel.hit_block_error": "Please right click on the clay block", "message.touhou_little_maid.chisel.offhand_not_photo": "Please have a photo of the maid in your offhand", + "message.touhou_little_maid.gomoku.win": "Player Win", + "message.touhou_little_maid.gomoku.lose": "Player Lost", + "message.touhou_little_maid.gomoku.reset": "Right click the chess box to reset", + "message.touhou_little_maid.gomoku.round": "Round: %d", + "message.touhou_little_maid.gomoku.no_maid": "There are no maid participating in the gomoku game", + "message.touhou_little_maid.trumpet.unloaded_maid": "There are also %d maids on unloaded chunks, use the Red Fox Scroll to find them!", + "message.touhou_little_maid.shrine.not_film": "Only film can be placed in the shrine!", + "message.touhou_little_maid.shrine.health_low": "Player's HP is too low!", "commands.touhou_little_maid.pack.reload.info": "All model pack have been reloaded!", "commands.touhou_little_maid.power.handle.info": "%d players's power point is changed", "commands.touhou_little_maid.maid_num.handle.info": "%d players's maid num is changed", @@ -243,6 +280,7 @@ "task.touhou_little_maid.torch": "Torch", "task.touhou_little_maid.feed_animal": "Breed", "task.touhou_little_maid.extinguishing": "Extinguishing", + "task.touhou_little_maid.board_games": "Games", "task.touhou_little_maid.desc.title": "[Tips]", "task.touhou_little_maid.desc.condition": "[Extra Condition]", "task.touhou_little_maid.idle.desc": "Maid will not do anything in this task", @@ -267,6 +305,7 @@ "task.touhou_little_maid.feed_animal.desc": "Maid will try to breed the surrounding animals", "task.touhou_little_maid.extinguishing.desc": "Maid will try to extinguish the surrounding fire", "task.touhou_little_maid.extinguishing.condition.has_extinguisher": "Mainhand holds a extinguisher", + "task.touhou_little_maid.board_games.desc": "Maid will search around the gomoku board", "button.touhou_little_maid.maid.mode.idle": "Idle", "button.touhou_little_maid.maid.mode.attack": "Attack", "button.touhou_little_maid.maid.mode.range_attack": "Range Attack", @@ -328,6 +367,8 @@ "subtitle.touhou_little_maid.item.camera_use": "Camera Use Sound", "subtitle.touhou_little_maid.item.album_open": "Album Open Sound", "subtitle.touhou_little_maid.block.altar_craft": "Altar Craft Sound", + "subtitle.touhou_little_maid.block.gomoku": "Gomoku Sound", + "subtitle.touhou_little_maid.block.gomoku_reset": "Gomoku Reset Sound", "subtitle.touhou_little_maid.entity.box": "Box Open Sound", "subtitle.touhou_little_maid.item.compass": "Compass Select Point Sound", "jei.touhou_little_maid.altar_craft.title": "Altar Craft", @@ -340,6 +381,8 @@ "top.touhou_little_maid.entity_maid.task": "Mode: ", "top.touhou_little_maid.entity_maid.invulnerable": "Invulnerable", "top.touhou_little_maid.entity_maid.schedule": "Schedule: ", + "top.touhou_little_maid.entity_maid.favorability": "Favorability Level: %d", + "top.touhou_little_maid.entity_maid.nex_favorability_point": "%d points to upgrade", "domesticationinnovation.touhou_little_maid.enchantment.undead_curse": "§4Undead Curse enchantment cannot be applied to maids!", "domesticationinnovation.touhou_little_maid.enchantment.blazing_protection": "§4Blazing Protection enchantment cannot be applied to maids!", "config.jade.plugin_touhou_little_maid.maid": "Show Maid's Extra Info", @@ -396,6 +439,8 @@ "patchouli.touhou_little_maid.book.entries.maid.snowball_fight.pages.0.text": "When the maid is on the snow and in idle task, the maid throws snowballs at the player or other maids", "patchouli.touhou_little_maid.book.entries.maid.chat_bubbles.name": "Chat Bubbles", "patchouli.touhou_little_maid.book.entries.maid.chat_bubbles.pages.0.text": "The maid occasionally displays several chat bubbles, which can also be customized through model packs", + "patchouli.touhou_little_maid.book.entries.maid.other_backpack.name": "Other Backpack", + "patchouli.touhou_little_maid.book.entries.maid.other_backpack.pages.0.text": "Some backpacks have other functions, such as crafting table backpack, furnace backpack, and ender chest backpack", "patchouli.touhou_little_maid.book.entries.other.chisel_and_statues.name": "Chisel & Statues", "patchouli.touhou_little_maid.book.entries.other.chisel_and_statues.pages.0.text": "Right-click the clay with a chisel can make statues of different sizes. Currently supports 1x1x1, 1x1x2, 2x2x4, 3x3x6 sizes.", "patchouli.touhou_little_maid.book.entries.other.chisel_and_statues.pages.1.text": "The player holds a photo of the maid in offhand, the mainhand holds the chisel, then right-clicks the lower left corner of the multi-block structure to engrave the maid in the photo.", @@ -425,6 +470,16 @@ "patchouli.touhou_little_maid.book.entries.other.model_switcher.pages.1.text": "First you need to right-click on the maid with the block in your hand, then place it on the ground and open the GUI to set the model you want to show.$(br2)Then enter the redstone signal on its left and right sides to switch the maid model.", "patchouli.touhou_little_maid.book.entries.other.chair_show.name": "Chair Show", "patchouli.touhou_little_maid.book.entries.other.chair_show.pages.0.text": "When holding the item and pressing the sneak button, all chair will show the collision box", + "patchouli.touhou_little_maid.book.entries.other.favorability_tool.name": "Favorability Tool", + "patchouli.touhou_little_maid.book.entries.other.favorability_tool.pages.0.text": "Right click on the maid to change maid's favorability, creative mode only.", + "patchouli.touhou_little_maid.book.entries.other.fox_scroll.name": "Fox Scroll", + "patchouli.touhou_little_maid.book.entries.other.fox_scroll.pages.0.text": "Use to find maid or maid's tombstone.", + "patchouli.touhou_little_maid.book.entries.other.gomoku.name": "Gomoku", + "patchouli.touhou_little_maid.book.entries.other.gomoku.pages.0.text": "You can play gomoku with the maid, and winning will increase the maid's favorability.", + "patchouli.touhou_little_maid.book.entries.other.maid_joy.name": "Maid's Joy", + "patchouli.touhou_little_maid.book.entries.other.maid_joy.pages.0.text": "Maid will play on it during her idle schedule, which can increase her favorability.", + "patchouli.touhou_little_maid.book.entries.other.shrine.name": "Shrine", + "patchouli.touhou_little_maid.book.entries.other.shrine.pages.0.text": "It can be found with a low probability in loot chest. It can consume the player's health and reborn the maid in the film.", "patchouli.touhou_little_maid.book.entries.overview.gohei_and_danmaku.name": "Gohei And Danmaku", "patchouli.touhou_little_maid.book.entries.overview.gohei_and_danmaku.pages.0.text": "The gohei is a normal melee weapon.$(br2)The maid holding a gohei will shoot danmaku in danmaku task.", "patchouli.touhou_little_maid.book.entries.overview.gohei_and_danmaku.pages.1.text": "The gohei is an important tool for building a multi-block altar.", @@ -452,5 +507,7 @@ "patchouli.touhou_little_maid.book.entries.overview.download_custom_model.pages.2.link_text": "Discord Link", "patchouli.touhou_little_maid.book.entries.overview.vanilla_change.name": "Vanilla Change", "patchouli.touhou_little_maid.book.entries.overview.vanilla_change.pages.0.text": "We modified the slime model to the yukkuri.", - "patchouli.touhou_little_maid.book.entries.overview.vanilla_change.pages.1.text": "We also changed the textures of Experience Orbs, Bottle of Experience, and Totem of Undying. $(br2)These can all be turned off in the configuration file." + "patchouli.touhou_little_maid.book.entries.overview.vanilla_change.pages.1.text": "We also changed the textures of Experience Orbs, Bottle of Experience, and Totem of Undying. $(br2)These can all be turned off in the configuration file.", + "patchouli.touhou_little_maid.book.entries.overview.favorability.name": "Favorability", + "patchouli.touhou_little_maid.book.entries.overview.favorability.pages.0.text": "Favorability can increase the maid's health and attack.$(br2)Improve maid favorability:$(li)When the maid in idle schedule, she plays with the keyboard, computer, bookshelf and gomoku.$(li)Maid sleep.$(br2)Reduce maid favorability:$(li)Death of the maid." } diff --git a/src/main/resources/assets/touhou_little_maid/lang/es_es.json b/src/main/resources/assets/touhou_little_maid/lang/es_es.json index e4f699f17..c78c351ad 100644 --- a/src/main/resources/assets/touhou_little_maid/lang/es_es.json +++ b/src/main/resources/assets/touhou_little_maid/lang/es_es.json @@ -10,9 +10,6 @@ "item_group.touhou_little_maid.garage_kit": "Touhou Little Maid: Kit de Garage", "item.touhou_little_maid.maid_spawn_egg": "Generar Maid", "item.touhou_little_maid.fairy_spawn_egg": "Generar Hada Maid", - "item.touhou_little_maid.maid_backpack_small": "Mochila Pequeña", - "item.touhou_little_maid.maid_backpack_middle": "Mochila Mediana", - "item.touhou_little_maid.maid_backpack_big": "Mochila Grande", "item.touhou_little_maid.chair": "Silla", "item.touhou_little_maid.hakurei_gohei": "Gohei de Hakurei", "item.touhou_little_maid.extinguisher": "Extintor", @@ -94,7 +91,6 @@ "gui.touhou_little_maid.button.maid_riding_set.false": "Montar Entidad está deshabilitada", "gui.touhou_little_maid.button.maid_riding_set.desc": "§Haz click en este botón para establecer si la maid va a seguir al jugador", "gui.touhou_little_maid.button.model_download": "Abrir el GUI de descarga para el paquete de Modelos", - "gui.touhou_little_maid.button.sound_download": "Abrir el GUI de descarga para el paquete de Sonidos", "gui.touhou_little_maid.name_tag.edit_box": "Cuadro de edición de etiqueta", "gui.touhou_little_maid.tag.always_show": "Siempre mostrar el nombre", "gui.touhou_little_maid.wireless_io.filter_mode": "Cambiar Filtro", @@ -140,7 +136,6 @@ "gui.touhou_little_maid.resources_download.tab.1": "Paquete de Recursos", "gui.touhou_little_maid.resources_download.tab.2": "Paquete de Recursos", "gui.touhou_little_maid.resources_download.tab.3": "Paquetes de recursos de Maid", - "gui.touhou_little_maid.resources_download.sound.downloaded.tip": "¡El paquete de sonido necesita ser cargado como paquetes de recursos de vainilla antes de poder ser usado!", "gui.touhou_little_maid.model_gui.easter_egg.encrypt": "This is an encrypted named easter egg model", "gui.touhou_little_maid.model_gui.easter_egg.normal": "gui. touhou_little_maid. model_gui. easter_egg. normal", "gui.touhou_little_maid.maid_config.sound_frequency": "Frecuencias de sonido de la Sirvienta", @@ -238,7 +233,6 @@ "patchouli.touhou_little_maid.book.entries.maid.maid_base.name": "Maid base", "patchouli.touhou_little_maid.book.entries.maid.maid_base.pages.0.text": "Hay muchas funciones básicas que las maids pueden hacer, tales como:$(li)por defecto, la doncella recogerá activamente los items, flechas orbes de experiencia que estén a su alrededor. La GUI principal tiene para cambiar el modo de recogida;$(li)Por defecto, la maid siempre seguirá al jugador.", "patchouli.touhou_little_maid.book.entries.maid.maid_base.pages.1.text": "$(li)Shift + click derecho en la maid teniendo la mano vacía la cambiara al modo sentado. En este modo, no hará nada, incluyendo evitar el peligro.$(li)Cuando la este montando alguna entidad, shit + click derecho con una mano vacía la bajara de dicha entidad.$(li)El jugador puede usar la manzana dorada o una poción en una maid apretando click derecho en ella sosteniendo el item.", - "patchouli.touhou_little_maid.book.entries.maid.maid_base.pages.2.text": "$(li)La experiencia obtenida por la maid se convierte en botellas de experiencia si el jugador hace click derecho sosteniendo una botella vacía.$(li)La maid se puede equipar una cráneo y otros modelos.$(li) Haga click derecho a una maid sin nombre,y podrás nombrarla directamente.", "patchouli.touhou_little_maid.book.entries.maid.maid_bauble.name": "Amuletos de la sirvienta", "patchouli.touhou_little_maid.book.entries.maid.maid_bauble.pages.0.text": "Este mod proporciona varios amuletos que se pueden activar para proteger a tus maids kawaii bajo ciertas condiciones, necesitan ser colocadas en el inventario de amuletos (bauble).$(li)Elixir Orbe Ultramarina: Se puede usar 6 veces para evitar la muerte de una majestuosa maid.", "patchouli.touhou_little_maid.book.entries.maid.maid_bauble.pages.1.text": "$(li)Joya de protección: Cuando la maid recibe el daño correspondiente, el evento de daño sera cancelado.$(li)Tela de agilidad: Cuando una maid es equipada con esta joya si es golpeada por un proyectil, por ejemplo una flecha, tendrá el mismo comportamiento que un enderman, osea se va a teletransportar.", @@ -268,7 +262,6 @@ "patchouli.touhou_little_maid.book.entries.overview.multiblocks_altar.pages.1.text": "Presiona el click derecho en el centro de la lana roja con $(l:overview/gohei_and_danmaku)gohei$() para construir el altar.", "patchouli.touhou_little_maid.book.entries.overview.multiblocks_altar.pages.3.text": "Puedes colocar un item dando click derecho en la parte superior de los seis pilares de madera del altar. Puedes remover un item teniendo la mano vacía y dando clilc derecho.$(br2)Cuando los items son colocados y los puntos de poder del jugador son suficientes, el altar activara el crafteo.$(br2)El crafteo puede ser interrumpido debido a la falta de poder, después de obtener el poder suficiente. haga click derecho para reactivar el crafteo.", "patchouli.touhou_little_maid.book.entries.overview.power_point.name": "Puntos de poder", - "patchouli.touhou_little_maid.book.entries.overview.power_point.pages.1.text": "Cuando sostienes el $(l:overview/gohei_and_danmaku)gohei$(), Puedes ver tus puntos de poder en la esquina superior izquierda de la pantalla.$(br2)El poder maximo que se puede obtener es 5, no subira mas de ese numero.", "patchouli.touhou_little_maid.book.entries.overview.what_is_touhou.pages.0.text": "Touhou project es una serie de juegos shoot 'em up creada por un único miembro de Team Shanghai Alice conocido como ZUN.", "patchouli.touhou_little_maid.book.entries.overview.world_spawn.name": "Punto de aparición" } diff --git a/src/main/resources/assets/touhou_little_maid/lang/fr_fr.json b/src/main/resources/assets/touhou_little_maid/lang/fr_fr.json index 733782d14..ed69a4168 100644 --- a/src/main/resources/assets/touhou_little_maid/lang/fr_fr.json +++ b/src/main/resources/assets/touhou_little_maid/lang/fr_fr.json @@ -105,7 +105,6 @@ "patchouli.touhou_little_maid.book.entries.maid.item_magnet.pages.0.text": "Une babiole qui peut étendre la portée de ramassage. $(br2)Après avoir été équipé sur une Maid, la portée de ramassage d'objets, de flèches, d'orbes d'EXP et de Point P sera considérablement augmentée.", "patchouli.touhou_little_maid.book.entries.maid.maid_base.name": "Maid : Les bases", "patchouli.touhou_little_maid.book.entries.maid.maid_base.pages.0.text": "Il y a beaucoup de fonctions basique que les maids sont en mesure d'exécuter, comme :$(li)par défaut, la maid ramasse automatiquement les objets, les flèches et les orbes d'EXP proche d'elle. L'interface de la maid a un bouton pour changer le mode de ramassage;$(li)Par défaut, la maid suivra toujours le joueur.", - "patchouli.touhou_little_maid.book.entries.maid.maid_base.pages.2.text": "$(li)L'expérience obtenue par la maid peut être convertie en fiole d'expérience si le joueur fait un clic droit sur elle tout en tenant une fiole vide.$(li)La maid peut équiper un crâne, l'adaptant à son modèle.$(li)Faites un clic droit sur la maid avec une étiquette pour la renommer.", "patchouli.touhou_little_maid.book.entries.maid.maid_config.name": "Configuration de Maid", "patchouli.touhou_little_maid.book.entries.maid.maid_sound.name": "Bruitage de Maid", "patchouli.touhou_little_maid.book.entries.maid.maid_task.name": "Tâche de Maid", @@ -129,7 +128,6 @@ "patchouli.touhou_little_maid.book.entries.overview.multiblocks_altar.pages.1.text": "Clic droit avec le $(l:overview/gohei_and_danmaku)Gohei$() sur la laine rouge au centre gauche pour le construire.", "patchouli.touhou_little_maid.book.entries.overview.multiblocks_altar.pages.3.text": "Placer un objet en faisant un clic droit avec sur le haut d'un des piliers du sanctuaire. Vous pouvez le retirer de la même façon en ayant la main vide.$(br2)Une fois les objets placé et avec suffisamment de Points P, le craft s’activera. Il sera cependant interrompu en cas d'un manque de Points P et devras être relancer avec un clic droit.", "patchouli.touhou_little_maid.book.entries.overview.power_point.name": "Point P", - "patchouli.touhou_little_maid.book.entries.overview.power_point.pages.1.text": "Lorsque vous tenez le $(l:overview/gohei_and_danmaku)Gohei$(), vous pouvez voir votre nombre de point P dans le coin supérieur gauche de l'écran du jeu. $(br2)Vous ne pouvez pas avoir plus de 5 Point P.", "patchouli.touhou_little_maid.book.entries.overview.what_is_touhou.pages.0.text": "Le projet Touhou est une série de jeux indé de genre shoot'em up créés par le seul membre de la Team Shanghai Alice connu sous le nom de ZUN.", "patchouli.touhou_little_maid.book.entries.overview.world_spawn.name": "Spawn de créature" } diff --git a/src/main/resources/assets/touhou_little_maid/lang/ja_jp.json b/src/main/resources/assets/touhou_little_maid/lang/ja_jp.json index 6c8df253a..a264e6dcf 100644 --- a/src/main/resources/assets/touhou_little_maid/lang/ja_jp.json +++ b/src/main/resources/assets/touhou_little_maid/lang/ja_jp.json @@ -10,9 +10,6 @@ "item_group.touhou_little_maid.garage_kit": "東方リトルメイド: ガレージキット", "item.touhou_little_maid.maid_spawn_egg": "東方リトルメイドのスポーンエッグ", "item.touhou_little_maid.fairy_spawn_egg": "妖精メイドのスポーンエッグ", - "item.touhou_little_maid.maid_backpack_small": "小さいバックパック", - "item.touhou_little_maid.maid_backpack_middle": "中くらいのバックパック", - "item.touhou_little_maid.maid_backpack_big": "大きいバックパック", "item.touhou_little_maid.chair": "チェア", "item.touhou_little_maid.hakurei_gohei": "博麗のお祓い棒", "item.touhou_little_maid.extinguisher": "消火器", @@ -69,7 +66,7 @@ "tooltips.touhou_little_maid.wireless_io.filter_mode.whitelist": "ホワイトリスト", "tooltips.touhou_little_maid.wireless_io.binding_pos.has": "チェストの座標: %d, %d, %d", "tooltips.touhou_little_maid.wireless_io.binding_pos.none": "チェストが設定されていません", - "tooltips.touhou_little_maid.maid_beacon.desc": "溜まったPower:%s", + "tooltips.touhou_little_maid.maid_beacon.desc": "溜まったP点:%s", "gui.touhou_little_maid.schedule.day": "日勤", "gui.touhou_little_maid.schedule.night": "夜勤", "gui.touhou_little_maid.schedule.all": "24時間労働", @@ -95,7 +92,6 @@ "gui.touhou_little_maid.button.maid_riding_set.false": "騎乗モードが無効です", "gui.touhou_little_maid.button.maid_riding_set.desc": "§7このボタンをクリックして、メイドが他のエンティティに乗るかどうかを設定します。", "gui.touhou_little_maid.button.model_download": "メイドのリソースパックのダウンロードGUIを開く", - "gui.touhou_little_maid.button.sound_download": "メイドのサウンドパックのダウンロードGUIを開く", "gui.touhou_little_maid.name_tag.edit_box": "名前変更欄", "gui.touhou_little_maid.tag.always_show": "常に名前を表示するか", "gui.touhou_little_maid.wireless_io.filter_mode": "フィルターモードの切り替え", @@ -141,12 +137,11 @@ "gui.touhou_little_maid.resources_download.tab.1": "メイド リソースパック", "gui.touhou_little_maid.resources_download.tab.2": "チェア リソースパック", "gui.touhou_little_maid.resources_download.tab.3": "サウンド リソースパック", - "gui.touhou_little_maid.resources_download.sound.downloaded.tip": "サウンドパックを使用するには、バニラのリソースパックのようにロードする必要があります!", "gui.touhou_little_maid.model_gui.easter_egg.encrypt": "This is an encrypted named easter egg model", "gui.touhou_little_maid.model_gui.easter_egg.normal": "これは通常のイースターエッグモデルです", "gui.touhou_little_maid.maid_config.sound_frequency": "メイドの声の周波数", "gui.touhou_little_maid.maid_config.show_chat_bubble": "チャットバブルを表示する", - "gui.touhou_little_maid.maid_config.show_backpack": "白色バックパック", + "gui.touhou_little_maid.maid_config.show_backpack": "バックパックを表示する", "message.touhou_little_maid.change_model.disabled": "メイドのモデルの変更が無効化されています。", "message.touhou_little_maid.extinguisher.player_cannot_use": "知識不足のためアイテムの使い方がわかりません。", "message.touhou_little_maid.altar.not_enough_power": "P点が足りません。", @@ -263,7 +258,6 @@ "patchouli.touhou_little_maid.book.entries.maid.maid_base.name": "メイドの基本", "patchouli.touhou_little_maid.book.entries.maid.maid_base.pages.0.text": "メイドは多くの基本的機能があります。$(li)デフォルトでは、メイドは周辺のアイテムと経験値オーブを回収します。メイドノメインGUIにはピックアップモードを切り替えるボタンがあります。$(li)デフォルトでは、メイドは常にプレイヤーの傍に居ます。", "patchouli.touhou_little_maid.book.entries.maid.maid_base.pages.1.text": "$(li)素手でメイドをShift+右クリックすると待機モードに切り替わります。待機モードでは危険を回避する行動を含め何もしません。$(li)メイドが乗り物に乗っているとき、素手でShift+右クリックすると降ろすことができます。$(li)プレイヤーは金リンゴやポーションを持ちながらメイドに向かって右クリックするとメイドに使用できます。", - "patchouli.touhou_little_maid.book.entries.maid.maid_base.pages.2.text": "$(li)メイドが拾った経験値オーブは経験値ポーションにできます。空の瓶を持ってメイドに向かって右クリック。$(li)メイドは被り物を身に着けることでモデルをレンダリングできます。$(li)空の名札を持ちメイドに右クリックをすると直接名前を付けることができます。", "patchouli.touhou_little_maid.book.entries.maid.maid_bauble.name": "メイド専用 宝玉", "patchouli.touhou_little_maid.book.entries.maid.maid_bauble.pages.0.text": "このModは、特定の条件下でメイドさんを保護するためにトリガーすることができるいくつかの宝玉を提供しています、これらは宝玉のインベントリに配置する必要があります。$(li)Ultramarine Orb Elixir: メイドの死を避けるために6回使用できます。", "patchouli.touhou_little_maid.book.entries.maid.maid_bauble.pages.1.text": "$(li)耐性の宝玉:特定の条件下の時、ダメージを無効化します。$(li)ひらり布:遠距離攻撃がメイドに向かってきた時、エンダーマンのようにテレポートします。", @@ -281,7 +275,7 @@ "patchouli.touhou_little_maid.book.entries.maid.mute_bauble.pages.0.text": "この宝玉をもたせると、メイドは喋らなくなります。", "patchouli.touhou_little_maid.book.entries.maid.naming_eggs.name": "スポーンエッグの名付け", "patchouli.touhou_little_maid.book.entries.maid.naming_eggs.pages.0.text": "メイドに特定の名前を名付けるとモデルが変わります。$(br2)通常、$(#ff0000)=>id$()という名前のメイドは、そのIDに対応するプレイヤーのモデルになることができます。$(br2)メイドのリソースパックでカスタムのモデルのスポーンエッグを追加することもできます。", - "patchouli.touhou_little_maid.book.entries.maid.naming_eggs.pages.1.text": "名前$(#ff0000)=>Notch$() を持つメイド最強", + "patchouli.touhou_little_maid.book.entries.maid.naming_eggs.pages.1.text": "$(#ff0000)=>Notch$() と名付けられたメイド", "patchouli.touhou_little_maid.book.entries.maid.spawn_maid.name": "メイドの召喚", "patchouli.touhou_little_maid.book.entries.maid.spawn_maid.pages.0.text": "メイドさんを召喚する方法は非常に特殊であり、祭壇を介して行う必要があります。$(br2)レシピは今後変更される可能性があるので、JEIでご確認ください。$(br2)メイドさんが祭壇に上手く召喚された後、ケーキボックスが発生します。右クリックして開封できます。", "patchouli.touhou_little_maid.book.entries.maid.spawn_maid.pages.1.text": "ケーキボックスは3回右クリックする必要があります。$(br2)メイドを雇うにはケーキが必要です。雇うのに必要なアイテムは設定から変更できます。$(br2)雇われたメイドはバニラのペットと同じような行動を取ります。メイドを右クリックするとそのメイドのGUIを開くことができます。", @@ -327,7 +321,6 @@ "patchouli.touhou_little_maid.book.entries.overview.multiblocks_altar.pages.3.text": "祭壇のオークの柱上部を右クリックするとアイテムを設置できます。手ぶらで右クリックしてアイテムを回収します。$(br2)アイテムがレシピ通りに設置されプレイヤーのP点が足りている時、祭壇はクラフトを開始します。$(br2)P点不足でクラフトが中断された場合、十分なP点が得られてからもう一度右クリックすることでクラフトがまた開始されます。", "patchouli.touhou_little_maid.book.entries.overview.power_point.name": "P点", "patchouli.touhou_little_maid.book.entries.overview.power_point.pages.0.text": "P点はこのmodにとって重要な素材です。$(l:overview/world_spawn#fairy)妖精メイド$()を倒すとドロップします。$(br2)$(l:overview/multiblocks_altar)祭壇$() でのクラフトにも消費します。", - "patchouli.touhou_little_maid.book.entries.overview.power_point.pages.1.text": "$(l:overview/gohei_and_danmaku)お祓い棒$()を持っているとき、ゲーム画面の左上に現在の所持P点が表示されます。 $(br2)P点の最大値は5で、これ以上は増えません。", "patchouli.touhou_little_maid.book.entries.overview.what_is_touhou.name": "東方プロジェクト", "patchouli.touhou_little_maid.book.entries.overview.what_is_touhou.pages.0.text": "東方プロジェクトは、日本の同人サークル[上海アリス幻想楽団]のZUN氏がプロデュースするファンダムプロダクションのシリーズです。", "patchouli.touhou_little_maid.book.entries.overview.what_is_touhou.pages.0.link_text": "東方wiki", @@ -337,5 +330,8 @@ "patchouli.touhou_little_maid.book.entries.overview.make_custom_model.name": "カスタムモデルを作成", "patchouli.touhou_little_maid.book.entries.overview.make_custom_model.pages.1.link_text": "リンクを開く", "patchouli.touhou_little_maid.book.entries.overview.download_custom_model.name": "カスタムモデルをダウンロードする", - "patchouli.touhou_little_maid.book.entries.overview.download_custom_model.pages.2.link_text": "Discordリンク" + "patchouli.touhou_little_maid.book.entries.overview.download_custom_model.pages.2.link_text": "Discordリンク", + "patchouli.touhou_little_maid.book.entries.overview.vanilla_change.name": "バニラ要素の変更", + "patchouli.touhou_little_maid.book.entries.overview.vanilla_change.pages.0.text": "スライムのモデルをゆっくりに変更しました。", + "patchouli.touhou_little_maid.book.entries.overview.vanilla_change.pages.1.text": "また、経験値オーブ、エンチャントの瓶、不死のトーテムのテクスチャも変更しました。 $(br2)これらはすべて設定ファイルでオフにできます。" } diff --git a/src/main/resources/assets/touhou_little_maid/lang/ko_kr.json b/src/main/resources/assets/touhou_little_maid/lang/ko_kr.json index 93b568079..2d7301ef4 100644 --- a/src/main/resources/assets/touhou_little_maid/lang/ko_kr.json +++ b/src/main/resources/assets/touhou_little_maid/lang/ko_kr.json @@ -10,9 +10,6 @@ "item_group.touhou_little_maid.garage_kit": "레진 피규어", "item.touhou_little_maid.maid_spawn_egg": "메이드 생성 알", "item.touhou_little_maid.fairy_spawn_egg": "요정 메이드 생성 알", - "item.touhou_little_maid.maid_backpack_small": "소형 배낭", - "item.touhou_little_maid.maid_backpack_middle": "중형 배낭", - "item.touhou_little_maid.maid_backpack_big": "대형 배낭", "item.touhou_little_maid.chair": "의자", "item.touhou_little_maid.hakurei_gohei": "하쿠레이의 무녀봉", "item.touhou_little_maid.extinguisher": "소화기", @@ -145,7 +142,6 @@ "patchouli.touhou_little_maid.book.entries.maid.backpack.name": "배낭", "patchouli.touhou_little_maid.book.entries.maid.maid_base.name": "메이드 기본 정보", "patchouli.touhou_little_maid.book.entries.maid.maid_base.pages.0.text": "메이드는 기본적으로 많은 일들을 할 수 있습니다. 예를 들어 $(li)기본적으로 메이드는 직접적으로 아이템, 화살, 경험치 오브를 주울 수 있습니다. 주 GUI에 이를 조절하는 버튼이 있습니다.;$(li)또한 기본적으로 메이드는 항상 플레이어를 따라다닙니다.", - "patchouli.touhou_little_maid.book.entries.maid.maid_base.pages.2.text": "$(li)메이드가 얻은 경험치 오브는 병을 들고 메이드를 우클릭시 경험치 병으로 바꿀 수 있습니다.$(li)메이드는 몬스터 머리를 착용할 수 있습니다.$(li)빈 이름표를 들고 메이드를 우클릭시 바로 메이드의 이름을 바꿀 수 있습니다.", "patchouli.touhou_little_maid.book.entries.maid.maid_config.name": "메이드 설정", "patchouli.touhou_little_maid.book.entries.maid.maid_sound.name": "메이스 음성", "patchouli.touhou_little_maid.book.entries.maid.maid_task.name": "메이드 직업", diff --git a/src/main/resources/assets/touhou_little_maid/lang/pt_br.json b/src/main/resources/assets/touhou_little_maid/lang/pt_br.json index 30f8a1904..b9b42596c 100644 --- a/src/main/resources/assets/touhou_little_maid/lang/pt_br.json +++ b/src/main/resources/assets/touhou_little_maid/lang/pt_br.json @@ -8,9 +8,6 @@ "item_group.touhou_little_maid.chair": "Touhou Little Maid: Cadeiras", "item.touhou_little_maid.maid_spawn_egg": "Ovo Gerador de Empregada", "item.touhou_little_maid.fairy_spawn_egg": "Ovo Gerador de Fada da Empregada", - "item.touhou_little_maid.maid_backpack_small": "Mochila pequena", - "item.touhou_little_maid.maid_backpack_middle": "§a[Mochila]", - "item.touhou_little_maid.maid_backpack_big": "§c[Mochilão]", "item.touhou_little_maid.chair": "Cadeiras", "item.touhou_little_maid.hakurei_gohei": "Gohei de Hakurei", "item.touhou_little_maid.extinguisher": "Extintor", @@ -92,7 +89,6 @@ "gui.touhou_little_maid.button.maid_riding_set.false": "Montagem de Entidades está desabilitada", "gui.touhou_little_maid.button.maid_riding_set.desc": "§7Clique neste botão para definir se a empregada vai montar em outras entidades", "gui.touhou_little_maid.button.model_download": "Abrir Interface de Baixar Pacote de Recurso", - "gui.touhou_little_maid.button.sound_download": "Abrir Interface de Baixar Pacote de Som", "gui.touhou_little_maid.name_tag.edit_box": "Caixa de Editar Etiqueta", "gui.touhou_little_maid.tag.always_show": "Sempre mostrar o nome", "gui.touhou_little_maid.wireless_io.filter_mode": "Trocar Modo do Filtro", @@ -138,7 +134,6 @@ "gui.touhou_little_maid.resources_download.tab.1": "Pacotes de Recursos da Empregada", "gui.touhou_little_maid.resources_download.tab.2": "Pacotes de Recursos da Cadeira", "gui.touhou_little_maid.resources_download.tab.3": "Pacotes de Recursos de Som", - "gui.touhou_little_maid.resources_download.sound.downloaded.tip": "O pacote de som precisa ser carregado como os pacotes de recursos do vanilla antes de poder ser usado!", "gui.touhou_little_maid.model_gui.easter_egg.encrypt": "Este é um nome criptografado modelo easter egg", "gui.touhou_little_maid.model_gui.easter_egg.normal": "Este é um nome normal modelo easter egg", "gui.touhou_little_maid.maid_config.show_chat_bubble": "Mostrar Bolhas de Bate-Papo", @@ -259,7 +254,6 @@ "patchouli.touhou_little_maid.book.entries.maid.maid_base.name": "Suporte de Maid", "patchouli.touhou_little_maid.book.entries.maid.maid_base.pages.0.text": "Há muitas funções básicas que as maids são capazes de executar, tais como:$(li)por padrão, a maid irá coletar itens, flechas e pontos de xp em torno dela. A interface principal tem um botão para alternar o modo de coleta;$(li)por padrão, a maid sempre seguirá o jogador.", "patchouli.touhou_little_maid.book.entries.maid.maid_base.pages.1.text": "$(li) shift+clique com o botão direito com a mão vazia na empregada pode mudaro modo para sentada. No modo sentada, a empregada não faz nada, inclusive evitar o perigo.$(li)Quando a empregada monta em outras entidades, Shift+clique com o botão direito com a mão vazia pode desmontá-la.$(li)O jogador pode usar uma maçã dourada ou poção em uma empregada clicando com o botão direito do mouse enquanto segura o item.", - "patchouli.touhou_little_maid.book.entries.maid.maid_base.pages.2.text": "$(li)A experiência adquirida pela maid pode ser convertida em um frasco de encantamento se o jogador clicar com o botão direito da empregada enquanto segura um frasco vazio.$(li)A maid pode equipar uma cabeça e renderizar um modelo.$(li)Botão direito do mouse com uma placa de identificação vazia, e você poderá nomeá-la diretamente.", "patchouli.touhou_little_maid.book.entries.maid.maid_bauble.name": "Ornamento da Empregada", "patchouli.touhou_little_maid.book.entries.maid.maid_bauble.pages.0.text": "Este mod fornece vários ornamentos que podem ser acionadas para proteger as empregadas sob certas condições, elas precisam ser colocadas no inventário de ornamentos.$(li)Orbe de Elixir Ultramarino: Pode ser usado 6 vezes para evitar a morte da empregada.", "patchouli.touhou_little_maid.book.entries.maid.maid_bauble.pages.1.text": "$(li)Ornamento Protetor: Quando a empregada recebe o dano correspondente, o evento de dano é cancelado.$(li)Tecido Ágil: Quando uma empregada equipada com este ornamento é atingida por um projétil, seu comportamento será o mesmo do enderman.", @@ -324,7 +318,6 @@ "patchouli.touhou_little_maid.book.entries.overview.multiblocks_altar.pages.3.text": "Você pode colocar o item clicando com o botão direito do mouse no topo dos seis pilares de carvalho do altar. Você pode remover o item clicando com o botão direito do mouse com as mãos vazias.$(br2)Quando o item está completamente colocado e o ponto de poder do jogador é suficiente, o altar pode acionar a criação.$(br2)A criação é interrompida devido à falta de poder, e depois de obter poder suficiente, clique com o botão direito novamente para reativar a criação.", "patchouli.touhou_little_maid.book.entries.overview.power_point.name": "Ponto de Poder", "patchouli.touhou_little_maid.book.entries.overview.power_point.pages.0.text": "O ponto de poder é um recurso importante para este mod, que é obtido matando a $(l:overview/world_spawn#fairy)fada da empregada$().$(br2)Pode ser usado para criação do $(l:overview/multiblocks_altar)altar$().", - "patchouli.touhou_little_maid.book.entries.overview.power_point.pages.1.text": "Quando você segura o $(l:overview/gohei_and_danmaku)gohei$(), você pode ver o número do seu ponto de poder no canto superior esquerdo da tela do jogo. $(br2)O ponto de poder máximo é 5, e mais do que esse número o ponto de poder não aumenta mais.", "patchouli.touhou_little_maid.book.entries.overview.what_is_touhou.name": "Touhou Project", "patchouli.touhou_little_maid.book.entries.overview.what_is_touhou.pages.0.text": "O Touhou Project é uma série de jogos de doujin japonesa de ZUN, membro da Sole Team Shanghai Alice, especializada em Shoot 'em Ups.", "patchouli.touhou_little_maid.book.entries.overview.what_is_touhou.pages.0.link_text": "Touhou Wiki", @@ -332,7 +325,6 @@ "patchouli.touhou_little_maid.book.entries.overview.world_spawn.pages.0.text": "Este mod adiciona um mob. $(br2)O saque do mob é um importante ingrediente de criação.$(br2)A chance de gerar criaturas hostis neste mod pode ser modificada no arquivo de configuração. Quando definido como 0, esses monstros são proibidos de aparecer.", "patchouli.touhou_little_maid.book.entries.overview.world_spawn.pages.1.text": "Gerar em qualquer bioma à noite, ela voará e atirará danmaku para atacar jogadores ou empregadas.", "patchouli.touhou_little_maid.book.entries.overview.make_custom_model.name": "Criar Modelo Personalizado", - "patchouli.touhou_little_maid.book.entries.overview.make_custom_model.pages.0.text": "Criar um pacote de modelo personalizado é muito simples, tudo que você precisa é do software Blockbench, Blockbench é um software gratuito e de código aberto que é fácil de usar.", "patchouli.touhou_little_maid.book.entries.overview.make_custom_model.pages.1.text": "Eu desenhei especialmente um plugin para fazer pacote de modelo para Blockbench, acesse o link abaixo para saber como instalar o plugin.", "patchouli.touhou_little_maid.book.entries.overview.make_custom_model.pages.1.link_text": "Abrir Link", "patchouli.touhou_little_maid.book.entries.overview.download_custom_model.name": "Baixar Modelo Personalizado", diff --git a/src/main/resources/assets/touhou_little_maid/lang/ru_ru.json b/src/main/resources/assets/touhou_little_maid/lang/ru_ru.json index 32416231b..6586f261c 100644 --- a/src/main/resources/assets/touhou_little_maid/lang/ru_ru.json +++ b/src/main/resources/assets/touhou_little_maid/lang/ru_ru.json @@ -10,9 +10,6 @@ "item_group.touhou_little_maid.garage_kit": "Touhou Little Maid: Фигурка", "item.touhou_little_maid.maid_spawn_egg": "Яйцо призыва горничной", "item.touhou_little_maid.fairy_spawn_egg": "Яйцо призыва феи-горничной", - "item.touhou_little_maid.maid_backpack_small": "Маленький рюкзак", - "item.touhou_little_maid.maid_backpack_middle": "Средний рюкзак", - "item.touhou_little_maid.maid_backpack_big": "Большой рюкзак", "item.touhou_little_maid.chair": "Стул", "item.touhou_little_maid.hakurei_gohei": "Посох Очищения Хакурей", "item.touhou_little_maid.extinguisher": "Огнетушитель", @@ -94,7 +91,6 @@ "gui.touhou_little_maid.button.maid_riding_set.false": "Поездка на энтити выключена", "gui.touhou_little_maid.button.maid_riding_set.desc": "§7Нажмите эту кнопку, чтобы установить будет ли горничная следовать за энтити", "gui.touhou_little_maid.button.model_download": "Открыть Интерфейс установки паков моделей", - "gui.touhou_little_maid.button.sound_download": "Открыть Интерфейс установки звуковых паков", "gui.touhou_little_maid.name_tag.edit_box": "Коробка смены бирки", "gui.touhou_little_maid.tag.always_show": "Всегда показывать имя", "gui.touhou_little_maid.wireless_io.filter_mode": "Переключить режим фильтра", @@ -140,7 +136,6 @@ "gui.touhou_little_maid.resources_download.tab.1": "Ресурс Паки Горничных", "gui.touhou_little_maid.resources_download.tab.2": "Ресурс Паки Стульев", "gui.touhou_little_maid.resources_download.tab.3": "Ресурс Паки Звуков", - "gui.touhou_little_maid.resources_download.sound.downloaded.tip": "Перед использованием необходимо загрузить набор звуков, как обычные наборы ресурсов!", "gui.touhou_little_maid.model_gui.easter_egg.encrypt": "Это секретная модель с зашифрованным именем", "gui.touhou_little_maid.model_gui.easter_egg.normal": "Это секретная модель с обычным именем", "message.touhou_little_maid.change_model.disabled": "Смена модели отключена.", @@ -259,7 +254,6 @@ "patchouli.touhou_little_maid.book.entries.maid.maid_base.name": "База горничной", "patchouli.touhou_little_maid.book.entries.maid.maid_base.pages.0.text": "Существует множество основных функций, которые могут выполняться, таких как:$(li)По умолчанию, горничная будет активно забирать предметы, стрелы и опыт вокруг нее. Главный графический интерфейс имеет кнопку, чтобы переключить режим поднятия предметов;$(li)По умолчанию горничная всегда будет следовать за игроком.", "patchouli.touhou_little_maid.book.entries.maid.maid_base.pages.1.text": "$(li)Шифт и Пкм по горничной с пустой рукой можно сменить посадить горничную. Сама горничная ничего делать не будет, в том числе избегая опасность.$(li)Когда горничная сидит на энтити, Шифт + ПКМ с пустой рукой, чтобы опешить её.$(li)Игрок может использовать Золотое яблоко или Зелье на горничной, нажав ПКМ с этим предметом.", - "patchouli.touhou_little_maid.book.entries.maid.maid_base.pages.2.text": "$(li)Опыт, собранный горничной, может быть превращен в бутылку опыта, если игрок щёлкает правой кнопкой мыши по горничной, держа при этом пустую бутылку.$(li)Горничная может экипировать череп и рендер модели.$(li)Щелкните правой кнопкой мыши по горничной с пустой табличкой и ее можно назвать напрямую.", "patchouli.touhou_little_maid.book.entries.maid.maid_bauble.name": "Украшение горничной", "patchouli.touhou_little_maid.book.entries.maid.maid_bauble.pages.0.text": "Этот мод предоставляет несколько украшений, которые могут защитить вашу горничную от разных ситуаций, они помещаются в инвентарь для украшений.$(li)Ультрамариновый шар-эликсир: Используется 6 раз, чтобы избежать смерти горничной.", "patchouli.touhou_little_maid.book.entries.maid.maid_bauble.pages.1.text": "$(li)Защитные украшения: когда горничная получает соответствующий урон, этот урон будет отменен.$(li)Ловкое полотно: когда горничная, оснащенная этим украшением, получает урон от дальнего оружия, её поведение будет точно таким же, как у Эндерменов.", @@ -324,7 +318,6 @@ "patchouli.touhou_little_maid.book.entries.overview.multiblocks_altar.pages.3.text": "Вы можете разместить предмет, щелкнув правой кнопкой мыши на верхней части дубового столба алтаря. Вы можете удалить этот предмет, щелкнув пустой рукой.$(br2)Когда предметы полностью размещены и очки силы игрока достаточны для создания ремесла, алтарь начнет создание.$(br2)Создание может прерываться из-за отсутствия силы, и после получения достаточной силы нажмите правую кнопку мыши снова, чтобы запустить создание.", "patchouli.touhou_little_maid.book.entries.overview.power_point.name": "Очки силы", "patchouli.touhou_little_maid.book.entries.overview.power_point.pages.0.text": "Очки силы являются важным ресурсом для этого мода, которые можно получить, убив $(l:overview/world_spawn#fairy)maid fairy$().$(br2)Могут быть использованы для создания из $(l:overview/multiblocks_altar)altar$().", - "patchouli.touhou_little_maid.book.entries.overview.power_point.pages.1.text": "Когда вы держите $(l:overview/gohei_and_danmaku)gohei$(), вы можете увидеть номер ваших очков сил в левом верхнем углу экрана. $(br2)Максимальные очки силы 5, и больше этого числа очки силы не увеличиваются.", "patchouli.touhou_little_maid.book.entries.overview.what_is_touhou.name": "Проект Восток", "patchouli.touhou_little_maid.book.entries.overview.what_is_touhou.pages.0.text": "Touhou Project - это японские додзинские серии игр, сделанные членом Team Shanghai Alice под ником ZUN, специализирующийся в Shoot 'em Ups.", "patchouli.touhou_little_maid.book.entries.overview.what_is_touhou.pages.0.link_text": "Touhou Вики", @@ -332,7 +325,6 @@ "patchouli.touhou_little_maid.book.entries.overview.world_spawn.pages.0.text": "Этот мод добавляет одного моба. $(br2)Добыча моба является важным ингредиентом для крафта.$(br2)Шанс спавна враждебных существ в этом моде можно изменить в конфигурационном файле. Если значение равно 0, то этим монстрам запрещен спавн.", "patchouli.touhou_little_maid.book.entries.overview.world_spawn.pages.1.text": "Появляется в мире ночью, будет летать и одновременно стрелять данмаку, чтобы атаковать игроков и горничных.", "patchouli.touhou_little_maid.book.entries.overview.make_custom_model.name": "Сделать Пользовательскую Модель", - "patchouli.touhou_little_maid.book.entries.overview.make_custom_model.pages.0.text": "Создание пользовательского пакета моделей очень просто, всё что вам нужно это программное обеспечение Blockbench, Blockbench - это бесплатное программное обеспечение с открытым исходным кодом, которое легко использовать.", "patchouli.touhou_little_maid.book.entries.overview.make_custom_model.pages.1.text": "Я специально разработал плагин для Blockbench для создания пака моделей. Посетите ссылку ниже, чтобы узнать, как установить плагин.", "patchouli.touhou_little_maid.book.entries.overview.make_custom_model.pages.1.link_text": "Открыть Ссылку", "patchouli.touhou_little_maid.book.entries.overview.download_custom_model.name": "Скачать пользовательскую модель", diff --git a/src/main/resources/assets/touhou_little_maid/lang/tr_tr.json b/src/main/resources/assets/touhou_little_maid/lang/tr_tr.json index 6b0c45247..1572318f7 100644 --- a/src/main/resources/assets/touhou_little_maid/lang/tr_tr.json +++ b/src/main/resources/assets/touhou_little_maid/lang/tr_tr.json @@ -8,9 +8,6 @@ "item_group.touhou_little_maid.chair": "Touhou Küçük Hizmetçi: Sandalye", "item.touhou_little_maid.maid_spawn_egg": "Hizmetçi Doğdurma Yumurtası", "item.touhou_little_maid.fairy_spawn_egg": "Peri Hizmetçi Doğurma Yumurtası", - "item.touhou_little_maid.maid_backpack_small": "Küçük Sırt Çantası", - "item.touhou_little_maid.maid_backpack_middle": "Orta sırt çantası", - "item.touhou_little_maid.maid_backpack_big": "Büyük sırt çantası", "item.touhou_little_maid.chair": "Sandalye", "item.touhou_little_maid.hakurei_gohei": "Hakurei'nin Gohei'si", "item.touhou_little_maid.extinguisher": "Yangın Sönüdürücü", @@ -46,5 +43,17 @@ "task.touhou_little_maid.feed_animal": "Çiftleştir", "task.touhou_little_maid.extinguishing": "Söndürülüyor", "task.touhou_little_maid.desc.condition": "[Extra Kondisyon]", - "task.touhou_little_maid.idle.desc": "Hizmetçi Görevde Bir şey Yapmayacak" + "task.touhou_little_maid.idle.desc": "Hizmetçi Görevde Bir şey Yapmayacak", + "patchouli.touhou_little_maid.book.categories.overview.name": "Genel Bakış", + "patchouli.touhou_little_maid.book.entries.maid.backpack.name": "Sırt çantası", + "patchouli.touhou_little_maid.book.entries.maid.backpack.pages.1.text": "Küçük sırt çantası olan bir Maid.", + "patchouli.touhou_little_maid.book.entries.maid.item_magnet.name": "Eşya Mıknatısı", + "patchouli.touhou_little_maid.book.entries.maid.maid_base.pages.3.text": "$(li)Creeper'lar Maid'lerden kaçar.", + "patchouli.touhou_little_maid.book.entries.maid.maid_bauble.name": "Maid Bauble Aksesuarı", + "patchouli.touhou_little_maid.book.entries.maid.maid_config.name": "Maid Konfigürasyonu", + "patchouli.touhou_little_maid.book.entries.maid.maid_skin.name": "Maid Dış Görünümü", + "patchouli.touhou_little_maid.book.entries.maid.maid_sound.name": "Maid Sesi", + "patchouli.touhou_little_maid.book.entries.maid.maid_task.name": "Maid Görevi", + "patchouli.touhou_little_maid.book.entries.maid.mute_bauble.name": "Bauble Aksesuarını Sustur", + "patchouli.touhou_little_maid.book.entries.maid.naming_eggs.name": "Yumurtaları İsimlendirmek" } diff --git a/src/main/resources/assets/touhou_little_maid/lang/vi_vn.json b/src/main/resources/assets/touhou_little_maid/lang/vi_vn.json index 4dbcdd9e4..99d4d413e 100644 --- a/src/main/resources/assets/touhou_little_maid/lang/vi_vn.json +++ b/src/main/resources/assets/touhou_little_maid/lang/vi_vn.json @@ -10,9 +10,6 @@ "item_group.touhou_little_maid.garage_kit": "Touhou Little Maid: Bộ dụng cụ Garage", "item.touhou_little_maid.maid_spawn_egg": "Trứng Sinh Ra Little Maid", "item.touhou_little_maid.fairy_spawn_egg": "Trứng Sinh Ra Fairy Maid", - "item.touhou_little_maid.maid_backpack_small": "Ba lô Nhỏ", - "item.touhou_little_maid.maid_backpack_middle": "Ba lô Vừa", - "item.touhou_little_maid.maid_backpack_big": "Ba lô Lớn", "item.touhou_little_maid.chair": "Ghế", "item.touhou_little_maid.hakurei_gohei": "Cây Gohei của Hakurei", "item.touhou_little_maid.extinguisher": "Bình chữa cháy", @@ -94,7 +91,6 @@ "gui.touhou_little_maid.button.maid_riding_set.false": "Cưỡi thực thể đã tắt", "gui.touhou_little_maid.button.maid_riding_set.desc": "§7Nhấn vào nút này để điều chỉnh khi nào người hầu sẽ cưỡi thực thể khác", "gui.touhou_little_maid.button.model_download": "Mở giao diện tải Gói mô hình", - "gui.touhou_little_maid.button.sound_download": "Mở giao diện tải Gói âm thanh", "gui.touhou_little_maid.name_tag.edit_box": "Hộp chỉnh sửa thẻ tên", "gui.touhou_little_maid.tag.always_show": "Điều chỉnh luôn luôn hiển thị tên", "gui.touhou_little_maid.wireless_io.filter_mode": "Đổi chế độ lọc", @@ -140,7 +136,6 @@ "gui.touhou_little_maid.resources_download.tab.1": "Gói tài nguyên của maid", "gui.touhou_little_maid.resources_download.tab.2": "Gói tài nguyên cho ghế", "gui.touhou_little_maid.resources_download.tab.3": "Gói tài nguyên âm thanh", - "gui.touhou_little_maid.resources_download.sound.downloaded.tip": "Gói tài nguyên âm thanh cần được tải lại để có thể sử dụng giống như gói tài nguyên vanilla của game!", "gui.touhou_little_maid.model_gui.easter_egg.encrypt": "Đây là tên được mã hoá của một mô hình bí mật", "gui.touhou_little_maid.model_gui.easter_egg.normal": "Đây là tên thông thường của một mô hình bí mật", "message.touhou_little_maid.change_model.disabled": "Đổi mô hình đang tắt.", @@ -255,7 +250,6 @@ "patchouli.touhou_little_maid.book.entries.maid.maid_base.name": "Cơ bản về maid", "patchouli.touhou_little_maid.book.entries.maid.maid_base.pages.0.text": "Có nhiều tính năng cơ bản mà maid có thể thực hiện, ví dụ như:$(li)Theo mặc định, maid sẽ nhặt vật phẩm, mũi tên và hạt kinh nghiệm xung quanh chúng. Giao diện chính có nút để bật, tắt chế độ nhặt;$(li)Theo mặc định, maid sẽ luôn đi theo người chơi.", "patchouli.touhou_little_maid.book.entries.maid.maid_base.pages.1.text": "$(li)Shift chuột phải maid với tay không sẽ cho maid ngồi. Khi đang ngồi, maid sẽ không làm gì cả, kể cả né tránh mối nguy hiểm.$(li)Khi maid đang cưỡi thực thể khác, shift phải chuột với tay không sẽ cho maid đi xuống.$(li)Người chơi có thể cho maid dùng táo vàng hoặc thuốc bằng cách nhấp chuột phải vào chúng với vật phẩm cầm trên tay.", - "patchouli.touhou_little_maid.book.entries.maid.maid_base.pages.2.text": "$(li)Kinh nghiệm nhặt được bởi maid có thể được chuyển đổi thành Chai kinh nghiệm nếu người chơi nhấp chuột phải vào maid với bình thuỷ tinh không.$(li)Maid có thể đội được đầu và sẽ hiển thị chúng.$(li)Bạn có thể đổi tên maid bằng cách nhấp chuột phải với Thẻ đổi tên trống trên tay.", "patchouli.touhou_little_maid.book.entries.maid.maid_bauble.name": "Trang sức cho maid", "patchouli.touhou_little_maid.book.entries.maid.maid_bauble.pages.0.text": "Mod này cung cấp một lượng nhỏ trang sức và có thể được kích hoạt để bảo vệ maid trong một số tình cảnh, chúng cần được đặt trong tuý trang sức của maid.$(li)Tiên được Ngọc Ultramarine: có thể được kích hoạt 6 lần để tránh những cái chết của maid.", "patchouli.touhou_little_maid.book.entries.maid.maid_bauble.pages.1.text": "$(li)Trang sức bảo vệ: Khi maid nhận được một lượng sát thương nhất định, lượng sát thương đó sẽ được chặn lại.$(li)Vải né tránh: Khi maid được trang bị vải này gặp một vật thể bắn tới, cô sẽ dịch chuyển để tránh vật thể như enderman.", @@ -320,7 +314,6 @@ "patchouli.touhou_little_maid.book.entries.overview.multiblocks_altar.pages.3.text": "Bạn có thể đặt vật phẩm vào các trụ gỗ bằng cách nhấp chuột phải vào chúng với vật phẩm trên tay. Bạn cũng có thể gỡ vật phẩm ra bằng cách nhấp chuột phải với tay không.$(br2)Khi vật phẩm đã được đặt đầy đủ và người chơi có đủ điểm sức mạnh, bàn thờ sẽ tự động chế tạo.$(br2)Khi bàn thờ không thể chế tạo do thiếu điểm sức mạnh, chỉ cần nhấp chuột phải vào nó sau khi đã có đủ điểm sức mạnh.", "patchouli.touhou_little_maid.book.entries.overview.power_point.name": "Điểm sức mạnh", "patchouli.touhou_little_maid.book.entries.overview.power_point.pages.0.text": "Điểm sức mạnh là thứ cực kì quan trọng trong mod này, và có thể được thu thập thông qua việc hạ gục các $(l:overview/world_spawn#fairy)tiên giúp việc$().$(br2)Điểm sức mạnh có thể được dùng trong chế tạo trên $(l:overview/multiblocks_altar)bàn thờ$().", - "patchouli.touhou_little_maid.book.entries.overview.power_point.pages.1.text": "Khi cầm $(l:overview/gohei_and_danmaku)cây gohei$() trên tay, bạn có thể thấy số điểm sức mạnh của mình ở góc trên bên trái màn hình game. $(br2)Số điểm sức mạnh bạn có thể có tối đa là 5, và bạn sẽ không thể nhận thêm điểm sức mạnh sau mức này.", "patchouli.touhou_little_maid.book.entries.overview.what_is_touhou.name": "Touhou Project", "patchouli.touhou_little_maid.book.entries.overview.what_is_touhou.pages.0.text": "Touhou Project là một dòng game doujin Nhật Bản, được phát triển độc lập bởi ZUN là thành viên duy nhất của Team Shanghai Alice, nổi bật với lối chơi Shoot 'em Ups.", "patchouli.touhou_little_maid.book.entries.overview.what_is_touhou.pages.0.link_text": "Touhou Wiki", @@ -328,7 +321,6 @@ "patchouli.touhou_little_maid.book.entries.overview.world_spawn.pages.0.text": "Mod này chỉ thêm một quá vật. $(br2)Vật phẩm rơi ra từ con quái này rất quan trọng trong việc chế tạo nguyên liệu.$(br2)Tỉ lệ xuất hiện của con quái này có thể được điều chỉnh trong tệp tuỳ chỉnh. Khi tỉ lệ được đặt là 0, con quái này sẽ không bao giờ xuất hiện.", "patchouli.touhou_little_maid.book.entries.overview.world_spawn.pages.1.text": "Xuất hiện trong moi quần thể vào đêm, nó sẽ bay xung quanh và bắn danmaku vào người chơi hoặc maid.", "patchouli.touhou_little_maid.book.entries.overview.make_custom_model.name": "Tự xây dựng mô hình", - "patchouli.touhou_little_maid.book.entries.overview.make_custom_model.pages.0.text": "Làm một gói mô hình rất đơn giản. Những gì bạn cần là phần mềm BlockBench. BlockBench là một phần mềm nguồn mở miễn phí và rất dễ dùng.", "patchouli.touhou_little_maid.book.entries.overview.make_custom_model.pages.1.text": "Tôi có làm một plugin cho BlockBench để giúp bạn nhiều hơn trong việc tự tạo gói mô hình, dùng liên kết bên dưới để biết cách tải và cài đặt plugin.", "patchouli.touhou_little_maid.book.entries.overview.make_custom_model.pages.1.link_text": "Mở liên kết", "patchouli.touhou_little_maid.book.entries.overview.download_custom_model.name": "Tải gói mô hình", diff --git a/src/main/resources/assets/touhou_little_maid/lang/zh_cn.json b/src/main/resources/assets/touhou_little_maid/lang/zh_cn.json index 9dd278754..7f4ba713f 100644 --- a/src/main/resources/assets/touhou_little_maid/lang/zh_cn.json +++ b/src/main/resources/assets/touhou_little_maid/lang/zh_cn.json @@ -5,6 +5,8 @@ "entity.touhou_little_maid.danmaku": "弹幕", "entity.touhou_little_maid.box": "蛋糕盒", "entity.touhou_little_maid.throw_power_point": "P 点", + "entity.touhou_little_maid.tombstone": "墓碑", + "entity.touhou_little_maid.tombstone.display": "在此长眠", "item_group.touhou_little_maid.main": "车万女仆丨主体", "item_group.touhou_little_maid.chair": "车万女仆丨坐垫", "item_group.touhou_little_maid.garage_kit": "车万女仆丨手办", @@ -13,6 +15,9 @@ "item.touhou_little_maid.maid_backpack_small": "小背包", "item.touhou_little_maid.maid_backpack_middle": "中背包", "item.touhou_little_maid.maid_backpack_big": "大背包", + "item.touhou_little_maid.crafting_table_backpack": "工作台背包", + "item.touhou_little_maid.ender_chest_backpack": "末影箱背包", + "item.touhou_little_maid.furnace_backpack": "熔炉背包", "item.touhou_little_maid.chair": "椅子", "item.touhou_little_maid.hakurei_gohei": "博丽的御币", "item.touhou_little_maid.extinguisher": "灭火器", @@ -37,10 +42,20 @@ "item.touhou_little_maid.trumpet": "小号", "item.touhou_little_maid.wireless_io": "§6隙间", "item.touhou_little_maid.chair_show": "坐垫显示器", + "item.touhou_little_maid.red_fox_scroll": "红狐狸卷轴", + "item.touhou_little_maid.white_fox_scroll": "白狐狸卷轴", + "item.touhou_little_maid.favorability_tool_add": "好感度工具", + "item.touhou_little_maid.favorability_tool_full": "好感度工具", + "item.touhou_little_maid.favorability_tool_reduce": "好感度工具", "block.touhou_little_maid.maid_bed": "女仆床", "block.touhou_little_maid.garage_kit": "手办", "block.touhou_little_maid.maid_beacon": "神社庭灯", "block.touhou_little_maid.model_switcher": "模型切换器", + "block.touhou_little_maid.gomoku": "五子棋", + "block.touhou_little_maid.keyboard": "电子琴", + "block.touhou_little_maid.bookshelf": "书架", + "block.touhou_little_maid.computer": "电脑", + "block.touhou_little_maid.shrine": "神龛", "tooltips.touhou_little_maid.chair.place.desc": "右击方块顶部进行放置。", "tooltips.touhou_little_maid.chair.destroy.desc": "Shift 左击破坏它。", "tooltips.touhou_little_maid.chair.gui.desc": "Shift 右击打开皮肤界面。", @@ -52,6 +67,8 @@ "tooltips.touhou_little_maid.info.favorability": "好感度", "tooltips.touhou_little_maid.bauble.desc": "§a[女仆饰品]", "tooltips.touhou_little_maid.bauble.usage": "需要放置在女仆的饰品栏中", + "tooltips.touhou_little_maid.backpack.desc": "§6[女仆背包]", + "tooltips.touhou_little_maid.backpack.usage": "使用此物品右击女仆", "tooltips.touhou_little_maid.substitute_jizo.desc": "右击女仆使其无敌,仅限创造模式", "tooltips.touhou_little_maid.photo.no_data.desc": "这张照片没有女仆数据", "tooltips.touhou_little_maid.film.no_data.desc": "这张胶片没有女仆数据", @@ -76,6 +93,13 @@ "tooltips.touhou_little_maid.custom_sound.credit": "播放一个声音包的备注说明", "tooltips.touhou_little_maid.custom_sound.open_url": "打开声音包的网络链接", "tooltips.touhou_little_maid.custom_sound.play_sound": "点击播放声音", + "tooltips.touhou_little_maid.fox_scroll.dimension": "维度:%s", + "tooltips.touhou_little_maid.fox_scroll.position": "位置:[%s]", + "tooltips.touhou_little_maid.fox_scroll.red": "用来在未加载区块上寻找女仆", + "tooltips.touhou_little_maid.fox_scroll.white": "用来寻找女仆墓碑", + "tooltips.touhou_little_maid.favorability_tool.add": "右击女仆可增加 64(潜行是 1)点好感度", + "tooltips.touhou_little_maid.favorability_tool.reduce": "右击女仆可减少 64(潜行是 1)点好感度", + "tooltips.touhou_little_maid.favorability_tool.full": "右击女仆可加满好感度", "gui.touhou_little_maid.schedule.day": "白班", "gui.touhou_little_maid.schedule.night": "夜班", "gui.touhou_little_maid.schedule.all": "全天", @@ -162,6 +186,11 @@ "gui.touhou_little_maid.custom_sound.pack.title": "§l§n女仆声音包", "gui.touhou_little_maid.custom_sound.pack.apply": "应用此声音包", "gui.touhou_little_maid.custom_sound.sounds.preview": "§l§n声音预览", + "gui.touhou_little_maid.fox_scroll.position": "位置:[%s]", + "gui.touhou_little_maid.fox_scroll.distance.same_dimension": "距离:%d 米", + "gui.touhou_little_maid.fox_scroll.distance.different_dimension": "距离:???米", + "gui.touhou_little_maid.fox_scroll.track": "追踪", + "gui.touhou_little_maid.fox_scroll.empty": "没有任何女仆坐标", "message.touhou_little_maid.change_model.disabled": "模型切换功能已被禁止", "message.touhou_little_maid.extinguisher.player_cannot_use": "你匮乏的知识并不了解这物品该如何使用", "message.touhou_little_maid.altar.not_enough_power": "你没有足够多的 P 点", @@ -170,6 +199,14 @@ "message.touhou_little_maid.photo.have_no_nbt_data": "这该死的照片没有 NBT 数据", "message.touhou_little_maid.chisel.hit_block_error": "请右击粘土块", "message.touhou_little_maid.chisel.offhand_not_photo": "请副手持有一张女仆照片", + "message.touhou_little_maid.gomoku.win": "玩家获胜", + "message.touhou_little_maid.gomoku.lose": "玩家失败", + "message.touhou_little_maid.gomoku.reset": "右击棋盒进行重置", + "message.touhou_little_maid.gomoku.round": "回合:%d", + "message.touhou_little_maid.gomoku.no_maid": "没有女仆来参与五子棋游戏", + "message.touhou_little_maid.trumpet.unloaded_maid": "在卸载区块上还有 %d 个女仆,请使用红狐狸卷轴来寻找她们!", + "message.touhou_little_maid.shrine.not_film": "只有胶片可以放入神龛中!", + "message.touhou_little_maid.shrine.health_low": "玩家血量过低!", "commands.touhou_little_maid.pack.reload.info": "所有的模型包已经重载!", "commands.touhou_little_maid.power.handle.info": "修改了 %d 位玩家的 P 点", "commands.touhou_little_maid.maid_num.handle.info": "修改了 %d 位玩家的女仆数量", @@ -243,6 +280,7 @@ "task.touhou_little_maid.torch": "火把", "task.touhou_little_maid.feed_animal": "繁殖动物", "task.touhou_little_maid.extinguishing": "灭火", + "task.touhou_little_maid.board_games": "游戏", "task.touhou_little_maid.desc.title": "[提示]", "task.touhou_little_maid.desc.condition": "[额外条件]", "task.touhou_little_maid.idle.desc": "此工作模式下,女仆什么也不会做", @@ -267,6 +305,7 @@ "task.touhou_little_maid.feed_animal.desc": "女仆会主动繁殖周围的动物", "task.touhou_little_maid.extinguishing.desc": "女仆会主动扑灭周围的火", "task.touhou_little_maid.extinguishing.condition.has_extinguisher": "主手持有灭火器", + "task.touhou_little_maid.board_games.desc": "女仆会寻找周围的五子棋盘", "button.touhou_little_maid.maid.mode.idle": "空闲", "button.touhou_little_maid.maid.mode.attack": "攻击", "button.touhou_little_maid.maid.mode.range_attack": "范围攻击", @@ -328,6 +367,8 @@ "subtitle.touhou_little_maid.item.camera_use": "相机咔哒声", "subtitle.touhou_little_maid.item.album_open": "相册:打开", "subtitle.touhou_little_maid.block.altar_craft": "祭坛:合成", + "subtitle.touhou_little_maid.block.gomoku": "五子棋:落子", + "subtitle.touhou_little_maid.block.gomoku_reset": "五子棋:重置", "subtitle.touhou_little_maid.entity.box": "盒子:打开", "subtitle.touhou_little_maid.item.compass": "罗盘:选择点", "jei.touhou_little_maid.altar_craft.title": "祭坛合成", @@ -340,6 +381,8 @@ "top.touhou_little_maid.entity_maid.task": "模式:", "top.touhou_little_maid.entity_maid.invulnerable": "无敌", "top.touhou_little_maid.entity_maid.schedule": "日程表:", + "top.touhou_little_maid.entity_maid.favorability": "好感度等级:%d", + "top.touhou_little_maid.entity_maid.nex_favorability_point": "还需要 %d 点来升级", "domesticationinnovation.touhou_little_maid.enchantment.undead_curse": "§4来世诅咒附魔无法用于女仆!", "domesticationinnovation.touhou_little_maid.enchantment.blazing_protection": "§4烈焰保护附魔无法用于女仆!", "config.jade.plugin_touhou_little_maid.maid": "显示女仆额外信息", @@ -396,6 +439,8 @@ "patchouli.touhou_little_maid.book.entries.maid.snowball_fight.pages.0.text": "当女仆在雪地中,并且处于空闲模式时,女仆就会朝玩家或者其他女仆丢雪球。", "patchouli.touhou_little_maid.book.entries.maid.chat_bubbles.name": "聊天气泡", "patchouli.touhou_little_maid.book.entries.maid.chat_bubbles.pages.0.text": "女仆偶尔会显示几个聊天气泡,这些也可以通过模型包自定义。", + "patchouli.touhou_little_maid.book.entries.maid.other_backpack.name": "其他背包", + "patchouli.touhou_little_maid.book.entries.maid.other_backpack.pages.0.text": "有些背包拥有其他功能,例如工作台背包、熔炉背包和末影箱背包", "patchouli.touhou_little_maid.book.entries.other.chisel_and_statues.name": "雕刻刀与雕像", "patchouli.touhou_little_maid.book.entries.other.chisel_and_statues.pages.0.text": "雕刻刀右击粘土可以做成不同尺寸大小的雕像,目前支持 1x1x1,1x1x2,2x2x4,3x3x6 大小尺寸。", "patchouli.touhou_little_maid.book.entries.other.chisel_and_statues.pages.1.text": "玩家副手持有女仆照片,主手持有雕刻刀,右击多方块结构的左下角,即可雕刻出照片中的女仆。", @@ -425,6 +470,16 @@ "patchouli.touhou_little_maid.book.entries.other.model_switcher.pages.1.text": "首先你需要手持此方块右击女仆。然后将其放置在地上,并右键打开 GUI 来设置你想展示的模型。$(br2)接下来在其左右两侧输入红石信号,即可切换此女仆的模型。", "patchouli.touhou_little_maid.book.entries.other.chair_show.name": "坐垫显示器", "patchouli.touhou_little_maid.book.entries.other.chair_show.pages.0.text": "当手持并按下潜行键时,所有的坐垫都会显示碰撞箱。", + "patchouli.touhou_little_maid.book.entries.other.favorability_tool.name": "好感度工具", + "patchouli.touhou_little_maid.book.entries.other.favorability_tool.pages.0.text": "右击女仆改变女仆的好感度,仅限创造模式。", + "patchouli.touhou_little_maid.book.entries.other.fox_scroll.name": "狐狸卷轴", + "patchouli.touhou_little_maid.book.entries.other.fox_scroll.pages.0.text": "用来寻找女仆或者女仆墓碑。", + "patchouli.touhou_little_maid.book.entries.other.gomoku.name": "五子棋", + "patchouli.touhou_little_maid.book.entries.other.gomoku.pages.0.text": "你可以和女仆一起玩五子棋,获胜能够增加女仆的好感度。", + "patchouli.touhou_little_maid.book.entries.other.maid_joy.name": "女仆娱乐", + "patchouli.touhou_little_maid.book.entries.other.maid_joy.pages.0.text": "女仆能够在空闲日程玩耍,这能够增加她的好感度。", + "patchouli.touhou_little_maid.book.entries.other.shrine.name": "神龛", + "patchouli.touhou_little_maid.book.entries.other.shrine.pages.0.text": "有很低概率在战利品箱中发现。能够通过消耗玩家血量来复活胶片中的女仆。", "patchouli.touhou_little_maid.book.entries.overview.gohei_and_danmaku.name": "御币与弹幕", "patchouli.touhou_little_maid.book.entries.overview.gohei_and_danmaku.pages.0.text": "御币是一个普通的近战武器。$(br2)手持御币的女仆,在弹幕模式下会发射弹幕。", "patchouli.touhou_little_maid.book.entries.overview.gohei_and_danmaku.pages.1.text": "御币还是构建多方块祭坛的重要工具。", @@ -452,5 +507,7 @@ "patchouli.touhou_little_maid.book.entries.overview.download_custom_model.pages.2.link_text": "Discord 链接", "patchouli.touhou_little_maid.book.entries.overview.vanilla_change.name": "原版修改", "patchouli.touhou_little_maid.book.entries.overview.vanilla_change.pages.0.text": "我们把史莱姆模型修改成了油库里。", - "patchouli.touhou_little_maid.book.entries.overview.vanilla_change.pages.1.text": "我们还修改了经验球、附魔之瓶和不死图腾的材质。$(br2)这些都可以在配置文件中关闭。" -} \ No newline at end of file + "patchouli.touhou_little_maid.book.entries.overview.vanilla_change.pages.1.text": "我们还修改了经验球、附魔之瓶和不死图腾的材质。$(br2)这些都可以在配置文件中关闭。", + "patchouli.touhou_little_maid.book.entries.overview.favorability.name": "好感度", + "patchouli.touhou_little_maid.book.entries.overview.favorability.pages.0.text": "好感度能够增加女仆的血量和攻击力。$(br2)提升女仆好感度:$(li)女仆在空闲日程,玩耍电子琴、电脑、书架和五子棋。$(li)女仆睡觉。$(br2)减少女仆好感度:$(li)女仆死亡。" +} diff --git a/src/main/resources/assets/touhou_little_maid/models/block/bookshelf.json b/src/main/resources/assets/touhou_little_maid/models/block/bookshelf.json new file mode 100644 index 000000000..9406a8491 --- /dev/null +++ b/src/main/resources/assets/touhou_little_maid/models/block/bookshelf.json @@ -0,0 +1,5 @@ +{ + "textures": { + "particle": "minecraft:block/oak_planks" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/touhou_little_maid/models/block/computer.json b/src/main/resources/assets/touhou_little_maid/models/block/computer.json new file mode 100644 index 000000000..9406a8491 --- /dev/null +++ b/src/main/resources/assets/touhou_little_maid/models/block/computer.json @@ -0,0 +1,5 @@ +{ + "textures": { + "particle": "minecraft:block/oak_planks" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/touhou_little_maid/models/block/gomoku.json b/src/main/resources/assets/touhou_little_maid/models/block/gomoku.json new file mode 100644 index 000000000..9406a8491 --- /dev/null +++ b/src/main/resources/assets/touhou_little_maid/models/block/gomoku.json @@ -0,0 +1,5 @@ +{ + "textures": { + "particle": "minecraft:block/oak_planks" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/touhou_little_maid/models/block/keyboard.json b/src/main/resources/assets/touhou_little_maid/models/block/keyboard.json new file mode 100644 index 000000000..9406a8491 --- /dev/null +++ b/src/main/resources/assets/touhou_little_maid/models/block/keyboard.json @@ -0,0 +1,5 @@ +{ + "textures": { + "particle": "minecraft:block/oak_planks" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/touhou_little_maid/models/block/shrine.json b/src/main/resources/assets/touhou_little_maid/models/block/shrine.json new file mode 100644 index 000000000..9406a8491 --- /dev/null +++ b/src/main/resources/assets/touhou_little_maid/models/block/shrine.json @@ -0,0 +1,5 @@ +{ + "textures": { + "particle": "minecraft:block/oak_planks" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/touhou_little_maid/models/item/bookshelf.json b/src/main/resources/assets/touhou_little_maid/models/item/bookshelf.json new file mode 100644 index 000000000..d091a8fe1 --- /dev/null +++ b/src/main/resources/assets/touhou_little_maid/models/item/bookshelf.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "touhou_little_maid:item/bookshelf" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/touhou_little_maid/models/item/computer.json b/src/main/resources/assets/touhou_little_maid/models/item/computer.json new file mode 100644 index 000000000..457c59309 --- /dev/null +++ b/src/main/resources/assets/touhou_little_maid/models/item/computer.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "touhou_little_maid:item/computer" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/touhou_little_maid/models/item/crafting_table_backpack.json b/src/main/resources/assets/touhou_little_maid/models/item/crafting_table_backpack.json new file mode 100644 index 000000000..e76d8313c --- /dev/null +++ b/src/main/resources/assets/touhou_little_maid/models/item/crafting_table_backpack.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "touhou_little_maid:item/crafting_table_backpack" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/touhou_little_maid/models/item/ender_chest_backpack.json b/src/main/resources/assets/touhou_little_maid/models/item/ender_chest_backpack.json new file mode 100644 index 000000000..73cf2aa2b --- /dev/null +++ b/src/main/resources/assets/touhou_little_maid/models/item/ender_chest_backpack.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "touhou_little_maid:item/ender_chest_backpack" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/touhou_little_maid/models/item/favorability_tool_add.json b/src/main/resources/assets/touhou_little_maid/models/item/favorability_tool_add.json new file mode 100644 index 000000000..49b4cfe8b --- /dev/null +++ b/src/main/resources/assets/touhou_little_maid/models/item/favorability_tool_add.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "touhou_little_maid:item/favorability_tool_add" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/touhou_little_maid/models/item/favorability_tool_full.json b/src/main/resources/assets/touhou_little_maid/models/item/favorability_tool_full.json new file mode 100644 index 000000000..43478f6ac --- /dev/null +++ b/src/main/resources/assets/touhou_little_maid/models/item/favorability_tool_full.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "touhou_little_maid:item/favorability_tool_full" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/touhou_little_maid/models/item/favorability_tool_reduce.json b/src/main/resources/assets/touhou_little_maid/models/item/favorability_tool_reduce.json new file mode 100644 index 000000000..4cfade987 --- /dev/null +++ b/src/main/resources/assets/touhou_little_maid/models/item/favorability_tool_reduce.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "touhou_little_maid:item/favorability_tool_reduce" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/touhou_little_maid/models/item/furnace_backpack.json b/src/main/resources/assets/touhou_little_maid/models/item/furnace_backpack.json new file mode 100644 index 000000000..9d8f95bee --- /dev/null +++ b/src/main/resources/assets/touhou_little_maid/models/item/furnace_backpack.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "touhou_little_maid:item/furnace_backpack" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/touhou_little_maid/models/item/gomoku.json b/src/main/resources/assets/touhou_little_maid/models/item/gomoku.json new file mode 100644 index 000000000..ed86a961c --- /dev/null +++ b/src/main/resources/assets/touhou_little_maid/models/item/gomoku.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "touhou_little_maid:item/gomoku" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/touhou_little_maid/models/item/keyboard.json b/src/main/resources/assets/touhou_little_maid/models/item/keyboard.json new file mode 100644 index 000000000..e8703745d --- /dev/null +++ b/src/main/resources/assets/touhou_little_maid/models/item/keyboard.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "touhou_little_maid:item/keyboard" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/touhou_little_maid/models/item/model_switcher.json b/src/main/resources/assets/touhou_little_maid/models/item/model_switcher.json index 073b577e4..dc0b567cc 100644 --- a/src/main/resources/assets/touhou_little_maid/models/item/model_switcher.json +++ b/src/main/resources/assets/touhou_little_maid/models/item/model_switcher.json @@ -1,3 +1,6 @@ { - "parent": "touhou_little_maid:block/model_switcher" -} + "parent": "item/generated", + "textures": { + "layer0": "touhou_little_maid:item/model_switcher" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/touhou_little_maid/models/item/red_fox_scroll.json b/src/main/resources/assets/touhou_little_maid/models/item/red_fox_scroll.json new file mode 100644 index 000000000..e14f8006f --- /dev/null +++ b/src/main/resources/assets/touhou_little_maid/models/item/red_fox_scroll.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "touhou_little_maid:item/red_fox_scroll" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/touhou_little_maid/models/item/shrine.json b/src/main/resources/assets/touhou_little_maid/models/item/shrine.json new file mode 100644 index 000000000..7d9334266 --- /dev/null +++ b/src/main/resources/assets/touhou_little_maid/models/item/shrine.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "touhou_little_maid:item/shrine" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/touhou_little_maid/models/item/white_fox_scroll.json b/src/main/resources/assets/touhou_little_maid/models/item/white_fox_scroll.json new file mode 100644 index 000000000..d0b95f551 --- /dev/null +++ b/src/main/resources/assets/touhou_little_maid/models/item/white_fox_scroll.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "touhou_little_maid:item/white_fox_scroll" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/touhou_little_maid/sounds.json b/src/main/resources/assets/touhou_little_maid/sounds.json index 60764ba7a..04cd26f16 100644 --- a/src/main/resources/assets/touhou_little_maid/sounds.json +++ b/src/main/resources/assets/touhou_little_maid/sounds.json @@ -227,6 +227,20 @@ "touhou_little_maid:block/altar_craft_2" ] }, + "block.gomoku": { + "category": "block", + "subtitle": "subtitle.touhou_little_maid.block.gomoku", + "sounds": [ + "touhou_little_maid:block/piece" + ] + }, + "block.gomoku_reset": { + "category": "block", + "subtitle": "subtitle.touhou_little_maid.block.gomoku_reset", + "sounds": [ + "touhou_little_maid:block/gomoku_reset" + ] + }, "entity.box": { "category": "voice", "subtitle": "subtitle.touhou_little_maid.entity.box", diff --git a/src/main/resources/assets/touhou_little_maid/sounds/block/gomoku_reset.ogg b/src/main/resources/assets/touhou_little_maid/sounds/block/gomoku_reset.ogg new file mode 100644 index 0000000000000000000000000000000000000000..a99e6cb855cccfcbf4f4c8ef681c98399eadf645 GIT binary patch literal 11628 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/touhou_little_maid/textures/book/fox_scroll.png b/src/main/resources/assets/touhou_little_maid/textures/book/fox_scroll.png new file mode 100644 index 0000000000000000000000000000000000000000..cd93337ef36e2ebe74ea033e12ae3db41b738d09 GIT binary patch literal 14946 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

@#H1VzWSI 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 TrReWJXMnu4id2OJB8E5JRKzw|7Db+ zx)xm4#oZb%%+1GT$;-5aWLc|L=mh zQ|E4FBc>y#@ZZYrdXfk`Pfu4d9v&YbA8sE3ZWni39zIb~Q666Ye>m zoIM!-%^+v(Vd-w~>S^!d4F8AG;+cz=rzGOe)c^9q$@PC|ojv}0p6+Ih$JfG@hmV{0 zAD{j$sILD17Ikv^A88Ly9qa#X@Bfw9L(k9Enn%al!^O+p@^0m982?e_Dkkf0ZQ<$S zuIJ+N{NK~4W#{7Q;$i3F3g_qN7J{=G*gIRf_;|4Y3!ttprtIwDY2j>Xtt=;rxTD~< zx3>}#=H(aRm*W=}6;Y7m3ATl#6W3I}1%)zHD07nh5?t8>Btn{k@#zt%<>*G~qyN z@y7=2aCYIw99=`@ho!OFW6UDw)1E~cnYXvMxfF48ce>!7IvTon-TzksCMMLs0pR$e zEGMJq`+YYEH{iR-XobEq%W{s^!&mmR8<)4YM|OLR^Z8ugvVNHyIKwEPrkcIlI2xYC zv&7>IH>~F?aC@hU?Qty-pF8*^1kduiqE(Z3yt`e*yt5nm!eSMRd!6XlL&idr-7xmNNd|jbILBb zctRUNhe74$Br-Y==-;+dh@WA^4e>dq1j5c>TED0-jfjha-tP={Io{9HKuYGXhM5~& zV5vvhJrwqi6hvT=4W>BMjP7kjQxl~fa97Q?ie>$P0VPLt&<&<#o$7C-pa^}EgB{Tb zu{hE05#br9^uTkxq3H-1kPfKdXrbLoU3URf&wXG(5`8Qxql7fVprTZAnmN%1ME>cr z)4A$;)l9HzKL095bUPjc2$Lt4av9!0L&tMr2MAtWubiB%s<)@*c#yNGPlK+xpPxL_ zAndn2=ctk<0@3^xaPiXcoa!H(C*&`Q>M*}dviQJI<5b0YcdujiZ<{NYZ!YtX>&F!R zAH2{;mMpMhATemAbDEg<;;*fE&W0JskS>^i0P0&70H*IrVw!g`(TRxDS07%PRa73^ zD*$ztcP0ip=+(h07#YdVPLEryUoV$_E_gFCpA_2+DQfA^9wru7bTPDebw;Xwf53`LqJhQn7}Mur?F4OKZvq!GA!teIaCSu3{!i7bTyvz;W2;?nY9=X zH~v;M`ZHF?0TaM$y?G#jBlmZ5ROmOx$>4Lyx%K|iAD9*vN+d6Rgr#KkHiPFC3!l>p z4{6QknVcu>F>tUP6dS1@a8gZt@VI@AvV(eJ|Lsp(V}z|q>`E{;rMA4=KuwIj1GoNo zLDz#lWxIff>ZA_WMuJc~azTuzid`$!)a25%r_q0W!{vE1Z4Omk?A7)#;IAyjtJn3a zx2@{Yyz237JhTgbuCR}-ymd4rGegb7Zke%UN~DgOUnbFbE45QnpO(4UwFJ01o&~@f zc*nB^Ys7x|p*4sTfhsh1c;jN6AfGpThueA`KQ}AkZfcgU8zjL#CbAYmG4VO6j@q14@YIF+yBHk;gOaESN^>U=q*>d8N5rDt2{vhVP{e%sjYRFkV7lJT{+e+Q9YK{qBU_ zkiKhU|JZ8kJG*ssMkJYt;WQSf}U zGh+d5%(A}ES`|Q;z zc{e`&%~u$FR)nui>t1h5)R)eyJCzn(k2WUzKdP<8LS5WTL=m5w2PM$i>qDpBmjTY9 zPqtam{Y|XMq`yw3B47O$$k8LXSfnv$8y*V}7D%D5-(Lz{rH=`dMU>0o=xmcTEJD`j zpPfU&k%Jy}9-HcxUA{kKca6(nmU9+tfJjm*XH0=0D={IqEc^F!U8czy4Zy3COVr+L z87f3)`aNaXouKfey1|jDdRwvJ+SIW1@Vzg#)a(oqbfy_)o{UZRTSsNc)gU?hZfebr^n`3 zg&1nx(#7I%sT=siO3&@B>EFOC*-PTp3Zn5krChHQNfEIax(_`_UzPEokB_6HlrAPk zTFVd3Ggs`3SJl`GM&*3|{yci1-Q?9ys^s5!P9bF=junYL+aFGKI9*%Ta(MwJJfDAl z4iWT@)Qz?tU_UTJ*{V_t*qO)d^M3eE-~UUIS!6gcH^ZH;doXG_LYG-r>%IkxY`zSfn(Y8Ap zl5*c1ik?wV@+JVA#PV71yT3E?4Xyz%p59JRJh5|~eYw90B?Be{ylWP6040+zjcWP) znapf5UX$++EU`zSUbz-5C*^UJuBLg=5>f}{;lXxng{{Pg9=tAEdbNI@H!XXEd3->I zwv9I{<*rlms$!y@cRMMM*G~bPN>))s;x7?TjWQ+cvx4QP8A562ABii!kW9iD0MQ~elsNa^oz_Vx|)^38bX zHt9FIC-O#+WtGj2PsD)G$HO^s1dZRK^E6ro-pAxl;AZ^ZsMa9o=RkyrM9zI{s>NLC zaNch=nUmCYHiy1u!Iqm)B!cUmfNmLz6GG(br}kAMZIy=E6`nWV6pCn$i2yAF%FHHQ z!r3arm?$Y7f`<%@IJNf#{wNI=2#RN1xke$;E#xupEiDkX=c6gu#NYqy_*lao4S;wq z<5|BT#BSLSl|~c(Q$M$Dc}+51Tp%}gwx;%#Y3+llC!>*~HNTJ`z14$X+@K*oq6emf zKJ1&3BxcO$mbClbEd;0H>j(PxJ*CGUbEe#7lsNR{=H{wpms^u@N2G}(sVXH*oU=+s zE9H?xCk|zFF|u#P%Rs4)UCJpDW)AO>&Mdv1VfrrY5_vvqoW1(<;==u1!xN@2)3=0` zmA&=91|l+|!s^OLOA-VONM6ZBp={W;Gwn9jaASW)Z9qxw?c@UhmJtL*pe!RAte`45 zZle#nQs3s#a;i!wpcMtl6f5K#|fn!umnZ0L4&km9gaV;*0F#NWpj z(#SNLXuk@tcP%M-we`vi=4#!;+ayrb?2LSD zu_JP32r*KLdBd;^_)|Oa@W{vj-uL`L8555%=`_3Fw!=|fy(_;GNO5j74e?Hga-FxS zJ-oQTyJCgB1`sK@D?Govk~$5C+^kvP;W$M4L!W2&Xqus4pDLU|zZ?->SHx)dbHKgO z3z_tdihshED6}hKA_j6^EA@Umen%jiTMctUL*T1qPJlLnJnNE!q${>&r^!)VNev^_ zJ&;o-zx}j>g#nQEOGY4wNE11el#h-z9}*Q72jj2ZdKay+3N=XmCR}<%Fyly`O>*!y z=13NhK#BZ@M#$grJjGm8-X{ho((PGi>*yqz$OBBnojfc3pEP{B@;2~XQj8wzAcZ5( zud2lI>fee9oXykPdoc%Z57+Jv5(nD#UB_|SPEKJ~rYg#Lw$gn=yqcNw6tASiqGwp~ z!|=whpCF%rz8Va1rFiUVLd`LETR|m+suf9DSQ-@i#AqbI_)^L*&*!obDhp8sT-sdB zsjJwq$MsA|j_4F_8qG*lpd$&a(fB&+{v;jP#K8jgx8K>uG)T zTNAB)3$1Rs7~v{G;G#LVsiw0Y3fO>jXhVF;QHqkj6^p^-A<4KONrK3b9OYsqT4RA~ znAfDLz=r+k?MH{Pq7tQzuay|aMciH|J96X)6tJ9mo)lW;i<-$1$S{w`Gb6Zx?Ij5f zg(W>;P0*moU7=HYw@#d)dloAO8BglrksX@XpFeD+4GLyOo&ZwYWEBVw8j zO5}cW<&pa)-a8zi4zpma05rUHVP8&g%Rw;X z@nc*!PhvR{wlE`sjtwrcPLV zu-Ih(q>5+xQzY@I2mQ1ly=NnuP610ixmt@fr$^w|Cz7$T%zM&D#!42MgyV4ya63r5 zLBJ2>6#_J%uc-3}Aa^&|FJZP_rVaz8-Cy63i+mE${?b+jqM<>G0bIlJGReBlXX z^HWs5s(FmfQayc)S94vrwErg`cGjGBjSJO^HeXGT6pSTjd!0G6kYU#2QR>I3<57Nb zZ_}ukmT`0Ig$GA4xed;Gn29*zeYgES*I>i7h(4#r>Cla}kC^G1 zFWQKpmio8*g&X`8tYV(=Awine9aChuCIY6zBdFn`}JfE+8dFMlAXyT}8X#CTxIF`WDb z#`>G&J{WeBT~}b3lf#YZ@oL`CZf-3QE}dV zeNl2fGI(Q~&yp^6Ei+m?>|zJsMfy@j7p?ZDaNc)zHynMGcf>Ns0z=7o?n~^?#r~A2p3!{ewx?B2OLyAOKtu0{HC+l5Sy)pOF{3pTIc22Rv6#tzal}`1 z-1|()f`i7rWP={D{Nl>Ma;>niYoZkaPkREYdN4yGc}rC{F~|M)jdD= zg7&ZSh~tyv-Upy6BuMg0AWZ^2Jx^s6j0wj~bB6C|MW%twP=Zkv&GcnJQeP3jMSnE` zt;#sV*AhlblKq~&_axed5=>Q$q-$9^o(MJzW1H8++hP-c1ElUr9_k2fjA!B27+|5?1J}w}{K%($Pv!+>h{2{!1`^9%C>4H(r&4y7rz> z$l#9P_0=H^h_s}ZpqX)CMD0iAQuQ3)c$%83f1dsPg(@L4212Iz1l6fRu_re|(l__X z^B&UbiCbA%ZSvsYfnJr@>VhOEG$~UTefj6lyX~9e}^ zZ%E#}-+Hy3WA8?Habe7pFxvGId1#nh^l=X zNy+RV>`5esr^6tFIrKTW@2@A5;?&GH6VVa!SFh;zFbK1^v6&U{X}F4D5%J<|Xz&m- zWLIP1p#k_qhN6gxz6cwH3N#nylA9BSelTP92?!6D!Fu^acM<|vD3J=HkJPDVt0I4@ z_E}on3_oXKmUSi3C5d`=+o}l6hUu49%xQ1W1ip0-%+FtDb<#J$XUJyBrgBt%uO1bB zc@*31bxXpg*vxjF5OTj;ns+?WsM_eNylVnH!3)%V5;mB zffTZuNO+{?BdzOWp(k0hQ)>Zp8U3m6gf1}+4UL-h760zH;X}`hSHqmD2J&J)maSB% z)!kTlkb3&DB+jtWXs>?pR&*${>!zOTNB;73*M1Zk^kFj{_g+xl{s0b& z17fsT8rWRBYuvSAC`G1TBHzbFU1RC-j6j@6r>TpT`6sT3o~nxNJ2!j@ z$!lEZ7tOe>=u+^fe?rr!gnP@?GhH+Euxk+STtk;X8d*C!+0K;LY-}bs^Tn2l8!V=y zGyz-EWbvYS_K2oe9|uh2R#VeUxHpvqv(dE;yOiV-F}c8Yjm#cU%M+R*#ydoNrrXLTp$MftPG2(w^;W%9(}+b zwNn!?Ey zoiAegSt_7F(2(|FD+xH?ATuAfJ%Zde$Pfg%HL=$9daK6!>PQBoh#tMh&!#l^bN!CY zZZ|9guZC%~Fd@Y5-R&ekq-ZVb>=ur=4ZI?iRgH=MAVj+>LP^kXqw+${y&3-jv74#C zb2`y{m$Qb&%XS*fO^oAS35&2asqYW*U~{DcE+WB(*i|d%eyXIGPhOIfif0*N6SmGr zp3wav=CZI!kdKcn5EGU04#5dvQa*N#=~+q?sA%?@92psLsnW+Y2CmDMOAo*Z;!5Hs z<~jh@E|T6?;l&3bm9el;GS+ispR(VBA;;rzu~CxW*=o_^`9xB3OY<<$D}l6&bx;{sov%RJuXSv_Y=uJW(eppQAL_g5CJp4JW_4p#A`crg*6fwCl=nES zO2Rn7@ao~Y*_1d(3l6xbmlz&cc?ChWEiL^(2NjoOO;Hzgtz5Dut~P-<0n0^D{z9#8 z%ygEqnBp#+lzv?a(%u52H8Z0aw4`4)BslGtE3VQY@^6;E{8}4LYg^a3IVAMg{u1F7 zW48;-smsTq&G~!AN|f}C=I;6Vx;E7a5P}bx2M%G>GCEJ9bjAEdEo%zhn=ou(n6W$aGM|GiJX6F5n zkTTYmvjCk?l+n@YY8kF}HON_;p5u&AP|n~jL+TdGqBA{gVzb4F;%0`=dMFTh*f5Vq%j8iiJ+~obWU5dfrA~^#VFLUx`1KG$DLJa% z=TK*Rp%dk!iniV;h%ne==Un~qnXrPy5;?6%`PoE*VMd!pk@qjADLYx;_c;C}huRNM za8nup(ipVQJEKc~!Iw6MC6*cG645gDr&y&Cz)MvRpb8fvHr{X)9ga(gXvcgQ7bVj>CzkkQa27V*7hbFX=;uTt!i1`t`s>Oi==r)>E zs~sYbH$~zdX2-B9CBj1)8ny|}@nmO6F~nBH`sfxyS$SERT7A7WOJCo2@ud##Q{Rrk zpk{IPjQ1Opa8v7{(s26(^?jVcvLc@BY6L|Iw`?Hz$8eOKa=0d^{`xHxb&3}jh z6t^gE|HTFJ`L)!b?SrQ>Gdqo-%5-^K9;aK|2aBj(nfsMGA^Jkn4jQd9Qx=80o>Al` zKRekWCFloD#ktxsiq|AS?W0{kwfHpw>NyK;>R-3nEvnwHA?qK9`|!G6zsccr|1p;3 zt~~&03lLCU$So57TsFrhup%iYU4%4ys(_(L+jQ8YtMitn0BJpG_4%odmJg?f1=l7- z3k?kUv!8dg%ix+EmSMO*-pc&#f7zv#z+i0WXc!=TZ-@a7TZJIM8PEv3Kf5Dl=AVsm> zw~z%3DKVnYVlPTC-Y7mnWL&%;Fb@10aeS`26X^>g7rztI=;aL@Fqj{xr%9tPTe1EQ z6#48J)G-eh9_J7OeJ%x?DaYeH4)yU93$tYJROWXr4W>I-X5Lt+y-=N)#qS0tA}`l* zS%NewD^&3DYd#%seCoX9!9dM1WTTK44Y5;?Ya{5p$v+fy%@UYM=g1H~V~Lz-lX{*t z#y}${#{EomG;WgCrgh}$KbqbcriccMrpbdCk)QS8}Z@8lROGeHBw_!#v%N zBm)M|9j#&~kJ=F*=yFgzyN@W`&p;2v;| z!Whm8p0JD;-^Up@>WmQqbws@1z9(Nu7JpiIx_#cbWq+6+Sg>bJn`hxtmZR{f;d}?B z1e7?G7-sptIL8~{Mon5YnU&Ldt34s6wyD^qNwi`BUnlWNNAfqv6pAK}XGZ(YSNEK`A7Qug309H1+)dQJ27K5{_w zWww;y^4j;uvX+|1~G3`)$$qO8Jl;cRx`5a%qN0*k6H@jNB zcRoSmv$qSUTM6S0PAsDg|3K*KV^0M(9YIxTIV2qmyk+AvBYqnqAyB*{zvTNY8KKVG_TTQyT-CD2c4 z!_3a(?4yRbfUyc7kJOY$h9)xct39L9;Z$DBv9kL^0gyq5Im2vuva0vXU-$JpTs@;) zb+9F47qzLyF>0@jN$CUyKB?(@Es77$!Er07feNgB%Ea7wfP{j}sYCcRK^^wcfc`re z_J@RZOd=e$qVQc}By~9!8?clD@9>Yd;JKC!YU!Urjm2Y6!Ytwf=9XiEcIn2P4pmfZ zRYKJ$>$i`;c_ov|nat?8%uQsBVz`ao_;biJ8kFU`LE1MV?Nmc6ML$Z%S|+`YoB`GT z<(9jP4;=+Fw0Chi1(bx7qgElKR;Gig6+8aXu?sKPLIz6M^{k%IL>BYqYz7$MOUDKe zT)I3%9c~R2OT6v8aNob;dT@tO6TbV7n>uEi2}muawe{?t$OU;xv&$u9-Uw}Liym_OIZF;Ym{Bg&)Ul7wa$b8qDI zBiGw!6i1!kxQwh2tVo(NXH!w-^^FBoo*+ZQKYWu`?A$^K8+#s zLgyxhPpije#tLJ-;eBO(zDH*Bv!dib0nr<^9_Ajbpawdc{=R5;SFnU&J*_m!GJRAR zM7~^9{#CqW=~0p1LEAEC)T2&p#;8Ru(US7X8;XSTrq%+c8}*Fn(16nYF9h9(A%!W* zx`?m9WVXFIo#IoouVVWXQdg+V1Tn+kB?m!@sj!YLOpaLH_lqvO@tMh^^k;(h&S)}m zAJ9d(THH>8QLc7-CIeUvLXoA4;XHzEG0IX}wF=9@z~p=eBpQSGihIkO8T4Gy7O8`A zq8BfJ5F%;YG3kP`UOR5YW2E&2C70Tq{&Z*9tkm|L#L>LVBX~s2`oyqe+85G?F5*=G zDXvjz@4Ii9zxz}9PTS^nEcVd#J-jaeH@8Hn195;cU{z({s7zcPqr=^fi6bGd@?zI; zkcab8;PUVkoSwtD$E#Q8Wn;Q>@(*wKEkfd)4G0 z)@wyGecrM4dT8H41~`u}+S#tO6a_`TYg%X0xJIh6C3!x#lv44o75%YRHEF3>f!2~) zClO1sU@EM?IsAsKWKlJT{IRp57HGSg3v)=Kvt#%F4~JcCC<09r>{-E+r=O%d?r==AEeq2 z%hu7-7nj!-&d(XJrPwVTKi3PIR%^V6yHE++SOPih4Db8|>}K+|9|+;Aco7O73#K`o zR8$zxT3tJYzM+lb(=~#@O(bz)FLw<9??dnzO7tS(SA6~lZzp7P4YVk?_};^}D;MiO zw@3&d#(4Y;?Je*RJ?>5-hl?~hM7%Os@x1#==`W@#)rKfGt1oXxyKDP<-zF?@FYC5l z)o#emW{e)UI8#1?0b-(`Q-spv{_p1as*Y-+6o+DB_B{R?o8EM|nd``OJdX*P#TjUL zP;vN6SUV3aZyP*@wa*etCoT1QeT?d-HvIDf^Q8@j(V)Rgk59nlO{k@aeW9v9ZR7Mi zMh9D={hBBmfX4p*}Sjn-pwyElB zqg;<1?#rnR;1E1W&2g>hC7oP4?sI`dT*&B>*2cr$ z*V*PpuMY$z+sY%)F~MZb4G~BMA}jZ$9K|PN*kv1|5u?K@R?; zf%XlEuQ1pU0((74bO73%q0p*4Ec2|~zdQmhkUy)9I0PN(D*90YIy*y(h=%D~_L;V* zCEQor#fo5|*o64rxIjeymStkX9sM>bGsOzlf9RM8fIbWq>0K&k#a>E#2p442MHHKo zwXt|HxM~()T?5`Y4J?y4fpsUx>+~Y6obzLaRAd2HMQL~UWbX7%!v}F^Z_wbx%Mwze zp@yir!w+Be3@{m)M%e7`5~jcr6)FPLPYS$3`;vEEPXaK34n;iW4Fc$tVyNQYsU?&) z1XaAcWpy={l!k9{=h|mm+nO&UPwKu~9txk%S8{3SeOu2ODAk=N#&4?9lyo${eA%7< zz?g&E#_Y$!{_b=(l$VBL9*Ww9DB0g-L19YULzeGO8R=y#{V?7eKH!Frif-SBj6Qp4 zFnCG^^ZGHmlq$;b4n2EU-h|fCS1049W)>!M*(}eCSb&utmWn0 z=nWxqfg#&?TCt@!(R04E8CcGJAJ~??fq;vbfXxv+L_=c#%Z)}^Xlhq)bUSRB*bop6 zS)EBrp?b2)B*a-uGbv5qCxz@72(-Q74j|pGrbwRP3**LWKmO*ZVm8w!5zuW~-@1H; zA%%H&)Z+pLZ7{e$kOsV-S5HCDEeT2!ZFv9!FE$6u#OG*0Hs@FB{XGjeKc$42`i4*C zb0n``pZa0ynds1zjju1T)XVWyp?wCNlQH$pPsY=zOs62yP4uWnQWS#ZzyNR(pC^EG zSx5r~YFtIS6lUUYLvxDF>Of`X=HKNj#mGb)ORrYuT(g6YUu}eLs)Bq57qna@>uu&|LU%^@L}?FDEHyYk5#lLt*{clj?pP1{=MV;=jSOr;;6b4&IM za!`|>MoI6E5zZY$O2{njYFH<#DZb+`y*x38CblevIt1aOuJnOOulN#Fx|#UVmI5IN z!MIijv6x6>C{Zd{m%@PaG#_Tbf4HD$t|C=Q$+MZAA4_MKQcAi_7m<$qvR9ihbh+Jj z^qjetZt6VC2R zlv3yvb|7_|?Lr9XfCTUb2Vfb}4ntvr|NjHGvh%kjNT9a5WXniT`|B>}6)4MV$W=T> GhW#)1+l)p4 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/touhou_little_maid/textures/book/other_backpack.png b/src/main/resources/assets/touhou_little_maid/textures/book/other_backpack.png new file mode 100644 index 0000000000000000000000000000000000000000..5cee0828afe4925c4cc37c059ede7355d6d02b39 GIT binary patch literal 3868 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

oy#JhOfV_!wSOG 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 NSzcYPT*ma<{{Y&7+|mF5 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/touhou_little_maid/textures/entity/computer.png b/src/main/resources/assets/touhou_little_maid/textures/entity/computer.png new file mode 100644 index 0000000000000000000000000000000000000000..6e260ead910885ed80492867a69a33f859611257 GIT binary patch literal 15461 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

Kh{o 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/touhou_little_maid/textures/entity/crafting_table_backpack.png b/src/main/resources/assets/touhou_little_maid/textures/entity/crafting_table_backpack.png new file mode 100644 index 0000000000000000000000000000000000000000..bc77cff95c2738381313e252caf34aacd480c37a GIT binary patch literal 8310 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 JhK!N-{{WG1rT_o{ literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/touhou_little_maid/textures/entity/keyboard.png b/src/main/resources/assets/touhou_little_maid/textures/entity/keyboard.png new file mode 100644 index 0000000000000000000000000000000000000000..30452fe582f4468cd444611e3de4cff34af95f41 GIT binary patch literal 2168 zcmbtWdpOhkAOBL*7*S!)aywJVS#!UX8D$biMNGL&DXpT+5M^>s5y`z6lBRSyuG67e zl2WE7%xy9bLnazwBNl#B=bY#HJiUY9WWd*+j&U}OF&(^rGa|=k=0eC`Ed9{a%-6!ATY%*WY zrniJ(m87Q2=yCG|ib=PQ&KLY545%pLZ97gGcejpTEIPlba?qE$;gX7i-lj$j-=ZbA zBZfM6b8N`E0n85FZ0sB>i#&HGYK7c-(r5gozeA(b>+cEt0HFh>8!uMkZrv&Nc+utR z+VcF52Lu+cWoG1U;^oC0UC_hcA0 zmHfFipAn*8FmlGR_I!CJ?Y4U0`V;|#X{pzquUqt(mbN9ZrB^zU)E~C=h&8&)CYqTt zV0vMo!-gDAf^ypiE=6{q8T4QVqKM*8Dm2?ZD^)HLk>=UKQq+hHY_}?gjyV{tP7f(T zw3(ycFfKH&4GwNBe9rHgS`=@@C7>@Yhvxtpt5xIT=3T|X|9 zh5(wt5-(VrG*~CXlNm|T!0ai-)}Ii5gwqdePb^GNyep#GQ)jO7^Q#Gw_#(p;fs3## zS4)nh^+J%4RU+=~qBG_^wQMJ|4d+DOiLue8nS}9!1S{U6Dm5Z`NM~;mjZ9rc{AP6c zLBdRl&nnTFVBI_oz9|3bz@0!RNgypMYo>khC~g~>5R50FAll`_(AGj9KemYv?wP-E zn5e{rdl@FA6!Pn1<9zVQrH8^I49EaBM#4PPiT$cKd8I~Gk=ynHt2$+fCJgt}K z?8HA{1>_<~_qrbry&LL!H=3A4dZZMtsUoYO;oQ21kkRB~c3WaTp>=sNFB8QXL%LUku6;!5g2{1Fo@ukOgFvh#;7 z+Dl5UexB_fmiq;EBg7B5y=w7;Cp(jMl@ky=DSh`Rmw5DNS7+oF|0}dINZcI1@Ntd| zr_=X`1Q|yRwRvAWYQi&Do0L}Du%;;AtjRUIwT})&{=MCqbwFqS2!I{BC9|CSmuDG7 zd#nX7n%X)Re&`>5?22!IKU8OXj%>wCZ)k&T%jEInw8tr%Ermt?DOrRg2XH=1# z@jb*+$k6(+2;(0J5mL7s>X7^X4s8pC^G09KvJXuMRutzLY9FsE{uBo1u;Y|tQDgDk~ z5i-mt{J#sE;w9V>XJlD^(N9pT-8E6Jl$K04eQm+~bXF#%JVV?bni+U%$4^yL2KWoX zK1-^06ir;cyw_IHCZ?wvV`=16-oY#Udu;PCtyHJ5&j^RWqwI&ks}>_vvo4G996aGh z9lk4T9)*Va=`8Rl+nBBHCMO0w>!*5a)@VI&mr!C+2UJ`Ho)zFh?n`pxzpPnOd25zt z4^#}|jJ$B?V*g|jRP0tpI|Zwgxanu#KuTg!)g!}r{p)JBH6k@??z4o&g6Aii86`0T zv=E*~+-h;Me(LgGMO)Dx%LG)9Imed^iXK$i3G0-DIW;B?_ZnjCuVpePe1OZV%cr2B ztHayud;C74_L)B2v#(>^RdUJ@u+Hn58RYx1W<;EC1nA)Airufirr$UXO5v&?ffLzc?HT)Lwrltk9 zl7|b;BnpAUO10k%;n9C?QfgvV`Zdl8GvqZP%z^^KyL#TYCIVK*fq}eBr{KvLar1)a|3pr+RFjn%T0Gu4$Fy(s#e)}KcYcW>< literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/touhou_little_maid/textures/entity/shrine.png b/src/main/resources/assets/touhou_little_maid/textures/entity/shrine.png new file mode 100644 index 0000000000000000000000000000000000000000..5fd8801c29714680373226b210495e0a43a200f2 GIT binary patch literal 2752 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/touhou_little_maid/textures/entity/tombstone_the_end.png b/src/main/resources/assets/touhou_little_maid/textures/entity/tombstone_the_end.png new file mode 100644 index 0000000000000000000000000000000000000000..d6b2a04fd836f7567c02c4812f08807d44cd75b8 GIT binary patch literal 2151 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/touhou_little_maid/textures/entity/white_piece.png b/src/main/resources/assets/touhou_little_maid/textures/entity/white_piece.png new file mode 100644 index 0000000000000000000000000000000000000000..e87ff5b5e77e3f8c0ad03de7b1cd4fb51b9ad348 GIT binary patch literal 90 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`ik>cxAr*1S2@OsEGZ@tTOcZW2 nl=Qw}i2bV}!MeD?je%jCnf<}41D8U9iWxjz{an^LB{Ts5p+y*$ literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/touhou_little_maid/textures/gui/maid_beacon.png b/src/main/resources/assets/touhou_little_maid/textures/gui/maid_beacon.png index 5c7cb5d207f106306b24b75185193d6a6e7379aa..ef4d1c36a0cd6070bc8c0fc5d15ec5a198c9100d 100644 GIT binary patch literal 4699 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 PM&Q8SfISU9_?-U%C<_g} literal 1222 zcmeAS@N?(olHy`uVBq!ia0y~yU<5K5893O0R7}x|GzJEiUQZXtkcv5P?>PD?yGt}& zd^NHE$SG~U#;G@-3hi^=mTiJtKS>9|IfOHqj&yJx6dqn#eVJe*Bd!z|Erx3ypCLd-M{O= z^5AZXx{7+U*=HyFy;JvR_GSeW zEC%O4-+2Fh{YLQw$KQYJ&V2s!;=D~2lZDkhmWJ<-uYBnLc!a^G)bK&=M@@#7H)Xru z?~Cs>T$VYD@3YPS*PaiOt>-bQz5lrI@w-F+Ol&Lud|<0)jJt1gOycsxgl7v+xjJwN zGeA*vulSBn^_7*D>(;HC7FQM%6Vvmy?DvPs+z;5-9{XXn!P|=O|IwpI7roBge%tkW zvsFH$f)4{ZDCg@T^A9mKu?d!!=hy$;`y;k-f6dS2nD}`8y>Be+?dA9X|F(KJ(_yEu z|NN=BSZZJYFZJTr1?Nm*?u9#?NJjMDC%iUy5A)aMU4FRXhi_wJ`JrfX h%_7pu;iT+0`y~DLJ_nxNCxPV?gQu&X%Q~loCIHJlc#Z%7 diff --git a/src/main/resources/assets/touhou_little_maid/textures/gui/maid_crafting_table.png b/src/main/resources/assets/touhou_little_maid/textures/gui/maid_crafting_table.png new file mode 100644 index 0000000000000000000000000000000000000000..a6ac8d6594ea8748cb68fcd3481f329269469b18 GIT binary patch literal 7239 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/touhou_little_maid/textures/gui/maid_ender_chest.png b/src/main/resources/assets/touhou_little_maid/textures/gui/maid_ender_chest.png new file mode 100644 index 0000000000000000000000000000000000000000..f3e6a70767f62a21903bf1bd074e1a5ff5af9f16 GIT binary patch literal 5190 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 bfyC92Ed8hIm_gz9LBQ_dahtLOXRrPjC4T9D literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/touhou_little_maid/textures/gui/maid_furnace.png b/src/main/resources/assets/touhou_little_maid/textures/gui/maid_furnace.png new file mode 100644 index 0000000000000000000000000000000000000000..6b17a0ff3bb8ef1273fb06b0a7a58bc61e6bcf62 GIT binary patch literal 3732 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 literal 3059 zcmeHJdsxz08vem}$?#Inh>4EaZL8HJH;GCG(@b4cva-}lOwCf))KnD1OIf0sVpA(k zRzzk}-otweHh9ZRD(0n&qEaDh0V)B;Wt^VbXWO0scmJ8czVm$NJ@5CP_xqmnoU8+0 zu6iqBD**t|b9dW&5CA~SOAxR^YdIW0M-K%6NTd7SJ%bnA4tYOGXHZCQ;6z-!otIZkFS1Cs zF4x*Ph*&=JRtI?)9${vxTAx61re(eL00UY~bTG3&go0z<%K z6VE6cMblK4p?RXJYoL#EIm#i<#v>54BcQSk=@i$!wa#tOJGc=&Jn9R?jIw0%oNh)t zhHM`@TpnIuQ9s2PY8fG6J_yj_J8GH)amg5`^pr^>5F~y86Qv+3XQKJ*;UOR^$MF^7 z%l3Xx6NJ`3Myv?$;dH>>gJlDzFK#D}VA-iW!Vq}PZT#zk$278ey?W&RdqFWZVBB1o zFd-mc9hXPxtL0p-vwc94jCoC7HtXQor9W@+n zVE6-)Nv!k{B5jVBO87!6q>#~{g^<4Hb5Zgw6Irac=DDRp2v<2&WR<3QrqSfeM%&|i za(+JIyym>oy`c+(K~t|_@U3s&5+g$uWKWZovs9Kh5c8$CgSKH(v?6XO%S>{zdu?N8S3wn z^8|q0GyPA(KR_H+rk|}d$#(EOX2iD^LpNu~xho3Nh&#<%)gL9se@l(QRWyCm1L$V5 z1X7z(-l@%Y`Sv!Cs=J4v~U_P;9>fx=l-!=|26Crt23XJid|Is;TrVG{g2G+PPHR$bCT~-)FhzOTyYVHUGVm*ihd)3&C}6E~WwI~Aef0Brr(;aG1mIZ|L>G# zDkYM!cyr8lO!wu@3NrHXhemaO{#SirjV5#(96aK1Z#+-$jgwuOd%iiN;9sM2p3DE?1$La!&_?nifBn9YPFB!Z7?-)w7W!MVg`)*iQw%VD}#0 z#~9JzIyY&KN1A58r)2%swAl(4(;ploD)jTPz~Igjsd9)#fO&mVS+!q&Yg0<@j@d47 z<~$Z8Nl@D=3b8;s{ca9gv$LT;MUW}{EDlaR4)#6{7L+(PMGpV2U_mo4;}qj$AH}J$ ztaqsvn2|QebMYzK{U-%qk`;6~$xfe0uQ|xX0685M2BXcLCWs=h4<~!lc1|qZNkOZA+JTn_E}u{0v9AYpZ2MX zT&)q>%;USJFyG41R%zk|3+#b{*0s50l%9GzNm-j!0fdRKThdiqQ?pv9iUm4eLNfEJPlK>RcOX~F+(3KZ~{-hV+t URi`d4f93&q7q7kLPJtJH1y7@<1ONa4 diff --git a/src/main/resources/assets/touhou_little_maid/textures/item/bookshelf.png b/src/main/resources/assets/touhou_little_maid/textures/item/bookshelf.png new file mode 100644 index 0000000000000000000000000000000000000000..69cb3f66edd37037c0b98b7651c3e44c806c437a GIT binary patch literal 348 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>V#W6(U za_)I=zQYPUt`~14Xqd5nT6y{J`YlZ?i(K{^pL=l9=kor9EB7;c4n_%YXmB}qxm&8C zwL$0HadUYA7Zp#vLO*qe2TJ?a+%C#%vHG<#*1=s?14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>|0(Iy~M&%ngSDf9UL)$u9WL1D2ovn(ot zb(|uKT|N9-q7DB4|L+>&X%94xu_VYZn8D%MjWiG^$=lsUVs>V$9FSA%>Eak7aXGc0 zldr{q!!TUi8Rw>10!0Cfq^ATK q-IK1+V3JwzeC1)T3-;1~r^+u3V0|LC*{}lW5C%_IKbLh*2~7Z<5O(tb literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/touhou_little_maid/textures/item/crafting_table_backpack.png b/src/main/resources/assets/touhou_little_maid/textures/item/crafting_table_backpack.png new file mode 100644 index 0000000000000000000000000000000000000000..00fe472f818db12b6b5007134a67210f548081c5 GIT binary patch literal 253 zcmV* zpcjx4;r$Q7=;A~HkjB%Cs{fy!6ivJ#FdISm?!gssHc^JaZA8P-ARUbqcGcQQ-^2qPO3X{-3(QC9-h01zj`!}|ZdY58D$xIPNZ_pvg@z^fNd2V)Ea zKpH`sU>IZwNW=OzFF1x7aPr80OaoxHBGdDuY*7p$$^c{+fDC{c0>dE97=cKN3qYF7 zL(Om-Kqw4An!ODrF%5uef?-^-fT0=L5MnX9UWxz!UB)<=Q&Pfx00000NkvXXu0mjf D+-hHB literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/touhou_little_maid/textures/item/empty_back_show_slot.png b/src/main/resources/assets/touhou_little_maid/textures/item/empty_back_show_slot.png new file mode 100644 index 0000000000000000000000000000000000000000..15e8996e9b2f5d1e263b0cbf84d0362f1e516788 GIT binary patch literal 168 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%``JOJ0Ar)~;`yBZWC~&a+b<6Ml z|6kdc$Mk?+*qg+pn7fT?N^kiELK+uvu}pmZ?$e_0wvC#7lbD1wW_{8*y8G|%(6Y-j zRkZtqjnzC^^DaHo6+OV_lVluLcDds~uDJYNw!j3{<4qi!^QSu&FtLQn88>?*=1&7T PgMq=*)z4*}Q$iB}%ON@> literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/touhou_little_maid/textures/item/empty_bauble_slot.png b/src/main/resources/assets/touhou_little_maid/textures/item/empty_bauble_slot.png new file mode 100644 index 0000000000000000000000000000000000000000..ce7b8a5a5f6e385d05623ee59ea1e6648cf3ad69 GIT binary patch literal 151 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`ah@)YAr*0Z`yIIsCAd?E8(^wi@5ey3x(3R^tblzM-2FPj*u(Eg9V u=Jz25BUygt@2Q=8UfeW#!1NFVgBV@xJ506gJsN>FF?hQAxvXoki literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/touhou_little_maid/textures/item/empty_mainhand_slot.png b/src/main/resources/assets/touhou_little_maid/textures/item/empty_mainhand_slot.png new file mode 100644 index 0000000000000000000000000000000000000000..977be2ec690ad63bef314ec5103c1aa2dffa5ff3 GIT binary patch literal 164 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`*`6+rAr)~;CmiHuP~>3AeO!0! z-|q!FJ!)|~lCL+|g_$I}ZCR1E^JI*R%ADn9v7X#)Dc?04TQ4)Q^h*gD+~B-+d_!7E z8B>Vl8rwlCzI@kBYHclYugoDs^WwRd>3Uy=!oemA*(E6`d7 MPgg&ebxsLQ0CD;_&;S4c literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/touhou_little_maid/textures/item/ender_chest_backpack.png b/src/main/resources/assets/touhou_little_maid/textures/item/ender_chest_backpack.png new file mode 100644 index 0000000000000000000000000000000000000000..e0077961afb9ab8e3f48e8134f52ec6921c880c8 GIT binary patch literal 300 zcmV+{0n`48P)* zpcjx4;r$Q7=;A~HkjB%Cs{fy!6ivJ#FdISm?!gssHc^JaZA8P-ARUW5&SIT@w_gpm!wX#m`h=uU@fv^P-tpWtbN-2f0r&eZI`qO}bQEhs1dUsy>Q zg@ze|VE{;zM`rf_rx(-yhgX$?`5;Y-`uhJ>OicbO8R-4j_HqXsjMV^`&Bx|OV7LH; zL7Fp9b^kZd@&wCa7y!})l7uOOxj;fk=fAj?HrN1=IEV)EF$@5iimV942N?iTET*9W yHVCHK+fWkI0Q3Mv4@4LnLo>1=#A0;46afHSaSqdpU6oG&0000* zpa+oW;`$H6=yDhUT_K45!_DpgTT@do#%l<&VwgcNjmQ|QAu!GVlal`5R#*T3z{m)~ zFD?da0*S#eh9PjxhYrCNf%%Ufp=iD@E&czzq~!k-y1Fi+>HB`^kQ-XJee*Z^dkK^p%*-TMFk0dGvrFatm)Bio2GQG{2msgWUiS07<|Mf(01Z3rIHO)Qk*Z3SoXG iXfrMV(?GP%qyhl&elR34zS0T+0000!wx|tvvwF)6LV=}=x2ypB5zWeFanBz-|m| zM>asndZM0;%i5S8%RZi~p^^0`@(_ueO;0C_nb9pXix4sEhm+WxS_zP1gMcWRE>|#; yH%JpzLZk>{Y~DXv43Nw_^#B$db^ONWCiMZV1UZBpIj-XX0000* zpa+oW;`$H6=yDhUT_K45!_DpgTT@do#%l<&VwgcNjmQ|QAu!GVlal`5R#*T3z{m)~ zFD?da0*S#eh9PjxhYrCNf%%Ufp=iD@E&czzq~!k-y1F!<*=D@9<1z%K31r*ykRf0LuxNfcO9#6FAOMnp83c1O%-P5oT^|O3Da7e#VgW^(i2(o#1RsPvr<=O~ O0000* zpcjx4;r$Q7=;A~HkjB%Cs{fy!6ivJ#FdISm?!gssHc^JaZA8P-ARUW5&SIT@w_gpmyaX*RdC!88EwM|7tn*$%>8{nIgB0OHJAunZ_U1x&-(kuiz? zlhd>RN5&?A*)Yvm4S;Em4G4g+pZn!ODrF%5v( x46_}CL4J;mfdnAPb`T#!GqNGXVsyO}0RRU!L|BUOAxi)N002ovPDHLkV1l+KcI^NF literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/touhou_little_maid/textures/item/gomoku.png b/src/main/resources/assets/touhou_little_maid/textures/item/gomoku.png new file mode 100644 index 0000000000000000000000000000000000000000..4796d7946793478ce11feb38fa81ca13676a8f39 GIT binary patch literal 223 zcmV<503iQ~P)* zpeK+K;r$Q8=yC)BNYm=cbzt0Iok^S_$hLv#!VoJk4z<_8Zvaf=ts@J-7{&)_gkh`( zpld|Oc*6i)(YYK9pA!^j4qYs9G;J&}MgL5(l~lEjus2x^1@kRpPKgb+Xu19Ul> Z0s#B+6LtRoP(1(u002ovPDHLkV1h%)S^EG0 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/touhou_little_maid/textures/item/keyboard.png b/src/main/resources/assets/touhou_little_maid/textures/item/keyboard.png new file mode 100644 index 0000000000000000000000000000000000000000..659c127b105e74323dceced54bd4c7110cce3b8f GIT binary patch literal 258 zcmV+d0sa1oP)* zpcfDo5&w@aMic;P1mW7sHZYAYj}yQ&R+qQ@&q&D!;+!iFGgglYEm@&Dgq8TNm} zn(bgdRzt99MAodKq51#%}YX4U)+WkQ| zdb=ip4Z*4zW&lV6q#2}9OiUcE5oXBY{im>Nh5?WS$N-QZn;SY%48dtLGC+0(NCU_a z5WRW*Zo-;j0NE8JYX$*iSAYy5Niz(<3?WN141hF}t(jB+06I|RM#a|3B>(^b07*qo IM6N<$f=%&fhyVZp literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/touhou_little_maid/textures/item/model_switcher.png b/src/main/resources/assets/touhou_little_maid/textures/item/model_switcher.png new file mode 100644 index 0000000000000000000000000000000000000000..9ade809b8ae5c835310669d31bcf389de0b31578 GIT binary patch literal 281 zcmV+!0p|XRP)* zpa)PE;`xs*h8M_)@cu`}Po%~F6Kx1c;h*2X!1(6Pn_zldt37cBfE0l=IhjSkv8je5 zSd5?nFq?Biy5Jf?7{&(4;WY$iJ4o@n4JBZMU~HmX0g?pS2GXD?tplb(d>9*K2u=fF znn8-*US9X#Ppbe-gEYd}FnO$ofCQ2K4AOv%k;4S50U!X1?{|l1fo+Aw1kCT;oKh&7 z@frfc$Tq?P5+n{%1JaB$VSyCE5)pDj0?{DXf#g9N@g^*oBA9nTY~&;j!gvjU0gwW8 fMKCdRaf$!{b@&LJXTT9f00000NkvXXu0mjf^xSQG literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/touhou_little_maid/textures/item/power_point.png b/src/main/resources/assets/touhou_little_maid/textures/item/power_point.png index 9f7c4e1d806a6c8b34e84d1a6a969a0fa3b52240..0c43c33f282c99ac149a0e385a052508ea121f46 100644 GIT binary patch delta 234 zcmdnO^owzVL_G^L0|Ud`yN`l^ltF+`h%1nm5#jxxp8i2X;=_jz>$$o6B_#4CB<`D- z)N^wOv$OyI{~xFZ4lXG_S_b4WmIV0)GdMiEkp|>cc)B=-RKz7GG%&fbxiKny{m;zZ zyntcC&v(q+ix(()Jh2sKR{p>DyF6>M-81u803?H%wvHZvNy`sNh^;#@rn6pm5s%)~`G~P512&=(g@` ZVr1axmko(L_w6mnd7iF*F6*2UngCAfRyF_t delta 293 zcmV+=0owlh0ki^;7=H)`0000V^Z#K0008?*L_t(|UZs*z5rZ%gL~($Fq5uatzyS_$ z5C=%WK^(*Z4&opVt_H5VSD1k&(4qa<2}$nWZW1b0t^QHAa94+k)U|%ovuU&lu!3n) zNl8WIT=Q(8GJwtAt8`uFoG3OEatU}mRASUbEsF{jf`-1=nSXn%xdd>8OBhFWXjnK0 zEW&+c3VasRHf|Tj_<+&XVcpWKH5njq6BlSB znj*d#d4l|^eN6_K*G-NIE`Asc!1-QVO$MNo;M{Cv0q36z*yBB&0ur9eekQ(mNk~SM rB49hlnfoT*#U8%r0Qg1O!d<-pBX_x$ghv04c_jAirP+hi5m^fShzs z7srr_xa5QeCO0-WMuo5cnYo)4rW{8bg=d#Wzp$Py)4N;H) literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/touhou_little_maid/textures/item/shrine.png b/src/main/resources/assets/touhou_little_maid/textures/item/shrine.png new file mode 100644 index 0000000000000000000000000000000000000000..1e6ce2135969f1ff59850530056a9d75cd453e48 GIT binary patch literal 348 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>JG%`V zmK~80J#pYb<=OvV?%iWt`Jds&FNT)e4B_WK$w|kA1qa8vJGxoe`5HTVn)wI11_DWU z^XL#mCr?X9XGPPTcAJ)@t8+?6KZ&Ds;R6ays#dNjEr zd>d|E+3NJ;oWsi9Uv3p-e!0GuvH5CX^Fof9yQ|MF(|YpgK;e$gRJORH^Y6}SdD#7^ j*}J@Q@8b#b`SlEL8qA5My;r6Ht!MCb^>bP0l+XkKRNQ)V literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/touhou_little_maid/textures/item/trumpet.png b/src/main/resources/assets/touhou_little_maid/textures/item/trumpet.png index 2c8a7d0bb3c85dcdae152addc178dfd798664b36..749fc9058b2fde7bc2de186f0d87a81e7199ae29 100644 GIT binary patch delta 389 zcmV;00eb%C0+j=h8Gi!+001a04^sdD00d`2O+f$vv5yP zfP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u0EbXaR7C&)02UAefnOSePXoZ6 zE$Fm0qIn@uI}-fyalMQtflwaf)HR%H3T#jr;jb?I>pR1&K7T(g2BCc+z?CSsmmzge z3D&wl!I&)4#4$cA2D6bMSw0HAn;~{n4YrUUk8BuaMhbLJ3ERCudt?@zaS>WS1g(r8 zbWaMNnTUaXZ*XW&V_Zr9|NlIvYQX>i00DGTPE!Ct=GbNc0004EOGiX3y4`WB0001U zNklEe8gw$A+v@ZAT)#0u$X zoMHPU&!!e;^Tl$7>xp%-Nxcv-H>BJuK8oznP(?xaN)--qF?tm0AVyAJXj&`h%eA}R jA4Wvr`Lh4HK~#8NV_={+kQQP755nl;L;;Y-r3)tf z?`*9g-Vm5V5M39+^S`n%;(tzR5OD^86veypgB61`-#g^_zczx8c+J&Of?&n|DV|8_W#dY=l{p%n}8LA z4B0b7^?#l}jsOG+c15uKk8~FQ|L0Q`So5o0wqRR#&Q$+@XpsioCFpv=03^|wq40lY zqsITvNR|IjwojV;Cu>z37vvV~5 oFD~Z&?_tV8yhda|vaOT?00*_f&M>xdRR91007*qoM6N<$f)2N*VgLXD diff --git a/src/main/resources/assets/touhou_little_maid/textures/item/white_fox_scroll.png b/src/main/resources/assets/touhou_little_maid/textures/item/white_fox_scroll.png new file mode 100644 index 0000000000000000000000000000000000000000..6a35e6c024e3c26d52c9fc8fcb6262c283779fd4 GIT binary patch literal 233 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPFP2=EDU1=2DiyzXf!(T-Y19v*H+ z3W_FXE@4R*FP?w@{(ax!Ghe=Z+Iao$|NsAO0|P*cy(X^!Qj8@*e!&b5&u*jvIq9A* zjv*Cs$q5ZiZftIh3Sa*-b2lqYIk|Jq=dAJWK9i?6U9xfje(pRpM*4y z9awRICFIzl54R&6p4O{x;yC=Dok#uvkDKDZ@`Pjick{ed*xk~=b?^UwhK*lr&+s}h ZFt~7wt0v8Hn+UX$!PC{xWt~$(69Ce0R3-ob literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/touhou_little_maid/textures/slot/empty_back_show_slot.png b/src/main/resources/assets/touhou_little_maid/textures/slot/empty_back_show_slot.png index 15e8996e9b2f5d1e263b0cbf84d0362f1e516788..59795edce4f9893e013ae4080340d8b535502613 100644 GIT binary patch delta 130 zcmV-|0Db?c0jL3xEqlQYz%U2{S);$IJF)*L^wr8>T!r*;QUdtEBEtSag(*~+LWPRE z2Vlo}tqCv$kc5H|#!&rhL9;alpLZ*Op?8arNI}~4(UStYKuJh|o4Y{}r0Z%kT~Gxm k*%nkf`*N5BJ5XWD4UfojVg2uArT_o{07*qoM6N<$f<65?fdBvi delta 130 zcmV-|0Db?c0jL3xEqg%@z#s?&{YGz%|NkRR3^l+mR_tM7T-|{tBJB?lRDqxh1d;3R z@}l=WfhmrX0um^i@+`%>{rgpR)0reIk5e-yNd<1w;w=@x225fzR(8{fz-<>F-3C!$ kCC7pYvu~F{Z~_EX9y5bTVQ-cY0000007*qoM6N<$f(oQFmjD0& diff --git a/src/main/resources/assets/touhou_little_maid/textures/slot/empty_bauble_slot.png b/src/main/resources/assets/touhou_little_maid/textures/slot/empty_bauble_slot.png index ce7b8a5a5f6e385d05623ee59ea1e6648cf3ad69..b1d47324c80406d15397be61f3e338e13458efac 100644 GIT binary patch delta 126 zcmbQvIGb^TN_mo}i(^Pd+}?git_A}hme0KTm+YVZ_wUpQxg{%jt*L!s$&H*_D^^7? z9%2Q-MU!W$9@%F8M4|2BXPKRI(iBsE+`IRFR=xIxMgO_&>K(n+mREMPzrCz&!vBy7 b42u}j*3Y{+w{`j}1|aZs^>bP0l+XkK9$`6+ delta 122 zcmbQuIGu5VN^zX0i(^Pd+}?git^*1JNB+3wFa7`jvDeaRiHi?~yilC}f<;#_u&kk= z5d>ezJQqFnxPag3Sgyhr4>qOV-`vY4hAOoGIi X7yAxVEqjkf1|aZs^>bP0l+XkK9`G~$ diff --git a/src/main/resources/assets/touhou_little_maid/tlm_custom_pack/touhou_little_maid-1.0.0.zip b/src/main/resources/assets/touhou_little_maid/tlm_custom_pack/touhou_little_maid-1.0.0.zip index a70c22d258dca45b8dc8e3042d301bfdfd1b3b60..de0a216cdc7323326539a4986abcbab1f6a1ae8d 100644 GIT binary patch delta 33536 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