Add Cake (#81)
CI / Build (ARMHF, Client) (push) Waiting to run Details
CI / Build (ARMHF, Server) (push) Waiting to run Details
CI / Test (Client) (push) Waiting to run Details
CI / Test (Server) (push) Waiting to run Details
CI / Build Example Mods (push) Waiting to run Details
CI / Release (push) Blocked by required conditions Details
CI / Build (AMD64, Server) (push) Successful in 10m3s Details
CI / Build (AMD64, Client) (push) Successful in 10m16s Details
CI / Build (ARM64, Client) (push) Has been cancelled Details
CI / Build (ARM64, Server) (push) Has been cancelled Details

Adds cake, crafting remainders, milk buckets, death messages, `misc_run_on_language_setup`, and a lot more symbols.

Co-authored-by: Bigjango13 <bigjango13@gmail.com>
Reviewed-on: #81
Co-authored-by: bigjango13 <bigjango13@noreply.thebrokenrail.org>
Co-committed-by: bigjango13 <bigjango13@noreply.thebrokenrail.org>
This commit is contained in:
bigjango13 2024-02-07 06:47:46 +00:00 committed by TheBrokenRail
parent c62d5264a8
commit f5a680af7b
36 changed files with 679 additions and 78 deletions

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,3 @@
#pragma once
extern bool buckets_enabled;

View File

@ -33,6 +33,7 @@ void init_death();
void init_options();
void init_chat();
void init_bucket();
void init_cake();
void init_home();
#ifdef __cplusplus

View File

@ -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

View File

@ -6,31 +6,57 @@
#include <mods/misc/misc.h>
// 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);
}
}

3
mods/src/cake/README.md Normal file
View File

@ -0,0 +1,3 @@
# ``cake`` Mod
This mod adds cake.

243
mods/src/cake/cake.cpp Normal file
View File

@ -0,0 +1,243 @@
#include <libreborn/libreborn.h>
#include <symbols/minecraft.h>
#include <mods/feature/feature.h>
#include <mods/init/init.h>
#include <mods/misc/misc.h>
#include <mods/bucket/bucket.h>
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<Recipes_Type> 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);
}
}
}

View File

@ -7,55 +7,136 @@
#include <mods/feature/feature.h>
// 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);
}

View File

@ -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);

View File

@ -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.

View File

@ -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);
}

View File

@ -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();

View File

@ -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);
}

View File

@ -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 "")

View File

@ -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;

View File

@ -0,0 +1,3 @@
extends Animal;
vtable 0x10ba38;

View File

@ -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;

View File

@ -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;

View File

@ -0,0 +1,6 @@
extends Screen;
method void craftSelectedItem() = 0x2e0e4;
method void recheckRecipes() = 0x2dc98;
property CItem *item = 0x74;

View File

@ -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<Button *> rendered_buttons = 0x18;
property std::vector<Button *> selectable_buttons = 0x30;
property int width = 0x8;
property int height = 0xc;
property bool passthrough_input = 0x10;
property Minecraft *minecraft = 0x14;
property std::vector<Button *> rendered_buttons = 0x18;
property std::vector<Button *> selectable_buttons = 0x30;
property Font *font = 0x40;

View File

@ -5,3 +5,6 @@ vtable 0x10e7b0;
vtable-size 0x98;
property int nutrition = 0x24;
// Always 0.6
property float unknown_param_1 = 0x28;
property bool meat = 0x2c;

View File

@ -6,13 +6,23 @@ vtable 0x10f128;
size 0x24;
constructor (int id) = 0x99488;
virtual-method void setIcon(int texture_x, int texture_y) = 0x18;
virtual-method int getIcon(int auxiliary) = 0x14;
virtual-method void setIcon(int texture_x, int texture_y) = 0x18;
virtual-method int useOn(ItemInstance *item_instance, Player *player, Level *level, int x, int y, int z, int hit_side, float hit_x, float hit_y, float hit_z) = 0x20;
// Normally returns 0
virtual-method int getUseDuration(ItemInstance *item_instance) = 0x24;
virtual-method ItemInstance useTimeDepleted(ItemInstance *item_instance, Level *level, Player *player) = 0x28;
virtual-method int getDestorySpeed(ItemInstance *item_instance, Tile *tile) = 0x2c;
virtual-method ItemInstance *use(ItemInstance *item_instance, Level *level, Player *player) = 0x30;
virtual-method bool mineBlock(ItemInstance *instance, int tile_id, int x, int y, int z) = 0x48;
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;
property int id = 0x4;
property int max_damage = 0x8;
@ -21,6 +31,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

View File

