diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 9d9ff59c..ef1feb53 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,10 +1,16 @@ # Changelog +**3.0.0** +* Add ``Add Cake`` Feature Flag (Enabled By Default) +* Add Milk Buckets +* Implement Crafting Remainders +* Add Death Messages + **2.5.3** * Add `Replace Block Highlight With Outline` Feature Flag (Enabled By Default) - * 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 +* 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 **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 583ea18e..b49f12dd 100644 --- a/launcher/src/client/available-feature-flags +++ b/launcher/src/client/available-feature-flags @@ -51,6 +51,7 @@ TRUE Disable Hostile AI In Creative Mode TRUE Load Custom Skins TRUE 3D Chest Model TRUE Replace Block Highlight With Outline +TRUE Add Cake TRUE Use Java Beta 1.3 Light Ramp TRUE Send Full Level When Hosting Game FALSE Food Overlay diff --git a/mods/CMakeLists.txt b/mods/CMakeLists.txt index a2e620fc..78149dfb 100644 --- a/mods/CMakeLists.txt +++ b/mods/CMakeLists.txt @@ -35,6 +35,8 @@ set(SRC src/options/options.cpp # bucket src/bucket/bucket.cpp + # cake + src/cake/cake.cpp # home src/home/home.c # test diff --git a/mods/include/mods/bucket/bucket.h b/mods/include/mods/bucket/bucket.h new file mode 100644 index 00000000..2547d638 --- /dev/null +++ b/mods/include/mods/bucket/bucket.h @@ -0,0 +1,3 @@ +#pragma once + +extern bool buckets_enabled; diff --git a/mods/include/mods/init/init.h b/mods/include/mods/init/init.h index 2809dd51..46e85cf2 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/include/mods/misc/misc.h b/mods/include/mods/misc/misc.h index b8a24c5f..493d270b 100644 --- a/mods/include/mods/misc/misc.h +++ b/mods/include/mods/misc/misc.h @@ -22,6 +22,7 @@ void misc_run_on_creative_inventory_setup(misc_update_function_FillingContainer_ typedef void (*misc_update_function_void_t)(void *obj); void misc_run_on_tiles_setup(misc_update_function_void_t function); // obj == NULL void misc_run_on_items_setup(misc_update_function_void_t function); // obj == NULL +void misc_run_on_language_setup(misc_update_function_void_t function); // obj == NULL typedef bool (*misc_update_function_key_press_t)(Minecraft *minecrtaft, int key); void misc_run_on_game_key_press(misc_update_function_key_press_t function); // In-Game Key Presses Only diff --git a/mods/src/bucket/bucket.cpp b/mods/src/bucket/bucket.cpp index 888a4eee..22af894c 100644 --- a/mods/src/bucket/bucket.cpp +++ b/mods/src/bucket/bucket.cpp @@ -6,31 +6,57 @@ #include // Items -static Item *bucket = NULL; +static FoodItem *bucket = NULL; // Description And Texture -static std::string BucketItem_getDescriptionId(__attribute__((unused)) Item *item, ItemInstance *item_instance) { +static std::string BucketItem_getDescriptionId(__attribute__((unused)) FoodItem *item, ItemInstance *item_instance) { if (item_instance->auxiliary == Tile_water->id) { return "item.bucketWater"; } else if (item_instance->auxiliary == Tile_lava->id) { return "item.bucketLava"; + } else if (item_instance->auxiliary == 1) { + return "item.bucketMilk"; } else { return "item.bucket"; } } -static int32_t BucketItem_getIcon(__attribute__((unused)) Item *item, int32_t auxiliary) { +static int32_t BucketItem_getIcon(__attribute__((unused)) FoodItem *item, int32_t auxiliary) { if (auxiliary == Tile_water->id) { return 75; } else if (auxiliary == Tile_lava->id) { return 76; + } else if (auxiliary == 1) { + return 77; } else { return 74; } } +// Filling +static bool fill_bucket(ItemInstance *item_instance, Player *player, int new_auxiliary) { + bool success = false; + if (item_instance->count == 1) { + item_instance->auxiliary = new_auxiliary; + success = true; + } else { + ItemInstance new_item; + new_item.id = bucket->id; + new_item.count = 1; + new_item.auxiliary = new_auxiliary; + Inventory *inventory = player->inventory; + if (inventory->vtable->add(inventory, &new_item)) { + // Added To Inventory + success = true; + item_instance->count -= 1; + } + } + return success; +} + + // Use Bucket -static int32_t BucketItem_useOn(__attribute__((unused)) Item *item, ItemInstance *item_instance, Player *player, Level *level, int32_t x, int32_t y, int32_t z, int32_t hit_side, __attribute__((unused)) float hit_x, __attribute__((unused)) float hit_y, __attribute__((unused)) float hit_z) { - if (item_instance->count < 1) { +static int32_t BucketItem_useOn(__attribute__((unused)) FoodItem *item, ItemInstance *item_instance, Player *player, Level *level, int32_t x, int32_t y, int32_t z, int32_t hit_side, __attribute__((unused)) float hit_x, __attribute__((unused)) float hit_y, __attribute__((unused)) float hit_z) { + if (item_instance->count < 1 || item_instance->auxiliary == 1) { return 0; } else if (item_instance->auxiliary == 0) { // Empty Bucket @@ -43,23 +69,7 @@ static int32_t BucketItem_useOn(__attribute__((unused)) Item *item, ItemInstance } if (new_auxiliary != 0) { // Valid - bool success = false; - if (item_instance->count == 1) { - item_instance->auxiliary = new_auxiliary; - success = true; - } else { - ItemInstance new_item; - new_item.id = bucket->id; - new_item.count = 1; - new_item.auxiliary = new_auxiliary; - Inventory *inventory = player->inventory; - if (inventory->vtable->add(inventory, &new_item)) { - // Added To Inventory - success = true; - item_instance->count -= 1; - } - } - if (success) { + if (fill_bucket(item_instance, player, new_auxiliary)) { Level_setTileAndData(level, x, y, z, 0, 0); return 1; } else { @@ -116,19 +126,68 @@ static int32_t BucketItem_useOn(__attribute__((unused)) Item *item, ItemInstance } } +static int BucketItem_getUseDuration(__attribute__((unused)) FoodItem *item, ItemInstance *item_instance) { + if (item_instance->auxiliary == 1) { + return 0x20; + } + return 0; +} + +static ItemInstance BucketItem_useTimeDepleted(FoodItem *item, ItemInstance *item_instance, Level *level, Player *player) { + if (item_instance->auxiliary == 1) { + *item_instance = FoodItem_useTimeDepleted_non_virtual(item, item_instance, level, player); + // Set it to a empty bucket + item_instance->auxiliary = 0; + item_instance->count = 1; + } + return *item_instance; +} + +static int BucketItem_getUseAnimation(__attribute__((unused)) FoodItem *item) { + return 2; +} + +static bool BucketItem_isFood(__attribute__((unused)) FoodItem *item) { + return true; +} + +static ItemInstance *BucketItem_use(FoodItem *item, ItemInstance *item_instance, __attribute__((unused)) Level *level, Player *player) { + if (item_instance->auxiliary == 1) { + return (*FoodItem_use_vtable_addr)(item, item_instance, level, player); + } + 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 -CUSTOM_VTABLE(bucket, Item) { +CUSTOM_VTABLE(bucket, FoodItem) { vtable->getDescriptionId = BucketItem_getDescriptionId; vtable->getIcon = BucketItem_getIcon; vtable->useOn = BucketItem_useOn; + vtable->getUseDuration = BucketItem_getUseDuration; + vtable->useTimeDepleted = BucketItem_useTimeDepleted; + vtable->getUseAnimation = BucketItem_getUseAnimation; + vtable->isFood = BucketItem_isFood; + vtable->use = BucketItem_use; + vtable->getCraftingRemainingItem = BucketItem_getCraftingRemainingItem; } // Create Items -static Item *create_bucket(int32_t id, int32_t texture_x, int32_t texture_y, std::string name) { +static FoodItem *create_bucket(int32_t id, int32_t texture_x, int32_t texture_y, std::string name) { // Construct - Item *item = alloc_Item(); + FoodItem *item = alloc_FoodItem(); ALLOC_CHECK(item); - Item_constructor(item, id); + Item_constructor((Item *) item, id); // Set VTable item->vtable = get_bucket_vtable(); @@ -140,6 +199,9 @@ static Item *create_bucket(int32_t id, int32_t texture_x, int32_t texture_y, std item->category = 2; item->max_damage = 0; item->max_stack_size = 1; + item->nutrition = 0; + item->unknown_param_1 = 0.6; + item->meat = false; // Return return item; @@ -159,17 +221,29 @@ static int32_t ItemInstance_getMaxStackSize_injection(ItemInstance *item_instanc } } +// Milking +bool Cow_interact_injection(Cow *self, Player *player) { + ItemInstance *item = Inventory_getSelected(player->inventory); + if (item && item->id == bucket->id && item->auxiliary == 0) { + // Fill with milk + fill_bucket(item, player, 1); + return true; + } + return Cow_interact_non_virtual(self, player); +} + // Creative Inventory -static void inventory_add_item(FillingContainer *inventory, Item *item, int32_t auxiliary) { +static void inventory_add_item(FillingContainer *inventory, FoodItem *item, int32_t auxiliary) { ItemInstance *item_instance = new ItemInstance; ALLOC_CHECK(item_instance); - item_instance = ItemInstance_constructor_item_extra(item_instance, item, 1, auxiliary); + item_instance = ItemInstance_constructor_item_extra(item_instance, (Item *) item, 1, auxiliary); FillingContainer_addItem(inventory, item_instance); } static void Inventory_setupDefault_FillingContainer_addItem_call_injection(FillingContainer *filling_container) { inventory_add_item(filling_container, bucket, 0); inventory_add_item(filling_container, bucket, Tile_water->id); inventory_add_item(filling_container, bucket, Tile_lava->id); + inventory_add_item(filling_container, bucket, 1); } // Make Liquids Selectable @@ -264,14 +338,23 @@ static void FurnaceTileEntity_tick_ItemInstance_setNull_injection(ItemInstance * } } +// Add the bucket name to the language file +static void Language_injection(__attribute__((unused)) void *null) { + I18n__strings.insert(std::make_pair("item.bucketMilk.name", "Milk Bucket")); +} + // Init +bool buckets_enabled = false; void init_bucket() { // Add Buckets - if (feature_has("Add Buckets", server_enabled)) { + buckets_enabled = feature_has("Add Buckets", server_enabled); + if (buckets_enabled) { // Add Items misc_run_on_items_setup(Item_initItems_injection); // Change Max Stack Size Based On Auxiliary overwrite_calls((void *) ItemInstance_getMaxStackSize, (void *) ItemInstance_getMaxStackSize_injection); + // Enable milking + patch_address((void *) Cow_interact_vtable_addr, (void *) Cow_interact_injection); // Creative Inventory misc_run_on_creative_inventory_setup(Inventory_setupDefault_FillingContainer_addItem_call_injection); // Make Liquids Selectable @@ -284,5 +367,7 @@ void init_bucket() { // Custom Furnace Fuel overwrite_calls((void *) FurnaceTileEntity_getBurnDuration, (void *) FurnaceTileEntity_getBurnDuration_injection); overwrite_call((void *) 0xd351c, (void *) FurnaceTileEntity_tick_ItemInstance_setNull_injection); + // Language for milk + misc_run_on_language_setup(Language_injection); } } diff --git a/mods/src/cake/README.md b/mods/src/cake/README.md new file mode 100644 index 00000000..7ce1b4ec --- /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 00000000..c9c00343 --- /dev/null +++ b/mods/src/cake/cake.cpp @@ -0,0 +1,243 @@ +#include +#include + +#include +#include +#include +#include + +static 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, __attribute__((unused)) int data) { + 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, LevelSource *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, 0); +} + +// 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, LevelSource *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 (buckets_enabled) { + // The recipe needs milk buckets + misc_run_on_recipes_setup(Recipes_injection); + } + } +} diff --git a/mods/src/death/death.cpp b/mods/src/death/death.cpp index 995829a7..606ece2f 100644 --- a/mods/src/death/death.cpp +++ b/mods/src/death/death.cpp @@ -7,55 +7,136 @@ #include // Death Messages -static std::string get_death_message(Player *player) { - // Get Username - std::string *username = &player->username; - +static const char *monster_names[] = {"Zombie", "Creeper", "Skeleton", "Spider", "Zombie Pigman"}; +static std::string get_death_message(Player *player, Entity *cause, bool was_shot = false) { // Prepare Death Message - std::string message; - message.append(username->c_str()); - message.append(" has died"); + std::string message = player->username; + if (cause) { + // Entity cause + int type_id = cause->vtable->getEntityTypeId(cause); + int aux = cause->vtable->getAuxData(cause); + bool is_player = cause->vtable->isPlayer(cause); + if (cause->vtable->getCreatureBaseType(cause) != 0 || is_player) { + // Killed by a creature + if (was_shot) { + message += " was shot by "; + } else { + message += " was killed by "; + } + if (is_player) { + // Killed by a player + message += ((Player *) cause)->username; + } else if (32 <= type_id && type_id <= 36) { + // Normal monster + message += "a "; + message += monster_names[type_id - 32]; + } else { + // Unknown creature + message += "a Mysterious Beast"; + } + return message; + } else if (aux) { + // Killed by a throwable with owner + Level *level = player->level; + Entity *shooter = Level_getEntity(level, aux); + return get_death_message(player, shooter, true); + } else if (type_id == 65) { + // Blown up by TNT + return message + " was blown apart"; + } else if (cause->vtable->isHangingEntity(cause)) { + // Painting? + return message + " admired too much art"; + } + } + + if (was_shot) { + // Throwable with invalid owner + return message + " was shot under mysterious circumstances"; + } else if (cause) { + // Unknown entity + return message + " was killed"; + } else { + // Anything else + return message + " has died"; + } // Return return message; } +static bool is_hurt = false; +static bool Mob_hurt_injection(Mob *mob, Entity *source, int dmg) { + // Call Original Method + is_hurt = true; + bool ret = Mob_hurt_non_virtual(mob, source, dmg); + is_hurt = false; + return ret; +} + // Death Message Logic -#define Player_actuallyHurt_injection(type) \ +#define Player_death_injections(type) \ + static void type##Player_die_injection(type##Player *player, Entity *cause) { \ + /* Call Original Method */ \ + type##Player_die_non_virtual(player, cause); \ + \ + /* Get Variable */ \ + RakNetInstance *rak_net_instance = player->minecraft->rak_net_instance; \ + /* Only Run On Server-Side */ \ + if (rak_net_instance->vtable->isServer(rak_net_instance)) { \ + /* Get Death Message */ \ + std::string message = get_death_message((Player *) player, cause); \ + \ + /* Post Death Message */ \ + ServerSideNetworkHandler *server_side_network_handler = (ServerSideNetworkHandler *) player->minecraft->network_handler; \ + ServerSideNetworkHandler_displayGameMessage(server_side_network_handler, &message); \ + } \ + } \ + \ static void type##Player_actuallyHurt_injection(type##Player *player, int32_t damage) { \ /* Store Old Health */ \ int32_t old_health = player->health; \ \ /* Call Original Method */ \ type##Player_actuallyHurt_non_virtual(player, damage); \ + if (is_hurt == true) return; \ \ /* Store New Health */ \ int32_t new_health = player->health; \ \ /* Get Variables */ \ - Minecraft *minecraft = player->minecraft; \ - RakNetInstance *rak_net_instance = minecraft->rak_net_instance; \ + RakNetInstance *rak_net_instance = player->minecraft->rak_net_instance; \ /* Only Run On Server-Side */ \ if (rak_net_instance->vtable->isServer(rak_net_instance)) { \ /* Check Health */ \ if (new_health < 1 && old_health >= 1) { \ /* Get Death Message */ \ - std::string message = get_death_message((Player *) player); \ + std::string message = get_death_message((Player *) player, NULL); \ \ /* Post Death Message */ \ - ServerSideNetworkHandler *server_side_network_handler = (ServerSideNetworkHandler *) minecraft->network_handler; \ + ServerSideNetworkHandler *server_side_network_handler = (ServerSideNetworkHandler *) player->minecraft->network_handler; \ ServerSideNetworkHandler_displayGameMessage(server_side_network_handler, &message); \ } \ } \ } -Player_actuallyHurt_injection(Local) -Player_actuallyHurt_injection(Server) + +Player_death_injections(Local); +Player_death_injections(Server); // Init void init_death() { // Death Messages if (feature_has("Implement Death Messages", server_auto)) { + patch_address(ServerPlayer_die_vtable_addr, (void *) ServerPlayer_die_injection); + patch_address(LocalPlayer_die_vtable_addr, (void *) LocalPlayer_die_injection); patch_address(ServerPlayer_actuallyHurt_vtable_addr, (void *) ServerPlayer_actuallyHurt_injection); patch_address(LocalPlayer_actuallyHurt_vtable_addr, (void *) LocalPlayer_actuallyHurt_injection); + patch_address(Mob_hurt_vtable_addr, (void *) Mob_hurt_injection); + overwrite_calls((void *) Mob_hurt_non_virtual, (void *) Mob_hurt_injection); } + // Fix TNT + // This changes PrimedTnt_explode from Level_explode(NULL, x, y, z, 3.1f) to Level_explode(this, x, y, z, 3.1f) + unsigned char cpy_r1_r0_patch[4] = {0x00, 0x10, 0xa0, 0xe1}; // "cpy r1,r0" + patch((void *) 0x87998, cpy_r1_r0_patch); + unsigned char ldr_r0_24_patch[4] = {0x24, 0x00, 0x90, 0xe5}; // "ldr r0,[r0,#0x24]" + patch((void *) 0x8799c, ldr_r0_24_patch); } diff --git a/mods/src/init/init.c b/mods/src/init/init.c index 100512bb..b92629aa 100644 --- a/mods/src/init/init.c +++ b/mods/src/init/init.c @@ -32,6 +32,7 @@ __attribute__((constructor)) static void init(int argc, char *argv[]) { init_options(); init_chat(); init_bucket(); + init_cake(); init_home(); #ifndef MCPI_SERVER_MODE init_benchmark(argc, argv); diff --git a/mods/src/misc/README.md b/mods/src/misc/README.md index 921b24a7..620d2b25 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 196c3882..d947cae1 100644 --- a/mods/src/misc/api.cpp +++ b/mods/src/misc/api.cpp @@ -79,6 +79,7 @@ SETUP_CALLBACK(creative_inventory_setup, FillingContainer); static void Inventory_setupDefault_FillingContainer_addItem_call_injection(FillingContainer *filling_container, ItemInstance *item_instance) { // Call Original Method FillingContainer_addItem(filling_container, item_instance); + // Run Functions handle_misc_creative_inventory_setup(filling_container); } @@ -98,11 +99,22 @@ static void Tile_initTiles_injection() { SETUP_CALLBACK(items_setup, void); // Handle Custom Items Setup Behavior static void Item_initItems_injection() { - // Run Functions - handle_misc_items_setup(NULL); - // Call Original Method Item_initItems(); + + // Run Functions + handle_misc_items_setup(NULL); +} + +// Run Functions On Language Setup +SETUP_CALLBACK(language_setup, void); +// Handle Custom Items Setup Behavior +static void I18n_loadLanguage_injection(AppPlatform *app, std::string language_name) { + // Call Original Method + I18n_loadLanguage(app, language_name); + + // Run Functions + handle_misc_language_setup(NULL); } // Run Functions On GUI Key Press @@ -140,6 +152,8 @@ void _init_misc_api() { // Handle Custom Item/Tile Init Behavior overwrite_calls((void *) Tile_initTiles, (void *) Tile_initTiles_injection); overwrite_calls((void *) Item_initItems, (void *) Item_initItems_injection); + // Handle Custom Language Entries + overwrite_calls((void *) I18n_loadLanguage, (void *) I18n_loadLanguage_injection); // Handle Key Presses overwrite_calls((void *) Gui_handleKeyPressed, (void *) Gui_handleKeyPressed_injection); } diff --git a/mods/src/misc/misc.c b/mods/src/misc/misc.c index 49f434b2..aac9da41 100644 --- a/mods/src/misc/misc.c +++ b/mods/src/misc/misc.c @@ -601,6 +601,19 @@ static int FurnaceTileEntity_getLitProgress_injection(FurnaceTileEntity *furnace return ret; } +// Fix used items transferring durability +static int selected_slot = -1; +static void Player_startUsingItem_injection(Player *self, ItemInstance *item_instance, int time) { + selected_slot = self->inventory->selectedSlot; + Player_startUsingItem(self, item_instance, time); +} +static void Player_stopUsingItem_injection(Player *self) { + if (selected_slot != self->inventory->selectedSlot) { + self->itemBeingUsed.id = 0; + } + Player_stopUsingItem(self); +} + // Java Light Ramp static void Dimension_updateLightRamp_injection(Dimension *self) { // https://github.com/ReMinecraftPE/mcpe/blob/d7a8b6baecf8b3b050538abdbc976f690312aa2d/source/world/level/Dimension.cpp#L92-L105 @@ -816,6 +829,10 @@ void init_misc() { overwrite((void *) Dimension_updateLightRamp_non_virtual, (void *) Dimension_updateLightRamp_injection); } + // Fix used items transferring durability + overwrite_calls((void *) Player_startUsingItem, (void *) Player_startUsingItem_injection); + overwrite_calls((void *) Player_stopUsingItem, (void *) Player_stopUsingItem_injection); + // Init C++ And Logging _init_misc_cpp(); _init_misc_logging(); diff --git a/mods/src/misc/misc.cpp b/mods/src/misc/misc.cpp index 6e34cbe2..410760ea 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 5bfae669..3eb63fbd 100644 --- a/symbols/CMakeLists.txt +++ b/symbols/CMakeLists.txt @@ -40,8 +40,9 @@ set(SRC src/entity/MobFactory.def src/entity/EntityRenderDispatcher.def src/entity/MobRenderer.def - src/entity/AgableMob.def - src/entity/Animal.def + src/entity/animal/AgableMob.def + src/entity/animal/Animal.def + src/entity/animal/Cow.def src/entity/Mob.def src/entity/player/ServerPlayer.def src/entity/player/Player.def @@ -82,10 +83,10 @@ set(SRC src/item/AuxDataTileItem.def src/item/ItemInstance.def src/item/Item.def - src/item/FoodItem.def src/item/ArmorMaterial.def src/item/ArmorItem.def src/item/TileItem.def + src/item/FoodItem.def src/api/OffsetPosTranslator.def src/api/CommandServer.def src/api/ConnectedClient.def @@ -102,6 +103,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/screens/ScreenChooser.def src/gui/Font.def src/gui/components/ImageButton.def @@ -131,6 +133,8 @@ set(SRC src/tile/GrassTile.def 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 @@ -154,6 +158,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/Entity.def b/symbols/src/entity/Entity.def index 096752e1..44aae8d3 100644 --- a/symbols/src/entity/Entity.def +++ b/symbols/src/entity/Entity.def @@ -1,7 +1,17 @@ virtual-method void remove() = 0x10; virtual-method void tick() = 0x34; +virtual-method bool interact(Player *with) = 0x6c; +virtual-method bool isPlayer() = 0x94; virtual-method bool hurt(Entity *attacker, int damage) = 0xa4; +// See https://mcpirevival.miraheze.org/wiki/Minecraft:_Pi_Edition_Complete_Entity_List for these two virtual-method int getEntityTypeId() = 0xdc; +virtual-method int getCreatureBaseType() = 0xe0; +virtual-method bool isMob() = 0xe8; +virtual-method bool isItemEntity() = 0xec; +// HangingEntity is a painting +virtual-method bool isHangingEntity() = 0xf0; +// The owner entity id for arrows/throwables, else 0 +virtual-method int getAuxData() = 0xf4; method void moveTo(float x, float y, float z, float yaw, float pitch) = 0x7a834; diff --git a/symbols/src/entity/AgableMob.def b/symbols/src/entity/animal/AgableMob.def similarity index 100% rename from symbols/src/entity/AgableMob.def rename to symbols/src/entity/animal/AgableMob.def diff --git a/symbols/src/entity/Animal.def b/symbols/src/entity/animal/Animal.def similarity index 100% rename from symbols/src/entity/Animal.def rename to symbols/src/entity/animal/Animal.def diff --git a/symbols/src/entity/animal/Cow.def b/symbols/src/entity/animal/Cow.def new file mode 100644 index 00000000..f18dd750 --- /dev/null +++ b/symbols/src/entity/animal/Cow.def @@ -0,0 +1,3 @@ +extends Animal; + +vtable 0x10ba38; diff --git a/symbols/src/entity/player/Player.def b/symbols/src/entity/player/Player.def index 87cc8a42..f82458ad 100644 --- a/symbols/src/entity/player/Player.def +++ b/symbols/src/entity/player/Player.def @@ -2,12 +2,19 @@ extends Mob; vtable 0x10de70; -method int isUsingItem() = 0x8f15c; virtual-method void drop(ItemInstance *item_instance, bool is_death) = 0x208; virtual-method void stopSleepInBed(bool param_1, bool param_2, bool param_3) = 0x228; virtual-method void openTextEdit(TileEntity *sign) = 0x230; -method ItemInstance *getArmor(int slot) = 0x8fda4; -property std::string username = 0xbf4; +method int isUsingItem() = 0x8f15c; +method void stopUsingItem() = 0x8f514; +method void startUsingItem(ItemInstance *item_instance, int use_duration) = 0x8f4b8; +method ItemInstance *getArmor(int slot) = 0x8fda4; +method bool isHurt() = 0x8fb44; + property Inventory *inventory = 0xbe0; +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/components/GuiComponent.def b/symbols/src/gui/components/GuiComponent.def index 93a801c1..4a14dc11 100644 --- a/symbols/src/gui/components/GuiComponent.def +++ b/symbols/src/gui/components/GuiComponent.def @@ -7,3 +7,4 @@ method void blit(int x_dest, int y_dest, int x_src, int y_src, int width_dest, i method void drawCenteredString(Font *font, std::string *text, int x, int y, int color) = 0x2821c; method void drawString(Font *font, std::string *text, int x, int y, int color) = 0x28284; method void fill(int x1, int y1, int x2, int y2, uint color) = 0x285f0; +method void fillGradient(int x1, int y1, int x2, int y2, int color1, int color2) = 0x287c0; \ No newline at end of file diff --git a/symbols/src/gui/screens/PaneCraftingScreen.def b/symbols/src/gui/screens/PaneCraftingScreen.def new file mode 100644 index 00000000..6ef4fda3 --- /dev/null +++ b/symbols/src/gui/screens/PaneCraftingScreen.def @@ -0,0 +1,6 @@ +extends Screen; + +method void craftSelectedItem() = 0x2e0e4; +method void recheckRecipes() = 0x2dc98; + +property CItem *item = 0x74; diff --git a/symbols/src/gui/screens/Screen.def b/symbols/src/gui/screens/Screen.def index fe904fca..0842c954 100644 --- a/symbols/src/gui/screens/Screen.def +++ b/symbols/src/gui/screens/Screen.def @@ -6,23 +6,23 @@ constructor () = 0x29028; vtable-size 0x74; vtable 0x1039d8; -virtual-method void updateEvents() = 0x14; -virtual-method void keyboardNewChar(char key) = 0x70; -virtual-method void keyPressed(int key) = 0x6c; +virtual-method void init() = 0xc; virtual-method void render(int x, int y, float param_1) = 0x8; +virtual-method void setupPositions() = 0x10; +virtual-method void updateEvents() = 0x14; virtual-method bool handleBackEvent(bool do_nothing) = 0x24; virtual-method void tick() = 0x28; -virtual-method void buttonClicked(Button *button) = 0x60; -virtual-method void init() = 0xc; -virtual-method void mouseClicked(int x, int y, int param_1) = 0x64; virtual-method void removed() = 0x2c; virtual-method void renderBackground() = 0x30; -virtual-method void setupPositions() = 0x10; +virtual-method void buttonClicked(Button *button) = 0x60; +virtual-method void mouseClicked(int x, int y, int param_1) = 0x64; +virtual-method void keyPressed(int key) = 0x6c; +virtual-method void keyboardNewChar(char key) = 0x70; -property Minecraft *minecraft = 0x14; -property std::vector