basin/src/item.c

870 lines
41 KiB
C

#include <basin/item.h>
#include <basin/block.h>
#include <basin/nbt.h>
#include <basin/game.h>
#include <basin/player.h>
#include <basin/item.h>
#include <avuna/json.h>
#include <avuna/string.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <math.h>
int onItemInteract_bed(struct world* world, struct player* player, uint8_t slot_index, struct slot* slot, int32_t x, int32_t y, int32_t z, uint8_t face) {
if (face != YP) return 0;
if (!getBlockInfo(world_get_block(world, x, y, z))->material->replacable) offsetCoordByFace(&x, &y, &z, face);
uint32_t h = (uint32_t) floor(player->entity->yaw / 90. + .5) & 3;
uint8_t rf = 0;
if (h == 0) rf = SOUTH;
else if (h == 1) rf = WEST;
else if (h == 2) rf = NORTH;
else if (h == 3) rf = EAST;
int32_t x2 = x;
int32_t y2 = y;
int32_t z2 = z;
offsetCoordByFace(&x2, &y2, &z2, rf);
block pre = world_get_block(world, x, y, z);
block pre2 = world_get_block(world, x2, y2, z2);
block b = BLK_BED | h;
block b2 = b;
b2 |= 0b1000;
if (player_can_place_block(player, b, x, y, z, face) && !world_set_block_noupdate(player->world, b, x, y, z)) {
if (!player_can_place_block(player, b2, x2, y2, z2, face) || world_set_block(player->world, b2, x2, y2, z2)) {
world_set_block(player->world, pre, x, y, z);
world_set_block(player->world, pre2, x2, y2, z2);
inventory_set_slot(player, player->inventory, 36 + player->currentItem, slot, 1);
return 0;
} else if (player->gamemode != 1) {
if (--slot->count <= 0) {
slot = NULL;
}
inventory_set_slot(player, player->inventory, 36 + player->currentItem, slot, 1);
}
world_update_block_guess(world, NULL, x, y, z);
world_update_block_guess(world, NULL, x + 1, y, z);
world_update_block_guess(world, NULL, x - 1, y, z);
world_update_block_guess(world, NULL, x, y + 1, z);
world_update_block_guess(world, NULL, x, y - 1, z);
world_update_block_guess(world, NULL, x, y, z + 1);
world_update_block_guess(world, NULL, x, y, z - 1);
} else {
world_set_block(player->world, pre, x, y, z);
world_set_block(player->world, pre2, x2, y2, z2);
inventory_set_slot(player, player->inventory, 36 + player->currentItem, slot, 1);
}
return 0;
}
int onItemInteract_painting(struct world* world, struct player* player, uint8_t slot_index, struct slot* slot, int32_t x, int32_t y, int32_t z, uint8_t face) {
int32_t ox = x;
int32_t oy = y;
int32_t oz = z;
offsetCoordByFace(&ox, &oy, &oz, face);
if (face != YP && face != YN && player_can_place_block(player, ITM_PAINTING, ox, oy, oz, face)) {
struct entity* ent = entity_new(nextEntityID++, (double) ox, (double) oy, (double) oz, ENT_PAINTING, 0., 0.);
if (face == NORTH) ent->data.painting.direction = 2;
if (face == SOUTH) ent->data.painting.direction = 0;
if (face == WEST) ent->data.painting.direction = 1;
if (face == EAST) ent->data.painting.direction = 3;
block b = world_get_block(world, x, y, z);
if (!block_is_normal_cube(getBlockInfo(b))) return 0;
b = world_get_block(world, ox, oy, oz);
if (b != 0) return 0;
int32_t p = -1;
for (int32_t i = 0; i < 50; i++) {
p = rand() % 26;
if (p < 7) {
}
if (p >= 7) {
b = world_get_block(world, x + (face == ZP ? 1 : (face == ZN ? -1 : 0)), y, z + (face == XP ? 1 : (face == XN ? -1 : 0)));
if (!block_is_normal_cube(getBlockInfo(b))) continue;
b = world_get_block(world, ox + (face == ZP ? 1 : (face == ZN ? -1 : 0)), oy, oz + (face == XP ? 1 : (face == XN ? -1 : 0)));
if (b != 0) return 0;
}
if (p >= 12) {
b = world_get_block(world, x, y + 1, z);
if (!block_is_normal_cube(getBlockInfo(b))) continue;
b = world_get_block(world, ox, oy + 1, oz);
if (b != 0) return 0;
}
if (p >= 14) {
b = world_get_block(world, x + (face == ZP ? 1 : (face == ZN ? -1 : 0)), y + 1, z + (face == XP ? 1 : (face == XN ? -1 : 0)));
if (!block_is_normal_cube(getBlockInfo(b))) continue;
b = world_get_block(world, ox + (face == ZP ? 1 : (face == ZN ? -1 : 0)), oy + 1, oz + (face == XP ? 1 : (face == XN ? -1 : 0)));
if (b != 0) return 0;
}
if (p >= 20) {
b = world_get_block(world, x + (face == ZP ? 1 : (face == ZN ? -1 : 0)) * 2, y, z + (face == XP ? 1 : (face == XN ? -1 : 0)) * 2);
if (!block_is_normal_cube(getBlockInfo(b))) continue;
b = world_get_block(world, x - (face == ZP ? 1 : (face == ZN ? -1 : 0)), y, z - (face == XP ? 1 : (face == XN ? -1 : 0)));
if (!block_is_normal_cube(getBlockInfo(b))) continue;
b = world_get_block(world, x + (face == ZP ? 1 : (face == ZN ? -1 : 0)) * 2, y + 1, z + (face == XP ? 1 : (face == XN ? -1 : 0))) * 2;
if (!block_is_normal_cube(getBlockInfo(b))) continue;
b = world_get_block(world, x - (face == ZP ? 1 : (face == ZN ? -1 : 0)), y + 1, z - (face == XP ? 1 : (face == XN ? -1 : 0)));
if (!block_is_normal_cube(getBlockInfo(b))) continue;
b = world_get_block(world, ox + (face == ZP ? 1 : (face == ZN ? -1 : 0)) * 2, oy, oz + (face == XP ? 1 : (face == XN ? -1 : 0)) * 2);
if (b != 0) return 0;
b = world_get_block(world, ox - (face == ZP ? 1 : (face == ZN ? -1 : 0)), oy, oz - (face == XP ? 1 : (face == XN ? -1 : 0)));
if (b != 0) return 0;
b = world_get_block(world, ox + (face == ZP ? 1 : (face == ZN ? -1 : 0)) * 2, oy + 1, oz + (face == XP ? 1 : (face == XN ? -1 : 0))) * 2;
if (b != 0) return 0;
b = world_get_block(world, ox - (face == ZP ? 1 : (face == ZN ? -1 : 0)), oy + 1, oz - (face == XP ? 1 : (face == XN ? -1 : 0)));
if (b != 0) return 0;
}
if (p >= 21) {
for (int32_t i = 2; i <= (p < 24 ? 2 : 3); i++) {
b = world_get_block(world, x + (face == ZP ? 1 : (face == ZN ? -1 : 0)) * 2, y + i, z + (face == XP ? 1 : (face == XN ? -1 : 0)) * 2);
if (!block_is_normal_cube(getBlockInfo(b))) continue;
b = world_get_block(world, x + (face == ZP ? 1 : (face == ZN ? -1 : 0)), y + i, z + (face == XP ? 1 : (face == XN ? -1 : 0)));
if (!block_is_normal_cube(getBlockInfo(b))) continue;
b = world_get_block(world, x * 2, y + i, z);
if (!block_is_normal_cube(getBlockInfo(b))) continue;
b = world_get_block(world, x - (face == ZP ? 1 : (face == ZN ? -1 : 0)), y + i, z - (face == XP ? 1 : (face == XN ? -1 : 0)));
if (!block_is_normal_cube(getBlockInfo(b))) continue;
b = world_get_block(world, ox + (face == ZP ? 1 : (face == ZN ? -1 : 0)) * 2, oy + i, oz + (face == XP ? 1 : (face == XN ? -1 : 0)) * 2);
if (b != 0) return 0;
b = world_get_block(world, ox + (face == ZP ? 1 : (face == ZN ? -1 : 0)), oy + i, oz + (face == XP ? 1 : (face == XN ? -1 : 0)));
if (b != 0) return 0;
b = world_get_block(world, ox, oy + i, oz);
if (b != 0) return 0;
b = world_get_block(world, ox - (face == ZP ? 1 : (face == ZN ? -1 : 0)), oy + i, oz - (face == XP ? 1 : (face == XN ? -1 : 0)));
if (b != 0) return 0;
}
}
break;
}
if (p < 0) {
freeEntity(ent);
return 0;
}
if (p == 0) ent->data.painting.title = "Kebab";
else if (p == 1) ent->data.painting.title = "Aztec";
else if (p == 2) ent->data.painting.title = "Alban";
else if (p == 3) ent->data.painting.title = "Aztec2";
else if (p == 4) ent->data.painting.title = "Bomb";
else if (p == 5) ent->data.painting.title = "Plant";
else if (p == 6) ent->data.painting.title = "Wasteland";
else if (p == 7) ent->data.painting.title = "Pool";
else if (p == 8) ent->data.painting.title = "Courbet";
else if (p == 9) ent->data.painting.title = "Sea";
else if (p == 10) ent->data.painting.title = "Sunset";
else if (p == 11) ent->data.painting.title = "Creebet";
else if (p == 12) ent->data.painting.title = "Wanderer";
else if (p == 13) ent->data.painting.title = "Graham";
else if (p == 14) ent->data.painting.title = "Match";
else if (p == 15) ent->data.painting.title = "Bust";
else if (p == 16) ent->data.painting.title = "Stage";
else if (p == 17) ent->data.painting.title = "Void";
else if (p == 18) ent->data.painting.title = "SkullAndRoses";
else if (p == 19) ent->data.painting.title = "Wither";
else if (p == 20) ent->data.painting.title = "Fighters";
else if (p == 21) ent->data.painting.title = "Pointer";
else if (p == 22) ent->data.painting.title = "Pigscene";
else if (p == 23) ent->data.painting.title = "BurningSkull";
else if (p == 24) ent->data.painting.title = "Skeleton";
else if (p == 25) ent->data.painting.title = "DonkeyKong";
world_spawn_entity(world, ent);
if (player->gamemode != 1) inventory_set_slot(player, player->inventory, 36 + player->currentItem, NULL, 1, 1);
}
return 0;
}
int onItemInteract_minecart(struct world* world, struct player* player, uint8_t slot_index, struct slot* slot, int32_t x, int32_t y, int32_t z, uint8_t face) {
block b = world_get_block(world, x, y, z);
if (b >> 4 != BLK_RAIL >> 4 && b >> 4 != BLK_ACTIVATORRAIL >> 4 && b >> 4 != BLK_GOLDENRAIL >> 4 && b >> 4 != BLK_DETECTORRAIL >> 4) return 0;
double dy = 0.;
if (b & 0b0110) dy += .5;
uint16_t et = ENT_MINECARTRIDEABLE;
if (slot->item == ITM_MINECARTCHEST) et = ENT_MINECARTCHEST;
else if (slot->item == ITM_MINECARTFURNACE) et = ENT_MINECARTFURNACE;
else if (slot->item == ITM_MINECARTTNT) et = ENT_MINECARTTNT;
else if (slot->item == ITM_MINECARTHOPPER) et = ENT_MINECARTHOPPER;
else if (slot->item == ITM_MINECARTCOMMANDBLOCK) et = ENT_MINECARTCOMMANDBLOCK;
struct entity* ent = entity_new(nextEntityID++, (double) x + .5, (double) y + dy, (double) z + .5, et, 0., 0.);
world_spawn_entity(world, ent);
if (player->gamemode != 1) inventory_set_slot(player, player->inventory, 36 + player->currentItem, NULL, 1, 1);
return 0;
}
int onItemInteract_door(struct world* world, struct player* player, uint8_t slot_index, struct slot* slot, int32_t x, int32_t y, int32_t z, uint8_t face, float cx, float cy, float cz) {
if (face != YP) return 0;
if (!getBlockInfo(world_get_block(world, x, y, z))->material->replacable) offsetCoordByFace(&x, &y, &z, face);
block pre = world_get_block(world, x, y, z);
block pre2 = world_get_block(world, x, y + 1, z);
block b = BLK_DOOROAK;
if (slot->item == ITM_DOORSPRUCE) b = BLK_DOORSPRUCE;
else if (slot->item == ITM_DOORBIRCH) b = BLK_DOORBIRCH;
else if (slot->item == ITM_DOORJUNGLE) b = BLK_DOORJUNGLE;
else if (slot->item == ITM_DOORACACIA) b = BLK_DOORACACIA;
else if (slot->item == ITM_DOORDARKOAK) b = BLK_DOORDARKOAK;
else if (slot->item == ITM_DOORIRON) b = BLK_DOORIRON;
block b2 = b;
uint32_t h = (uint32_t) floor(player->entity->yaw / 90. + .5) & 3;
if (h == 0) b |= 1;
else if (h == 1) b |= 2;
else if (h == 2) b |= 3; // should fold into "f" below
uint8_t f = getFaceFromPlayer(player);
if ((f == XN && cz < .5) || (f == XP && cz > .5) || (f == ZN && cx > .5) || (f == ZP && cx < .5)) {
b2 |= 0x01;
}
b2 |= 0b1000;
if (player_can_place_block(player, b, x, y, z, face) && !world_set_block_noupdate(player->world, b, x, y, z)) {
if (!player_can_place_block(player, b2, x, y + 1, z, face) || world_set_block(player->world, b2, x, y + 1, z)) {
world_set_block(player->world, pre, x, y, z);
world_set_block(player->world, pre2, x, y + 1, z);
inventory_set_slot(player, player->inventory, 36 + player->currentItem, slot, 1);
return 0;
} else if (player->gamemode != 1) {
if (--slot->count <= 0) {
slot = NULL;
}
inventory_set_slot(player, player->inventory, 36 + player->currentItem, slot, 1);
}
world_update_block_guess(world, NULL, x, y, z);
world_update_block_guess(world, NULL, x + 1, y, z);
world_update_block_guess(world, NULL, x - 1, y, z);
world_update_block_guess(world, NULL, x, y + 1, z);
world_update_block_guess(world, NULL, x, y - 1, z);
world_update_block_guess(world, NULL, x, y, z + 1);
world_update_block_guess(world, NULL, x, y, z - 1);
} else {
world_set_block(player->world, pre, x, y, z);
world_set_block(player->world, pre2, x, y + 1, z);
inventory_set_slot(player, player->inventory, 36 + player->currentItem, slot, 1);
}
return 0;
}
int onItemInteract_itemblock(struct world* world, struct player* player, uint8_t slot_index, struct slot* slot, int32_t x, int32_t y, int32_t z, uint8_t face) {
if (!getBlockInfo(world_get_block(world, x, y, z))->material->replacable) offsetCoordByFace(&x, &y, &z, face);
block pre = world_get_block(world, x, y, z);
uint8_t f = 0;
uint32_t h = (uint32_t) floor(player->entity->yaw / 90. + .5) & 3;
if (h == 0) f = SOUTH;
else if (h == 1) f = WEST;
else if (h == 2) f = NORTH;
else if (h == 3) f = EAST;
block b = pre;
if (slot->item == ITM_STRING) {
b = BLK_TRIPWIRE;
} else if (slot->item == ITM_REDSTONE) {
b = BLK_REDSTONEDUST;
} else if (slot->item == ITM_CAKE) {
b = BLK_CAKE;
} else if (slot->item == ITM_DIODE) {
b = BLK_DIODE;
if (f == SOUTH) {
b |= 2;
} else if (f == WEST) {
b |= 3;
} else if (f == NORTH) {
b |= 0;
} else if (f == EAST) {
b |= 1;
}
} else if (slot->item == ITM_BREWINGSTAND) {
b = BLK_BREWINGSTAND;
} else if (slot->item == ITM_CAULDRON) {
b = BLK_CAULDRON;
} else if (slot->item == ITM_FLOWERPOT) {
b = BLK_FLOWERPOT;
} else if (slot->item == ITM_COMPARATOR) {
b = BLK_COMPARATOR;
}
if (player_can_place_block(player, b, x, y, z, face)) {
if (world_set_block(player->world, b, x, y, z)) {
world_set_block(player->world, pre, x, y, z);
inventory_set_slot(player, player->inventory, 36 + player->currentItem, slot, 1);
} else if (player->gamemode != 1) {
if (--slot->count <= 0) {
slot = NULL;
}
inventory_set_slot(player, player->inventory, 36 + player->currentItem, slot, 1);
}
} else {
world_set_block(player->world, pre, x, y, z);
inventory_set_slot(player, player->inventory, 36 + player->currentItem, slot, 1);
}
return 0;
}
int onItemBreakBlock_tool(struct world* world, struct player* player, uint8_t slot_index, struct slot* slot, int32_t x, int32_t y, int32_t z) {
if (slot == NULL) return 0;
struct item_info* ii = getItemInfo(slot->item);
if (ii == NULL) return 0;
if (player->gamemode != 1 && ++slot->damage >= ii->maxDamage) {
slot = NULL;
}
inventory_set_slot(player, player->inventory, slot_index, slot, 1);
return 0;
}
float onItemAttacked_tool(struct world* world, struct player* player, uint8_t slot_index, struct slot* slot, struct entity* entity) {
if (slot == NULL) return 1.;
struct item_info* ii = getItemInfo(slot->item);
if (ii == NULL) return 1.;
if (player->gamemode != 1 && ++slot->damage >= ii->maxDamage) {
slot = NULL;
}
inventory_set_slot(player, player->inventory, slot_index, slot, 1);
return ii->damage;
}
void offsetCoordByFace(int32_t* x, int32_t* y, int32_t* z, uint8_t face) {
if (face == YN) (*y)--;
else if (face == YP) (*y)++;
else if (face == ZN) (*z)--;
else if (face == ZP) (*z)++;
else if (face == XN) (*x)--;
else if (face == XP) (*x)++;
}
int onItemInteract_flintandsteel(struct world* world, struct player* player, uint8_t slot_index, struct slot* slot, int32_t x, int32_t y, int32_t z, uint8_t face) {
if (slot == NULL) return 0;
offsetCoordByFace(&x, &y, &z, face);
if (world_get_block(world, x, y, z) != 0) return 0;
if (!player_can_place_block(player, BLK_FIRE, x, y, z, face)) return 0;
struct item_info* ii = getItemInfo(slot->item);
if (ii == NULL) return 0;
if (player->gamemode != 1 && ++slot->damage >= ii->maxDamage) {
slot = NULL;
}
inventory_set_slot(player, player->inventory, slot_index, slot, 1);
world_set_block(world, BLK_FIRE, x, y, z);
return 0;
}
int onItemInteract_spawnegg(struct world* world, struct player* player, uint8_t slot_index, struct slot* slot, int32_t x, int32_t y, int32_t z, uint8_t face) {
if (slot == NULL || slot->nbt == NULL) return 0;
//TODO: mob spawners
if (getBlockInfo(world_get_block(world, x, y, z))->boundingBox_count > 0) offsetCoordByFace(&x, &y, &z, face);
//if (world_get_block(world, x, y, z) != 0) return 0;
struct item_info* ii = getItemInfo(slot->item);
if (ii == NULL) return 0;
struct nbt_tag* et = nbt_get(slot->nbt, "EntityTag");
if (et == NULL) return 0;
struct nbt_tag* tmp = nbt_get(et, "id");
if (tmp == NULL || tmp->id != NBT_TAG_STRING) return 0;
uint32_t etx = getIDFromEntityDataName(tmp->data.nbt_string);
struct entity* ent = entity_new(nextEntityID++, (float) x + .5, (float) y, (float) z + .5, etx, 0., 0.);
world_spawn_entity(world, ent);
if (player->gamemode != 1 && --slot->count <= 0) {
slot = NULL;
}
inventory_set_slot(player, player->inventory, slot_index, slot, 1);
return 0;
}
int onItemInteract_reeds(struct world* world, struct player* player, uint8_t slot_index, struct slot* slot, int32_t x, int32_t y, int32_t z, uint8_t face) {
if (slot == NULL) return 0;
if (!getBlockInfo(world_get_block(world, x, y, z))->material->replacable) offsetCoordByFace(&x, &y, &z, face);
if (!player_can_place_block(player, BLK_REEDS, x, y, z, face)) return 0;
if (world_get_block(world, x, y, z) != 0) return 0;
if (!canBePlaced_reeds(world, BLK_REEDS, x, y, z)) return 0;
struct item_info* ii = getItemInfo(slot->item);
if (ii == NULL) return 0;
if (player->gamemode != 1 && --slot->count <= 0) {
slot = NULL;
}
inventory_set_slot(player, player->inventory, slot_index, slot, 1);
world_set_block(world, BLK_REEDS, x, y, z);
return 0;
}
int onItemInteract_bucket(struct world* world, struct player* player, uint8_t slot_index, struct slot* slot, int32_t x, int32_t y, int32_t z, uint8_t face) {
if (slot == NULL) return 0;
if (!getBlockInfo(world_get_block(world, x, y, z))->material->replacable) offsetCoordByFace(&x, &y, &z, face);
block b = world_get_block(world, x, y, z);
struct block_info* bi = getBlockInfo(b);
if (bi == NULL) return 0;
uint16_t ba = b >> 4;
if (slot->item == ITM_BUCKETWATER) {
if (b != 0 && !bi->material->replacable && ba != BLK_WATER >> 4 && ba != BLK_WATER_1 >> 4) return 0;
if (!player_can_place_block(player, BLK_WATER_1, x, y, z, face)) return 0;
world_set_block(world, BLK_WATER_1, x, y, z);
if (player->gamemode != 1) {
slot->item = ITM_BUCKET;
inventory_set_slot(player, player->inventory, slot_index, slot, 1);
}
} else if (slot->item == ITM_BUCKETLAVA) {
if (b != 0 && !bi->material->replacable && ba != BLK_LAVA >> 4 && ba != BLK_LAVA_1 >> 4) return 0;
if (!player_can_place_block(player, BLK_LAVA_1, x, y, z, face)) return 0;
world_set_block(world, BLK_LAVA_1, x, y, z);
if (player->gamemode != 1) {
slot->item = ITM_BUCKET;
inventory_set_slot(player, player->inventory, slot_index, slot, 1);
}
} else if (slot->item == ITM_BUCKET) {
if ((b & 0x0f) != 0) return 0;
if (ba == BLK_WATER >> 4 || ba == BLK_WATER_1 >> 4) {
world_set_block(world, 0, x, y, z);
if (player->gamemode != 1) {
slot->item = ITM_BUCKETWATER;
inventory_set_slot(player, player->inventory, slot_index, slot, 1);
}
} else if (ba == BLK_LAVA >> 4 || ba == BLK_LAVA_1 >> 4) {
world_set_block(world, 0, x, y, z);
if (player->gamemode != 1) {
slot->item = ITM_BUCKETLAVA;
inventory_set_slot(player, player->inventory, slot_index, slot, 1);
}
}
}
return 0;
}
int onItemInteract_bonemeal(struct world* world, struct player* player, uint8_t slot_index, struct slot* slot, int32_t x, int32_t y, int32_t z, uint8_t face) {
if (slot == NULL) return 0;
if (!player_can_place_block(player, ITM_DYEPOWDER_BLACK, x, y, z, face)) return 0;
block b = world_get_block(world, x, y, z);
uint16_t ba = b >> 4;
if (ba == (BLK_CROPS >> 4) || (ba == BLK_PUMPKINSTEM >> 4) || (ba == BLK_PUMPKINSTEM_1 >> 4) || (ba == BLK_CARROTS >> 4) || (ba == BLK_POTATOES >> 4) || (ba == BLK_BEETROOTS >> 4) || (ba == BLK_COCOA >> 4)) {
uint16_t grow = 2 + rand() % 3;
uint16_t maxAge = 7;
if (b == BLK_BEETROOTS) maxAge = 3;
uint16_t age = b & 0x0f;
age += grow;
if (age > maxAge) age = maxAge;
world_set_block(world, (ba << 4) | age, x, y, z);
} else if (ba == (BLK_SAPLING_OAK >> 4)) {
randomTick_sapling(world, world_get_chunk(world, x >> 4, z >> 4), b, x, y, z);
} else if (b == BLK_GRASS || b == BLK_DIRT) {
for (int i = 0; i < 128; i++) {
int j = 0;
int32_t bx = x;
uint8_t by = y + 1;
int32_t bz = z;
while (1) {
block bb = world_get_block(world, bx, by - 1, bz);
block b = world_get_block(world, bx, by, bz);
struct block_info* bi = getBlockInfo(b);
if (j >= i / 16) {
if (streq_nocase(bi->material->name, "air")) {
if (rand() % 8 == 0) {
block nb = rand() % 3 > 0 ? BLK_FLOWER1_DANDELION : BLK_FLOWER2_POPPY; // TODO: biome registry
if (bb == BLK_DIRT || bb == BLK_GRASS) {
world_set_block(world, nb, bx, by, bz);
}
} else {
block nb = BLK_TALLGRASS_GRASS;
if (bb == BLK_DIRT || bb == BLK_GRASS) {
world_set_block(world, nb, bx, by, bz);
}
}
}
break;
}
bx += rand() % 3 - 1;
by += (rand() % 3 - 1) * (rand() % 3) / 2;
bz += rand() % 3 - 1;
if (bb != BLK_GRASS || block_is_normal_cube(bi)) break;
j++;
}
}
} else return 0;
if (player->gamemode != 1 && --slot->count <= 0) {
slot = NULL;
}
inventory_set_slot(player, player->inventory, slot_index, slot, 1);
return 0;
}
int onItemInteract_seeds(struct world* world, struct player* player, uint8_t slot_index, struct slot* slot, int32_t x, int32_t y, int32_t z, uint8_t face) {
if (slot == NULL) return 0;
if (!getBlockInfo(world_get_block(world, x, y, z))->material->replacable) offsetCoordByFace(&x, &y, &z, face);
if (!player_can_place_block(player, ITM_DYEPOWDER_BLACK, x, y, z, face)) return 0;
block b = world_get_block(world, x, y, z);
if (slot->item == ITM_DYEPOWDER_BLACK) {
if (slot->damage == 3 && (face == 1 || face == 0 || b != BLK_LOG_JUNGLE)) return 0;
if (slot->damage == 15) return onItemInteract_bonemeal(world, player, slot_index, slot, x, y, z, face);
else if (slot->damage != 3) return 0;
} else if (face != 1 || (b >> 4) != (slot->item == ITM_NETHERSTALKSEEDS ? (BLK_HELLSAND >> 4) : (BLK_FARMLAND >> 4))) return 0;
b = world_get_block(world, x, y, z);
if (b != 0) return 0;
block tp = 0;
if (slot->item == ITM_SEEDS) tp = BLK_CROPS;
else if (slot->item == ITM_SEEDS_PUMPKIN) tp = BLK_PUMPKINSTEM;
else if (slot->item == ITM_SEEDS_MELON) tp = BLK_PUMPKINSTEM_1;
else if (slot->item == ITM_CARROTS) tp = BLK_CARROTS;
else if (slot->item == ITM_POTATO) tp = BLK_POTATOES;
else if (slot->item == ITM_NETHERSTALKSEEDS) tp = BLK_NETHERSTALK;
else if (slot->item == ITM_BEETROOT_SEEDS) tp = BLK_BEETROOTS;
else if (slot->item == ITM_DYEPOWDER_BLACK) tp = BLK_COCOA;
if (player->gamemode != 1 && --slot->count <= 0) {
slot = NULL;
}
inventory_set_slot(player, player->inventory, slot_index, slot, 1);
world_set_block(world, tp, x, y, z);
return 0;
}
int onItemInteract_hoe(struct world* world, struct player* player, uint8_t slot_index, struct slot* slot, int32_t x, int32_t y, int32_t z, uint8_t face) {
if (slot == NULL) return 0;
struct item_info* ii = getItemInfo(slot->item);
if (ii == NULL) return 0;
block b = world_get_block(world, x, y, z);
if (b == BLK_DIRT || b == BLK_GRASS) {
if (!player_can_place_block(player, slot->item, x, y, z, face)) return 0;
if (player->gamemode != 1 && ++slot->damage >= ii->maxDamage) {
slot = NULL;
}
inventory_set_slot(player, player->inventory, slot_index, slot, 1);
world_set_block(world, BLK_FARMLAND, x, y, z);
}
return 0;
}
int onItemInteract_shovel(struct world* world, struct player* player, uint8_t slot_index, struct slot* slot, int32_t x, int32_t y, int32_t z, uint8_t face) {
if (slot == NULL) return 0;
struct item_info* ii = getItemInfo(slot->item);
if (ii == NULL) return 0;
block b = world_get_block(world, x, y, z);
if (b == BLK_GRASS) {
if (!player_can_place_block(player, slot->item, x, y, z, face)) return 0;
if (player->gamemode != 1 && ++slot->damage >= ii->maxDamage) {
slot = NULL;
}
inventory_set_slot(player, player->inventory, slot_index, slot, 1);
world_set_block(world, BLK_GRASSPATH, x, y, z);
}
return 0;
}
void onItemUse_armor(struct world* world, struct player* player, uint8_t slot_index, struct slot* slot, uint32_t ticks) {
if (slot == NULL) return;
uint16_t sli = 5;
struct item_info* ii = getItemInfo(slot->item);
if (ii->armorType == ARMOR_HELMET) sli = 5;
else if (ii->armorType == ARMOR_CHESTPLATE) sli = 6;
else if (ii->armorType == ARMOR_LEGGINGS) sli = 7;
else if (ii->armorType == ARMOR_BOOTS) sli = 8;
if (inventory_get(player, player->inventory, sli) != NULL) return;
inventory_set_slot(player, player->inventory, sli, slot, 1);
inventory_set_slot(player, player->inventory, slot_index, NULL, 1, 0);
}
float onEntityHitWhileWearing_armor(struct world* world, struct player* player, uint8_t slot_index, struct slot* slot, float damage) {
if (slot == NULL) return damage;
struct item_info* ii = getItemInfo(slot->item);
if (ii == NULL) return damage;
if (player->gamemode != 1 && ++slot->damage >= ii->maxDamage) {
slot = NULL;
}
inventory_set_slot(player, player->inventory, slot_index, slot, 1);
return damage;
}
int bow_isArrow(struct slot* slot) {
item i = slot == NULL ? 0 : slot->item;
return i == ITM_ARROW || i == ITM_SPECTRAL_ARROW || i == ITM_TIPPED_ARROW || i == ITM_LINGERING_POTION;
}
int bow_findAmmo(struct player* player) {
struct slot* tmp = inventory_get(player, player->inventory, 45);
if (bow_isArrow(tmp)) return 45;
tmp = inventory_get(player, player->inventory, 36 + player->currentItem);
if (bow_isArrow(tmp)) return 36 + player->currentItem;
for (int i = 0; i < player->inventory->slot_count; i++) {
struct slot* tmp = inventory_get(player, player->inventory, i);
if (bow_isArrow(tmp)) return i;
}
return -1;
}
void onItemUse_bow(struct world* world, struct player* player, uint8_t slot_index, struct slot* slot, uint32_t ticks) {
if (slot == NULL) return;
struct item_info* ii = getItemInfo(slot->item);
if (ii == NULL) return;
int bs = bow_findAmmo(player);
struct slot* ammo = NULL;
if (player->gamemode != 1) {
if (bs < 0) return;
//TODO: or infinity enchantment
ammo = inventory_get(player, player->inventory, bs);
}
float velocity = (float) ticks / 20.;
if (velocity > 1.) velocity = 1.;
velocity = (velocity * velocity + velocity * 2.) / 3.;
if (velocity > 1.) velocity = 1.;
if (velocity >= .1) {
int sp = ammo != NULL && ammo->item == ITM_SPECTRAL_ARROW;
struct entity* arrow = entity_new(nextEntityID++, player->entity->x, player->entity->y + 1.52, player->entity->z, sp ? ENT_SPECTRALARROW : ENT_ARROW, player->entity->yaw, player->entity->pitch);
//player->entity->pitch = 0.;
//player->entity->yaw = 0.;
float x = -sinf(player->entity->yaw / 360. * 2 * M_PI) * cosf(player->entity->pitch / 360. * 2 * M_PI);
float y = -sinf(player->entity->pitch / 360. * 2 * M_PI);
float z = cosf(player->entity->yaw / 360. * 2 * M_PI) * cosf(player->entity->pitch / 360. * 2 * M_PI);
float s = sqrtf(x * x + y * y + z * z);
x /= s;
y /= s;
z /= s;
//TODO: inaccuracy calc gaussian
x *= velocity * 3.;
y *= velocity * 3.;
z *= velocity * 3.;
arrow->motX = x;
arrow->motY = y;
arrow->motZ = z;
float sr = sqrtf(x * x + z * z);
arrow->yaw = atan2f(x, z) * 180. / M_PI;
arrow->pitch = atan2f(y, sr) * 180. / M_PI;
arrow->last_yaw = arrow->yaw;
arrow->last_pitch = arrow->pitch;
//arrow->motX += player->entity->motX;
//arrow->motZ += player->entity->motZ;
//if (!player->entity->onGround) arrow->motY += player->entity->motY;
if (velocity == 1.) arrow->data.arrow.isCritical = 1;
arrow->data.arrow.damage = 2.;
arrow->objectData = 1 + player->entity->id;
arrow->data.arrow.knockback = 1.;
//TODO: power enchant
//TODO: punch enchant
//TODO: flame enchant
if (ammo != NULL) {
if (--ammo->count <= 0) {
ammo = NULL;
}
inventory_set_slot(player, player->inventory, bs, ammo, 1, 1);
}
world_spawn_entity(player->world, arrow);
}
}
int canUseItem_bow(struct world* world, struct player* player, uint8_t slot_index, struct slot* slot) {
if (slot == NULL) return 0;
struct item_info* ii = getItemInfo(slot->item);
if (ii == NULL) return 0;
if (player->gamemode != 1 && bow_findAmmo(player) < 0) return 0;
return 1;
}
int canUseItem_food(struct world* world, struct player* player, uint8_t slot_index, struct slot* slot) {
if (slot == NULL) return 0;
struct item_info* ii = getItemInfo(slot->item);
if (ii == NULL) return 0;
struct itemfood_arg* arg = ii->callback_arg;
if (!arg->alwaysEat && player->food >= 20) return 0;
return 1;
}
void onItemUse_food(struct world* world, struct player* player, uint8_t slot_index, struct slot* slot, uint32_t ticks) {
if (slot == NULL) return;
struct item_info* ii = getItemInfo(slot->item);
if (ii == NULL) return;
struct itemfood_arg* arg = ii->callback_arg;
if (ticks >= 32) {
player->food += arg->food;
if (player->food > 20) player->food = 20;
player->saturation += arg->saturation * arg->food * 2.;
if (player->saturation > player->food) player->saturation = player->food;
player_hungerUpdate(player);
if (--slot->count <= 0) slot = NULL;
inventory_set_slot(player, player->inventory, slot_index, slot, 1);
}
}
void init_food(item i, int food, float saturation, int alwaysEat) {
struct itemfood_arg* ia = xmalloc(sizeof(struct itemfood_arg));
ia->food = food;
ia->saturation = saturation;
ia->alwaysEat = alwaysEat;
struct item_info* ii = getItemInfo(i);
ii->callback_arg = ia;
ii->onItemUse = &onItemUse_food;
ii->canUseItem = &canUseItem_food;
}
struct collection* item_infos;
struct item_info* getItemInfo(item id) {
if (id < 0 || id > item_infos->size) return NULL;
return item_infos->data[id];
}
void init_items() {
item_infos = new_collection(128, 0);
char* jsf = xmalloc(4097);
size_t jsfs = 4096;
size_t jsr = 0;
int fd = open("items.json", O_RDONLY);
ssize_t r = 0;
while ((r = read(fd, jsf + jsr, jsfs - jsr)) > 0) {
jsr += r;
if (jsfs - jsr < 512) {
jsfs += 4096;
jsf = xrealloc(jsf, jsfs + 1);
}
}
jsf[jsr] = 0;
if (r < 0) {
printf("Error reading item information: %s\n", strerror(errno));
}
close(fd);
struct json_object json;
json_parse(&json, jsf);
for (size_t i = 0; i < json.child_count; i++) {
struct json_object* ur = json.children[i];
struct item_info* ii = xcalloc(sizeof(struct item_info));
struct json_object* tmp = json_get(ur, "id");
if (tmp == NULL || tmp->type != JSON_NUMBER) goto cerr;
item id = (item) tmp->data.number;
if (id < 0) goto cerr;
tmp = json_get(ur, "toolType");
if (tmp == NULL || tmp->type != JSON_STRING) goto cerr;
if (streq_nocase(tmp->data.string, "none")) ii->toolType = NULL;
else ii->toolType = tools_get(tmp->data.string);
tmp = json_get(ur, "harvestLevel");
if (tmp == NULL || tmp->type != JSON_NUMBER) goto cerr;
ii->harvestLevel = (uint8_t) tmp->data.number;
tmp = json_get(ur, "maxStackSize");
if (tmp == NULL || tmp->type != JSON_NUMBER) goto cerr;
ii->maxStackSize = (uint8_t) tmp->data.number;
if (ii->maxStackSize > 64) goto cerr;
tmp = json_get(ur, "maxDamage");
if (tmp == NULL || tmp->type != JSON_NUMBER) goto cerr;
ii->maxDamage = (int16_t) tmp->data.number;
tmp = json_get(ur, "maxUseDuration");
if (tmp == NULL || tmp->type != JSON_NUMBER) goto cerr;
ii->maxUseDuration = (uint32_t) tmp->data.number;
tmp = json_get(ur, "toolProficiency");
if (tmp == NULL || tmp->type != JSON_NUMBER) goto cerr;
ii->toolProficiency = (float) tmp->data.number;
tmp = json_get(ur, "damage");
if (tmp == NULL || tmp->type != JSON_NUMBER) goto cerr;
ii->damage = (float) tmp->data.number;
tmp = json_get(ur, "armorType");
if (tmp == NULL || tmp->type != JSON_STRING) goto cerr;
if (streq_nocase(tmp->data.string, "none")) ii->armorType = ARMOR_NONE;
else if (streq_nocase(tmp->data.string, "helmet")) ii->armorType = ARMOR_HELMET;
else if (streq_nocase(tmp->data.string, "chestplate")) ii->armorType = ARMOR_CHESTPLATE;
else if (streq_nocase(tmp->data.string, "leggings")) ii->armorType = ARMOR_LEGGINGS;
else if (streq_nocase(tmp->data.string, "boots")) ii->armorType = ARMOR_BOOTS;
else goto cerr;
tmp = json_get(ur, "armor");
if (tmp == NULL || tmp->type != JSON_NUMBER) goto cerr;
ii->armor = (uint8_t) tmp->data.number;
tmp = json_get(ur, "attackSpeed");
if (tmp == NULL || tmp->type != JSON_NUMBER) goto cerr;
ii->attackSpeed = (float) tmp->data.number;
ensure_collection(item_infos, id + 1);
item_infos->data[id] = ii;
if (item_infos->size < id) item_infos->size = id;
item_infos->count++;
continue;
cerr: ;
printf("[WARNING] Error Loading Item \"%s\"! Skipped.\n", ur->name);
}
freeJSON(&json);
xfree(jsf);
//
for (item i = ITM_SHOVELIRON; i <= ITM_HATCHETIRON; i++) {
getItemInfo(i)->onItemBreakBlock = &onItemBreakBlock_tool;
getItemInfo(i)->onItemAttacked = &onItemAttacked_tool;
}
getItemInfo(ITM_FLINTANDSTEEL)->onItemInteract = &onItemInteract_flintandsteel;
for (item i = ITM_SWORDIRON; i <= ITM_HATCHETDIAMOND; i++) {
getItemInfo(i)->onItemBreakBlock = &onItemBreakBlock_tool;
getItemInfo(i)->onItemAttacked = &onItemAttacked_tool;
}
for (item i = ITM_SWORDGOLD; i <= ITM_HATCHETGOLD; i++) {
getItemInfo(i)->onItemBreakBlock = &onItemBreakBlock_tool;
getItemInfo(i)->onItemAttacked = &onItemAttacked_tool;
}
getItemInfo(ITM_SHEARS)->onItemBreakBlock = &onItemBreakBlock_tool;
for (item i = ITM_HOEWOOD; i <= ITM_HOEGOLD; i++) {
getItemInfo(i)->onItemInteract = &onItemInteract_hoe;
}
for (item i = ITM_HELMETCLOTH; i <= ITM_BOOTSGOLD; i++) {
getItemInfo(i)->onEntityHitWhileWearing = &onEntityHitWhileWearing_armor;
getItemInfo(i)->onItemUse = &onItemUse_armor;
}
getItemInfo(ITM_DOOROAK)->onItemInteract = &onItemInteract_door;
getItemInfo(ITM_DOORSPRUCE)->onItemInteract = &onItemInteract_door;
getItemInfo(ITM_DOORBIRCH)->onItemInteract = &onItemInteract_door;
getItemInfo(ITM_DOORJUNGLE)->onItemInteract = &onItemInteract_door;
getItemInfo(ITM_DOORACACIA)->onItemInteract = &onItemInteract_door;
getItemInfo(ITM_DOORDARKOAK)->onItemInteract = &onItemInteract_door;
getItemInfo(ITM_DOORIRON)->onItemInteract = &onItemInteract_door;
getItemInfo(ITM_SEEDS)->onItemInteract = &onItemInteract_seeds;
getItemInfo(ITM_SEEDS_PUMPKIN)->onItemInteract = &onItemInteract_seeds;
getItemInfo(ITM_SEEDS_MELON)->onItemInteract = &onItemInteract_seeds;
getItemInfo(ITM_CARROTS)->onItemInteract = &onItemInteract_seeds;
getItemInfo(ITM_POTATO)->onItemInteract = &onItemInteract_seeds;
getItemInfo(ITM_NETHERSTALKSEEDS)->onItemInteract = &onItemInteract_seeds;
getItemInfo(ITM_BEETROOT_SEEDS)->onItemInteract = &onItemInteract_seeds;
getItemInfo(ITM_DYEPOWDER_BLACK)->onItemInteract = &onItemInteract_seeds;
getItemInfo(ITM_SHOVELWOOD)->onItemInteract = &onItemInteract_shovel;
getItemInfo(ITM_SHOVELGOLD)->onItemInteract = &onItemInteract_shovel;
getItemInfo(ITM_SHOVELSTONE)->onItemInteract = &onItemInteract_shovel;
getItemInfo(ITM_SHOVELIRON)->onItemInteract = &onItemInteract_shovel;
getItemInfo(ITM_SHOVELDIAMOND)->onItemInteract = &onItemInteract_shovel;
getItemInfo(ITM_REEDS)->onItemInteract = &onItemInteract_reeds;
getItemInfo(ITM_BUCKET)->onItemInteract = &onItemInteract_bucket;
getItemInfo(ITM_BUCKETWATER)->onItemInteract = &onItemInteract_bucket;
getItemInfo(ITM_BUCKETLAVA)->onItemInteract = &onItemInteract_bucket;
getItemInfo(ITM_MONSTERPLACER)->onItemInteract = &onItemInteract_spawnegg;
getItemInfo(ITM_BOW)->onItemUse = &onItemUse_bow;
getItemInfo(ITM_BOW)->canUseItem = &canUseItem_bow;
getItemInfo(ITM_STRING)->onItemInteract = &onItemInteract_itemblock;
getItemInfo(ITM_REDSTONE)->onItemInteract = &onItemInteract_itemblock;
getItemInfo(ITM_CAKE)->onItemInteract = &onItemInteract_itemblock;
getItemInfo(ITM_DIODE)->onItemInteract = &onItemInteract_itemblock;
getItemInfo(ITM_BREWINGSTAND)->onItemInteract = &onItemInteract_itemblock;
getItemInfo(ITM_CAULDRON)->onItemInteract = &onItemInteract_itemblock;
getItemInfo(ITM_FLOWERPOT)->onItemInteract = &onItemInteract_itemblock;
getItemInfo(ITM_COMPARATOR)->onItemInteract = &onItemInteract_itemblock;
getItemInfo(ITM_MINECART)->onItemInteract = &onItemInteract_minecart;
getItemInfo(ITM_MINECARTCHEST)->onItemInteract = &onItemInteract_minecart;
getItemInfo(ITM_MINECARTFURNACE)->onItemInteract = &onItemInteract_minecart;
getItemInfo(ITM_MINECARTTNT)->onItemInteract = &onItemInteract_minecart;
getItemInfo(ITM_MINECARTHOPPER)->onItemInteract = &onItemInteract_minecart;
getItemInfo(ITM_MINECARTCOMMANDBLOCK)->onItemInteract = &onItemInteract_minecart;
getItemInfo(ITM_PAINTING)->onItemInteract = &onItemInteract_painting;
getItemInfo(ITM_BED)->onItemInteract = &onItemInteract_bed;
init_food(ITM_APPLE, 4, 0.3, 0);
init_food(ITM_MUSHROOMSTEW, 6, 0.6, 0);
init_food(ITM_BREAD, 5, 0.6, 0);
init_food(ITM_PORKCHOPRAW, 3, 0.3, 0);
init_food(ITM_PORKCHOPCOOKED, 8, 0.8, 0);
init_food(ITM_APPLEGOLD, 4, 1.2, 1);
init_food(ITM_FISH_COD_RAW, 2, 0.1, 0);
init_food(ITM_FISH_COD_COOKED, 5, 0.6, 0);
init_food(ITM_COOKIE, 2, 0.1, 0);
init_food(ITM_MELON, 2, 0.3, 0);
init_food(ITM_BEEFRAW, 3, 0.3, 0);
init_food(ITM_BEEFCOOKED, 8, 0.8, 0);
init_food(ITM_CHICKENRAW, 2, 0.3, 0);
init_food(ITM_CHICKENCOOKED, 6, 0.6, 0);
init_food(ITM_ROTTENFLESH, 4, 0.1, 0);
init_food(ITM_SPIDEREYE, 2, 0.8, 0);
init_food(ITM_CARROTS, 3, 0.6, 0);
init_food(ITM_POTATO, 1, 0.3, 0);
init_food(ITM_POTATOBAKED, 5, 0.6, 0);
init_food(ITM_POTATOPOISONOUS, 2, 0.3, 0);
init_food(ITM_CARROTGOLDEN, 6, 1.2, 0);
init_food(ITM_PUMPKINPIE, 8, 0.3, 0);
init_food(ITM_RABBITRAW, 3, 0.3, 0);
init_food(ITM_RABBITCOOKED, 5, 0.6, 0);
init_food(ITM_RABBITSTEW, 10, 0.6, 0);
init_food(ITM_MUTTONRAW, 2, 0.3, 0);
init_food(ITM_MUTTONCOOKED, 6, 0.8, 0);
init_food(ITM_CHORUSFRUIT, 4, 0.3, 1);
init_food(ITM_BEETROOT, 1, 0.6, 0);
init_food(ITM_BEETROOT_SOUP, 6, 0.6, 0);
}
void add_item(item id, struct item_info* info) {
ensure_collection(item_infos, id + 1);
if (item_infos->data[id] != NULL) {
xfree(item_infos->data[id]);
}
item_infos->data[id] = info;
if (item_infos->size < id) item_infos->size = id;
item_infos->count++;
}