#include "Entity.h" #include "Dimension.h" #include "Game.h" #include "BlockType.h" #include "ItemSkill.h" #include "PlaceBlockUpdate.h" #include "EntityRemovedUpdate.h" ActionTarget::ActionTarget(Vec3 blockPos, Direction blockSide) : blockPos(blockPos), targetBlockSide(blockSide), entityId(-1) {} ActionTarget::ActionTarget(int entityId) : entityId(entityId) {} bool ActionTarget::isBlock(Framework::Vec3 blockPos, Direction blockSide) const { return this->entityId == -1 && this->blockPos == blockPos && this->targetBlockSide == targetBlockSide; } bool ActionTarget::isEntity(int entityId) const { return this->entityId == entityId; } void ActionTarget::applyItemSkillOnTarget(Entity* zActor, ItemSkill* zItemSkill, Item* zUsedItem) { if (entityId >= 0) { // TODO: get entity from game and apply skill } else { Block* block = Game::INSTANCE->zRealBlockInstance(blockPos, zActor->getCurrentDimensionId()); if (block) zItemSkill->use(zActor, zUsedItem, block); } } void ActionTarget::placeBlock(Entity* zActor, Item* zItem) { // TODO: check stamina of actor Block* block = zItem->zPlacedBlockType()->createBlockAt(blockPos + getDirection(targetBlockSide), zItem); if (block) { if (Game::INSTANCE->requestWorldUpdate(new PlaceBlockUpdate(block, block->getPos(), zActor->getCurrentDimensionId()))) { zItem->onPlaced(); // TODO: decrese stamina of actor } } } void ActionTarget::save(ActionTarget* zTarget, Framework::StreamWriter* zWriter) { if (zTarget) { if (zTarget->entityId >= 0) { char b = 1; zWriter->schreibe(&b, 1); zWriter->schreibe((char*)&zTarget->entityId, 4); } else { char b = 2; zWriter->schreibe(&b, 1); zWriter->schreibe((char*)&zTarget->blockPos.x, 4); zWriter->schreibe((char*)&zTarget->blockPos.y, 4); zWriter->schreibe((char*)&zTarget->blockPos.z, 4); zWriter->schreibe((char*)&zTarget->targetBlockSide, 4); } } else { char b = 0; zWriter->schreibe(&b, 1); } } ActionTarget* ActionTarget::load(Framework::StreamReader* zReader) { char b; zReader->lese(&b, 1); if (b == 1) { int id; zReader->lese((char*)&id, 4); return new ActionTarget(id); } else if (b == 2) { Framework::Vec3 pos; Direction side; zReader->lese((char*)&pos.x, 4); zReader->lese((char*)&pos.y, 4); zReader->lese((char*)&pos.z, 4); zReader->lese((char*)&side, 4); return new ActionTarget(pos, side); } return 0; } Entity::Entity(const EntityType* zType, Framework::Vec3 location, int dimensionId, int entityId) : Inventory(location, true), speed(0, 0, 0), faceDir(1, 0, 0), target(0), zEntityType(zType), currentDimensionId(dimensionId), removed(0), gravityMultiplier(1.f), needUpdate(0), id(entityId) {} void Entity::onDeath() { removed = 1; Game::INSTANCE->requestWorldUpdate(new EntityRemovedUpdate(id, currentDimensionId, location)); } void Entity::useItem(const ItemType* zType, Item* zItem) { if (zItem && zItem->isEatable()) { // TODO: eat item zItem->applyFoodEffects(this); } else if (zItem && zItem->isPlaceable()) { // TODO: place item if (target) target->placeBlock(this, zItem); } else if (!zItem || zItem->isUsable()) { // use item skill if (target) { ItemSkill* selected = 0; for (ItemSkill* skill : skills) { if (skill->zSkillType() == zType) { selected = skill; break; } } if (!selected) { selected = zType->createDefaultItemSkill(); skills.add(selected); } target->applyItemSkillOnTarget(this, selected, zItem); } } } void Entity::prepareTick(const Dimension* zDimension) { Vec3 headPosition = location + faceOffset; int px = (int)floor(headPosition.x); int py = (int)floor(headPosition.y); int pz = (int)floor(headPosition.z); faceDir.normalize(); Direction dir = BOTTOM; while (true) { if (getDefaultBlock(Game::INSTANCE->zBlockAt(Vec3{ px, py, pz }, zDimension->getDimensionId()))->isInteractable()) { if (!target || !target->isBlock({ px, py, pz }, dir)) { delete target; target = new ActionTarget({ px, py, pz }, dir); needUpdate = 1; } break; } // collision to neighbor of current block if (faceDir.x > 0) { float xt = ((float)px + 1.f - headPosition.x) / faceDir.x; Vec3 tmp = headPosition + faceDir * xt; if (xt <= targetDistanceLimit && tmp.y >= (float)py && tmp.y < (float)py + 1.f && tmp.z >= (float)pz && tmp.z < (float)pz + 1.f) { dir = WEST; px++; continue; } } if (faceDir.x < 0) { float xt = ((float)px - headPosition.x) / faceDir.x; Vec3 tmp = headPosition + faceDir * xt; if (xt <= targetDistanceLimit && tmp.y >= (float)py && tmp.y < (float)py + 1.f && tmp.z >= (float)pz && tmp.z < (float)pz + 1.f) { dir = EAST; px--; continue; } } if (faceDir.y > 0) { float yt = ((float)py + 1.f - headPosition.y) / faceDir.y; Vec3 tmp = headPosition + faceDir * yt; if (yt <= targetDistanceLimit && tmp.x >= (float)px && tmp.x < (float)px + 1.f && tmp.z >= (float)pz && tmp.z < (float)pz + 1.f) { dir = NORTH; py++; continue; } } if (faceDir.y < 0) { float yt = ((float)py - headPosition.y) / faceDir.y; Vec3 tmp = headPosition + faceDir * yt; if (yt <= targetDistanceLimit && tmp.x >= (float)px && tmp.x < (float)px + 1.f && tmp.z >= (float)pz && tmp.z < (float)pz + 1.f) { dir = SOUTH; py--; continue; } } if (faceDir.z > 0) { float zt = ((float)pz + 1.f - headPosition.z) / faceDir.z; Vec3 tmp = headPosition + faceDir * zt; if (zt <= targetDistanceLimit && tmp.x >= (float)px && tmp.x < (float)px + 1.f && tmp.y >= (float)py && tmp.y < (float)py + 1.f) { dir = BOTTOM; pz++; continue; } } if (faceDir.z < 0) { float zt = ((float)pz - headPosition.z) / faceDir.z; Vec3 tmp = headPosition + faceDir * zt; if (zt <= targetDistanceLimit && tmp.x >= (float)px && tmp.x < (float)px + 1.f && tmp.y >= (float)py && tmp.y < (float)py + 1) { dir = TOP; pz--; continue; } } if (target) { delete target; target = 0; needUpdate = 1; } break; } } void Entity::tick(const Dimension* zDimension) { Vec3 oldPos = location; // current block cooredinates int px = (int)floor(location.x); int py = (int)floor(location.y); int pz = (int)floor(location.z); // falling down speed.z -= (zDimension->getGravity() * gravityMultiplier) / 30.f; // movement in current tick Vec3 frameSpeed = speed / 30.f; // loop through all collided blocks bool needCollisionCheck = 1; while (needCollisionCheck) { needCollisionCheck = 0; // collision to neighbor of current block if (speed.x > 0) { float xt = ((float)px + 1.f - oldPos.x) / frameSpeed.x; Vec3 tmp = oldPos + frameSpeed * xt; if (xt <= 1.f && tmp.y >= (float)py && tmp.y < (float)py + 1.f && tmp.z >= (float)pz && tmp.z < (float)pz + 1.f) { if (!getDefaultBlock(Game::INSTANCE->zBlockAt(Vec3{ px + 1, py, pz }, zDimension->getDimensionId()))->isPassable()) { frameSpeed.x = frameSpeed.x * (xt - 0.1f); speed.x = 0; } else px++; needCollisionCheck = 1; continue; } } if (speed.x < 0) { float xt = ((float)px - oldPos.x) / frameSpeed.x; Vec3 tmp = oldPos + frameSpeed * xt; if (xt <= 1.f && tmp.y >= (float)py && tmp.y < (float)py + 1.f && tmp.z >= (float)pz && tmp.z < (float)pz + 1.f) { if (!getDefaultBlock(Game::INSTANCE->zBlockAt(Vec3{ px - 1, py, pz }, zDimension->getDimensionId()))->isPassable()) { frameSpeed.x = frameSpeed.x * (xt - 0.1f); speed.x = 0; } else px--; needCollisionCheck = 1; continue; } } if (speed.y > 0) { float yt = ((float)py + 1.f - oldPos.y) / frameSpeed.y; Vec3 tmp = oldPos + frameSpeed * yt; if (yt <= 1.f && tmp.x >= (float)px && tmp.x < (float)px + 1.f && tmp.z >= (float)pz && tmp.z < (float)pz + 1.f) { if (!getDefaultBlock(Game::INSTANCE->zBlockAt(Vec3{ px, py + 1, pz }, zDimension->getDimensionId()))->isPassable()) { frameSpeed.y = frameSpeed.y * (yt - 0.1f); speed.y = 0; } else py++; needCollisionCheck = 1; continue; } } if (speed.y < 0) { float yt = ((float)py - oldPos.y) / frameSpeed.y; Vec3 tmp = oldPos + frameSpeed * yt; if (yt <= 1.f && tmp.x >= (float)px && tmp.x < (float)px + 1.f && tmp.z >= (float)pz && tmp.z < (float)pz + 1.f) { if (!getDefaultBlock(Game::INSTANCE->zBlockAt(Vec3{ px, py - 1, pz }, zDimension->getDimensionId()))->isPassable()) { frameSpeed.y = frameSpeed.y * (yt - 0.1f); speed.y = 0; } else py--; needCollisionCheck = 1; continue; } } if (speed.z > 0) { float zt = ((float)pz + 1.f - oldPos.z) / frameSpeed.z; Vec3 tmp = oldPos + frameSpeed * zt; if (zt <= 1.f && tmp.x >= (float)px && tmp.x < (float)px + 1.f && tmp.y >= (float)py && tmp.y < (float)py + 1.f) { if (!getDefaultBlock(Game::INSTANCE->zBlockAt(Vec3{ px, py, pz + 1 }, zDimension->getDimensionId()))->isPassable()) { frameSpeed.z = frameSpeed.z * (zt - 0.1f); speed.z = 0; } else pz++; needCollisionCheck = 1; continue; } } if (speed.z < 0) { float zt = ((float)pz - oldPos.z) / frameSpeed.z; Vec3 tmp = oldPos + frameSpeed * zt; if (zt <= 1.f && tmp.x >= (float)px && tmp.x < (float)px + 1.f && tmp.y >= (float)py && tmp.y < (float)py + 1) { if (!getDefaultBlock(Game::INSTANCE->zBlockAt(Vec3{ px, py, pz - 1 }, zDimension->getDimensionId()))->isPassable()) { frameSpeed.z = frameSpeed.z * (zt - 0.1f); onFall(speed.z); speed.z = 0; } else pz--; needCollisionCheck = 1; continue; } } } location += frameSpeed; if (oldPos != location || needUpdate) { NetworkResponse changeMsg; changeMsg.adressEntity(this); char msg[13]; msg[0] = 0; // position changed *(float*)(msg + 1) = this->location.x; *(float*)(msg + 5) = this->location.y; *(float*)(msg + 9) = this->location.z; changeMsg.setMessage(msg, 13, 0); Game::INSTANCE->distributeResponse(&changeMsg); } } void Entity::api(Framework::StreamReader* zRequest, NetworkResponse* zResponse) { char type; zRequest->lese(&type, 1); switch (type) { case 100: // request inventory { char idLen; zRequest->lese(&idLen, 1); char* id = new char[idLen + 1]; zRequest->lese(id, idLen); id[(int)idLen] = 0; zResponse->adressGui(id); delete[] id; char filterLen; zRequest->lese(&filterLen, 1); char* filter = new char[filterLen + 1]; zRequest->lese(filter, filterLen); filter[(int)filterLen] = 0; InMemoryBuffer buffer; int count = 0; for (ItemSlot* slot : *this) { if (slot->getName().istGleich(filter)) { count++; int id = slot->getId(); buffer.schreibe((char*)&id, 4); int itemCount = slot->getNumberOfItems(); buffer.schreibe((char*)&itemCount, 4); if (itemCount > 0) { float f = slot->zStack()->zItem()->getDamage(); buffer.schreibe((char*)&f, 4); f = slot->zStack()->zItem()->getMaxDamage(); buffer.schreibe((char*)&f, 4); f = slot->zStack()->zItem()->getDurability(); buffer.schreibe((char*)&f, 4); f = slot->zStack()->zItem()->getMaxDurability(); buffer.schreibe((char*)&f, 4); int id = slot->zStack()->zItem()->zItemType()->getId(); buffer.schreibe((char*)&id, 4); } } } delete[] filter; char* msg = new char[5 + buffer.getSize()]; msg[0] = 0; *(int*)(msg + 1) = count; buffer.lese(msg + 5, (int)buffer.getSize()); zResponse->setMessage(msg, 5 + (int)buffer.getSize(), 1); break; } } } void Entity::onFall(float collisionSpeed) { if (collisionSpeed > 5) { // TODO: take damage } } void Entity::setPosition(Framework::Vec3 pos) { location = pos; } float Entity::getMaxHP() const { return maxHP; } float Entity::getCurrentHP() const { return currentHP; } float Entity::getStamina() const { return stamina; } float Entity::getMaxStamina() const { return maxStamina; } float Entity::getHunger() const { return hunger; } float Entity::getMaxHunger() const { return maxHunger; } float Entity::getThirst() const { return thirst; } float Entity::getMaxThirst() const { return maxThirst; } Framework::Vec3 Entity::getSpeed() const { return speed; } Framework::Vec3 Entity::getFaceDir() const { return faceDir; } Framework::Vec3 Entity::getPosition() const { return location; } float Entity::getGravityMultiplier() const { return gravityMultiplier; } int Entity::getCurrentDimensionId() const { return currentDimensionId; } bool Entity::isRemoved() const { return removed; } const EntityType* Entity::zType() const { return zEntityType; } const ActionTarget* Entity::zTarget() const { return target; } int Entity::getId() const { return id; } bool Entity::hasDefaultModel() const { return 1; } ModelInfo Entity::getSpecialModel() const { return ModelInfo("", "", 0); }