@ -1,4 +1,5 @@
static-method void renderGuiItem_one(Font *font, Textures *textures, ItemInstance *item_instance, float param_1, float param_2, bool param_3) = 0x63e58;
static-method void renderGuiItem_two(Font *font, Textures *textures, ItemInstance *item_instance, float param_1, float param_2, float param_3, float param_4, bool param_5) = 0x63be0;
static-method void renderGuiItemCorrect(Font *font, Textures *textures, ItemInstance *item_instance, int param_1, int param_2) = 0x639a0;
static-method void renderGuiItemDecorations(ItemInstance *item, float x, float y) = 0x63748;
static-method void renderGuiItem_one(Font *font, Textures *textures, ItemInstance *item_instance, float x, float y, bool param_3) = 0x63e58;
static-method void renderGuiItem_two(Font *font, Textures *textures, ItemInstance *item_instance, float x, float y, float w, float h, bool param_5) = 0x63be0;
static-method void renderGuiItemCorrect(Font *font, Textures *textures, ItemInstance *item_instance, int x, int y) = 0x639a0;
static-method void renderGuiItemDecorations(ItemInstance *item, float x, float y) = 0x63748;
static-method void blit(float x, float y, float texture_x, float texture_y, float w, float h) = 0x638c0;

View File

@ -1,4 +1,7 @@
virtual-method bool isSolid() = 0x8;
// Globals
static-property Material *Material_stone = 0x180a9c;
static-property Material *dirt = 0x180a94;
static-property Material *stone = 0x180a9c;
static-property Material *metal = 0x180aa0;
static-property Material *glass = 0x180acc;

View File

@ -0,0 +1,3 @@
static-method void loadLanguage(AppPlatform *app, std::string language_name) = 0x680d0;
static-property std::map<std::string, std::string> _strings = 0x137d98;

View File

@ -0,0 +1,5 @@
size 0x4;
method void eat(int amount) = 0x91470;
property int health = 0x0;

View File

@ -1,6 +1,8 @@
method void begin(int mode) = 0x529d4;
method void draw() = 0x52e08;
method void colorABGR(int color) = 0x52b54;
method void color(int r, int g, int b, int a) = 0x52a48;
method void vertex(float x, float y, float z) = 052bc0;
method void vertexUV(float x, float y, float z, float u, float v) = 0x52d40;
static-property Tesselator instance = 0x137538;

View File

@ -1,4 +1,15 @@
method void Write_uchar(uchar *i) = 0x18448;
method void Write_int(int *i) = 0x18454;
method void Write_ushort(ushort *i) = 0x45a68;
method void Write_short(short *i) = 0x71918;
// right_aligned should be true
method void WriteBits(uchar *buff, uint bits, bool right_aligned) = 0xd41b4;
method void Read_uchar(uchar *i) = 0x45ab0;
method void Read_int(int *i) = 0x184ec;
method void Read_ushort(ushort *i) = 0x45acc;
method void Read_short(short *i) = 0x72070;
// right_aligned should be true
method void ReadBits(uchar *buff, uint bits, bool right_aligned) = 0xd3e18;
property uint readOffset = 0x8;

View File

@ -0,0 +1,3 @@
property ItemInstance item = 0x0;
property std::vector<ReqItem> ingredients = 0x20;
property bool craftable = 0x2c;

View File

@ -0,0 +1,3 @@
size 0x10;
property ItemInstance requested_item = 0x0;

View File

@ -3,21 +3,43 @@ 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 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 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(LevelSource *level, int x, int y, int z) = 0x14;
virtual-method void updateDefaultShape() = 0x18;
virtual-method float getBrightness(LevelSource *level, int x, int y, int z) = 0x20;
virtual-method int getTexture1() = 0x28;
virtual-method int getTexture2(int face, int data) = 0x2c;
virtual-method int getTexture3(LevelSource *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 void setPlacedBy(Level *level, int x, int y, int z, Mob *placer) = 0xa8;
virtual-method int getColor(LevelSource *level_source, int x, int y, int z) = 0xb8;
virtual-method void entityInside(Level *level, int x, int y, int z, Entity *entity) = 0xcc;
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 *setLightEmission(float light) = 0xf0;
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;
@ -49,3 +71,6 @@ static-property Tile *grass_carried = 0x181dd4;
// Sounds
static-property Tile_SoundType SOUND_STONE = 0x181c80;
// Light
static-property float *lightEmission = 0x181214;

View File

@ -1 +1,4 @@
method void tesselateBlockInWorld(Tile *tile, int x, int y, int z) = 0x59e30;
method bool tesselateBlockInWorld(Tile *tile, int x, int y, int z) = 0x59e30;
method bool tesselateInWorld(Tile *tile, int x, int y, int z) = 0x5e80c;
property Level *level = 0x0;