Add buckets of milk

- Milk Buckets can be obtained by using an empty bucket on a cow
- They can be drunk, but they don't heal you
- Adds the `misc_run_on_language_setup` function
- Changes `misc_run_on_tile_setup` and `misc_run_on_item_setup` to run after the original function is called
- Fix used items transferring durability
- Add more symbols
This commit is contained in:
Bigjango13 2024-01-27 16:00:44 -05:00
parent 6c5d647a7f
commit f78a4e47ac
15 changed files with 186 additions and 43 deletions

@ -1 +1 @@
Subproject commit 0b696bd55b31416929d0a29d84ad50ab5ba0ceae
Subproject commit ea31be1cb2b43decb9c906215f80ff20efd2b479

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
void Level_saveLevelData_injection(Level *level);

View File

@ -6,31 +6,57 @@
#include <mods/misc/misc.h>
// Items
Item *bucket = NULL;
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,28 +126,64 @@ 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 void BucketItem_useTimeDepleted(FoodItem *item, uchar *param_1, ItemInstance *item_instance, Level *level, Player *player) {
if (item_instance->auxiliary == 1) {
(*FoodItem_useTimeDepleted_vtable_addr)(item, param_1, item_instance, level, player);
// Set it to a empty bucket
item_instance->auxiliary = 0;
item_instance->count = 1;
}
}
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;
}
// Bucket VTable
static Item_vtable *get_bucket_vtable() {
static Item_vtable *vtable = NULL;
static FoodItem_vtable *get_bucket_vtable() {
static FoodItem_vtable *vtable = NULL;
if (vtable == NULL) {
// Init
vtable = dup_Item_vtable(Item_vtable_base);
vtable = dup_FoodItem_vtable(FoodItem_vtable_base);
ALLOC_CHECK(vtable);
// Modify
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;
}
return vtable;
}
// 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();
@ -149,6 +195,7 @@ 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;
// Return
return item;
@ -168,17 +215,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
@ -273,6 +332,10 @@ static void FurnaceTileEntity_tick_ItemInstance_setNull_injection(ItemInstance *
}
}
static void Language_injection(__attribute__((unused)) void *null) {
I18n__strings.insert(std::make_pair("item.bucketMilk.name", "Milk Bucket"));
}
// Init
void init_bucket() {
// Add Buckets
@ -281,6 +344,8 @@ void init_bucket() {
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
@ -293,5 +358,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);
}
}

View File

@ -77,6 +77,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);
}
@ -85,22 +86,33 @@ static void Inventory_setupDefault_FillingContainer_addItem_call_injection(Filli
SETUP_CALLBACK(tiles_setup, void);
// Handle Custom Tiles Setup Behavior
static void Tile_initTiles_injection() {
// Run Functions
handle_misc_tiles_setup(NULL);
// Call Original Method
Tile_initTiles();
// Run Functions
handle_misc_tiles_setup(NULL);
}
// Run Functions On Items Setup
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);
}
// Init
@ -117,4 +129,6 @@ 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);
}

View File

@ -545,6 +545,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);
}
// Init
static void nop() {
}
@ -731,6 +744,10 @@ void init_misc() {
unsigned char mov_r3_ff[4] = {0xff, 0x30, 0xa0, 0xe3}; // "mov r3, #0xff"
patch((void *) 0x7178c, mov_r3_ff);
// 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

@ -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,6 +83,7 @@ set(SRC
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
@ -125,6 +127,7 @@ set(SRC
src/tile/GrassTile.def
src/tile/HeavyTile.def
src/misc/Strings.def
src/misc/I18n.def
src/entity/ModelPart.def
src/misc/Tesselator.def
src/misc/AABB.def

View File

@ -1,5 +1,6 @@
virtual-method void remove() = 0x10;
virtual-method void tick() = 0x34;
virtual-method bool interact(Player *with) = 0x6c;
virtual-method bool hurt(Entity *attacker, int damage) = 0xa4;
virtual-method int getEntityTypeId() = 0xdc;

View File

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

View File

@ -2,12 +2,18 @@ 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;

View File

@ -0,0 +1,7 @@
extends Item;
size 0x30;
vtable 0x10e7b0;
vtable-size 0x98;
property int nutrition = 0x24;

View File

@ -6,12 +6,22 @@ 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;
// I don't know much about param_1, it might be some partially initialized ItemInstance*
virtual-method void useTimeDepleted(uchar *param_1, 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;
// 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;

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

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