diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index a06335b..35af98a 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -5,6 +5,9 @@ * By Default, The Outline Width Is Set Using The GUI Scale * This Can Be Overridden Using The ``MCPI_BLOCK_OUTLINE_WIDTH`` Environmental Variable * Added ``overwrite_calls_within`` Function +* Add ``Add Cake`` Feature Flag (Enabled By Default) +* Add Milk Buckets +* Implement Crafting Remainders **2.5.2** * Add ``3D Chest Model`` Feature Flag (Enabled By Default) diff --git a/launcher/src/client/available-feature-flags b/launcher/src/client/available-feature-flags index a8436a3..b6f75d1 100644 --- a/launcher/src/client/available-feature-flags +++ b/launcher/src/client/available-feature-flags @@ -51,3 +51,4 @@ TRUE Disable Hostile AI In Creative Mode TRUE Load Custom Skins TRUE 3D Chest Model TRUE Replace Block Highlight With Outline +TRUE Add Cake diff --git a/mods/CMakeLists.txt b/mods/CMakeLists.txt index b9dac35..6a6c05d 100644 --- a/mods/CMakeLists.txt +++ b/mods/CMakeLists.txt @@ -109,6 +109,9 @@ target_link_libraries(options mods-headers reborn-patch symbols feature home) add_library(bucket SHARED src/bucket/bucket.cpp) target_link_libraries(bucket mods-headers reborn-patch symbols feature misc) +add_library(cake SHARED src/cake/cake.cpp) +target_link_libraries(cake mods-headers reborn-patch symbols feature misc) + add_library(home SHARED src/home/home.c) target_link_libraries(home mods-headers reborn-patch symbols) @@ -116,7 +119,7 @@ add_library(test SHARED src/test/test.c) target_link_libraries(test mods-headers reborn-patch home) add_library(init SHARED src/init/init.c) -target_link_libraries(init symbols mods-headers reborn-util compat game-mode misc death options chat creative bucket textures home version test media-layer-core) +target_link_libraries(init symbols mods-headers reborn-util compat game-mode misc death options chat creative bucket cake textures home version test media-layer-core) if(MCPI_SERVER_MODE) target_link_libraries(init server) else() @@ -127,7 +130,7 @@ if(NOT MCPI_HEADLESS_MODE) endif() ## Install Mods -set(MODS_TO_INSTALL init compat readdir feature game-mode misc override death options chat creative bucket textures home version test) +set(MODS_TO_INSTALL init compat readdir feature game-mode misc override death options chat creative bucket cake textures home version test) if(MCPI_SERVER_MODE) list(APPEND MODS_TO_INSTALL server) else() diff --git a/mods/include/mods/init/init.h b/mods/include/mods/init/init.h index ea7b952..5f9d3b7 100644 --- a/mods/include/mods/init/init.h +++ b/mods/include/mods/init/init.h @@ -33,6 +33,7 @@ void init_death(); void init_options(); void init_chat(); void init_bucket(); +void init_cake(); void init_home(); #ifdef __cplusplus diff --git a/mods/src/bucket/bucket.cpp b/mods/src/bucket/bucket.cpp index 0c0cf33..1e95ddc 100644 --- a/mods/src/bucket/bucket.cpp +++ b/mods/src/bucket/bucket.cpp @@ -157,6 +157,17 @@ static ItemInstance *BucketItem_use(FoodItem *item, ItemInstance *item_instance, return item_instance; } +static ItemInstance *BucketItem_getCraftingRemainingItem(FoodItem *item, ItemInstance *item_instance) { + if (item_instance->auxiliary == 0) { + return NULL; + } + ItemInstance *ret = alloc_ItemInstance(); + ret->id = item->id; + ret->count = item_instance->count; + ret->auxiliary = 0; + return ret; +} + // Bucket VTable static FoodItem_vtable *get_bucket_vtable() { static FoodItem_vtable *vtable = NULL; @@ -174,6 +185,7 @@ static FoodItem_vtable *get_bucket_vtable() { vtable->getUseAnimation = BucketItem_getUseAnimation; vtable->isFood = BucketItem_isFood; vtable->use = BucketItem_use; + vtable->getCraftingRemainingItem = BucketItem_getCraftingRemainingItem; } return vtable; } diff --git a/mods/src/cake/README.md b/mods/src/cake/README.md new file mode 100644 index 0000000..7ce1b4e --- /dev/null +++ b/mods/src/cake/README.md @@ -0,0 +1,3 @@ +# ``cake`` Mod + +This mod adds cake. diff --git a/mods/src/cake/cake.cpp b/mods/src/cake/cake.cpp new file mode 100644 index 0000000..cb38dd4 --- /dev/null +++ b/mods/src/cake/cake.cpp @@ -0,0 +1,242 @@ +#include +#include + +#include +#include +#include + +Tile *cake = NULL; + +#define CAKE_LEN 0.0625F + +// Description +static std::string Cake_getDescriptionId(__attribute__((unused)) Tile *tile) { + return "tile.cake"; +} + +// Textures +static int Cake_getTexture2(__attribute__((unused)) Tile *tile, int face) { + if (face == 1) { + // Top texture + return 121; + } else if (face == 0) { + // Bottom texture + return 124; + } + // Side texture + return 122; +} + +static int Cake_getTexture3(__attribute__((unused)) Tile *tile, Level *level, int x, int y, int z, int face) { + // Eaten face + if (face == 3) { + int data = level->vtable->getData(level, x, y, z); + if (data != 0 && data < 6) { + // Sliced texture + return 123; + } + } + // Normal + return Cake_getTexture2(tile, face); +} + +// Rendering +static bool Cake_isSolidRender(__attribute__((unused)) Tile *tile) { + // Stop it from turning other blocks invisable + return 0; +} + +static int Cake_getRenderLayer(__attribute__((unused)) Tile *tile) { + // Stop weird transparency issues + return 1; +} + +static bool Cake_isCubeShaped(__attribute__((unused)) Tile *tile) { + return false; +} + +// Size +static void Cake_updateDefaultShape(Tile *tile) { + // Set the default shape + tile->vtable->setShape( + tile, + CAKE_LEN, 0.0, CAKE_LEN, + 1.0 - CAKE_LEN, 0.5, 1.0 - CAKE_LEN + ); +} + +static AABB *Cake_getAABB(Tile *tile, Level *level, int x, int y, int z) { + // Get the size of the slices + int data = level->vtable->getData(level, x, y, z); + if (data >= 6) data = 0; + float slice_size = (1.0 / 7.0) * (float) data; + + // Corner 1 + AABB *aabb = &tile->aabb; + aabb->x1 = (float) x + CAKE_LEN; + aabb->y1 = (float) y; + aabb->z1 = (float) z + CAKE_LEN; + + // Corner 2 + aabb->x2 = (float) x + (1.0 - CAKE_LEN); + aabb->y2 = (float) y + 0.5; + aabb->z2 = (float) z + (1.0 - CAKE_LEN) - slice_size; + + return aabb; +} + +static void Cake_updateShape(Tile *tile, Level *level, int x, int y, int z) { + // Set cake + int data = level->vtable->getData(level, x, y, z); + if (data >= 6) data = 0; + // Get slice amount + float slice_size = (1.0 / 7.0) * (float) data; + tile->vtable->setShape( + tile, + CAKE_LEN, 0.0, CAKE_LEN, + 1.0 - CAKE_LEN, 0.5, (1.0 - CAKE_LEN) - slice_size + ); +} + +// Eating +static int Cake_use(__attribute__((unused)) Tile *tile, Level *level, int x, int y, int z, Player *player) { + // Eat + SimpleFoodData_eat(&player->foodData, 3); + // Set the new tile + int data = level->vtable->getData(level, x, y, z); + if (data >= 5) { + // Remove the cake, it has been completely gobbled up + Level_setTileAndData(level, x, y, z, 0, 0); + } else { + // Remove a slice + Level_setTileAndData(level, x, y, z, 92, data + 1); + } + return 1; +} + +// Makes the cakes +static void make_cake() { + // Construct + cake = alloc_Tile(); + ALLOC_CHECK(cake); + int texture = 122; + Tile_constructor(cake, 92, texture, Material_dirt); + cake->texture = texture; + + // Set VTable + cake->vtable = dup_Tile_vtable(Tile_vtable_base); + ALLOC_CHECK(cake->vtable); + + // Set shape + cake->vtable->setShape( + cake, + CAKE_LEN, 0.0, CAKE_LEN, + 1.0 - CAKE_LEN, 0.5, 1.0 - CAKE_LEN + ); + + // Modify functions + cake->vtable->getDescriptionId = Cake_getDescriptionId; + cake->vtable->getTexture3 = Cake_getTexture3; + cake->vtable->getTexture2 = Cake_getTexture2; + cake->vtable->isSolidRender = Cake_isSolidRender; + cake->vtable->getRenderLayer = Cake_getRenderLayer; + cake->vtable->isCubeShaped = Cake_isCubeShaped; + cake->vtable->updateShape = Cake_updateShape; + cake->vtable->updateDefaultShape = Cake_updateDefaultShape; + cake->vtable->getAABB = Cake_getAABB; + cake->vtable->use = Cake_use; + + // Init + Tile_init(cake); + cake->vtable->setDestroyTime(cake, 1.0f); + cake->vtable->setExplodeable(cake, 20.0f); + cake->category = 4; + std::string name = "Cake"; + cake->vtable->setDescriptionId(cake, &name); +} + +static void Tile_initTiles_injection(__attribute__((unused)) void *null) { + make_cake(); +} + +// Add cake to creative inventory +static void Inventory_setupDefault_FillingContainer_addItem_call_injection(FillingContainer *filling_container) { + ItemInstance *cake_instance = new ItemInstance; + ALLOC_CHECK(cake_instance); + cake_instance->count = 255; + cake_instance->auxiliary = 0; + cake_instance->id = 92; + (*FillingContainer_addItem)(filling_container, cake_instance); +} + +// Recipe (only when buckets are enabled) +static void Recipes_injection(Recipes *recipes) { + // Sugar + Recipes_Type sugar = { + .item = 0, + .tile = 0, + .instance = { + .count = 1, + .id = 353, + .auxiliary = 0 + }, + .letter = 's' + }; + // Wheat + Recipes_Type wheat = { + .item = 0, + .tile = 0, + .instance = { + .count = 1, + .id = 296, + .auxiliary = 0 + }, + .letter = 'w' + }; + // Eggs + Recipes_Type eggs = { + .item = 0, + .tile = 0, + .instance = { + .count = 1, + .id = 344, + .auxiliary = 0 + }, + .letter = 'e' + }; + // Milk + Recipes_Type milk = { + .item = 0, + .tile = 0, + .instance = { + .count = 1, + .id = 325, + .auxiliary = 1 + }, + .letter = 'm' + }; + // Cake + ItemInstance cake_item = { + .count = 1, + .id = 92, + .auxiliary = 0 + }; + // Add + std::string line1 = "mmm"; + std::string line2 = "ses"; + std::string line3 = "www"; + std::vector ingredients = {milk, sugar, wheat, eggs}; + Recipes_addShapedRecipe_3(recipes, &cake_item, &line1, &line2, &line3, &ingredients); +} + +void init_cake() { + // Add cakes + if (feature_has("Add Cake", server_enabled)) { + misc_run_on_tiles_setup(Tile_initTiles_injection); + misc_run_on_creative_inventory_setup(Inventory_setupDefault_FillingContainer_addItem_call_injection); + if (feature_has("Add Buckets", server_enabled)) { + // The recipe needs milk buckets + misc_run_on_recipes_setup(Recipes_injection); + } + } +} diff --git a/mods/src/init/init.c b/mods/src/init/init.c index 5823111..041fc90 100644 --- a/mods/src/init/init.c +++ b/mods/src/init/init.c @@ -32,6 +32,7 @@ __attribute__((constructor)) static void init() { init_options(); init_chat(); init_bucket(); + init_cake(); init_home(); #ifndef MCPI_SERVER_MODE init_benchmark(); diff --git a/mods/src/misc/README.md b/mods/src/misc/README.md index 3b1231b..882a7b6 100644 --- a/mods/src/misc/README.md +++ b/mods/src/misc/README.md @@ -5,3 +5,4 @@ This mod has several miscellaneous mods that are too small to be their own mod, * Removing the red background from unobtainable items in the inventory. * Loading the bundled language file. * Printing chat messages to the log. +* Implementing crafting remainders. diff --git a/mods/src/misc/api.cpp b/mods/src/misc/api.cpp index 6e5a7b7..c3022d0 100644 --- a/mods/src/misc/api.cpp +++ b/mods/src/misc/api.cpp @@ -86,11 +86,11 @@ static void Inventory_setupDefault_FillingContainer_addItem_call_injection(Filli SETUP_CALLBACK(tiles_setup, void); // Handle Custom Tiles Setup Behavior static void Tile_initTiles_injection() { - // Call Original Method - Tile_initTiles(); - // Run Functions handle_misc_tiles_setup(NULL); + + // Call Original Method + Tile_initTiles(); } // Run Functions On Items Setup diff --git a/mods/src/misc/misc.cpp b/mods/src/misc/misc.cpp index 6e34cbe..410760e 100644 --- a/mods/src/misc/misc.cpp +++ b/mods/src/misc/misc.cpp @@ -58,6 +58,38 @@ static void PauseScreen_init_injection(PauseScreen *screen) { } } +// Implement crafting remainders +void PaneCraftingScreen_craftSelectedItem_PaneCraftingScreen_recheckRecipes_injection(PaneCraftingScreen *self) { + // Check for crafting remainders + CItem *item = self->item; + for (size_t i = 0; i < item->ingredients.size(); i++) { + ItemInstance requested_item_instance = item->ingredients[i].requested_item; + Item *requested_item = Item_items[requested_item_instance.id]; + ItemInstance *craftingRemainingItem = requested_item->vtable->getCraftingRemainingItem(requested_item, &requested_item_instance); + if (craftingRemainingItem != NULL) { + // Add or drop remainder + LocalPlayer *player = self->minecraft->player; + if (!player->inventory->vtable->add(player->inventory, craftingRemainingItem)) { + // Drop + player->vtable->drop(player, craftingRemainingItem, false); + } + } + } + // Call Original Method + PaneCraftingScreen_recheckRecipes(self); +} + +ItemInstance *Item_getCraftingRemainingItem_injection(Item *self, ItemInstance *item_instance) { + if (self->craftingRemainingItem != NULL) { + ItemInstance *ret = alloc_ItemInstance(); + ret->id = self->craftingRemainingItem->id; + ret->count = item_instance->count; + ret->auxiliary = 0; + return ret; + } + return NULL; +} + // Init void _init_misc_cpp() { // Implement AppPlatform::readAssetFile So Translations Work @@ -70,4 +102,8 @@ void _init_misc_cpp() { // Add Missing Buttons To Pause Menu patch_address(PauseScreen_init_vtable_addr, (void *) PauseScreen_init_injection); } + + // Implement crafting remainders + overwrite_call((void *) 0x2e230, (void *) PaneCraftingScreen_craftSelectedItem_PaneCraftingScreen_recheckRecipes_injection); + overwrite((void *) Item_getCraftingRemainingItem_non_virtual, (void *) Item_getCraftingRemainingItem_injection); } diff --git a/symbols/CMakeLists.txt b/symbols/CMakeLists.txt index 7715ed9..6b2ff44 100644 --- a/symbols/CMakeLists.txt +++ b/symbols/CMakeLists.txt @@ -100,6 +100,7 @@ set(SRC src/gui/screens/StartMenuScreen.def src/gui/screens/ProgressScreen.def src/gui/screens/Touch_SelectWorldScreen.def + src/gui/screens/PaneCraftingScreen.def src/gui/Font.def src/gui/components/ImageButton.def src/gui/components/OptionButton.def @@ -128,6 +129,7 @@ set(SRC src/tile/HeavyTile.def src/misc/Strings.def src/misc/I18n.def + src/misc/SimpleFoodData.def src/entity/ModelPart.def src/misc/Tesselator.def src/misc/AABB.def @@ -147,6 +149,8 @@ set(SRC src/recipes/FurnaceRecipes.def src/recipes/Recipes.def src/recipes/Recipes_Type.def + src/recipes/ReqItem.def + src/recipes/CItem.def ) # Resolve Definition Files set(RESOLVED_SRC "") diff --git a/symbols/src/entity/player/Player.def b/symbols/src/entity/player/Player.def index a63a002..f82458a 100644 --- a/symbols/src/entity/player/Player.def +++ b/symbols/src/entity/player/Player.def @@ -17,3 +17,4 @@ property std::string username = 0xbf4; property bool immortal = 0xbfc; property bool infinite_items = 0xbff; property ItemInstance itemBeingUsed = 0xc34; +property SimpleFoodData foodData = 0xc00; diff --git a/symbols/src/gui/screens/PaneCraftingScreen.def b/symbols/src/gui/screens/PaneCraftingScreen.def new file mode 100644 index 0000000..b561ce8 --- /dev/null +++ b/symbols/src/gui/screens/PaneCraftingScreen.def @@ -0,0 +1,5 @@ +method void craftSelectedItem() = 0x2e0e4; +method void recheckRecipes() = 0x2dc98; + +property Minecraft *minecraft = 0x14; +property CItem *item = 0x74; diff --git a/symbols/src/item/Item.def b/symbols/src/item/Item.def index c1e72e4..656c977 100644 --- a/symbols/src/item/Item.def +++ b/symbols/src/item/Item.def @@ -20,6 +20,8 @@ virtual-method bool isFood() = 0x64; virtual-method bool isArmor() = 0x68; virtual-method void setDescriptionId(std::string *name) = 0x6c; virtual-method std::string getDescriptionId(ItemInstance *item_instance) = 0x7c; +// This returns an Item*, but it's never called normally so it doesn't matter if we invent a better system over top of it +virtual-method ItemInstance *getCraftingRemainingItem(ItemInstance *item_instance) = 0x84; // Swing = 0, eating = 1, drinking = 2, bow = 4, anything else is nothing virtual-method int getUseAnimation() = 0x94; @@ -30,6 +32,7 @@ property int category = 0x10; property int max_stack_size = 0x14; property bool equipped = 0x18; property bool is_stacked_by_data = 0x19; +property Item *craftingRemainingItem = 0x1c; property std::string description_id = 0x20; // Globals diff --git a/symbols/src/level/Material.def b/symbols/src/level/Material.def index 9d54f76..fb420b0 100644 --- a/symbols/src/level/Material.def +++ b/symbols/src/level/Material.def @@ -1,4 +1,7 @@ virtual-method bool isSolid() = 0x8; // Globals -static-property Material *Material_stone = 0x180a9c; \ No newline at end of file +static-property Material *dirt = 0x180a94; +static-property Material *stone = 0x180a9c; +static-property Material *metal = 0x180aa0; +static-property Material *glass = 0x180acc; diff --git a/symbols/src/misc/SimpleFoodData.def b/symbols/src/misc/SimpleFoodData.def new file mode 100644 index 0000000..880dc2a --- /dev/null +++ b/symbols/src/misc/SimpleFoodData.def @@ -0,0 +1,5 @@ +size 0x4; + +method void eat(int amount) = 0x91470; + +property int health = 0x0; diff --git a/symbols/src/recipes/CItem.def b/symbols/src/recipes/CItem.def new file mode 100644 index 0000000..e555ae6 --- /dev/null +++ b/symbols/src/recipes/CItem.def @@ -0,0 +1,3 @@ +property ItemInstance item = 0x0; +property std::vector ingredients = 0x20; +property bool craftable = 0x2c; diff --git a/symbols/src/recipes/ReqItem.def b/symbols/src/recipes/ReqItem.def new file mode 100644 index 0000000..a01fe2a --- /dev/null +++ b/symbols/src/recipes/ReqItem.def @@ -0,0 +1,3 @@ +size 0x10; + +property ItemInstance requested_item = 0x0; diff --git a/symbols/src/tile/Tile.def b/symbols/src/tile/Tile.def index fe01648..91e12e2 100644 --- a/symbols/src/tile/Tile.def +++ b/symbols/src/tile/Tile.def @@ -3,20 +3,38 @@ static-method void initTiles() = 0xc358c; vtable 0x115670; vtable-size 0x104; -constructor (int id, int texture, void *material) = 0xc33a0; +constructor (int id, int texture, Material *material) = 0xc33a0; size 0x5c; method Tile *init() = 0xc34dc; -virtual-method Tile *setDestroyTime(float destroy_time) = 0xf8; -virtual-method Tile *setExplodeable(float explodeable) = 0xf4; -virtual-method Tile *setSoundType(Tile_SoundType *sound_type) = 0xe8; + +virtual-method bool isCubeShaped() = 0x8; +virtual-method int getRenderShape() = 0xc; +virtual-method void setShape(float x1, float y1, float z1, float x2, float y2, float z2) = 0x10; +virtual-method void updateShape(Level *level, int x, int y, int z) = 0x14; +virtual-method void updateDefaultShape() = 0x18; +virtual-method int getTexture1() = 0x28; +virtual-method int getTexture2(int face) = 0x2c; +virtual-method int getTexture3(Level *level, int x, int y, int z, int face) = 0x30; +virtual-method AABB *getAABB(Level *level, int x, int y, int z) = 0x34; +virtual-method bool isSolidRender() = 0x40; +virtual-method void tick(Level *level, int x, int y, int z) = 0x58; +virtual-method void neighborChanged(Level *level, int x, int y, int z, int neighborId) = 0x64; +virtual-method void onPlace(Level *level, int x, int y, int z) = 0x68; +virtual-method void onRemove(Level *level, int x, int y, int z) = 0x6c; +virtual-method int getRenderLayer() = 0x94; virtual-method int use(Level *level, int x, int y, int z, Player *player) = 0x98; virtual-method int getColor(LevelSource *level_source, int x, int y, int z) = 0xb8; -virtual-method int getRenderShape() = 0xc; +virtual-method std::string getDescriptionId() = 0xdc; virtual-method Tile *setDescriptionId(std::string *description_id) = 0xe0; +virtual-method Tile *setSoundType(Tile_SoundType *sound_type) = 0xe8; +virtual-method Tile *setExplodeable(float explodeable) = 0xf4; +virtual-method Tile *setDestroyTime(float destroy_time) = 0xf8; +property int texture = 0x4; property int id = 0x8; property int category = 0x3c; +property AABB aabb = 0x40; // Globals static-property-array Tile *tiles = 0x180e08;