Add Cake #81
|
@ -1 +1 @@
|
|||
Subproject commit 0b696bd55b31416929d0a29d84ad50ab5ba0ceae
|
||||
Subproject commit ea31be1cb2b43decb9c906215f80ff20efd2b479
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
bigjango13 marked this conversation as resolved
Outdated
TheBrokenRail
commented
Use Use `FoodItem_useTimeDepleted_non_virtual` rather than de-referencing the address yourself.
|
||||
// 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;
|
||||
bigjango13 marked this conversation as resolved
Outdated
TheBrokenRail
commented
Does Does `FoodItem` not have its own constructor?
bigjango13
commented
I think it was inlined, it must have been pretty minor, as I can't find any trace of it in minecraft-pi or libminecraftpe.so I think it was inlined, it must have been pretty minor, as I can't find any trace of it in minecraft-pi or libminecraftpe.so
|
||||
|
||||
// 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
|
||||
bigjango13 marked this conversation as resolved
Outdated
TheBrokenRail
commented
This should probably have a comment before it. This should probably have a comment before it.
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
TheBrokenRail
commented
Why was the order of Why was the order of `Item_initItems_injection` changed?
bigjango13
commented
A few reasons:
A few reasons:
- It allows for mods to modify and overwrite existing items
- It allows for mods to tell if an item is already assigned to an id
- It doesn't really change anything beyond that (the same isn't true for tiles)
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
bigjango13 marked this conversation as resolved
Outdated
TheBrokenRail
commented
Rather than all this, why not just make the player stop using the item when the selected slot is changed? Rather than all this, why not just make the player stop using the item when the selected slot is changed?
bigjango13
commented
That already happens, however if
That already happens, however if `itemBeingUsed.id == heldItem.id`, `heldItem`'s `auxiliary` will be set to that of `itemBeingUsed`. This fixes it by insuring that `itemBeingUsed`'s `id` cannot be the same, as empty slots are (almost[^1]) always NULL instead of having an id of 0.
[^1]: There are a few way to obtain an EmptyItemInstance, however it's throught bugs/mods and it almost always crashes within a few seconds of being held.
|
||||
}
|
||||
|
||||
// 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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
extends Animal;
|
||||
|
||||
vtable 0x10ba38;
|
|
@ -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;
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
extends Item;
|
||||
|
||||
size 0x30;
|
||||
bigjango13 marked this conversation as resolved
TheBrokenRail
commented
`FoodItem` is 3 32-bit ints bigger than `Item`, but only one is accounted for `nutrition`. What are the others? This is important because `FoodItem` is constructed manually and therefore anything not set manually will be left uninitialized.
|
||||
vtable 0x10e7b0;
|
||||
vtable-size 0x98;
|
||||
|
||||
property int nutrition = 0x24;
|
|
@ -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;
|
||||
bigjango13 marked this conversation as resolved
TheBrokenRail
commented
This seems wrong. According to Ghidra, this function's signature is It could be returning a structure, IIRC if a function returns a structure, the caller allocates the structure and passes a pointer to it as the first argument top the function (like how This seems wrong. According to Ghidra, this function's signature is `Item::useTimeDepleted(ItemInstance*, Level*, Player*)` (and of course an extra `Item *self`).
It could be returning a structure, IIRC if a function returns a structure, the caller allocates the structure and passes a pointer to it as the first argument top the function (like how `this` is passed) and then the function writes to that pointer. Check out `CommandServer::parse` in Ghidra.
bigjango13
commented
Hmm, seems like it. it tripped me up because both Item and ItemInstance have the id at 0x4. Hmm, seems like it. it tripped me up because both Item and ItemInstance have the id at 0x4.
|
||||
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;
|
||||
|
|
|
@ -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;
|
|
@ -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;
|
||||
|
|
Do we really care about MCPE compatibility that much? IMO, the code might be nicer if the milk bucket was just a separate item. What are your thoughts?
What MCPE compatibility? As far as I know, each type of bucket has it's own id (326 water bucket, 327 lava bucket, and 335 milk bucket). While it might be a little bit nicer without all the aux value checks, it would also be a lot more verbose (even with just splitting off milk buckets), and I think that the current systems is a bit harder to understand the first time, but once it's understood it's a lot easier to read/work with than splitting it off. (It also takes up 1-3 less item ids, but that's a pretty minor bonus.)