Add Cake #81

Merged
TheBrokenRail merged 22 commits from bigjango13/minecraft-pi-reborn:master into master 2024-02-07 06:47:47 +00:00
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)
bigjango13 marked this conversation as resolved Outdated

Why did you add this to the v2.5.3 changelog?

Why did you add this to the v2.5.3 changelog?

Should I add it to v2.5.4 or v2.6.0 instead?

Should I add it to v2.5.4 or v2.6.0 instead?

Add it to v3.0.0.

Add it to v3.0.0.
* 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
bigjango13 marked this conversation as resolved Outdated

Please add a #pragma once.

Also, instead of a fancy method containing a static bool, why not just use an extern bool buckets_enabled, or just have buckets_enabled() check a variable.

bool buckets_enabled() {
    static bool ret = feature_has("Add Buckets", server_enabled);
    return ret;
}

just seems a bit over-complicated.

Please add a `#pragma once`. Also, instead of a fancy method containing a `static bool`, why not just use an `extern bool buckets_enabled`, or just have `buckets_enabled()` check a variable. ```c++ bool buckets_enabled() { static bool ret = feature_has("Add Buckets", server_enabled); return ret; } ``` just seems a bit over-complicated.
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;
Review

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?

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?
Review

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

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.)
// 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);
bigjango13 marked this conversation as resolved Outdated

Use FoodItem_useTimeDepleted_non_virtual rather than de-referencing the address yourself.

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;
}
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 *
}
}
bigjango13 marked this conversation as resolved Outdated

This should probably have a comment before it.

This should probably have a comment before it.
// 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>
bigjango13 marked this conversation as resolved
Review

This should probably be static.

This should probably be `static`.
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;
bigjango13 marked this conversation as resolved
Review

Remove the (* and ).

Remove the `(*` and `)`.
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);
bigjango13 marked this conversation as resolved
Review

feature_has should ideally only run once per feature (to avoid redundant log entries). Maybe bucket could set a variable if it's enabled?

`feature_has` should ideally only run once per feature (to avoid redundant log entries). Maybe `bucket` could set a variable if it's enabled?
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) {
bigjango13 marked this conversation as resolved
Review

Shouldn't this check if this was an arrow? What if another entity has aux data?

Shouldn't this check if this was an arrow? What if another entity has aux data?
Review

That's intended, only throwables have aux values:

// The owner entity id for arrows/throwables, else 0
virtual-method int getAuxData() = 0xf4;

So if I egg you to death, it still says that I'm the one who did it.

That's intended, only throwables have aux values: ``` // The owner entity id for arrows/throwables, else 0 virtual-method int getAuxData() = 0xf4; ``` So if I egg you to death, it still says that I'm the one who did it.
// Blown up by TNT
return message + " was blown apart";
} else if (cause->vtable->isHangingEntity(cause)) {
// Painting?
return message + " admired too much art";
bigjango13 marked this conversation as resolved Outdated

A lot of these death messages are duplicated. Is there any chance these could be consolidated into fewer if statements?

A lot of these death messages are duplicated. Is there any chance these could be consolidated into fewer if statements?

Done 👍

Done 👍
}
}
if (was_shot) {
// Throwable with invalid owner
return message + " was shot under mysterious circumstances";

When would this ever happen?

When would this ever happen?

No idea, I thought it might be useful in case the paintings ever try to take over.

No idea, I thought it might be useful in case the paintings ever try to take over.
} 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); \
\
bigjango13 marked this conversation as resolved
Review

IIRC when originally implementing death message, that hooking Player::die caused some issues on servers.

Did you test:

  • Host player dying (in LAN)
  • Guest player dying (in LAN)
  • Server killing player (kill console command)
IIRC when originally implementing death message, that hooking `Player::die` caused some issues on servers. Did you test: - Host player dying (in LAN) - Guest player dying (in LAN) - Server killing player (`kill` console command)
Review

That's why I also hooked actuallyHurt as it did before. There are quite a few ways to die without calling Player::die, falling into the void is a notable one. If the player dies without calling Player::die, it will just post the usual " has died". As for your question, yes, all of those have been tested.

That's why I also hooked `actuallyHurt` as it did before. There are quite a few ways to die without calling `Player::die`, falling into the void is a notable one. If the player dies without calling `Player::die`, it will just post the usual "<username> has died". As for your question, yes, all of those have been tested.
/* 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)
bigjango13 marked this conversation as resolved Outdated

So, uh, what does these patches actually do? Does it set an entity ID somewhere? Because that should probably be in the comment.

So, uh, what does these patches actually do? Does it set an entity ID somewhere? Because that should probably be in the comment.
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();

Why was the order of Item_initItems_injection changed?

Why was the order of `Item_initItems_injection` changed?

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)
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
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);
bigjango13 marked this conversation as resolved
Review

How does this work if, for instance, the remaining item is 5 sticks and the player's inventory has space for 3 sticks? Do 3 sticks get added to the inventory and 2 dropped?

How does this work if, for instance, the remaining item is 5 sticks and the player's inventory has space for 3 sticks? Do 3 sticks get added to the inventory and 2 dropped?
Review

Yep!

Yep!
}
}
}
// 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;
bigjango13 marked this conversation as resolved
Review

This feels wrong? Was this added for a specific reason?

This feels wrong? Was this added for a specific reason?
Review

Yes, in case anyone decides to use Item's craftingRemainingItem instead of overwriting it's Item_getCraftingRemainingItem. I mentioned it here.

Yes, in case anyone decides to use `Item`'s `craftingRemainingItem` instead of overwriting it's `Item_getCraftingRemainingItem`. I mentioned it [here](https://discord.com/channels/740287937727561779/761048906242981948/1200982540501135401).
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;
bigjango13 marked this conversation as resolved Outdated

Shouldn't this extend Screen?

Shouldn't this extend `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;
bigjango13 marked this conversation as resolved
Review

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.

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

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