mirror of https://github.com/basinserver/basin/
309 lines
14 KiB
C
309 lines
14 KiB
C
|
#include "basin/entity_impl.h"
|
||
|
|
||
|
|
||
|
void onSpawned_minecart(struct world* world, struct entity* entity) {
|
||
|
if (entity->type == ENT_MINECARTRIDEABLE) entity->objectData = 0;
|
||
|
else if (entity->type == ENT_MINECARTCHEST) entity->objectData = 1;
|
||
|
else if (entity->type == ENT_MINECARTFURNACE) entity->objectData = 2;
|
||
|
else if (entity->type == ENT_MINECARTTNT) entity->objectData = 3;
|
||
|
else if (entity->type == ENT_MINECARTSPAWNER) entity->objectData = 4;
|
||
|
else if (entity->type == ENT_MINECARTHOPPER) entity->objectData = 5;
|
||
|
else if (entity->type == ENT_MINECARTCOMMANDBLOCK) entity->objectData = 6;
|
||
|
}
|
||
|
|
||
|
|
||
|
int onTick_tnt(struct world* world, struct entity* ent) {
|
||
|
if (ent->data.tnt.fuse-- <= 0) {
|
||
|
world_despawn_entity(world, ent);
|
||
|
world_explode(world, NULL, ent->x, ent->y + .5, ent->z, 4.);
|
||
|
freeEntity(ent);
|
||
|
return 1;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int onTick_fallingblock(struct world* world, struct entity* ent) {
|
||
|
// TODO: mc has some methods to prevent dupes here, we should see if basin is afflicted
|
||
|
if (ent->on_ground && ent->age > 1) {
|
||
|
block b = world_get_block(world, (int32_t) floor(ent->x), (int32_t) floor(ent->y), (int32_t) floor(ent->z));
|
||
|
if (!falling_canFallThrough(b)) {
|
||
|
struct slot sl;
|
||
|
sl.item = ent->data.fallingblock.b >> 4;
|
||
|
sl.damage = ent->data.fallingblock.b & 0x0f;
|
||
|
sl.count = 1;
|
||
|
sl.nbt = NULL;
|
||
|
game_drop_block(world, &sl, (int32_t) floor(ent->x), (int32_t) floor(ent->y - .01), (int32_t) floor(ent->z));
|
||
|
//ent->onGround = 0;
|
||
|
//return 0;
|
||
|
} else {
|
||
|
world_set_block(world, ent->data.fallingblock.b, (int32_t) floor(ent->x), (int32_t) floor(ent->y), (int32_t) floor(ent->z));
|
||
|
}
|
||
|
//TODO: tile entities
|
||
|
world_despawn_entity(world, ent);
|
||
|
freeEntity(ent);
|
||
|
return 1;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void onInteract_cow(struct world* world, struct entity* entity, struct player* interacter, struct slot* item, int16_t slot_index) {
|
||
|
if (item->item == ITM_BUCKET && interacter->gamemode != 1) { // TODO: not child
|
||
|
if (item->count == 1) {
|
||
|
item->item = ITM_MILK;
|
||
|
inventory_set_slot(interacter, interacter->inventory, slot_index, item, 1, 0);
|
||
|
} else {
|
||
|
item->count--;
|
||
|
inventory_set_slot(interacter, interacter->inventory, slot_index, item, 1, 0);
|
||
|
struct slot slot;
|
||
|
slot.item = ITM_MILK;
|
||
|
slot.count = 1;
|
||
|
slot.damage = 0;
|
||
|
slot.nbt = NULL;
|
||
|
if (inventory_add_player(interacter, interacter->inventory, &slot, 1)) {
|
||
|
dropPlayerItem(interacter, &slot);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void onInteract_mooshroom(struct world* world, struct entity* entity, struct player* interacter, struct slot* item, int16_t slot_index) {
|
||
|
if (item->item == ITM_BOWL && interacter->gamemode != 1) { // TODO: not child
|
||
|
if (item->count == 1) {
|
||
|
item->item = ITM_MUSHROOMSTEW;
|
||
|
inventory_set_slot(interacter, interacter->inventory, slot_index, item, 1, 0);
|
||
|
} else {
|
||
|
item->count--;
|
||
|
inventory_set_slot(interacter, interacter->inventory, slot_index, item, 1, 0);
|
||
|
struct slot slot;
|
||
|
slot.item = ITM_MUSHROOMSTEW;
|
||
|
slot.count = 1;
|
||
|
slot.damage = 0;
|
||
|
slot.nbt = NULL;
|
||
|
if (inventory_add_player(interacter, interacter->inventory, &slot, 1)) {
|
||
|
dropPlayerItem(interacter, &slot);
|
||
|
}
|
||
|
}
|
||
|
} else if (item->item == ITM_SHEARS && interacter->gamemode != 1) { // TODO: not child
|
||
|
//TODO: explosion
|
||
|
struct entity* ent = entity_new(nextEntityID++, entity->x, entity->y, entity->z, ENT_COW, entity->yaw, entity->pitch);
|
||
|
ent->health = entity->health;
|
||
|
memcpy(&ent->data, &entity->data, sizeof(union entity_data));
|
||
|
world_spawn_entity(world, ent);
|
||
|
world_despawn_entity(world, entity);
|
||
|
freeEntity(entity);
|
||
|
struct item_info* ii = getItemInfo(ITM_SHEARS);
|
||
|
if (ii != NULL && ii->onItemAttacked != NULL) (*ii->onItemAttacked)(world, interacter, slot_index, item, ent);
|
||
|
struct slot slot;
|
||
|
slot.item = BLK_MUSHROOM_1;
|
||
|
slot.count = 5;
|
||
|
slot.damage = 0;
|
||
|
slot.nbt = NULL;
|
||
|
dropEntityItem_explode(ent, &slot); // TODO: fix?
|
||
|
} else onInteract_cow(world, entity, interacter, item, slot_index);
|
||
|
}
|
||
|
|
||
|
int tick_arrow(struct world* world, struct entity* entity) {
|
||
|
if (entity->data.arrow.ticksInGround == 0) {
|
||
|
double hx = 0.;
|
||
|
double hy = 0.;
|
||
|
double hz = 0.;
|
||
|
int hf = world_rayTrace(entity->world, entity->x, entity->y, entity->z, entity->x + entity->motX, entity->y + entity->motY, entity->z + entity->motZ, 0, 1, 0, &hx, &hy, &hz);
|
||
|
//printf("hf = %i -- %f, %f, %f\n", hf, entity->x, entity->y, entity->z);
|
||
|
//printf("hf = %i -- %f, %f\n", hf, entity->yaw, entity->pitch);
|
||
|
struct entity* ehit = NULL;
|
||
|
struct entity* shooter = world_get_entity(world, entity->objectData - 1);
|
||
|
double bd = 999.;
|
||
|
BEGIN_HASHMAP_ITERATION(world->entities)
|
||
|
struct entity* e2 = value;
|
||
|
double rd = entity_distsq_block(e2, entity->x + entity->motX, entity->y + entity->motY, entity->z + entity->motZ);
|
||
|
if (rd > 4) continue;
|
||
|
//printf("4d %f\n", rd);
|
||
|
if (e2 != entity && e2 != shooter && hasFlag(getEntityInfo(e2->type), "livingbase")) { //todo: ticksInAir >= 5?
|
||
|
struct boundingbox eb;
|
||
|
getEntityCollision(e2, &eb);
|
||
|
//eb.minX -= .3;
|
||
|
//eb.maxX += .3;
|
||
|
//eb.minY -= .3;
|
||
|
//eb.maxY += .3;
|
||
|
//eb.minZ -= .3;
|
||
|
//eb.maxZ += .3;
|
||
|
double rx = 0.;
|
||
|
double ry = 0.;
|
||
|
double rz = 0.;
|
||
|
int face = world_blockRayTrace(&eb, 0, 0, 0, entity->x, entity->y, entity->z, entity->x + entity->motX, entity->y + entity->motY, entity->z + entity->motZ, &rx, &ry, &rz);
|
||
|
if (face >= 0) {
|
||
|
double dist = (entity->x + entity->motX - rx) * (entity->x + entity->motX - rx) + (entity->y + entity->motY - ry) * (entity->y + entity->motY - ry) + (entity->z + entity->motZ - rz) * (entity->z + entity->motZ - rz); //entity_distsq_block(entity, rx, ry, rz);
|
||
|
//printf("%f\n", dist);
|
||
|
if (dist < bd) {
|
||
|
bd = dist;
|
||
|
ehit = e2;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
END_HASHMAP_ITERATION(world->entities)
|
||
|
if (ehit != NULL) {
|
||
|
float speed = sqrtf(entity->motX * entity->motX + entity->motY * entity->motY + entity->motZ * entity->motZ);
|
||
|
int damage = ceil(speed * entity->data.arrow.damage);
|
||
|
if (entity->data.arrow.isCritical) damage += rand() % (damage / 2 + 2);
|
||
|
//TODO: if burning and not enderman, set entity on fire for 5 ticks.
|
||
|
if (damageEntity(ehit, damage, 1)) {
|
||
|
//TODO: entity arrow count ++;
|
||
|
if (entity->data.arrow.knockback > 0.) {
|
||
|
float hspeed = sqrtf(entity->motX * entity->motX + entity->motZ * entity->motZ);
|
||
|
if (hspeed > 0.) applyVelocity(ehit, entity->motX * entity->data.arrow.knockback * .6 / hspeed, .1, entity->motZ * entity->data.arrow.knockback * .6 / hspeed);
|
||
|
}
|
||
|
} //TODO: else bounce
|
||
|
//TODO: arrow sound
|
||
|
if (ehit->type != ENT_ENDERMAN) {
|
||
|
world_despawn_entity(world, entity);
|
||
|
freeEntity(entity);
|
||
|
return 1;
|
||
|
}
|
||
|
} else if (hf >= 0) {
|
||
|
entity->x = hx;
|
||
|
entity->y = hy;
|
||
|
entity->z = hz;
|
||
|
if (hf == YN) entity->y -= .001;
|
||
|
if (hf == XN) entity->x -= .001;
|
||
|
if (hf == ZN) entity->z -= .001;
|
||
|
entity->motX = hx - entity->x;
|
||
|
entity->motY = hy - entity->y;
|
||
|
entity->motZ = hz - entity->z;
|
||
|
//float ds = sqrt(entity->motX * entity->motX + entity->motY * entity->motY + entity->motZ * entity->motZ);
|
||
|
//entity->x -= entity->motX / ds * 0.05000000074505806;
|
||
|
//entity->y -= entity->motY / ds * 0.05000000074505806;
|
||
|
//entity->z -= entity->motZ / ds * 0.05000000074505806;
|
||
|
entity->data.arrow.ticksInGround = 1;
|
||
|
entity->data.arrow.isCritical = 0;
|
||
|
entity->immovable = 1;
|
||
|
entity->last_x = 0.;
|
||
|
entity->last_y = 0.;
|
||
|
entity->last_z = 0.;
|
||
|
}
|
||
|
} else {
|
||
|
if (entity->data.arrow.ticksInGround == 1) {
|
||
|
entity->motX = 0.;
|
||
|
entity->motY = 0.;
|
||
|
entity->motZ = 0.;
|
||
|
}
|
||
|
entity->data.arrow.ticksInGround++;
|
||
|
if (entity->data.arrow.ticksInGround == 1200) {
|
||
|
world_despawn_entity(world, entity);
|
||
|
freeEntity(entity);
|
||
|
return 1;
|
||
|
}
|
||
|
}
|
||
|
//printf("hf2c = %f, %f, %f\n", entity->x, entity->y, entity->z);
|
||
|
|
||
|
if (entity->data.arrow.ticksInGround == 0) {
|
||
|
float dhz = sqrtf(entity->motX * entity->motX + entity->motZ * entity->motZ);
|
||
|
entity->yaw = atan2f(entity->motX, entity->motZ) * 180. / M_PI;
|
||
|
entity->pitch = atan2f(entity->motY, dhz) * 180. / M_PI;
|
||
|
//printf("desired %f, %f, %f\n", entity->pitch, entity->motY, dhz);
|
||
|
//printf("yaw = %f, last_yaw = %f\npitch = %f, last_pitch = %f\n", entity->yaw, entity->last_yaw, entity->pitch, entity->last_pitch);
|
||
|
if ((entity->last_yaw == 0. && entity->last_pitch == 0.)) {
|
||
|
entity->last_yaw = entity->yaw;
|
||
|
entity->last_pitch = entity->pitch;
|
||
|
} else {
|
||
|
while (entity->pitch - entity->last_pitch < -180.)
|
||
|
entity->last_pitch -= 360.;
|
||
|
while (entity->pitch - entity->last_pitch >= 180.)
|
||
|
entity->last_pitch += 360.;
|
||
|
while (entity->yaw - entity->last_yaw < -180.)
|
||
|
entity->last_yaw -= 360.;
|
||
|
while (entity->yaw - entity->last_yaw >= 180.)
|
||
|
entity->last_yaw += 360.;
|
||
|
entity->pitch = entity->last_pitch + (entity->pitch - entity->last_pitch) * .2;
|
||
|
entity->yaw = entity->last_yaw + (entity->yaw - entity->last_yaw) * .2;
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int tick_itemstack(struct world* world, struct entity* entity) {
|
||
|
if (entity->data.itemstack.delayBeforeCanPickup > 0) {
|
||
|
entity->data.itemstack.delayBeforeCanPickup--;
|
||
|
return 0;
|
||
|
}
|
||
|
if (entity->age >= 6000) {
|
||
|
world_despawn_entity(world, entity);
|
||
|
freeEntity(entity);
|
||
|
return 1;
|
||
|
}
|
||
|
if (tick_counter % 10 != 0) return 0;
|
||
|
struct boundingbox cebb;
|
||
|
getEntityCollision(entity, &cebb);
|
||
|
cebb.minX -= .625;
|
||
|
cebb.maxX += .625;
|
||
|
cebb.maxY += .75;
|
||
|
cebb.minZ -= .625;
|
||
|
cebb.maxZ += .625;
|
||
|
struct boundingbox oebb;
|
||
|
//int32_t chunk_x = ((int32_t) entity->x) >> 4;
|
||
|
//int32_t chunk_z = ((int32_t) entity->z) >> 4;
|
||
|
//for (int32_t icx = chunk_x - 1; icx <= chunk_x + 1; icx++)
|
||
|
//for (int32_t icz = chunk_z - 1; icz <= chunk_z + 1; icz++) {
|
||
|
//struct chunk* ch = world_get_chunk(entity->world, icx, icz);
|
||
|
//if (ch != NULL) {
|
||
|
BEGIN_HASHMAP_ITERATION(entity->world->entities)
|
||
|
struct entity* oe = (struct entity*) value;
|
||
|
if (oe == entity || entity_distsq(entity, oe) > 16. * 16.) continue;
|
||
|
if (oe->type == ENT_PLAYER && oe->health > 0.) {
|
||
|
getEntityCollision(oe, &oebb);
|
||
|
//printf("%f, %f, %f vs %f, %f, %f\n", entity->x, entity->y, entity->z, oe->x, oe->y, oe->z);
|
||
|
if (boundingbox_intersects(&oebb, &cebb)) {
|
||
|
int os = entity->data.itemstack.slot->count;
|
||
|
pthread_mutex_lock(&oe->data.player.player->inventory->mut);
|
||
|
int r = inventory_add_player(oe->data.player.player, oe->data.player.player->inventory, entity->data.itemstack.slot, 1);
|
||
|
pthread_mutex_unlock(&oe->data.player.player->inventory->mut);
|
||
|
if (r <= 0) {
|
||
|
BEGIN_BROADCAST_DIST(entity, 32.)
|
||
|
struct packet* pkt = xmalloc(sizeof(struct packet));
|
||
|
pkt->id = PKT_PLAY_CLIENT_COLLECTITEM;
|
||
|
pkt->data.play_client.collectitem.collected_entity_id = entity->id;
|
||
|
pkt->data.play_client.collectitem.collector_entity_id = oe->id;
|
||
|
pkt->data.play_client.collectitem.pickup_item_count = os - r;
|
||
|
add_queue(bc_player->outgoing_packets, pkt);
|
||
|
END_BROADCAST(entity->world->players)
|
||
|
world_despawn_entity(world, entity);
|
||
|
freeEntity(entity);
|
||
|
return 1;
|
||
|
} else {
|
||
|
BEGIN_BROADCAST_DIST(entity, 128.)
|
||
|
struct packet* pkt = xmalloc(sizeof(struct packet));
|
||
|
pkt->id = PKT_PLAY_CLIENT_ENTITYMETADATA;
|
||
|
pkt->data.play_client.entitymetadata.entity_id = entity->id;
|
||
|
writeMetadata(entity, &pkt->data.play_client.entitymetadata.metadata.metadata, &pkt->data.play_client.entitymetadata.metadata.metadata_size);
|
||
|
add_queue(bc_player->outgoing_packets, pkt);
|
||
|
END_BROADCAST(entity->world->players)
|
||
|
BREAK_HASHMAP_ITERATION(entity->world->entities)
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
} else if (oe->type == ENT_ITEM) {
|
||
|
if (oe->data.itemstack.slot->item == entity->data.itemstack.slot->item && oe->data.itemstack.slot->damage == entity->data.itemstack.slot->damage && oe->data.itemstack.slot->count + entity->data.itemstack.slot->count <= slot_max_size(entity->data.itemstack.slot)) {
|
||
|
getEntityCollision(oe, &oebb);
|
||
|
oebb.minX -= .625;
|
||
|
oebb.maxX += .625;
|
||
|
cebb.maxY += .75;
|
||
|
oebb.minZ -= .625;
|
||
|
oebb.maxZ += .625;
|
||
|
if (boundingbox_intersects(&oebb, &cebb)) {
|
||
|
world_despawn_entity(world, entity);
|
||
|
oe->data.itemstack.slot->count += entity->data.itemstack.slot->count;
|
||
|
freeEntity(entity);
|
||
|
BEGIN_BROADCAST_DIST(oe, 128.)
|
||
|
struct packet* pkt = xmalloc(sizeof(struct packet));
|
||
|
pkt->id = PKT_PLAY_CLIENT_ENTITYMETADATA;
|
||
|
pkt->data.play_client.entitymetadata.entity_id = oe->id;
|
||
|
writeMetadata(oe, &pkt->data.play_client.entitymetadata.metadata.metadata, &pkt->data.play_client.entitymetadata.metadata.metadata_size);
|
||
|
add_queue(bc_player->outgoing_packets, pkt);
|
||
|
END_BROADCAST(oe->world->players)
|
||
|
return 1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
END_HASHMAP_ITERATION(entity->world->entities)
|
||
|
return 0;
|
||
|
}
|