basin/src/block.c

2042 lines
109 KiB
C

#include "basin/packet.h"
#include <basin/inventory.h>
#include <basin/globals.h>
#include "basin/network.h"
#include <basin/world.h>
#include <basin/game.h>
#include <basin/smelting.h>
#include <basin/tileentity.h>
#include <basin/smelting.h>
#include <basin/item.h>
#include <basin/block.h>
#include <avuna/json.h>
#include <avuna/string.h>
#include <avuna/list.h>
#include <avuna/hash.h>
#include <unistd.h>
#include <math.h>
#include <fcntl.h>
#include <errno.h>
#include <avuna/util.h>
struct list* block_infos;
struct hashmap* block_materials;
uint8_t getFaceFromPlayer(struct player* player) {
uint32_t h = (uint32_t) floor(player->entity->yaw / 90. + .5) & 3;
uint8_t f = 0;
if (h == 0) f = SOUTH;
else if (h == 1) f = WEST;
else if (h == 2) f = NORTH;
else if (h == 3) f = EAST;
return f;
}
void onBlockUpdate_checkPlace(struct world* world, block blk, int32_t x, int32_t y, int32_t z) {
struct block_info* bi = getBlockInfo(blk);
if (bi != NULL && bi->canBePlaced != NULL && !(*bi->canBePlaced)(world, blk, x, y, z)) {
world_set_block(world, 0, x, y, z);
dropBlockDrops(world, blk, NULL, x, y, z);
}
}
uint8_t getBlockPower_block(struct world* world, block blk, int32_t x, int32_t y, int32_t z, uint8_t face) {
if (blk >> 4 == BLK_REDSTONEDUST >> 4) {
return blk & 0x0f;
} else if (blk >> 4 == BLK_NOTGATE_1 >> 4) {
return 16; // TODO: transmit thru blocks
} else if (blk >> 4 == BLK_DIODE_1 >> 4) {
uint8_t ori = blk & 0x03;
if ((ori == 0 && face == NORTH) || (ori == 1 && face == EAST) || (ori == 2 && face == SOUTH) || (ori == 3 && face == WEST)) return 16;
} else if (blk >> 4 == BLK_BLOCKREDSTONE >> 4 || blk == (BLK_PRESSUREPLATEWOOD | 0x01) || blk == (BLK_PRESSUREPLATESTONE | 0x01)) {
return 16;
} else if (blk >> 4 == BLK_WEIGHTEDPLATE_LIGHT >> 4 || blk >> 4 == BLK_WEIGHTEDPLATE_HEAVY >> 4 || blk >> 4 == BLK_DAYLIGHTDETECTOR >> 4) {
return blk & 0x0f;
} else if ((blk >> 4 == BLK_DETECTORRAIL >> 4 || blk >> 4 == BLK_TRIPWIRESOURCE >> 4 || blk >> 4 == BLK_LEVER >> 4 || blk >> 4 == BLK_BUTTON >> 4 || blk >> 4 == BLK_BUTTON_1 >> 4) && (blk & 0x08)) {
return 16;
} else if (blk >> 4 == BLK_COMPARATOR_1 >> 4) {
//TODO:
}
return 0;
}
uint8_t getPropogatedPower_block(struct world* world, struct chunk* ch, int32_t x, int32_t y, int32_t z, uint8_t excludeFace) {
uint8_t maxPower = 0;
uint8_t cPower = 0;
if (excludeFace != XP && (cPower = getBlockPower_block(world, world_get_block_guess(world, ch, x + 1, y, z), x + 1, y, z, XN)) > maxPower) maxPower = cPower;
if (excludeFace != XN && (cPower = getBlockPower_block(world, world_get_block_guess(world, ch, x - 1, y, z), x - 1, y, z, XP)) > maxPower) maxPower = cPower;
if (excludeFace != ZP && (cPower = getBlockPower_block(world, world_get_block_guess(world, ch, x, y, z + 1), x, y, z + 1, ZN)) > maxPower) maxPower = cPower;
if (excludeFace != ZN && (cPower = getBlockPower_block(world, world_get_block_guess(world, ch, x, y, z - 1), x, y, z - 1, ZP)) > maxPower) maxPower = cPower;
if (excludeFace != YP && (cPower = getBlockPower_block(world, world_get_block_guess(world, ch, x, y + 1, z), x, y + 1, z, YN)) > maxPower) maxPower = cPower;
if (excludeFace != YN && (cPower = getBlockPower_block(world, world_get_block_guess(world, ch, x, y - 1, z), x, y - 1, z, YP)) > maxPower) maxPower = cPower;
if (maxPower > 0) {
maxPower--;
}
if (maxPower > 15) maxPower = 15;
return maxPower;
}
uint8_t getSpecificPower_block(struct world* world, struct chunk* ch, int32_t x, int32_t y, int32_t z, uint8_t face) {
uint8_t maxPower = 0;
uint8_t cPower = 0;
if (face == XP && (cPower = getBlockPower_block(world, world_get_block_guess(world, ch, x + 1, y, z), x + 1, y, z, XN)) > maxPower) maxPower = cPower;
if (face == XN && (cPower = getBlockPower_block(world, world_get_block_guess(world, ch, x - 1, y, z), x - 1, y, z, XP)) > maxPower) maxPower = cPower;
if (face == ZP && (cPower = getBlockPower_block(world, world_get_block_guess(world, ch, x, y, z + 1), x, y, z + 1, ZN)) > maxPower) maxPower = cPower;
if (face == ZN && (cPower = getBlockPower_block(world, world_get_block_guess(world, ch, x, y, z - 1), x, y, z - 1, ZP)) > maxPower) maxPower = cPower;
if (face == YP && (cPower = getBlockPower_block(world, world_get_block_guess(world, ch, x, y + 1, z), x, y + 1, z, YN)) > maxPower) maxPower = cPower;
if (face == YN && (cPower = getBlockPower_block(world, world_get_block_guess(world, ch, x, y - 1, z), x, y - 1, z, YP)) > maxPower) maxPower = cPower;
if (maxPower > 0) {
maxPower--;
}
if (maxPower > 15) maxPower = 15;
return maxPower;
}
int canBePlaced_torch(struct world* world, block blk, int32_t x, int32_t y, int32_t z) {
uint8_t ori = blk & 0x0f;
int32_t ax = x;
int32_t ay = y;
int32_t az = z;
uint8_t rori = YN;
if (ori == 1) rori = EAST;
else if (ori == 2) rori = WEST;
else if (ori == 3) rori = NORTH;
else if (ori == 4) rori = SOUTH;
else if (ori == 5) rori = YP;
offsetCoordByFace(&ax, &ay, &az, rori);
block b = world_get_block(world, ax, ay, az);
struct block_info* bi = getBlockInfo(b);
if (rori == YN) {
if (bi->lightOpacity == 255) return 1;
if (b >> 4 == BLK_FENCE >> 4 || b >> 4 == BLK_NETHERFENCE >> 4 || b >> 4 == BLK_SPRUCEFENCE >> 4 || b >> 4 == BLK_BIRCHFENCE >> 4 || b >> 4 == BLK_JUNGLEFENCE >> 4 || b >> 4 == BLK_DARKOAKFENCE >> 4 || b >> 4 == BLK_ACACIAFENCE >> 4 || b >> 4 == BLK_GLASS >> 4 || b >> 4 == BLK_COBBLEWALL_NORMAL >> 4 || b >> 4 == BLK_STAINEDGLASS_WHITE >> 4) return 1;
} else if (block_is_normal_cube(bi)) return 1;
return 0;
}
block onBlockPlacedPlayer_lever(struct player* player, struct world* world, block blk, int32_t x, int32_t y, int32_t z, uint8_t face) {
uint8_t f = getFaceFromPlayer(player);
if (face == NORTH) return blk | 0x04;
else if (face == SOUTH) return blk | 0x03;
else if (face == EAST) return blk | 0x01;
else if (face == WEST) return blk | 0x02;
else if (face == YN) return blk | (f == EAST || f == WEST ? 0x00 : 0x07);
else if (face == YP) return blk | (f == NORTH || f == SOUTH ? 0x05 : 0x06);
offsetCoordByFace(&x, &y, &z, face);
return block_is_normal_cube(getBlockInfo(world_get_block(world, x, y, z))) ? blk : 0;
}
//TODO: burnout on redstone torches
void onBlockUpdate_redstonetorch(struct world* world, block blk, int32_t x, int32_t y, int32_t z) {
onBlockUpdate_checkPlace(world, blk, x, y, z);
uint8_t ori = blk & 0x0f;
int32_t ax = x;
int32_t ay = y;
int32_t az = z;
uint8_t rori = YP;
if (ori == 1) rori = WEST;
else if (ori == 2) rori = EAST;
else if (ori == 3) rori = SOUTH;
else if (ori == 4) rori = NORTH;
else if (ori == 5) rori = YN;
offsetCoordByFace(&ax, &ay, &az, rori);
uint8_t arori = YN;
if (rori == WEST) arori = EAST;
else if (rori == EAST) arori = WEST;
else if (rori == NORTH) arori = SOUTH;
else if (rori == SOUTH) arori = NORTH;
else if (rori == YN) arori = YP;
uint8_t ppower = getPropogatedPower_block(world, world_get_chunk(world, ax >> 4, az >> 4), ax, ay, az, arori);
if (blk >> 4 == BLK_NOTGATE >> 4 && ppower == 0) {
world_set_block(world, BLK_NOTGATE_1 | (blk & 0x0f), x, y, z);
} else if (blk >> 4 == BLK_NOTGATE_1 >> 4 && ppower > 1) {
world_set_block(world, BLK_NOTGATE | (blk & 0x0f), x, y, z);
}
}
void onBlockUpdate_redstonedust(struct world* world, block blk, int32_t x, int32_t y, int32_t z) {
struct chunk* ch = world_get_chunk(world, x >> 4, z >> 4);
uint8_t maxPower = getPropogatedPower_block(world, ch, x, y, z, -1);
if (maxPower != (blk & 0x0f)) {
world_set_block_guess(world, ch, BLK_REDSTONEDUST | maxPower, x, y, z);
}
onBlockUpdate_checkPlace(world, blk, x, y, z);
}
void onBlockUpdate_repeater(struct world* world, block blk, int32_t x, int32_t y, int32_t z) {
onBlockUpdate_checkPlace(world, blk, x, y, z);
if (world_is_block_tick_scheduled(world, x, y, z)) {
return;
}
uint8_t ori = blk & 0x03;
uint8_t excl = 0;
if (ori == 0) {
excl = SOUTH;
} else if (ori == 1) {
excl = WEST;
} else if (ori == 2) {
excl = NORTH;
} else if (ori == 3) {
excl = EAST;
}
uint8_t sPower = getSpecificPower_block(world, world_get_chunk(world, x >> 4, z >> 4), x, y, z, excl);
uint8_t update = 0;
if (blk >> 4 == BLK_DIODE_1 >> 4 && sPower <= 0) {
update = 1;
} else if (sPower > 0 && blk >> 4 == BLK_DIODE >> 4) {
update = 1;
}
if (update) {
float priority = -1.;
{
int32_t sx = x;
int32_t sy = y;
int32_t sz = z;
offsetCoordByFace(&sx, &sy, &sz, excl);
block b = world_get_block(world, sx, sy, sz);
if (b >> 4 == BLK_DIODE_1 >> 4 || b >> 4 == BLK_DIODE >> 4) {
priority = -3.;
}
}
if (priority == -1. && blk >> 4 == BLK_DIODE_1 >> 4) {
priority = -2.;
}
world_schedule_block_tick(world, x, y, z, 2 * (((blk & 0x0f) >> 2) + 1), priority);
}
}
void onBlockUpdate_lamp(struct world* world, block blk, int32_t x, int32_t y, int32_t z) {
struct chunk* ch = world_get_chunk(world, x >> 4, z >> 4);
uint8_t maxPower = getPropogatedPower_block(world, ch, x, y, z, -1);
if (maxPower > 0 && blk >> 4 == BLK_REDSTONELIGHT >> 4) {
world_set_block_guess(world, ch, BLK_REDSTONELIGHT_1, x, y, z);
} else if (maxPower == 0 && blk >> 4 == BLK_REDSTONELIGHT_1 >> 4) {
world_set_block_guess(world, ch, BLK_REDSTONELIGHT, x, y, z);
}
}
void onBlockUpdate_tnt(struct world* world, block blk, int32_t x, int32_t y, int32_t z) {
uint8_t maxPower = getPropogatedPower_block(world, world_get_chunk(world, x >> 4, z >> 4), x, y, z, -1);
if (maxPower > 0) {
world_set_block(world, 0, x, y, z);
struct entity* e = entity_new(world, world->server->next_entity_id++, (double) x + .5, (double) y, (double) z + .5, ENT_PRIMEDTNT, 0., 0.);
e->data.tnt.fuse = 80;
e->objectData = blk >> 4;
float ra = game_rand_float() * M_PI * 2.;
e->motX = -sinf(ra) * .02;
e->motY = .2;
e->motZ = -cosf(ra) * .02;
world_spawn_entity(world, e);
}
}
int scheduledTick_repeater(struct world* world, block blk, int32_t x, int32_t y, int32_t z) {
uint8_t ori = blk & 0x03;
uint8_t excl = 0;
uint8_t lock1 = NORTH;
uint8_t mLock1 = 2;
uint8_t mLock2 = 0;
uint8_t lock2 = SOUTH;
if (ori == 0) {
excl = SOUTH;
lock1 = EAST;
lock2 = WEST;
mLock1 = 3;
mLock2 = 1;
} else if (ori == 1) {
excl = WEST;
} else if (ori == 2) {
excl = NORTH;
lock1 = EAST;
lock2 = WEST;
mLock1 = 3;
mLock2 = 1;
} else if (ori == 3) {
excl = EAST;
}
{
int32_t sx = x;
int32_t sy = y;
int32_t sz = z;
offsetCoordByFace(&sx, &sy, &sz, lock1);
block b = world_get_block(world, sx, sy, sz);
if (b >> 4 == BLK_DIODE_1 >> 4 && (b & 0x03) == mLock1) {
return 0;
}
}
{
int32_t sx = x;
int32_t sy = y;
int32_t sz = z;
offsetCoordByFace(&sx, &sy, &sz, lock2);
block b = world_get_block(world, sx, sy, sz);
if (b >> 4 == BLK_DIODE_1 >> 4 && (b & 0x03) == mLock2) {
return 0;
}
}
uint8_t sPower = getSpecificPower_block(world, world_get_chunk(world, x >> 4, z >> 4), x, y, z, excl);
//if (sPower > 0 && blk >> 4 == BLK_DIODE >> 4) {
//printf("attempt, power = %i\n", sPower);
if (blk >> 4 == BLK_DIODE_1 >> 4 && sPower <= 0) {
//} else if (sPower <= 0 && blk >> 4 == BLK_DIODE_1 >> 4) {
//printf("close\n");
world_set_block(world, BLK_DIODE | (blk & 0x0f), x, y, z);
} else if (blk >> 4 == BLK_DIODE >> 4) {
//printf("open\n");
world_set_block(world, BLK_DIODE_1 | (blk & 0x0f), x, y, z);
if (sPower <= 0) {
world_schedule_block_tick(world, x, y, z, 2 * (((blk & 0x0f) >> 2) + 1), -2.);
return -1;
}
}
//}
return 0;
}
void onBlockInteract_repeater(struct world* world, block blk, int32_t x, int32_t y, int32_t z, struct player* player, uint8_t face, float curPosX, float curPosY, float curPosZ) {
world_set_block(world, (blk >> 4 << 4) | (((((blk & 0x0f) >> 2) + 1) & 0x03) << 2) | (blk & 0x03), x, y, z);
}
void onBlockInteract_trapdoor(struct world* world, block blk, int32_t x, int32_t y, int32_t z, struct player* player, uint8_t face, float curPosX, float curPosY, float curPosZ) {
world_set_block(world, (blk >> 4 << 4) | (blk & 0b1011) | (blk & 0b0100 ? 0 : 0b0100), x, y, z);
}
void onBlockUpdate_trapdoor(struct world* world, block blk, int32_t x, int32_t y, int32_t z) {
uint8_t maxPower = getPropogatedPower_block(world, world_get_chunk(world, x >> 4, z >> 4), x, y, z, -1);
if (maxPower > 0 && !(blk & 0b0100)) {
world_set_block(world, (blk >> 4 << 4) | (blk & 0b1011) | 0b0100, x, y, z);
} //TODO: closing?
onBlockUpdate_checkPlace(world, blk, x, y, z);
}
block onBlockPlacedPlayer_trapdoor(struct player* player, struct world* world, block blk, int32_t x, int32_t y, int32_t z, uint8_t face) {
if (face == YP || face == YN) {
if (block_is_normal_cube(getBlockInfo(world_get_block(world, x + 1, y, z)))) face = XP;
else if (block_is_normal_cube(getBlockInfo(world_get_block(world, x - 1, y, z)))) face = XN;
else if (block_is_normal_cube(getBlockInfo(world_get_block(world, x, y, z + 1)))) face = ZP;
else if (block_is_normal_cube(getBlockInfo(world_get_block(world, x, y, z - 1)))) face = ZN;
}
if (face == XN) blk |= 2;
else if (face == XP) blk |= 3;
else if (face == ZP) blk |= 1;
return blk;
}
int canBePlaced_trapdoor(struct world* world, block blk, int32_t x, int32_t y, int32_t z) {
uint8_t ori = blk & 0b0011;
if (ori == 2 && block_is_normal_cube(getBlockInfo(world_get_block(world, x + 1, y, z)))) return 1;
else if (ori == 3 && block_is_normal_cube(getBlockInfo(world_get_block(world, x - 1, y, z)))) return 1;
else if (ori == 0 && block_is_normal_cube(getBlockInfo(world_get_block(world, x, y, z + 1)))) return 1;
else if (ori == 1 && block_is_normal_cube(getBlockInfo(world_get_block(world, x, y, z - 1)))) return 1;
return 0;
}
void onBlockInteract_lever(struct world* world, block blk, int32_t x, int32_t y, int32_t z, struct player* player, uint8_t face, float curPosX, float curPosY, float curPosZ) {
if (blk >> 4 == BLK_LEVER >> 4 && (blk & 0x08)) {
world_set_block(world, BLK_LEVER | (blk & 0x07), x, y, z);
} else if (blk >> 4 == BLK_LEVER >> 4 && !(blk & 0x08)) {
world_set_block(world, BLK_LEVER | (blk & 0x07) | 0x08, x, y, z);
}
}
void onBlockInteract_button(struct world* world, block blk, int32_t x, int32_t y, int32_t z, struct player* player, uint8_t face, float curPosX, float curPosY, float curPosZ) {
world_set_block(world, (blk ^ (blk & 0x0f)) | (blk & 0x07) | 0x08, x, y, z);
world_schedule_block_tick(world, x, y, z, blk >> 4 == BLK_BUTTON >> 4 ? 20 : 30, 0.);
}
int scheduledTick_button(struct world* world, block blk, int32_t x, int32_t y, int32_t z) {
world_set_block(world, (blk ^ (blk & 0x0f)) | (blk & 0x07), x, y, z);
return 0;
}
block onBlockPlacedPlayer_button(struct player* player, struct world* world, block blk, int32_t x, int32_t y, int32_t z, uint8_t face) {
if (face == NORTH) return blk | 0x04;
else if (face == SOUTH) return blk | 0x03;
else if (face == EAST) return blk | 0x01;
else if (face == WEST) return blk | 0x02;
else if (face == YN) return blk | 0x00;
else if (face == YP) return blk | 0x05;
offsetCoordByFace(&x, &y, &z, face);
return block_is_normal_cube(getBlockInfo(world_get_block(world, x, y, z))) ? blk : 0;
}
int canBePlaced_redstone(struct world* world, block blk, int32_t x, int32_t y, int32_t z) {
block b = world_get_block(world, x, y - 1, z);
struct block_info* bi = getBlockInfo(b);
return block_is_normal_cube(bi);
}
int canBePlaced_bed(struct world* world, block blk, int32_t x, int32_t y, int32_t z) {
block b = world_get_block(world, x, y - 1, z);
struct block_info* bi = getBlockInfo(b);
return block_is_normal_cube(bi);
}
void onBlockUpdate_bed(struct world* world, block blk, int32_t x, int32_t y, int32_t z) {
block b = world_get_block(world, x, y - 1, z);
struct block_info* bi = getBlockInfo(b);
if (!block_is_normal_cube(bi)) goto delete;
int facing = blk & 0b0011;
int head = blk & 0b1000;
uint8_t rf = 0;
if (facing == 0) rf = SOUTH;
else if (facing == 1) rf = WEST;
else if (facing == 2) rf = NORTH;
else if (facing == 3) rf = EAST;
if (head) {
if (rf == SOUTH) rf = NORTH;
else if (rf == NORTH) rf = SOUTH;
else if (rf == EAST) rf = WEST;
else if (rf == WEST) rf = EAST;
}
int32_t ox = x;
int32_t oy = y;
int32_t oz = z;
offsetCoordByFace(&ox, &oy, &oz, rf);
b = world_get_block(world, ox, oy, oz);
if (b >> 4 == blk >> 4) return;
delete:;
world_set_block(world, 0, x, y, z);
if (head) dropBlockDrops(world, blk, NULL, x, y, z);
}
int canBePlaced_door(struct world* world, block blk, int32_t x, int32_t y, int32_t z) {
block b = world_get_block(world, x, y - 1, z);
struct block_info* bi = getBlockInfo(b);
return (b >> 4 == blk >> 4 && !(b & 0b1000) && (blk & 0b1000)) || block_is_normal_cube(bi);
}
void onBlockUpdate_door(struct world* world, block blk, int32_t x, int32_t y, int32_t z) {
block b = world_get_block(world, x, y - 1, z);
struct block_info* bi = getBlockInfo(b);
int upper = blk & 0b1000;
if (!upper && !block_is_normal_cube(bi)) goto delete;
b = world_get_block(world, x, y + (upper ? -1 : 1), z);
int upperD = upper ? (blk & 0x0f) : (b & 0x0f);
if (b >> 4 == blk >> 4) {
uint8_t upperPower = getPropogatedPower_block(world, world_get_chunk(world, x >> 4, z >> 4), x, y + (upper ? 0 : -1), z, -1);
uint8_t lowerPower = getPropogatedPower_block(world, world_get_chunk(world, x >> 4, z >> 4), x, y + (upper ? -1 : 0), z, -1);
uint8_t maxPower = upperPower > lowerPower ? upperPower : lowerPower;
if ((upperD & 0x02) && maxPower <= 0) {
world_set_block(world, (upper ? blk : b) ^ 0x02, x, y + (upper ? 0 : 1), z);
world_set_block(world, (upper ? b : blk) ^ 0x04, x, y + (upper ? -1 : 0), z);
} else if (!(upperD & 0x02) && maxPower > 0) {
world_set_block(world, (upper ? blk : b) | 0x02, x, y + (upper ? 0 : 1), z);
world_set_block(world, (upper ? b : blk) | 0x04, x, y + (upper ? -1 : 0), z);
}
return;
}
delete:;
world_set_block(world, 0, x, y, z);
if (!upper) dropBlockDrops(world, blk, NULL, x, y, z);
}
void onBlockInteract_woodendoor(struct world* world, block blk, int32_t x, int32_t y, int32_t z, struct player* player, uint8_t face, float curPosX, float curPosY, float curPosZ) {
uint8_t curMeta = blk & 0x0f;
uint8_t isUpper = curMeta & 0b1000;
uint8_t lowerMeta = isUpper ? world_get_block(world, x, y - 1, z) & 0x0f : curMeta;
uint8_t open = (lowerMeta & 0b0100) >> 2;
open = !open;
lowerMeta = (lowerMeta & 0b1011) | (open << 2);
world_set_block(world, (blk & 0xfff0) | lowerMeta, x, y + (isUpper ? -1 : 0), z);
}
void onBlockInteract_workbench(struct world* world, block blk, int32_t x, int32_t y, int32_t z, struct player* player, uint8_t face, float curPosX, float curPosY, float curPosZ) {
struct inventory* wb = inventory_new(psub(player->pool), INVTYPE_WORKBENCH, 1, 10, "{\"text\": \"Crafting Table\"}");
player_openInventory(player, wb);
}
block onBlockPlaced_chest(struct world* world, block blk, int32_t x, int32_t y, int32_t z, block replaced) {
int16_t meta = blk & 0x0f;
if (meta < 2 || meta > 5) meta = 2;
struct tile_entity* tile = tile_new(mempool_new(), "minecraft:chest", x, y, z);
tile->data.chest.lock = NULL;
tile->data.chest.inv = inventory_new(tile->pool, INVTYPE_CHEST, 2, 27, "{\"text\": \"Chest\"}");
tile->data.chest.inv->tile = tile;
world_set_tile(world, x, y, z, tile);
return (blk & ~0x0f) | meta;
}
block onBlockPlaced_furnace(struct world* world, block blk, int32_t x, int32_t y, int32_t z, block replaced) {
if ((replaced >> 4) == (BLK_FURNACE_1 >> 4) || (replaced >> 4) == (BLK_FURNACE >> 4)) return blk;
int16_t meta = blk & 0x0f;
if (meta < 2 || meta > 5) meta = 2;
struct tile_entity* tile = tile_new(mempool_new(), "minecraft:furnace", x, y, z);
tile->data.furnace.lock = NULL;
tile->data.furnace.inv = inventory_new(tile->pool, INVTYPE_FURNACE, 3, 3, "{\"text\": \"Furnace\"}");
tile->data.furnace.inv->tile = tile;
world_set_tile(world, x, y, z, tile);
return (blk & ~0x0f) | meta;
}
int onBlockDestroyed_chest(struct world* world, block blk, int32_t x, int32_t y, int32_t z, block replacedBy) {
struct tile_entity* te = world_get_tile(world, x, y, z);
if (te == NULL) return 0;
for (size_t i = 0; i < te->data.chest.inv->slot_count; i++) {
struct slot* sl = inventory_get(NULL, te->data.chest.inv, i);
game_drop_block(world, sl, x, y, z);
}
world_set_tile(world, x, y, z, NULL);
return 0;
}
void onBlockInteract_chest(struct world* world, block blk, int32_t x, int32_t y, int32_t z, struct player* player, uint8_t face, float curPosX, float curPosY, float curPosZ) {
struct tile_entity* tile = world_get_tile(world, x, y, z);
if (tile == NULL || !str_eq(tile->id, "minecraft:chest")) {
onBlockPlaced_chest(world, blk, x, y, z, 0);
tile = world_get_tile(world, x, y, z);
}
//TODO: impl locks, loot
player_openInventory(player, tile->data.chest.inv);
BEGIN_BROADCAST_DIST(player->entity, 128.);
struct packet* pkt = packet_new(mempool_new(), PKT_PLAY_CLIENT_BLOCKACTION);
pkt->data.play_client.blockaction.location.x = x;
pkt->data.play_client.blockaction.location.y = y;
pkt->data.play_client.blockaction.location.z = z;
pkt->data.play_client.blockaction.action_id = 1;
pkt->data.play_client.blockaction.action_param = tile->data.chest.inv->watching_players->entry_count;
pkt->data.play_client.blockaction.block_type = blk >> 4;
queue_push(bc_player->outgoing_packets, pkt);
END_BROADCAST(player->world->players)
}
int onBlockDestroyed_furnace(struct world* world, block blk, int32_t x, int32_t y, int32_t z, block replacedBy) {
if ((replacedBy >> 4) == (BLK_FURNACE_1 >> 4) || (replacedBy >> 4) == (BLK_FURNACE >> 4)) return 0;
struct tile_entity* tile = world_get_tile(world, x, y, z);
if (tile == NULL) return 0;
for (size_t i = 0; i < tile->data.furnace.inv->slot_count; i++) {
struct slot* slot = inventory_get(NULL, tile->data.furnace.inv, i);
game_drop_block(world, slot, x, y, z);
}
world_set_tile(world, x, y, z, NULL);
return 0;
}
void onBlockInteract_furnace(struct world* world, block blk, int32_t x, int32_t y, int32_t z, struct player* player, uint8_t face, float curPosX, float curPosY, float curPosZ) {
struct tile_entity* tile = world_get_tile(world, x, y, z);
if (tile == NULL || !str_eq(tile->id, "minecraft:furnace")) {
onBlockPlaced_furnace(world, blk, x, y, z, 0);
tile = world_get_tile(world, x, y, z);
}
//TODO: impl locks, loot
player_openInventory(player, tile->data.furnace.inv);
struct packet* pkt = packet_new(mempool_new(), PKT_PLAY_CLIENT_WINDOWPROPERTY);
pkt->data.play_client.windowproperty.window_id = tile->data.furnace.inv->window;
pkt->data.play_client.windowproperty.property = 1;
pkt->data.play_client.windowproperty.value = tile->data.furnace.lastBurnMax;
queue_push(player->outgoing_packets, pkt);
pkt = packet_new(mempool_new(), PKT_PLAY_CLIENT_WINDOWPROPERTY);
pkt->data.play_client.windowproperty.window_id = tile->data.furnace.inv->window;
pkt->data.play_client.windowproperty.property = 3;
pkt->data.play_client.windowproperty.value = 200;
queue_push(player->outgoing_packets, pkt);
}
void update_furnace(struct world* world, struct tile_entity* tile) {
struct slot* input_slot = inventory_get(NULL, tile->data.furnace.inv, 0);
struct slot* fuel_slot = inventory_get(NULL, tile->data.furnace.inv, 1);
struct slot* actual_output_slot = inventory_get(NULL, tile->data.furnace.inv, 2);
struct slot* output_slot = smelting_output(input_slot);
int16_t burn_time = smelting_burnTime(fuel_slot);
int burning = 0;
if (output_slot != NULL && ((slot_stackable(output_slot, actual_output_slot) && (actual_output_slot->count + output_slot->count) <= slot_max_size(output_slot)) || actual_output_slot == NULL) && (tile->data.furnace.burnTime > 0 || burn_time > 0)) {
burning = 1;
if (!tile->tick) {
tile->tick = &tetick_furnace;
world_tile_set_tickable(world, tile);
world_set_block(world, BLK_FURNACE_1 | (world_get_block(world, tile->x, tile->y, tile->z) & 0x0f), tile->x, tile->y, tile->z);
}
if (tile->data.furnace.burnTime <= 0 && burn_time > 0 && fuel_slot != NULL) {
tile->data.furnace.burnTime += burn_time + 1;
if (--fuel_slot->count == 0) fuel_slot = NULL;
inventory_set_slot(NULL, tile->data.furnace.inv, 1, fuel_slot, 1);
tile->data.furnace.lastBurnMax = burn_time;
BEGIN_BROADCAST(tile->data.furnace.inv->watching_players){
struct packet* pkt = packet_new(mempool_new(), PKT_PLAY_CLIENT_WINDOWPROPERTY);
pkt->data.play_client.windowproperty.window_id = tile->data.furnace.inv->window;
pkt->data.play_client.windowproperty.property = 1;
pkt->data.play_client.windowproperty.value = tile->data.furnace.lastBurnMax;
queue_push(bc_player->outgoing_packets, pkt);
END_BROADCAST(tile->data.furnace.inv->watching_players)
}
}
if (tile->data.furnace.cookTime <= 0) {
tile->data.furnace.cookTime++;
} else if (tile->data.furnace.cookTime < 200) {
tile->data.furnace.cookTime++;
} else if (tile->data.furnace.cookTime == 200) {
tile->data.furnace.cookTime = 0;
if (actual_output_slot == NULL) inventory_set_slot(NULL, tile->data.furnace.inv, 2, output_slot, 1);
else {
actual_output_slot->count++;
inventory_set_slot(NULL, tile->data.furnace.inv, 2, actual_output_slot, 1);
}
if (--input_slot->count == 0) input_slot = NULL;
inventory_set_slot(NULL, tile->data.furnace.inv, 0, input_slot, 1);
tile->data.furnace.burnTime++; // freebie for accounting
}
} else tile->data.furnace.cookTime = 0;
if (tile->data.furnace.burnTime > 0) {
tile->data.furnace.burnTime--;
} else {
if (tile->tick) {
BEGIN_BROADCAST(tile->data.furnace.inv->watching_players){
struct packet* pkt = packet_new(mempool_new(), PKT_PLAY_CLIENT_WINDOWPROPERTY);
pkt->id = PKT_PLAY_CLIENT_WINDOWPROPERTY;
pkt->data.play_client.windowproperty.window_id = tile->data.furnace.inv->window;
pkt->data.play_client.windowproperty.property = 0;
pkt->data.play_client.windowproperty.value = 0;
queue_push(bc_player->outgoing_packets, pkt);
pkt = packet_new(mempool_new(), PKT_PLAY_CLIENT_WINDOWPROPERTY);
pkt->id = PKT_PLAY_CLIENT_WINDOWPROPERTY;
pkt->data.play_client.windowproperty.window_id = tile->data.furnace.inv->window;
pkt->data.play_client.windowproperty.property = 2;
pkt->data.play_client.windowproperty.value = 0;
queue_push(bc_player->outgoing_packets, pkt);
END_BROADCAST(tile->data.furnace.inv->watching_players)
}
world_tile_unset_tickable(world, tile);
tile->tick = NULL;
world_set_block(world, BLK_FURNACE | (world_get_block(world, tile->x, tile->y, tile->z) & 0x0f), tile->x, tile->y, tile->z);
}
tile->data.furnace.cookTime = 0;
}
if (burning && world->server->tick_counter % 5 == 0) {
BEGIN_BROADCAST(tile->data.furnace.inv->watching_players) {
struct packet* pkt = packet_new(mempool_new(), PKT_PLAY_CLIENT_WINDOWPROPERTY);
pkt->data.play_client.windowproperty.window_id = tile->data.furnace.inv->window;
pkt->data.play_client.windowproperty.property = 0;
pkt->data.play_client.windowproperty.value = tile->data.furnace.burnTime;
queue_push(bc_player->outgoing_packets, pkt);
pkt = packet_new(mempool_new(), PKT_PLAY_CLIENT_WINDOWPROPERTY);
pkt->id = PKT_PLAY_CLIENT_WINDOWPROPERTY;
pkt->data.play_client.windowproperty.window_id = tile->data.furnace.inv->window;
pkt->data.play_client.windowproperty.property = 2;
pkt->data.play_client.windowproperty.value = tile->data.furnace.cookTime;
queue_push(bc_player->outgoing_packets, pkt);
END_BROADCAST(tile->data.furnace.inv->watching_players)
}
}
}
void dropItems_gravel(struct world* world, block blk, int32_t x, int32_t y, int32_t z, int fortune) {
if (fortune > 3) fortune = 3;
if (fortune == 3 || (rand() % (10 - fortune * 3)) == 0) {
struct slot drop;
drop.item = ITM_FLINT;
drop.damage = 0;
drop.count = 1;
drop.nbt = NULL;
game_drop_block(world, &drop, x, y, z);
} else {
struct slot drop;
drop.item = BLK_GRAVEL >> 4;
drop.damage = BLK_GRAVEL & 0x0f;
drop.count = 1;
drop.nbt = NULL;
game_drop_block(world, &drop, x, y, z);
}
}
void dropItems_leaves(struct world* world, block blk, int32_t x, int32_t y, int32_t z, int fortune) {
int chance = 20;
if (fortune > 0) chance -= 2 << fortune;
if (chance < 10) chance = 10;
if (rand() % chance == 0) {
struct slot drop;
uint8_t m = blk & 0x0f;
if (blk >> 4 == BLK_LEAVES_OAK >> 4) {
if ((m & 0x03) == 0) {
drop.item = BLK_SAPLING_OAK >> 4;
drop.damage = 0;
} else if ((m & 0x03) == 1) {
drop.item = BLK_SAPLING_SPRUCE >> 4;
drop.damage = 1;
} else if ((m & 0x03) == 2) {
drop.item = BLK_SAPLING_BIRCH >> 4;
drop.damage = 2;
} else if ((m & 0x03) == 3) {
drop.item = BLK_SAPLING_JUNGLE >> 4;
drop.damage = 3;
}
} else if (blk >> 4 == BLK_LEAVES_ACACIA >> 4) {
if ((m & 0x03) == 0) {
drop.item = BLK_SAPLING_ACACIA >> 4;
drop.damage = 4;
} else if ((m & 0x03) == 1) {
drop.item = BLK_SAPLING_BIG_OAK >> 4;
drop.damage = 5;
} else return;
} else return;
drop.count = 1;
drop.nbt = NULL;
game_drop_block(world, &drop, x, y, z);
}
if (blk == BLK_LEAVES_OAK || blk == BLK_LEAVES_BIG_OAK) {
chance = 200;
if (fortune > 0) {
chance -= 10 << fortune;
if (chance < 40) chance = 40;
}
if (rand() % chance == 0) {
struct slot drop;
drop.item = ITM_APPLE;
drop.damage = 0;
drop.count = 1;
drop.nbt = NULL;
game_drop_block(world, &drop, x, y, z);
}
}
}
void dropItems_tallgrass(struct world* world, block blk, int32_t x, int32_t y, int32_t z, int fortune) {
if (rand() % 8 == 0) {
struct slot drop;
drop.item = ITM_SEEDS;
drop.damage = 0;
drop.count = 1 + (rand() % (fortune * 2 + 1));
drop.nbt = NULL;
game_drop_block(world, &drop, x, y, z);
}
}
int falling_canFallThrough(block b) {
struct block_info* block_info = getBlockInfo(b);
return block_info == NULL || str_eq(block_info->material->name, "fire") || str_eq(block_info->material->name, "air") || str_eq(block_info->material->name, "water") || str_eq(block_info->material->name, "lava");
}
void onBlockUpdate_falling(struct world* world, block blk, int32_t x, int32_t y, int32_t z) {
if (y > 0 && falling_canFallThrough(world_get_block(world, x, y - 1, z))) {
world_schedule_block_tick(world, x, y, z, 4, 0.);
}
}
int scheduledTick_falling(struct world* world, block blk, int32_t x, int32_t y, int32_t z) {
if (y > 0 && falling_canFallThrough(world_get_block(world, x, y - 1, z))) {
world_set_block(world, 0, x, y, z);
struct entity* e = entity_new(world, world->server->next_entity_id++, (double) x + .5, (double) y, (double) z + .5, ENT_FALLINGBLOCK, 0., 0.);
e->data.fallingblock.b = blk;
e->objectData = blk >> 4;
world_spawn_entity(world, e);
}
return 0;
}
void sponge_floodfill(struct world* world, int32_t x, int32_t y, int32_t z, int32_t* pos, uint32_t* removed) {
if (*removed > 64) return;
block b = world_get_block(world, x, y, z);
if (b >> 4 == BLK_WATER >> 4 || b >> 4 == BLK_WATER_1 >> 4) {
pos[*removed * 3] = x;
pos[*removed * 3 + 1] = y;
pos[*removed * 3 + 2] = z;
(*removed)++;
if (*removed > 64) return;
}
}
void onBlockUpdate_sponge(struct world* world, block blk, int32_t x, int32_t y, int32_t z) {
if ((blk & 0x0f) == 0) {
uint32_t removed = 0;
int32_t pos[65][3];
uint32_t lRemoved = 0;
while (removed < 65) {
if (removed == 0) {
sponge_floodfill(world, x - 1, y, z, pos, &removed);
sponge_floodfill(world, x + 1, y, z, pos, &removed);
sponge_floodfill(world, x, y + 1, z, pos, &removed);
sponge_floodfill(world, x, y - 1, z, pos, &removed);
sponge_floodfill(world, x, y, z + 1, pos, &removed);
sponge_floodfill(world, x, y, z - 1, pos, &removed);
if (removed == 0) break;
else world_set_block(world, blk | 1, x, y, z);
}
uint32_t rRemoved = removed;
for (uint32_t i = lRemoved; i < removed; i++) {
world_set_block(world, 0, pos[i][0], pos[i][1], pos[i][2]);
sponge_floodfill(world, pos[i][0] - 1, pos[i][1], pos[i][2], pos, &rRemoved);
sponge_floodfill(world, pos[i][0] + 1, pos[i][1], pos[i][2], pos, &rRemoved);
sponge_floodfill(world, pos[i][0], pos[i][1] + 1, pos[i][2], pos, &rRemoved);
sponge_floodfill(world, pos[i][0], pos[i][1] - 1, pos[i][2], pos, &rRemoved);
sponge_floodfill(world, pos[i][0], pos[i][1], pos[i][2] + 1, pos, &rRemoved);
sponge_floodfill(world, pos[i][0], pos[i][1], pos[i][2] - 1, pos, &rRemoved);
}
if (lRemoved == removed) break;
lRemoved = removed;
removed = rRemoved;
}
}
}
int canBePlaced_ladder(struct world* world, block blk, int32_t x, int32_t y, int32_t z) {
uint8_t m = blk & 0x0f;
block b = 0;
if (m == 3) b = world_get_block(world, x, y, z - 1);
else if (m == 4) b = world_get_block(world, x + 1, y, z);
else if (m == 5) b = world_get_block(world, x - 1, y, z);
else b = world_get_block(world, x, y, z + 1);
return block_is_normal_cube(getBlockInfo(b));
}
block onBlockPlacedPlayer_ladder(struct player* player, struct world* world, block blk, int32_t x, int32_t y, int32_t z, uint8_t face) {
int zpg = 0;
int zng = 0;
int xng = 0;
int xpg = 0;
if ((zpg = block_is_normal_cube(getBlockInfo(world_get_block(world, x, y, z - 1)))) && face == ZP) return (blk & ~0xf) | 3;
else if ((zng = block_is_normal_cube(getBlockInfo(world_get_block(world, x, y, z + 1)))) && face == ZN) return (blk & ~0xf) | 2;
else if ((xpg = block_is_normal_cube(getBlockInfo(world_get_block(world, x + 1, y, z)))) && face == XN) return (blk & ~0xf) | 4;
else if ((xng = block_is_normal_cube(getBlockInfo(world_get_block(world, x - 1, y, z)))) && face == XP) return (blk & ~0xf) | 5;
else if (zpg) return (blk & ~0xf) | 2;
else if (zng) return (blk & ~0xf) | 3;
else if (xng) return (blk & ~0xf) | 4;
else if (xpg) return (blk & ~0xf) | 5;
else return 0;
}
block onBlockPlacedPlayer_vine(struct player* player, struct world* world, block blk, int32_t x, int32_t y, int32_t z, uint8_t face) {
block out = blk & ~0x0f;
block b = world_get_block(world, x, y, z + 1);
struct block_info* bi = getBlockInfo(b);
if (bi != NULL && bi->fullCube && bi->material->blocksMovement) out |= 0x1;
b = world_get_block(world, x - 1, y, z);
bi = getBlockInfo(b);
if (bi != NULL && bi->fullCube && bi->material->blocksMovement) out |= 0x2;
b = world_get_block(world, x, y, z - 1);
bi = getBlockInfo(b);
if (bi != NULL && bi->fullCube && bi->material->blocksMovement) out |= 0x4;
b = world_get_block(world, x + 1, y, z);
bi = getBlockInfo(b);
if (bi != NULL && bi->fullCube && bi->material->blocksMovement) out |= 0x8;
b = world_get_block(world, x, y + 1, z);
bi = getBlockInfo(b);
//printf("block placed vine = ");
if ((b >> 4) == (BLK_VINE >> 4)) {
//printf("%i\n", b);
return b;
} else if (!(bi != NULL && bi->fullCube && bi->material->blocksMovement) && (out & 0x0f) == 0) {
//printf("0 -- %i, %i, %i, %i, %i\n", b, bi != NULL, bi->fullCube, bi->material->blocksMovement, out);
return 0;
}
//printf("2/%i\n", out);
return out;
}
void onBlockUpdate_vine(struct world* world, block blk, int32_t x, int32_t y, int32_t z) {
//printf("bu vine <%i, %i, %i> ", x, y, z);
block b = onBlockPlacedPlayer_vine(NULL, world, blk, x, y, z, -1);
//printf("%i != %i\n", b, blk);
if (b != blk) {
if (b >> 4 == blk >> 4 && b >> 4 == BLK_VINE >> 4) world_set_block_noupdate(world, b, x, y, z);
else world_set_block(world, b, x, y, z);
}
}
int canBePlaced_vine(struct world* world, block blk, int32_t x, int32_t y, int32_t z) {
//printf("cbp vine at\n");
block b = world_get_block(world, x, y, z + 1);
struct block_info* bi = getBlockInfo(b);
if (bi != NULL && bi->fullCube && bi->material->blocksMovement) return 1;
b = world_get_block(world, x - 1, y, z);
bi = getBlockInfo(b);
if (bi != NULL && bi->fullCube && bi->material->blocksMovement) return 1;
b = world_get_block(world, x, y, z - 1);
bi = getBlockInfo(b);
if (bi != NULL && bi->fullCube && bi->material->blocksMovement) return 1;
b = world_get_block(world, x + 1, y, z);
bi = getBlockInfo(b);
if (bi != NULL && bi->fullCube && bi->material->blocksMovement) return 1;
b = world_get_block(world, x, y + 1, z);
if ((b >> 4) == (BLK_VINE >> 4)) return 1;
bi = getBlockInfo(b);
if (bi != NULL && bi->fullCube && bi->material->blocksMovement) return 1;
//printf("cbp vine at --- cannot be placed\n");
return 0;
}
void randomTick_vine(struct world* world, struct chunk* ch, block blk, int32_t x, int32_t y, int32_t z) {
if (rand() % 4 != 0) return;
block below = world_get_block_guess(world, ch, x, y - 1, z);
if (below == 0) {
//printf("rtv grow\n");
world_set_block_guess(world, ch, onBlockPlacedPlayer_vine(NULL, world, BLK_VINE, x, y, z, -1), x, y - 1, z);
//printf("rtv growdun\n");
}
}
int canBePlaced_requiredirt(struct world* world, block blk, int32_t x, int32_t y, int32_t z) {
block b = world_get_block(world, x, y - 1, z);
return b == BLK_DIRT || b == BLK_GRASS;
}
int canBePlaced_sapling(struct world* world, block blk, int32_t x, int32_t y, int32_t z) {
block b = world_get_block(world, x, y - 1, z);
return b == BLK_DIRT || b == BLK_GRASS || b == BLK_FARMLAND;
}
int canBePlaced_doubleplant(struct world* world, block blk, int32_t x, int32_t y, int32_t z) {
block b = world_get_block(world, x, y - 1, z);
block b2 = world_get_block(world, x, y + 1, z);
if ((b == blk && b2 != 0) || (b2 == blk && b != BLK_GRASS && b != BLK_DIRT)) {
return 0;
}
return 1;
}
int canBePlaced_requiresand(struct world* world, block blk, int32_t x, int32_t y, int32_t z) {
block b = world_get_block(world, x, y - 1, z);
return b == BLK_SAND;
}
int canBePlaced_cactus(struct world* world, block blk, int32_t x, int32_t y, int32_t z) {
block b = world_get_block(world, x, y - 1, z);
if (b != BLK_SAND && b != BLK_CACTUS) return 0;
b = world_get_block(world, x, y, z + 1);
if (b != 0) return 0;
b = world_get_block(world, x, y, z - 1);
if (b != 0) return 0;
b = world_get_block(world, x + 1, y, z);
if (b != 0) return 0;
b = world_get_block(world, x - 1, y, z);
if (b != 0) return 0;
return 1;
}
int canBePlaced_reeds(struct world* world, block blk, int32_t x, int32_t y, int32_t z) {
block b = world_get_block(world, x, y - 1, z);
if (b == BLK_REEDS) return 1;
if (b != BLK_DIRT && b != BLK_GRASS && b != BLK_SAND) return 0;
b = world_get_block(world, x, y - 1, z + 1);
if ((b >> 4) == (BLK_WATER >> 4) || (b >> 4) == (BLK_WATER_1 >> 4)) return 1;
b = world_get_block(world, x, y - 1, z - 1);
if ((b >> 4) == (BLK_WATER >> 4) || (b >> 4) == (BLK_WATER_1 >> 4)) return 1;
b = world_get_block(world, x + 1, y - 1, z);
if ((b >> 4) == (BLK_WATER >> 4) || (b >> 4) == (BLK_WATER_1 >> 4)) return 1;
b = world_get_block(world, x - 1, y - 1, z);
if ((b >> 4) == (BLK_WATER >> 4) || (b >> 4) == (BLK_WATER_1 >> 4)) return 1;
return 0;
}
int canBePlaced_requirefarmland(struct world* world, block blk, int32_t x, int32_t y, int32_t z) {
block b = world_get_block(world, x, y - 1, z);
return b >> 4 == BLK_FARMLAND >> 4;
}
int canBePlaced_requiresoulsand(struct world* world, block blk, int32_t x, int32_t y, int32_t z) {
block b = world_get_block(world, x, y - 1, z);
return b == BLK_HELLSAND;
}
void dropItems_hugemushroom(struct world* world, block blk, int32_t x, int32_t y, int32_t z, int fortune) {
int ct = rand() % 10 - 7;
if (ct > 0) {
struct slot drop;
drop.item = ((blk >> 4) == (BLK_MUSHROOM_2 >> 4) ? 39 : 40);
drop.damage = 0;
drop.count = ct;
drop.nbt = NULL;
game_drop_block(world, &drop, x, y, z);
}
}
void dropItems_crops(struct world* world, block blk, int32_t x, int32_t y, int32_t z, int fortune) {
struct slot drop;
uint8_t age = blk & 0x0f;
uint8_t maxAge = 0;
item seed = 0;
if ((blk >> 4) == (BLK_CROPS >> 4)) {
maxAge = 7;
if (age >= maxAge) {
drop.item = ITM_WHEAT;
drop.count = 1;
drop.damage = 0;
drop.nbt = NULL;
game_drop_block(world, &drop, x, y, z);
}
seed = ITM_SEEDS;
} else if ((blk >> 4) == (BLK_PUMPKINSTEM >> 4)) {
drop.item = ITM_SEEDS_PUMPKIN;
drop.count = 1;
drop.damage = 0;
drop.nbt = NULL;
for (int i = 0; i < 3; i++) {
if (rand() % 15 <= age) {
game_drop_block(world, &drop, x, y, z);
}
}
seed = ITM_SEEDS_PUMPKIN;
maxAge = 7;
} else if ((blk >> 4) == (BLK_PUMPKINSTEM_1 >> 4)) {
drop.item = ITM_SEEDS_MELON;
drop.count = 1;
drop.damage = 0;
drop.nbt = NULL;
for (int i = 0; i < 3; i++) {
if (rand() % 15 <= age) {
game_drop_block(world, &drop, x, y, z);
}
}
seed = ITM_SEEDS_PUMPKIN;
maxAge = 7;
} else if ((blk >> 4) == (BLK_CARROTS >> 4)) {
maxAge = 7;
if (age >= maxAge) {
drop.item = ITM_CARROTS;
drop.count = 1;
drop.damage = 0;
drop.nbt = NULL;
game_drop_block(world, &drop, x, y, z);
}
seed = ITM_CARROTS;
} else if ((blk >> 4) == (BLK_POTATOES >> 4)) {
maxAge = 7;
seed = ITM_POTATO;
if (age >= maxAge) {
drop.item = ITM_POTATO;
drop.count = 1;
drop.damage = 0;
drop.nbt = NULL;
game_drop_block(world, &drop, x, y, z);
if (rand() % 50 == 0) {
drop.item = ITM_POTATOPOISONOUS;
drop.count = 1;
drop.damage = 0;
drop.nbt = NULL;
game_drop_block(world, &drop, x, y, z);
}
}
} else if ((blk >> 4) == (BLK_NETHERSTALK >> 4)) {
int rct = 1;
drop.item = ITM_NETHERSTALKSEEDS;
drop.count = 1;
drop.damage = 0;
drop.nbt = NULL;
if (age >= 3) {
rct += 2 + rand() % 3;
if (fortune > 0) rct += rand() % (fortune + 1);
}
for (int i = 0; i < rct; i++)
game_drop_block(world, &drop, x, y, z);
return;
} else if ((blk >> 4) == (BLK_BEETROOTS >> 4)) {
maxAge = 3;
if (age >= maxAge) {
drop.item = ITM_BEETROOT;
drop.count = 1;
drop.damage = 0;
drop.nbt = NULL;
game_drop_block(world, &drop, x, y, z);
}
seed = ITM_BEETROOT_SEEDS;
} else if ((blk >> 4) == (BLK_COCOA >> 4)) {
int rc = 1;
if (age >= 2) rc = 3;
drop.item = ITM_DYEPOWDER_BLACK;
drop.count = 1;
drop.damage = 3;
drop.nbt = NULL;
for (int i = 0; i < rc; i++)
game_drop_block(world, &drop, x, y, z);
return;
}
if (seed > 0) {
drop.item = seed;
drop.count = 1;
drop.damage = 0;
drop.nbt = NULL;
if (age >= maxAge) {
int rct = 3 + fortune;
for (int i = 0; i < rct; i++) {
if (rand() % (2 * maxAge) <= age) game_drop_block(world, &drop, x, y, z);
}
}
game_drop_block(world, &drop, x, y, z);
}
}
float crops_getGrowthChance(struct world* world, block b, int32_t x, int32_t y, int32_t z) {
float chance = 1.;
for (int32_t sx = x - 1; sx <= x + 1; sx++) {
for (int32_t sz = x - 1; sz <= x + 1; sz++) {
block b2 = world_get_block(world, sx, y - 1, sz);
float subchance = 0.;
if ((b2 >> 4) == (BLK_FARMLAND >> 4)) {
subchance = 1.;
if ((b2 & 0x0f) > 0) {
subchance = 3;
}
}
if (sx != 0 || sz != 0) {
subchance /= 4.;
}
chance += subchance;
}
}
int xSame = world_get_block(world, x - 1, y, z) >> 4 == b >> 4 || world_get_block(world, x + 1, y, z) >> 4 == b >> 4;
int zSame = world_get_block(world, x, y, z - 1) >> 4 == b >> 4 || world_get_block(world, x, y, z + 1) >> 4 == b >> 4;
if (xSame && zSame) {
chance /= 2.;
} else {
int dSame = world_get_block(world, x - 1, y, z - 1) >> 4 == b >> 4 || world_get_block(world, x + 1, y, z - 1) >> 4 == b >> 4 || world_get_block(world, x - 1, y, z + 1) >> 4 == b >> 4 || world_get_block(world, x + 1, y, z + 1) >> 4 == b >> 4;
if (dSame) {
chance /= 2.;
}
}
return chance;
}
void randomTick_crops(struct world* world, struct chunk* ch, block blk, int32_t x, int32_t y, int32_t z) {
uint8_t lwu = world_get_light_guess(world, ch, x, y + 1, z);
if (lwu > 9) {
uint8_t age = blk & 0x0f;
if (blk >> 4 == BLK_CROPS >> 4) {
if (age < 7) {
float gChance = crops_getGrowthChance(world, blk, x, y, z);
if (rand() % ((int) (25. / gChance) + 1) == 0) {
block nb = (blk & 0xfff0) | ((blk & 0x0f) + 1);
world_set_block_guess(world, ch, nb, x, y, z);
}
}
}
}
}
void randomTick_farmland(struct world* world, struct chunk* ch, block blk, int32_t x, int32_t y, int32_t z) {
uint8_t moisture = blk & 0x0f;
uint8_t hasWaterOrRain = 0; // TODO: true if raining
if (!hasWaterOrRain) for (int32_t sx = x - 4; sx <= x + 4; sx++) {
for (int32_t sz = z - 4; sz <= z + 4; sz++) {
if (str_eq(getBlockInfo(world_get_block_guess(world, ch, sx, y, sz))->material->name, "water")) {
hasWaterOrRain = 1;
goto postWater;
}
}
}
postWater: ;
if (hasWaterOrRain && moisture < 7) {
block nb = (blk & 0xfff0) | 7;
world_set_block_guess(world, ch, nb, x, y, z);
} else if (moisture > 0 && !hasWaterOrRain) {
block nb = (blk & 0xfff0) | (moisture - 1);
world_set_block_guess(world, ch, nb, x, y, z);
}
}
void randomTick_grass(struct world* world, struct chunk* chunk, block blk, int32_t x, int32_t y, int32_t z) {
struct block_info* ni = getBlockInfo(world_get_block_guess(world, chunk, x, y + 1, z));
uint8_t lwu = world_get_light(world, x, y, z, 1);
if (lwu < 4 && ni != NULL && ni->lightOpacity > 2) {
world_set_block_guess(world, chunk, BLK_DIRT, x, y, z);
} else if (lwu >= 9) {
for (int i = 0; i < 4; i++) {
int32_t gx = x + rand() % 3 - 1;
int32_t gy = y + rand() % 5 - 3;
int32_t gz = z + rand() % 3 - 1;
block up = world_get_block_guess(world, chunk, gx, gy + 1, gz);
block blk = world_get_block_guess(world, chunk, gx, gy, gz);
ni = getBlockInfo(up);
if (blk == BLK_DIRT && (ni == NULL || ni->lightOpacity <= 2) && world_get_light(world, gx, gy + 1, gz, 1) >= 4) {
world_set_block_guess(world, chunk, BLK_GRASS, gx, gy, gz);
}
}
}
}
int tree_canGrowInto(block b) {
struct block_info* bi = getBlockInfo(b);
if (bi == NULL) return 1;
char* mn = bi->material->name;
return b == BLK_GRASS || b == BLK_DIRT || (b >> 4) == (BLK_LOG_OAK >> 4) || (b >> 4) == (BLK_LOG_ACACIA >> 4) || (b >> 4) == (BLK_SAPLING_OAK >> 4) || b == BLK_VINE || str_eq(mn, "air") || str_eq(mn, "leaves");
}
void tree_addHangingVine(struct world* world, struct chunk* chunk, block b, int32_t x, int32_t y, int32_t z) {
world_set_block_guess(world, chunk, b, x, y, z);
int32_t iy = y;
for (y--; y > iy - 4 && world_get_block_guess(world, chunk, x, y, z) == 0; y--) {
world_set_block_guess(world, chunk, b, x, y, z);
}
}
int tree_checkAndPlaceLeaf(struct world* world, struct chunk* chunk, block b, int32_t x, int32_t y, int32_t z) {
struct block_info* bi = getBlockInfo(world_get_block_guess(world, chunk, x, y, z));
if (bi == NULL || str_eq(bi->material->name, "air") || str_eq(bi->material->name, "leaves")) {
world_set_block_guess(world, chunk, b, x, y, z);
return 1;
}
return 0;
}
void randomTick_sapling(struct world* world, struct chunk* chunk, block blk, int32_t x, int32_t y, int32_t z) {
uint8_t lwu = world_get_light(world, x, y + 1, z, 1);
if (lwu >= 9 && rand() % 7 == 0) {
if ((blk & 0x8) == 0x8) {
uint8_t type = blk & 0x7;
block log = BLK_LOG_OAK;
block leaf = BLK_LEAVES_OAK_2;
if (type == 1) {
log = BLK_LOG_SPRUCE;
leaf = BLK_LEAVES_SPRUCE_2;
} else if (type == 2) {
log = BLK_LOG_BIRCH;
leaf = BLK_LEAVES_BIRCH_2;
} else if (type == 3) {
log = BLK_LOG_JUNGLE;
leaf = BLK_LEAVES_JUNGLE_2;
} else if (type == 4) {
log = BLK_LOG_ACACIA_1;
leaf = BLK_LEAVES_ACACIA_2;
} else if (type == 5) {
log = BLK_LOG_BIG_OAK_1;
leaf = BLK_LEAVES_BIG_OAK_2;
}
uint8_t biome = world_get_biome(world, x, z);
uint8_t vines = type == 0 && (biome == BIOME_SWAMPLAND || biome == BIOME_SWAMPLAND_M);
uint8_t cocoa = type == 3;
int big = type == 0 && rand() % 10 == 0;
if (!big) {
uint8_t height = 0;
if (type == 0) height = rand() % 3 + 4;
else if (type == 1) height = 6 + rand() % 4;
else if (type == 2) height = rand() % 3 + 5;
else if (type == 3) height = 3 + rand() % 10;
else if (type == 4) height = 5 + rand() % 6;
if (y < 1 || y + height + 1 > 256) return;
for (int ty = y; ty <= y + height + 1; ty++) {
if (ty < 0 || ty >= 256) return;
int width = 1;
if (ty == y) width = 0;
if (ty >= y + 1 + height - 2) width = 2;
for (int tx = x - width; tx <= x + width; tx++) {
for (int tz = z - width; tz <= z + width; tz++) {
if (!tree_canGrowInto(world_get_block_guess(world, chunk, tx, ty, tz))) return;
}
}
}
block down = world_get_block_guess(world, chunk, x, y - 1, z);
if ((down != BLK_GRASS && down != BLK_DIRT && down != BLK_FARMLAND) || y >= 256 - height - 1) return;
if (down == BLK_FARMLAND && type == 4) return;
world_set_block_guess(world, chunk, BLK_DIRT, x, y - 1, z);
if (type == 4) {
int k2 = height - rand() % 4 - 1;
int l2 = 3 - rand() % 3;
int i3 = x;
int j1 = z;
int k1 = 0;
int rx = rand() % 4;
int ox = 0;
int oz = 0;
if (rx == 0) ox++;
else if (rx == 1) ox--;
else if (rx == 2) oz++;
else if (rx == 3) oz--;
for (int l1 = 0; l1 < height; l1++) {
int i2 = y + l1;
if (l1 >= k2 && l2 > 0) {
i3 += ox;
j1 += oz;
l2--;
}
struct block_info* bi = getBlockInfo(world_get_block_guess(world, chunk, i3, i2, j1));
if (bi != NULL && (str_eq(bi->material->name, "air") || str_eq(bi->material->name, "leaves") || str_eq(bi->material->name, "vine") || str_eq(bi->material->name, "plants"))) {
world_set_block_guess(world, chunk, log, i3, i2, j1);
k1 = i2;
}
}
for (int j3 = -3; j3 <= 3; j3++) {
for (int i4 = -3; i4 <= +3; i4++) {
if (abs(j3) != 3 || abs(i4) != 3) {
tree_checkAndPlaceLeaf(world, chunk, leaf, i3 + j3, k1, j1 + i4);
}
if (abs(j3) <= 1 || abs(i4) <= 1) {
tree_checkAndPlaceLeaf(world, chunk, leaf, i3 + j3, k1 + 1, j1 + i4);
}
}
}
tree_checkAndPlaceLeaf(world, chunk, leaf, i3 - 2, k1 + 1, j1);
tree_checkAndPlaceLeaf(world, chunk, leaf, i3 + 2, k1 + 1, j1);
tree_checkAndPlaceLeaf(world, chunk, leaf, i3, k1 + 1, j1 - 2);
tree_checkAndPlaceLeaf(world, chunk, leaf, i3, k1 + 1, j1 + 1);
i3 = x;
j1 = z;
int nrx = rand() % 4;
if (rx == nrx) return;
rx = nrx;
ox = 0;
oz = 0;
if (rx == 0) ox++;
else if (rx == 1) ox--;
else if (rx == 2) oz++;
else if (rx == 3) oz--;
int l3 = k2 - rand() % 2 - 1;
int k4 = 1 + rand() % 3;
k1 = 0;
for (int l4 = l3; l4 < height && k4 > 0; k4--) {
if (l4 >= 1) {
int j2 = y + l4;
i3 += ox;
j1 += oz;
if (tree_checkAndPlaceLeaf(world, chunk, log, i3, j2, j1)) {
k1 = j2;
}
}
l4++;
}
if (k1 > 0) {
for (int i5 = -2; i5 <= 2; i5++) {
for (int k5 = -2; k5 <= 2; k5++) {
if (abs(i5) != 2 || abs(k5) != 2) tree_checkAndPlaceLeaf(world, chunk, leaf, i3 + i5, k1, j1 + k5);
if (abs(i5) <= 1 && abs(k5) <= 1) tree_checkAndPlaceLeaf(world, chunk, leaf, i3 + i5, k1 + 1, j1 + k5);
}
}
}
} else if (type == 1) {
int32_t j = 1 + rand() % 2;
int k = height - j;
int l = 2 + rand() % 2;
int i3 = rand() % 2;
int j3 = 1;
int k3 = 0;
for (int l3 = 0; l3 <= k; l3++) {
int yoff = y + height - l3;
for (int i2 = x - i3; i2 <= x + i3; i2++) {
int xoff = i2 - x;
for (int k2 = z - i3; k2 <= z + i3; k2++) {
int zoff = k2 - z;
if (abs(xoff) != i3 || abs(zoff) != i3 || i3 <= 0) {
struct block_info* bi = getBlockInfo(world_get_block_guess(world, chunk, i2, yoff, k2));
if (bi == NULL || !bi->fullCube) {
world_set_block_guess(world, chunk, leaf, i2, yoff, k2);
}
}
}
}
if (i3 >= j3) {
i3 = j3;
k3 = 1;
j3++;
if (j3 > l) {
j3 = l;
}
} else {
i3++;
}
}
} else for (int ty = y - 3 + height; ty <= y + height; ty++) {
int dist_from_top = ty - y - height;
int width = 1 - dist_from_top / 2;
for (int tx = x - width; tx <= x + width; tx++) {
int tx2 = tx - x;
for (int tz = z - width; tz <= z + width; tz++) {
int tz2 = tz - z;
if (abs(tx2) != width || abs(tz2) != width || (rand() % 2 == 0 && dist_from_top != 0)) {
struct block_info* bi = getBlockInfo(world_get_block_guess(world, chunk, tx, ty, tz));
if (bi != NULL && (str_eq(bi->material->name, "air") || str_eq(bi->material->name, "leaves") || str_eq(bi->material->name, "vine"))) {
world_set_block_guess(world, chunk, leaf, tx, ty, tz);
}
}
}
}
}
if (type != 4) {
for (int th = 0; th < height - (type == 1 ? (rand() % 3) : 0); th++) {
struct block_info* bi = getBlockInfo(world_get_block_guess(world, chunk, x, y + th, z));
if (bi != NULL && (str_eq(bi->material->name, "air") || str_eq(bi->material->name, "leaves") || str_eq(bi->material->name, "vine") || str_eq(bi->material->name, "plants"))) {
world_set_block_guess(world, chunk, log, x, y + th, z);
if (vines && th > 0) {
if (rand() % 3 > 0 && world_get_block_guess(world, chunk, x - 1, y + th, z) == 0) world_set_block_guess(world, chunk, BLK_VINE | 0x8, x - 1, y + th, z);
if (rand() % 3 > 0 && world_get_block_guess(world, chunk, x + 1, y + th, z) == 0) world_set_block_guess(world, chunk, BLK_VINE | 0x2, x + 1, y + th, z);
if (rand() % 3 > 0 && world_get_block_guess(world, chunk, x, y + th, z - 1) == 0) world_set_block_guess(world, chunk, BLK_VINE | 0x1, x + 1, y + th, z - 1);
if (rand() % 3 > 0 && world_get_block_guess(world, chunk, x, y + th, z + 1) == 0) world_set_block_guess(world, chunk, BLK_VINE | 0x4, x + 1, y + th, z + 1);
}
}
}
if (vines) {
for (int ty = y - 3 + height; ty <= y + height; ty++) {
int dist_from_top = ty - y - height;
int width = 2 - dist_from_top / 2;
for (int tx = x - width; tx <= x + width; tx++) {
for (int tz = z - width; tz <= z + width; tz++) {
struct block_info* bi = getBlockInfo(world_get_block_guess(world, chunk, tx, ty, tz));
if (bi != NULL && str_eq(bi->material->name, "leaves")) {
if (rand() % 4 == 0 && world_get_block_guess(world, chunk, tx - 1, ty, tz) == 0) tree_addHangingVine(world, chunk, BLK_VINE | 0x8, tx - 1, ty, tz);
if (rand() % 4 == 0 && world_get_block_guess(world, chunk, tx + 1, ty, tz) == 0) tree_addHangingVine(world, chunk, BLK_VINE | 0x2, tx + 1, ty, tz);
if (rand() % 4 == 0 && world_get_block_guess(world, chunk, tx, ty, tz - 1) == 0) tree_addHangingVine(world, chunk, BLK_VINE | 0x1, tx + 1, ty, tz - 1);
if (rand() % 4 == 0 && world_get_block_guess(world, chunk, tx, ty, tz + 1) == 0) tree_addHangingVine(world, chunk, BLK_VINE | 0x4, tx + 1, ty, tz + 1);
}
}
}
}
if (cocoa) if (rand() % 5 == 0 && height > 5) {
for (int ty = y; ty < y + 2; ty++) {
if (rand() % (4 - ty - y) == 0) {
//TODO: cocoa
}
}
}
}
}
} else {
}
} else {
world_set_block_guess(world, chunk, (blk | 0x8), x, y, z);
}
}
}
block onBlockPlacedPlayer_log(struct player* player, struct world* world, block blk, int32_t x, int32_t y, int32_t z, uint8_t face) {
block nb = blk & ~0xC;
if (face == XP || face == XN) {
nb |= 0x4;
} else if (face == ZP || face == ZN) {
nb |= 0x8;
}
return nb;
}
void onBlockInteract_fencegate(struct world* world, block blk, int32_t x, int32_t y, int32_t z, struct player* player, uint8_t face, float curPosX, float curPosY, float curPosZ) {
blk ^= (block) 0b0100; // toggle opened bit
world_set_block_guess(world, NULL, blk, x, y, z);
}
int fluid_getDepth(int water, block b) {
uint16_t ba = b >> 4;
if (water && (ba != BLK_WATER >> 4 && ba != BLK_WATER_1 >> 4)) return -1;
else if (!water && (ba != BLK_LAVA >> 4 && ba != BLK_LAVA_1 >> 4)) return -1;
return b & 0x0f;
}
int fluid_checkAdjacentBlock(int water, struct world* world, struct chunk* ch, int32_t x, uint8_t y, int32_t z, int cmin, int* adj) {
block b = world_get_block_guess(world, ch, x, y, z);
int m = fluid_getDepth(water, b);
if (m < 0) return cmin;
else {
if (m == 0) (*adj)++;
if (m >= 8) m = 0;
return cmin >= 0 && m >= cmin ? cmin : m;
}
}
int fluid_isUnblocked(int water, block b, struct block_info* bi) {
uint16_t ba = b >> 4;
if (ba == BLK_DOOROAK >> 4 || ba == BLK_DOORIRON >> 4 || ba == BLK_SIGN >> 4 || ba == BLK_LADDER >> 4 || ba == BLK_REEDS >> 4) return 0;
if (bi == NULL) return 0;
if (!str_eq(bi->material->name, "portal") && !str_eq(bi->material->name, "structure_void") ? bi->material->blocksMovement : 1) return 0;
return 1;
}
int fluid_canFlowInto(int water, block b) {
struct block_info* bi = getBlockInfo(b);
if (bi == NULL) return 1;
if (!((water ? !str_eq(bi->material->name, "water") : 1) && !str_eq(bi->material->name, "lava"))) return 0;
return fluid_isUnblocked(water, b, bi);
}
int lava_checkForMixing(struct world* world, struct chunk* ch, block blk, int32_t x, uint8_t y, int32_t z) {
int cww = 0;
block b = world_get_block_guess(world, ch, x + 1, y, z);
if (b >> 4 == BLK_WATER >> 4 || b >> 4 == BLK_WATER_1 >> 4) cww = 1;
if (!cww) {
b = world_get_block_guess(world, ch, x - 1, y, z);
if (b >> 4 == BLK_WATER >> 4 || b >> 4 == BLK_WATER_1 >> 4) cww = 1;
}
if (!cww) {
b = world_get_block_guess(world, ch, x, y, z + 1);
if (b >> 4 == BLK_WATER >> 4 || b >> 4 == BLK_WATER_1 >> 4) cww = 1;
}
if (!cww) {
b = world_get_block_guess(world, ch, x, y, z - 1);
if (b >> 4 == BLK_WATER >> 4 || b >> 4 == BLK_WATER_1 >> 4) cww = 1;
}
if (!cww) {
b = world_get_block_guess(world, ch, x, y + 1, z);
if (b >> 4 == BLK_WATER >> 4 || b >> 4 == BLK_WATER_1 >> 4) cww = 1;
}
if (cww) {
if ((blk & 0x0f) == 0) {
world_set_block_guess(world, ch, BLK_OBSIDIAN, x, y, z);
return 1;
} else if ((blk & 0x0f) <= 4) {
world_set_block_guess(world, ch, BLK_COBBLESTONE, x, y, z);
return 1;
}
}
return 0;
}
void fluid_doFlowInto(int water, struct world* world, struct chunk* ch, int32_t x, uint8_t y, int32_t z, int level, block b) {
// potentially triggerMixEffects
struct block_info* bi = getBlockInfo(b);
if (bi != NULL && !str_eq(bi->material->name, "air") && !str_eq(bi->material->name, "lava")) {
dropBlockDrops(world, b, NULL, x, y, z);
}
world_set_block_guess(world, ch, (water ? BLK_WATER : BLK_LAVA) | level, x, y, z);
world_schedule_block_tick(world, x, y, z, water ? 5 : (world->dimension == -1 ? 10 : 30), 0.);
}
void onBlockUpdate_staticfluid(struct world* world, block blk, int32_t x, int32_t y, int32_t z) {
int water = (blk >> 4) == (BLK_WATER >> 4) || (blk >> 4) == (BLK_WATER_1 >> 4);
if (water || !lava_checkForMixing(world, NULL, blk, x, y, z)) {
world_schedule_block_tick(world, x, y, z, water ? 5 : (world->dimension == -1 ? 10 : 30), 0.);
if (water) world_set_block_noupdate(world, BLK_WATER | (blk & 0x0f), x, y, z);
else if (!water) world_set_block_noupdate(world, BLK_LAVA | (blk & 0x0f), x, y, z);
}
}
void randomTick_staticlava(struct world* world, struct chunk* ch, block blk, int32_t x, int32_t y, int32_t z) {
int t = rand() % 3;
if (t == 0) {
for (int i = 0; i < 3; i++) {
int32_t nx = x + rand() % 3 - 1;
int32_t nz = z + rand() % 3 - 1;
if (world_get_block_guess(world, ch, nx, y + 1, nz) == 0) {
struct block_info* bi = getBlockInfo(world_get_block_guess(world, ch, nx, y, nz));
if (bi != NULL && bi->material->flammable) {
world_set_block_guess(world, ch, BLK_FIRE, nx, y + 1, nz);
}
}
}
} else {
for (int i = 0; i < t; i++) {
int32_t nx = x + rand() % 3 - 1;
int32_t nz = z + rand() % 3 - 1;
block mb = world_get_block_guess(world, ch, nx, y + 1, nz);
struct block_info* bi = getBlockInfo(mb);
if (mb == 0 || bi == NULL) {
int g = 0;
bi = getBlockInfo(world_get_block_guess(world, ch, nx + 1, y + 1, nz));
if (bi != NULL && bi->material->flammable) g = 1;
if (!g) {
bi = getBlockInfo(world_get_block_guess(world, ch, nx - 1, y + 1, nz));
if (bi != NULL && bi->material->flammable) g = 1;
}
if (!g) {
bi = getBlockInfo(world_get_block_guess(world, ch, nx, y + 1, nz - 1));
if (bi != NULL && bi->material->flammable) g = 1;
}
if (!g) {
bi = getBlockInfo(world_get_block_guess(world, ch, nx, y + 1, nz + 1));
if (bi != NULL && bi->material->flammable) g = 1;
}
if (!g) {
bi = getBlockInfo(world_get_block_guess(world, ch, nx, y, nz - 1));
if (bi != NULL && bi->material->flammable) g = 1;
}
if (!g) {
bi = getBlockInfo(world_get_block_guess(world, ch, nx, y + 2, nz - 1));
if (bi != NULL && bi->material->flammable) g = 1;
}
if (g) {
world_set_block_guess(world, ch, BLK_FIRE, nx, y + 1, nz);
return;
}
} else if (bi->material->blocksMovement) return;
}
}
}
void onBlockUpdate_flowinglava(struct world* world, block blk, int32_t x, int32_t y, int32_t z) {
lava_checkForMixing(world, NULL, blk, x, y, z);
}
int scheduledTick_flowingfluid(struct world* world, block blk, int32_t x, int32_t y, int32_t z) {
struct chunk* ch = world_get_chunk(world, x >> 4, z >> 4);
if (ch == NULL) return 0;
int level = blk & 0x0f;
int resistance = 1;
if (((blk >> 4) == (BLK_LAVA >> 4) || (blk >> 4) == (BLK_LAVA_1 >> 4)) && world->dimension != -1) resistance++;
int adjSource = 0;
int water = (blk >> 4) == (BLK_WATER >> 4) || (blk >> 4) == (BLK_WATER_1 >> 4);
int tickRate = water ? 5 : (world->dimension == -1 ? 10 : 30);
if (level > 0) {
int l = -100;
l = fluid_checkAdjacentBlock(water, world, ch, x + 1, y, z, l, &adjSource);
l = fluid_checkAdjacentBlock(water, world, ch, x - 1, y, z, l, &adjSource);
l = fluid_checkAdjacentBlock(water, world, ch, x, y, z + 1, l, &adjSource);
l = fluid_checkAdjacentBlock(water, world, ch, x, y, z - 1, l, &adjSource);
int i1 = l + resistance;
if (i1 >= 8 || l < 0) i1 = -1;
int ha = fluid_getDepth(water, world_get_block_guess(world, ch, x, y + 1, z));
if (ha >= 0) {
if (ha >= 8) i1 = ha;
else i1 = ha + 8;
}
if (adjSource >= 2 && water) {
block below = world_get_block_guess(world, ch, x, y - 1, z);
if ((below >> 4) == (BLK_WATER >> 4) || (below >> 4) == (BLK_WATER_1 >> 4)) {
i1 = 0;
} else {
struct block_info* bi = getBlockInfo(below);
if (bi != NULL && bi->material->solid) i1 = 0;
}
}
if (!water && level < 8 && i1 < 8 && i1 > level && rand() % 4 > 0) {
tickRate *= 4;
}
if (i1 == level) {
if (water) world_set_block_noupdate(world, BLK_WATER_1 | (i1), x, y, z);
else if (!water) world_set_block_noupdate(world, BLK_LAVA_1 | (i1), x, y, z);
tickRate = 0;
} else {
level = i1;
if (i1 < 0) world_set_block_guess(world, ch, 0, x, y, z);
else {
world_set_block_guess(world, ch, (blk & ~0x0f) | i1, x, y, z);
}
}
} else {
if (water) world_set_block_noupdate(world, BLK_WATER_1 | (level), x, y, z);
else if (!water) world_set_block_noupdate(world, BLK_LAVA_1 | (level), x, y, z);
tickRate = 0;
}
block down = world_get_block_guess(world, ch, x, y - 1, z);
if (fluid_canFlowInto(water, down)) {
if (!water && ((down >> 4) == (BLK_WATER >> 4) || (down >> 4) == (BLK_WATER_1 >> 4))) {
world_set_block_guess(world, ch, BLK_STONE, x, y - 1, z);
//trigger mix effects
return tickRate;
}
fluid_doFlowInto(water, world, ch, x, y - 1, z, level >= 8 ? level : (level + 8), down);
} else if (level >= 0) {
if (level == 0 || !fluid_isUnblocked(water, down, getBlockInfo(down))) {
int k1 = level + resistance;
if (level >= 8) k1 = 1;
if (k1 >= 8) return tickRate;
block b = world_get_block_guess(world, ch, x + 1, y, z);
if (fluid_canFlowInto(water, b)) fluid_doFlowInto(water, world, ch, x + 1, y, z, k1, b);
b = world_get_block_guess(world, ch, x - 1, y, z);
if (fluid_canFlowInto(water, b)) fluid_doFlowInto(water, world, ch, x - 1, y, z, k1, b);
b = world_get_block_guess(world, ch, x, y, z + 1);
if (fluid_canFlowInto(water, b)) fluid_doFlowInto(water, world, ch, x, y, z + 1, k1, b);
b = world_get_block_guess(world, ch, x, y, z - 1);
if (fluid_canFlowInto(water, b)) fluid_doFlowInto(water, world, ch, x, y, z - 1, k1, b);
}
}
return tickRate;
}
//
struct block_info* getBlockInfo(block b) {
if (b >= block_infos->size) {
b &= ~0x0f;
if (b >= block_infos->size) return NULL;
else return block_infos->data[b];
}
if (block_infos->data[b] != NULL) return block_infos->data[b];
return block_infos->data[b & ~0x0f];
}
struct block_info* getBlockInfoLoose(block b) {
return block_infos->data[b & ~0x0f];
}
// index = item ID
const char* nameToIDMap[] = { "minecraft:air", "minecraft:stone", "minecraft:grass", "minecraft:dirt", "minecraft:cobblestone", "minecraft:planks", "minecraft:sapling", "minecraft:bedrock", "", "", "", "", "minecraft:sand", "minecraft:gravel", "minecraft:gold_ore", "minecraft:iron_ore", "minecraft:coal_ore", "minecraft:log", "minecraft:leaves", "minecraft:sponge", "minecraft:glass", "minecraft:lapis_ore", "minecraft:lapis_block", "minecraft:dispenser", "minecraft:sandstone", "minecraft:noteblock", "", "minecraft:golden_rail", "minecraft:detector_rail", "minecraft:sticky_piston", "minecraft:web", "minecraft:tallgrass", "minecraft:deadbush", "minecraft:piston", "", "minecraft:wool", "", "minecraft:yellow_flower", "minecraft:red_flower", "minecraft:brown_mushroom", "minecraft:red_mushroom", "minecraft:gold_block", "minecraft:iron_block", "", "minecraft:stone_slab", "minecraft:brick_block", "minecraft:tnt", "minecraft:bookshelf", "minecraft:mossy_cobblestone", "minecraft:obsidian",
"minecraft:torch", "", "minecraft:mob_spawner", "minecraft:oak_stairs", "minecraft:chest", "", "minecraft:diamond_ore", "minecraft:diamond_block", "minecraft:crafting_table", "", "minecraft:farmland", "minecraft:furnace", "", "", "", "minecraft:ladder", "minecraft:rail", "minecraft:stone_stairs", "", "minecraft:lever", "minecraft:stone_pressure_plate", "", "minecraft:wooden_pressure_plate", "minecraft:redstone_ore", "", "", "minecraft:redstone_torch", "minecraft:stone_button", "minecraft:snow_layer", "minecraft:ice", "minecraft:snow", "minecraft:cactus", "minecraft:clay", "", "minecraft:jukebox", "minecraft:fence", "minecraft:pumpkin", "minecraft:netherrack", "minecraft:soul_sand", "minecraft:glowstone", "", "minecraft:lit_pumpkin", "", "", "", "minecraft:stained_glass", "minecraft:trapdoor", "minecraft:monster_egg", "minecraft:stonebrick", "minecraft:brown_mushroom_block", "minecraft:red_mushroom_block", "minecraft:iron_bars", "minecraft:glass_pane", "minecraft:melon_block",
"", "", "minecraft:vine", "minecraft:fence_gate", "minecraft:brick_stairs", "minecraft:stone_brick_stairs", "minecraft:mycelium", "minecraft:waterlily", "minecraft:nether_brick", "minecraft:nether_brick_fence", "minecraft:nether_brick_stairs", "", "minecraft:enchanting_table", "", "", "", "minecraft:end_portal_frame", "minecraft:end_stone", "minecraft:dragon_egg", "minecraft:redstone_lamp", "", "", "minecraft:wooden_slab", "", "minecraft:sandstone_stairs", "minecraft:emerald_ore", "minecraft:ender_chest", "minecraft:tripwire_hook", "", "minecraft:emerald_block", "minecraft:spruce_stairs", "minecraft:birch_stairs", "minecraft:jungle_stairs", "minecraft:command_block", "minecraft:beacon", "minecraft:cobblestone_wall", "", "", "", "minecraft:wooden_button", "", "minecraft:anvil", "minecraft:trapped_chest", "minecraft:light_weighted_pressure_plate", "minecraft:heavy_weighted_pressure_plate", "", "", "minecraft:daylight_detector", "minecraft:redstone_block", "minecraft:quartz_ore",
"minecraft:hopper", "minecraft:quartz_block", "minecraft:quartz_stairs", "minecraft:activator_rail", "minecraft:dropper", "minecraft:stained_hardened_clay", "minecraft:stained_glass_pane", "minecraft:leaves2", "minecraft:log2", "minecraft:acacia_stairs", "minecraft:dark_oak_stairs", "minecraft:slime", "minecraft:barrier", "minecraft:iron_trapdoor", "minecraft:prismarine", "minecraft:sea_lantern", "minecraft:hay_block", "minecraft:carpet", "minecraft:hardened_clay", "minecraft:coal_block", "minecraft:packed_ice", "minecraft:double_plant", "", "", "", "minecraft:red_sandstone", "minecraft:red_sandstone_stairs", "", "minecraft:stone_slab2", "minecraft:spruce_fence_gate", "minecraft:birch_fence_gate", "minecraft:jungle_fence_gate", "minecraft:dark_oak_fence_gate", "minecraft:acacia_fence_gate", "minecraft:spruce_fence", "minecraft:birch_fence", "minecraft:jungle_fence", "minecraft:dark_oak_fence", "minecraft:acacia_fence", "", "", "", "", "", "minecraft:end_rod",
"minecraft:chorus_plant", "minecraft:chorus_flower", "minecraft:purpur_block", "minecraft:purpur_pillar", "minecraft:purpur_stairs", "", "minecraft:purpur_slab", "minecraft:end_bricks", "", "minecraft:grass_path", "", "minecraft:repeating_command_block", "minecraft:chain_command_block", "", "minecraft:magma", "minecraft:nether_wart_block", "minecraft:red_nether_brick", "minecraft:bone_block", "minecraft:structure_void", "minecraft:observer", "minecraft:white_shulker_box", "minecraft:orange_shulker_box", "minecraft:magenta_shulker_box", "minecraft:light_blue_shulker_box", "minecraft:yellow_shulker_box", "minecraft:lime_shulker_box", "minecraft:pink_shulker_box", "minecraft:gray_shulker_box", "minecraft:silver_shulker_box", "minecraft:cyan_shulker_box", "minecraft:purple_shulker_box", "minecraft:blue_shulker_box", "minecraft:brown_shulker_box", "minecraft:green_shulker_box", "minecraft:red_shulker_box", "minecraft:black_shulker_box", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "minecraft:structure_block", "minecraft:iron_shovel", "minecraft:iron_pickaxe", "minecraft:iron_axe", "minecraft:flint_and_steel", "minecraft:apple", "minecraft:bow", "minecraft:arrow", "minecraft:coal", "minecraft:diamond", "minecraft:iron_ingot", "minecraft:gold_ingot", "minecraft:iron_sword", "minecraft:wooden_sword", "minecraft:wooden_shovel", "minecraft:wooden_pickaxe", "minecraft:wooden_axe", "minecraft:stone_sword", "minecraft:stone_shovel", "minecraft:stone_pickaxe", "minecraft:stone_axe", "minecraft:diamond_sword", "minecraft:diamond_shovel", "minecraft:diamond_pickaxe", "minecraft:diamond_axe", "minecraft:stick", "minecraft:bowl", "minecraft:mushroom_stew", "minecraft:golden_sword", "minecraft:golden_shovel", "minecraft:golden_pickaxe", "minecraft:golden_axe", "minecraft:string", "minecraft:feather", "minecraft:gunpowder", "minecraft:wooden_hoe", "minecraft:stone_hoe", "minecraft:iron_hoe", "minecraft:diamond_hoe",
"minecraft:golden_hoe", "minecraft:wheat_seeds", "minecraft:wheat", "minecraft:bread", "minecraft:leather_helmet", "minecraft:leather_chestplate", "minecraft:leather_leggings", "minecraft:leather_boots", "minecraft:chainmail_helmet", "minecraft:chainmail_chestplate", "minecraft:chainmail_leggings", "minecraft:chainmail_boots", "minecraft:iron_helmet", "minecraft:iron_chestplate", "minecraft:iron_leggings", "minecraft:iron_boots", "minecraft:diamond_helmet", "minecraft:diamond_chestplate", "minecraft:diamond_leggings", "minecraft:diamond_boots", "minecraft:golden_helmet", "minecraft:golden_chestplate", "minecraft:golden_leggings", "minecraft:golden_boots", "minecraft:flint", "minecraft:porkchop", "minecraft:cooked_porkchop", "minecraft:painting", "minecraft:golden_apple", "minecraft:sign", "minecraft:wooden_door", "minecraft:bucket", "minecraft:water_bucket", "minecraft:lava_bucket", "minecraft:minecart", "minecraft:saddle", "minecraft:iron_door", "minecraft:redstone",
"minecraft:snowball", "minecraft:boat", "minecraft:leather", "minecraft:milk_bucket", "minecraft:brick", "minecraft:clay_ball", "minecraft:reeds", "minecraft:paper", "minecraft:book", "minecraft:slime_ball", "minecraft:chest_minecart", "minecraft:furnace_minecart", "minecraft:egg", "minecraft:compass", "minecraft:fishing_rod", "minecraft:clock", "minecraft:glowstone_dust", "minecraft:fish", "minecraft:cooked_fish", "minecraft:dye", "minecraft:bone", "minecraft:sugar", "minecraft:cake", "minecraft:bed", "minecraft:repeater", "minecraft:cookie", "minecraft:filled_map", "minecraft:shears", "minecraft:melon", "minecraft:pumpkin_seeds", "minecraft:melon_seeds", "minecraft:beef", "minecraft:cooked_beef", "minecraft:chicken", "minecraft:cooked_chicken", "minecraft:rotten_flesh", "minecraft:ender_pearl", "minecraft:blaze_rod", "minecraft:ghast_tear", "minecraft:gold_nugget", "minecraft:nether_wart", "minecraft:potion", "minecraft:glass_bottle", "minecraft:spider_eye",
"minecraft:fermented_spider_eye", "minecraft:blaze_powder", "minecraft:magma_cream", "minecraft:brewing_stand", "minecraft:cauldron", "minecraft:ender_eye", "minecraft:speckled_melon", "minecraft:spawn_egg", "minecraft:experience_bottle", "minecraft:fire_charge", "minecraft:writable_book", "minecraft:written_book", "minecraft:emerald", "minecraft:item_frame", "minecraft:flower_pot", "minecraft:carrot", "minecraft:potato", "minecraft:baked_potato", "minecraft:poisonous_potato", "minecraft:map", "minecraft:golden_carrot", "minecraft:skull", "minecraft:carrot_on_a_stick", "minecraft:nether_star", "minecraft:pumpkin_pie", "minecraft:fireworks", "minecraft:firework_charge", "minecraft:enchanted_book", "minecraft:comparator", "minecraft:netherbrick", "minecraft:quartz", "minecraft:tnt_minecart", "minecraft:hopper_minecart", "minecraft:prismarine_shard", "minecraft:prismarine_crystals", "minecraft:rabbit", "minecraft:cooked_rabbit", "minecraft:rabbit_stew", "minecraft:rabbit_foot",
"minecraft:rabbit_hide", "minecraft:armor_stand", "minecraft:iron_horse_armor", "minecraft:golden_horse_armor", "minecraft:diamond_horse_armor", "minecraft:lead", "minecraft:name_tag", "minecraft:command_block_minecart", "minecraft:mutton", "minecraft:cooked_mutton", "", "minecraft:end_crystal", "minecraft:spruce_door", "minecraft:birch_door", "minecraft:jungle_door", "minecraft:acacia_door", "minecraft:dark_oak_door", "minecraft:chorus_fruit", "minecraft:chorus_fruit_popped", "minecraft:beetroot", "minecraft:beetroot_seeds", "minecraft:beetroot_soup", "minecraft:dragon_breath", "minecraft:splash_potion", "minecraft:spectral_arrow", "minecraft:tipped_arrow", "minecraft:lingering_potion", "minecraft:shield", "minecraft:elytra", "minecraft:spruce_boat", "minecraft:birch_boat", "minecraft:jungle_boat", "minecraft:acacia_boat", "minecraft:dark_oak_boat", "minecraft:totem", "minecraft:shulker_shell", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "minecraft:record_13", "minecraft:record_cat", "minecraft:record_blocks", "minecraft:record_chirp", "minecraft:record_far", "minecraft:record_mall", "minecraft:record_mellohi", "minecraft:record_stal", "minecraft:record_strad", "minecraft:record_ward", "minecraft:record_11", "minecraft:record_wait" };
item getItemFromName(const char* name) {
for (size_t i = 0; i < (sizeof(nameToIDMap) / sizeof(char*)); i++) {
if (str_eq(nameToIDMap[i], name)) return i;
}
return -1;
}
struct block_material* get_block_material(char* name) {
return hashmap_get(block_materials, name);
}
size_t get_block_count() {
return block_infos->size;
}
void add_block_material(struct block_material* material) {
hashmap_put(block_materials, material->name, material);
}
void add_block_info(block blk, struct block_info* info) {
list_ensure_capacity(block_infos, blk + 1);
block_infos->data[blk] = info;
if (block_infos->size < blk) block_infos->size = blk;
block_infos->count++;
}
struct mempool* block_pool;
void init_materials() {
if (block_pool == NULL) {
block_pool = mempool_new();
}
block_materials = hashmap_new(64, block_pool);
char* json_file = (char*) read_file_fully(block_pool, "materials.json", NULL);
if (json_file == NULL) {
errlog(delog, "Error reading material data: %s\n", strerror(errno));
return;
}
struct json_object* json = NULL;
json_parse(block_pool, &json, json_file);
pprefree(block_pool, json_file);
ITER_LLIST(json->children_list, value) {
struct json_object* material_json = value;
struct block_material* material = pcalloc(block_pool, sizeof(struct block_material));
material->name = str_dup(material_json->name, 0, block_pool);
struct json_object* tmp = json_get(material_json, "flammable");
if (tmp == NULL || (tmp->type != JSON_TRUE && tmp->type != JSON_FALSE)) goto format_error_material;
material->flammable = tmp->type == JSON_TRUE;
tmp = json_get(material_json, "replaceable");
if (tmp == NULL || (tmp->type != JSON_TRUE && tmp->type != JSON_FALSE)) goto format_error_material;
material->replacable = tmp->type == JSON_TRUE;
tmp = json_get(material_json, "requiresnotool");
if (tmp == NULL || (tmp->type != JSON_TRUE && tmp->type != JSON_FALSE)) goto format_error_material;
material->requiresnotool = tmp->type == JSON_TRUE;
tmp = json_get(material_json, "mobility");
if (tmp == NULL || tmp->type != JSON_NUMBER) goto format_error_material;
material->mobility = (uint8_t) tmp->data.number;
tmp = json_get(material_json, "adventure_exempt");
if (tmp == NULL || (tmp->type != JSON_TRUE && tmp->type != JSON_FALSE)) goto format_error_material;
material->adventure_exempt = tmp->type == JSON_TRUE;
tmp = json_get(material_json, "liquid");
if (tmp == NULL || (tmp->type != JSON_TRUE && tmp->type != JSON_FALSE)) goto format_error_material;
material->liquid = tmp->type == JSON_TRUE;
tmp = json_get(material_json, "solid");
if (tmp == NULL || (tmp->type != JSON_TRUE && tmp->type != JSON_FALSE)) goto format_error_material;
material->solid = tmp->type == JSON_TRUE;
tmp = json_get(material_json, "blocksLight");
if (tmp == NULL || (tmp->type != JSON_TRUE && tmp->type != JSON_FALSE)) goto format_error_material;
material->blocksLight = tmp->type == JSON_TRUE;
tmp = json_get(material_json, "blocksMovement");
if (tmp == NULL || (tmp->type != JSON_TRUE && tmp->type != JSON_FALSE)) goto format_error_material;
material->blocksMovement = tmp->type == JSON_TRUE;
tmp = json_get(material_json, "opaque");
if (tmp == NULL || (tmp->type != JSON_TRUE && tmp->type != JSON_FALSE)) goto format_error_material;
material->opaque = tmp->type == JSON_TRUE;
add_block_material(material);
continue;
format_error_material:;
printf("[WARNING] Error Loading Material \"%s\"! Skipped.\n", material_json->name);
ITER_LLIST_END();
}
pfree(json->pool);
}
int block_is_normal_cube(struct block_info* info) {
return info == NULL ? 0 : (info->material->opaque && info->fullCube && !info->canProvidePower);
}
void init_blocks() {
if (block_pool == NULL) {
block_pool = mempool_new();
}
block_infos = list_new(128, block_pool);
char* json_file = (char*) read_file_fully(block_pool, "blocks.json", NULL);
if (json_file == NULL) {
errlog(delog, "Error reading block data: %s\n", strerror(errno));
return;
}
struct json_object* json = NULL;
json_parse(block_pool, &json, json_file);
pprefree(block_pool, json_file);
ITER_LLIST(json->children_list, value) {
struct json_object* block_json = value;
struct block_info* info = pcalloc(block_pool, sizeof(struct block_info));
struct json_object* tmp = json_get(block_json, "id");
if (tmp == NULL || tmp->type != JSON_NUMBER) goto format_error_block;
block id = (block) tmp->data.number;
if (id < 0) goto format_error_block;
struct json_object* colls = json_get(block_json, "collision");
if (colls == NULL || colls->type != JSON_OBJECT) goto format_error_block;
info->boundingBox_count = colls->children_list->size;
info->boundingBoxes = info->boundingBox_count == 0 ? NULL : pmalloc(block_pool, sizeof(struct boundingbox) * info->boundingBox_count);
size_t x = 0;
ITER_LLIST(json->children_list, collision_json) {
struct json_object* coll = collision_json;
struct boundingbox* box = &info->boundingBoxes[x];
tmp = json_get(coll, "minX");
if (tmp == NULL || tmp->type != JSON_NUMBER) goto format_error_block;
box->minX = tmp->data.number;
tmp = json_get(coll, "maxX");
if (tmp == NULL || tmp->type != JSON_NUMBER) goto format_error_block;
box->maxX = tmp->data.number;
tmp = json_get(coll, "minY");
if (tmp == NULL || tmp->type != JSON_NUMBER) goto format_error_block;
box->minY = tmp->data.number;
tmp = json_get(coll, "maxY");
if (tmp == NULL || tmp->type != JSON_NUMBER) goto format_error_block;
box->maxY = tmp->data.number;
tmp = json_get(coll, "minZ");
if (tmp == NULL || tmp->type != JSON_NUMBER) goto format_error_block;
box->minZ = tmp->data.number;
tmp = json_get(coll, "maxZ");
if (tmp == NULL || tmp->type != JSON_NUMBER) goto format_error_block;
box->maxZ = tmp->data.number;
++x;
ITER_LLIST_END();
}
tmp = json_get(block_json, "dropItem");
if (tmp == NULL || tmp->type != JSON_NUMBER) goto format_error_block;
info->drop = (item) tmp->data.number;
tmp = json_get(block_json, "dropDamage");
if (tmp == NULL || tmp->type != JSON_NUMBER) goto format_error_block;
info->drop_damage = (int16_t) tmp->data.number;
tmp = json_get(block_json, "dropAmountMin");
if (tmp == NULL || tmp->type != JSON_NUMBER) goto format_error_block;
info->drop_min = (uint8_t) tmp->data.number;
tmp = json_get(block_json, "dropAmountMax");
if (tmp == NULL || tmp->type != JSON_NUMBER) goto format_error_block;
info->drop_max = (uint8_t) tmp->data.number;
tmp = json_get(block_json, "hardness");
if (tmp == NULL || tmp->type != JSON_NUMBER) goto format_error_block;
info->hardness = (float) tmp->data.number;
tmp = json_get(block_json, "material");
if (tmp == NULL || tmp->type != JSON_STRING) goto format_error_block;
info->material = get_block_material(tmp->data.string);
tmp = json_get(block_json, "slipperiness");
if (tmp == NULL || tmp->type != JSON_NUMBER) goto format_error_block;
info->slipperiness = (float) tmp->data.number;
tmp = json_get(block_json, "isFullCube");
if (tmp == NULL || (tmp->type != JSON_TRUE && tmp->type != JSON_FALSE)) goto format_error_block;
info->fullCube = tmp->type == JSON_TRUE;
tmp = json_get(block_json, "canProvidePower");
if (tmp == NULL || (tmp->type != JSON_TRUE && tmp->type != JSON_FALSE)) goto format_error_block;
info->canProvidePower = tmp->type == JSON_TRUE;
tmp = json_get(block_json, "lightOpacity");
if (tmp == NULL || tmp->type != JSON_NUMBER) goto format_error_block;
info->lightOpacity = (uint8_t) tmp->data.number;
tmp = json_get(block_json, "lightEmission");
if (tmp == NULL || tmp->type != JSON_NUMBER) goto format_error_block;
info->lightEmission = (uint8_t) tmp->data.number;
tmp = json_get(block_json, "resistance");
if (tmp == NULL || tmp->type != JSON_NUMBER) {
info->resistance = info->hardness * 5;
} else {
info->resistance = (float) tmp->data.number * 3;
}
add_block_info(id, info);
continue;
format_error_block: ;
printf("[WARNING] Error Loading Block \"%s\"! Skipped.\n", block_json->name);
ITER_LLIST_END();
}
pfree(json->pool);
struct block_info* tmp = getBlockInfo(BLK_CHEST);
tmp->onBlockInteract = &onBlockInteract_chest;
tmp->onBlockDestroyed = &onBlockDestroyed_chest;
tmp->onBlockPlaced = &onBlockPlaced_chest;
tmp = getBlockInfo(BLK_WORKBENCH);
tmp->onBlockInteract = &onBlockInteract_workbench;
tmp = getBlockInfo(BLK_FURNACE);
tmp->onBlockInteract = &onBlockInteract_furnace;
tmp->onBlockDestroyed = &onBlockDestroyed_furnace;
tmp->onBlockPlaced = &onBlockPlaced_furnace;
tmp = getBlockInfo(BLK_FURNACE_1);
tmp->onBlockInteract = &onBlockInteract_furnace;
tmp->onBlockDestroyed = &onBlockDestroyed_furnace;
tmp->onBlockPlaced = &onBlockPlaced_furnace;
tmp = getBlockInfo(BLK_CHESTTRAP);
tmp->onBlockInteract = &onBlockInteract_chest;
tmp->onBlockDestroyed = &onBlockDestroyed_chest;
tmp->onBlockPlaced = &onBlockPlaced_chest;
tmp = getBlockInfo(BLK_GRAVEL);
tmp->dropItems = &dropItems_gravel;
getBlockInfo(BLK_LEAVES_OAK)->dropItems = &dropItems_leaves;
getBlockInfo(BLK_LEAVES_SPRUCE)->dropItems = &dropItems_leaves;
getBlockInfo(BLK_LEAVES_BIRCH)->dropItems = &dropItems_leaves;
getBlockInfo(BLK_LEAVES_JUNGLE)->dropItems = &dropItems_leaves;
getBlockInfo(BLK_LEAVES_ACACIA)->dropItems = &dropItems_leaves;
getBlockInfo(BLK_LEAVES_BIG_OAK)->dropItems = &dropItems_leaves;
tmp = getBlockInfo(BLK_TALLGRASS_GRASS);
tmp->dropItems = &dropItems_tallgrass;
tmp->canBePlaced = &canBePlaced_requiredirt;
tmp->onBlockUpdate = &onBlockUpdate_checkPlace;
tmp = getBlockInfo(BLK_TALLGRASS_FERN);
tmp->canBePlaced = &canBePlaced_requiredirt;
tmp->onBlockUpdate = &onBlockUpdate_checkPlace;
tmp = getBlockInfo(BLK_TALLGRASS_SHRUB);
tmp->canBePlaced = &canBePlaced_requiresand;
tmp->onBlockUpdate = &onBlockUpdate_checkPlace;
tmp = getBlockInfo(BLK_CACTUS);
tmp->canBePlaced = &canBePlaced_cactus;
tmp->onBlockUpdate = &onBlockUpdate_checkPlace;
tmp = getBlockInfo(BLK_REEDS);
tmp->canBePlaced = &canBePlaced_reeds;
tmp->onBlockUpdate = &onBlockUpdate_checkPlace;
getBlockInfo(BLK_MUSHROOM_2)->dropItems = &dropItems_hugemushroom;
getBlockInfo(BLK_MUSHROOM_3)->dropItems = &dropItems_hugemushroom;
tmp = getBlockInfo(BLK_CROPS);
tmp->dropItems = &dropItems_crops;
tmp->canBePlaced = &canBePlaced_requirefarmland;
tmp->onBlockUpdate = &onBlockUpdate_checkPlace;
tmp->randomTick = &randomTick_crops;
tmp = getBlockInfo(BLK_PUMPKINSTEM);
tmp->dropItems = &dropItems_crops;
tmp->canBePlaced = &canBePlaced_requirefarmland;
tmp->onBlockUpdate = &onBlockUpdate_checkPlace;
tmp = getBlockInfo(BLK_PUMPKINSTEM_1);
tmp->dropItems = &dropItems_crops;
tmp->canBePlaced = &canBePlaced_requirefarmland;
tmp->onBlockUpdate = &onBlockUpdate_checkPlace;
tmp = getBlockInfo(BLK_CARROTS);
tmp->dropItems = &dropItems_crops;
tmp->canBePlaced = &canBePlaced_requirefarmland;
tmp->onBlockUpdate = &onBlockUpdate_checkPlace;
tmp = getBlockInfo(BLK_POTATOES);
tmp->dropItems = &dropItems_crops;
tmp->canBePlaced = &canBePlaced_requirefarmland;
tmp->onBlockUpdate = &onBlockUpdate_checkPlace;
tmp = getBlockInfo(BLK_NETHERSTALK);
tmp->dropItems = &dropItems_crops;
tmp->canBePlaced = &canBePlaced_requiresoulsand;
tmp->onBlockUpdate = &onBlockUpdate_checkPlace;
tmp = getBlockInfo(BLK_BEETROOTS);
tmp->dropItems = &dropItems_crops;
tmp->canBePlaced = &canBePlaced_requirefarmland;
tmp->onBlockUpdate = &onBlockUpdate_checkPlace;
tmp = getBlockInfo(BLK_COCOA);
tmp->dropItems = &dropItems_crops;
//tmp->onBlockUpdate = &onBlockUpdate_cocoa;
for (block b = BLK_FLOWER1_DANDELION; b <= BLK_FLOWER2_OXEYEDAISY; b++) {
tmp = getBlockInfo(b);
tmp->canBePlaced = &canBePlaced_requiredirt;
tmp->onBlockUpdate = &onBlockUpdate_checkPlace;
}
for (block b = BLK_SAPLING_OAK; b <= BLK_SAPLING_OAK_5; b++) {
tmp = getBlockInfo(b);
tmp->canBePlaced = &canBePlaced_sapling;
tmp->onBlockUpdate = &onBlockUpdate_checkPlace;
}
for (block b = BLK_DOUBLEPLANT_SUNFLOWER; b <= BLK_DOUBLEPLANT_SUNFLOWER_8; b++) {
tmp = getBlockInfo(b);
tmp->canBePlaced = &canBePlaced_doubleplant;
tmp->onBlockUpdate = &onBlockUpdate_checkPlace;
}
for (block b = BLK_LOG_OAK; b < BLK_LOG_OAK_8; b++)
getBlockInfo(b)->onBlockPlacedPlayer = &onBlockPlacedPlayer_log;
for (block b = BLK_LOG_ACACIA_1; b < BLK_LOG_OAK_14; b++)
getBlockInfo(b)->onBlockPlacedPlayer = &onBlockPlacedPlayer_log;
getBlockInfo(BLK_GRASS)->randomTick = &randomTick_grass;
tmp = getBlockInfo(BLK_VINE);
tmp->onBlockPlacedPlayer = &onBlockPlacedPlayer_vine;
tmp->onBlockUpdate = &onBlockUpdate_vine;
tmp->canBePlaced = &canBePlaced_vine;
tmp->randomTick = &randomTick_vine;
tmp = getBlockInfo(BLK_LADDER);
tmp->onBlockPlacedPlayer = &onBlockPlacedPlayer_ladder;
tmp->canBePlaced = &canBePlaced_ladder;
tmp->onBlockUpdate = &onBlockUpdate_checkPlace;
for (block b = BLK_FENCEGATE; b < BLK_FENCEGATE + 16; b++)
getBlockInfo(b)->onBlockInteract = &onBlockInteract_fencegate;
getBlockInfo(BLK_WATER)->scheduledTick = &scheduledTick_flowingfluid;
getBlockInfo(BLK_WATER_1)->onBlockUpdate = &onBlockUpdate_staticfluid;
tmp = getBlockInfo(BLK_LAVA);
tmp->scheduledTick = &scheduledTick_flowingfluid;
tmp->onBlockUpdate = &onBlockUpdate_flowinglava;
tmp = getBlockInfo(BLK_LAVA_1);
tmp->onBlockUpdate = &onBlockUpdate_staticfluid;
tmp->randomTick = &randomTick_staticlava;
tmp = getBlockInfo(BLK_FARMLAND);
tmp->randomTick = &randomTick_farmland;
getBlockInfo(BLK_SAND)->onBlockUpdate = &onBlockUpdate_falling;
getBlockInfo(BLK_SAND)->scheduledTick = &scheduledTick_falling;
getBlockInfo(BLK_GRAVEL)->onBlockUpdate = &onBlockUpdate_falling;
getBlockInfo(BLK_GRAVEL)->scheduledTick = &scheduledTick_falling;
getBlockInfo(BLK_ANVIL)->onBlockUpdate = &onBlockUpdate_falling;
getBlockInfo(BLK_ANVIL)->scheduledTick = &scheduledTick_falling;
getBlockInfo(BLK_SPONGE_DRY)->onBlockUpdate = &onBlockUpdate_sponge;
for (block b = BLK_DOOROAK; b < BLK_DOOROAK + 16; b++) {
tmp = getBlockInfo(b);
tmp->onBlockInteract = &onBlockInteract_woodendoor;
tmp->canBePlaced = &canBePlaced_door;
tmp->onBlockUpdate = &onBlockUpdate_door;
}
for (block b = BLK_DOORSPRUCE; b < BLK_DOORSPRUCE + 16; b++) {
tmp = getBlockInfo(b);
tmp->onBlockInteract = &onBlockInteract_woodendoor;
tmp->canBePlaced = &canBePlaced_door;
tmp->onBlockUpdate = &onBlockUpdate_door;
}
for (block b = BLK_DOORBIRCH; b < BLK_DOORBIRCH + 16; b++) {
tmp = getBlockInfo(b);
tmp->onBlockInteract = &onBlockInteract_woodendoor;
tmp->canBePlaced = &canBePlaced_door;
tmp->onBlockUpdate = &onBlockUpdate_door;
}
for (block b = BLK_DOORJUNGLE; b < BLK_DOORJUNGLE + 16; b++) {
tmp = getBlockInfo(b);
tmp->onBlockInteract = &onBlockInteract_woodendoor;
tmp->canBePlaced = &canBePlaced_door;
tmp->onBlockUpdate = &onBlockUpdate_door;
}
for (block b = BLK_DOORACACIA; b < BLK_DOORACACIA + 16; b++) {
tmp = getBlockInfo(b);
tmp->onBlockInteract = &onBlockInteract_woodendoor;
tmp->canBePlaced = &canBePlaced_door;
tmp->onBlockUpdate = &onBlockUpdate_door;
}
for (block b = BLK_DOORDARKOAK; b < BLK_DOORDARKOAK + 16; b++) {
tmp = getBlockInfo(b);
tmp->onBlockInteract = &onBlockInteract_woodendoor;
tmp->canBePlaced = &canBePlaced_door;
tmp->onBlockUpdate = &onBlockUpdate_door;
}
for (block b = BLK_DOORIRON; b < BLK_DOORIRON + 16; b++) {
tmp = getBlockInfo(b);
tmp->canBePlaced = &canBePlaced_door;
tmp->onBlockUpdate = &onBlockUpdate_door;
}
tmp = getBlockInfo(BLK_BED);
tmp->canBePlaced = &canBePlaced_bed;
tmp->onBlockUpdate = &onBlockUpdate_bed;
getBlockInfo(BLK_REDSTONEDUST)->onBlockUpdate = &onBlockUpdate_redstonedust;
getBlockInfo(BLK_REDSTONEDUST)->canBePlaced = &canBePlaced_redstone;
getBlockInfo(BLK_TORCH)->canBePlaced = &canBePlaced_torch;
getBlockInfo(BLK_TORCH)->onBlockUpdate = &onBlockUpdate_checkPlace;
getBlockInfo(BLK_NOTGATE)->canBePlaced = &canBePlaced_torch;
getBlockInfo(BLK_NOTGATE_1)->canBePlaced = &canBePlaced_torch;
getBlockInfo(BLK_NOTGATE)->onBlockUpdate = &onBlockUpdate_redstonetorch;
getBlockInfo(BLK_NOTGATE_1)->onBlockUpdate = &onBlockUpdate_redstonetorch;
getBlockInfo(BLK_DIODE)->onBlockUpdate = &onBlockUpdate_repeater;
getBlockInfo(BLK_DIODE_1)->onBlockUpdate = &onBlockUpdate_repeater;
getBlockInfo(BLK_DIODE)->scheduledTick = &scheduledTick_repeater;
getBlockInfo(BLK_DIODE_1)->scheduledTick = &scheduledTick_repeater;
getBlockInfo(BLK_DIODE)->onBlockInteract = &onBlockInteract_repeater;
getBlockInfo(BLK_DIODE_1)->onBlockInteract = &onBlockInteract_repeater;
getBlockInfo(BLK_DIODE)->canBePlaced = &canBePlaced_redstone;
getBlockInfo(BLK_DIODE_1)->canBePlaced = &canBePlaced_redstone;
getBlockInfo(BLK_REDSTONELIGHT)->onBlockUpdate = &onBlockUpdate_lamp;
getBlockInfo(BLK_REDSTONELIGHT_1)->onBlockUpdate = &onBlockUpdate_lamp;
getBlockInfo(BLK_LEVER)->onBlockInteract = &onBlockInteract_lever;
getBlockInfo(BLK_LEVER)->onBlockPlacedPlayer = &onBlockPlacedPlayer_lever;
getBlockInfo(BLK_BUTTON)->onBlockPlacedPlayer = &onBlockPlacedPlayer_button;
getBlockInfo(BLK_BUTTON)->onBlockInteract = &onBlockInteract_button;
getBlockInfo(BLK_BUTTON)->scheduledTick = &scheduledTick_button;
getBlockInfo(BLK_BUTTON_1)->onBlockPlacedPlayer = &onBlockPlacedPlayer_button;
getBlockInfo(BLK_BUTTON_1)->onBlockInteract = &onBlockInteract_button;
getBlockInfo(BLK_BUTTON_1)->scheduledTick = &scheduledTick_button;
getBlockInfo(BLK_TNT)->onBlockUpdate = &onBlockUpdate_tnt;
getBlockInfo(BLK_TRAPDOOR)->onBlockInteract = &onBlockInteract_trapdoor;
getBlockInfo(BLK_TRAPDOOR)->onBlockUpdate = &onBlockUpdate_trapdoor;
getBlockInfo(BLK_IRONTRAPDOOR)->onBlockUpdate = &onBlockUpdate_trapdoor;
getBlockInfo(BLK_TRAPDOOR)->canBePlaced = &canBePlaced_trapdoor;
getBlockInfo(BLK_IRONTRAPDOOR)->canBePlaced = &canBePlaced_trapdoor;
getBlockInfo(BLK_TRAPDOOR)->onBlockPlacedPlayer = &onBlockPlacedPlayer_trapdoor;
getBlockInfo(BLK_IRONTRAPDOOR)->onBlockPlacedPlayer = &onBlockPlacedPlayer_trapdoor;
//TODO: redstone torch burnout
}