#include "Entity.h" #include "Dimension.h" #include "Game.h" #include "BlockType.h" #include "ItemSkill.h" #include "EntityRemovedUpdate.h" #include "NoBlock.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 auto zB = Game::INSTANCE->zBlockAt(blockPos + getDirection(targetBlockSide), zActor->getCurrentDimensionId()); if ((zB.isA() && zB.getA()->zBlockType()->getId() == AirBlockBlockType::ID) || (zB.isB() && zB.isB() == AirBlockBlockType::ID)) { Block* block = zItem->zPlacedBlockType()->createBlockAt(blockPos + getDirection(targetBlockSide), zItem); if (block) { Game::INSTANCE->zDimension(zActor->getCurrentDimensionId())->placeBlock(block->getPos(), block); zItem->onPlaced(); // TODO: decrese stamina of actor } } } void ActionTarget::toMessage(const ActionTarget* zTarget, NetworkMessage* zMsg) { if (zTarget) { if (zTarget->entityId >= 0) { char* message = new char[6]; message[0] = 3; message[1] = 1; *(int*)(message + 2) = zTarget->entityId; zMsg->setMessage(message, 6); } else { char* message = new char[18]; message[0] = 3; message[1] = 2; *(int*)(message + 2) = zTarget->blockPos.x; *(int*)(message + 6) = zTarget->blockPos.y; *(int*)(message + 10) = zTarget->blockPos.z; *(int*)(message + 14) = zTarget->targetBlockSide; zMsg->setMessage(message, 18); } } else { char* message = new char[2]; message[0] = 3; message[1] = 0; zMsg->setMessage(message, 2); } } 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), 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 cs.lock(); if (target) target->placeBlock(this, zItem); cs.unlock(); } else if (!zItem || zItem->isUsable()) { // use item skill cs.lock(); 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); } cs.unlock(); } } void Entity::onTargetChange() {} void Entity::addMovementFrame(MovementFrame& frame) { cs.lock(); movements.add(frame); cs.unlock(); NetworkMessage* message = new NetworkMessage(); message->addressEntity(this); char* msg = new char[37]; msg[0] = 0; *(float*)(msg + 1) = frame.direction.x; *(float*)(msg + 5) = frame.direction.y; *(float*)(msg + 9) = frame.direction.z; *(float*)(msg + 13) = frame.targetPosition.x; *(float*)(msg + 17) = frame.targetPosition.y; *(float*)(msg + 21) = frame.targetPosition.z; *(int*)(msg + 25) = frame.movementFlags; *(double*)(msg + 29) = frame.duration; message->setMessage(msg, 37); Game::INSTANCE->broadcastMessage(message); faceDir = frame.direction; // TODO implement subscription system to notify only interested clients } void Entity::calculateTarget(Framework::Vec3 basePos, Framework::Vec3 direction) { Vec3 headPosition = basePos + faceOffset; int px = (int)floor(headPosition.x); int py = (int)floor(headPosition.y); int pz = (int)floor(headPosition.z); direction.normalize(); Direction dir = BOTTOM; while (true) { if (getDefaultBlock(Game::INSTANCE->zBlockAt(Vec3{ px, py, pz }, currentDimensionId))->isInteractable()) { if (!target || !target->isBlock({ px, py, pz }, dir)) { cs.lock(); delete target; target = new ActionTarget({ px, py, pz }, dir); cs.unlock(); onTargetChange(); } break; } // collision to neighbor of current block if (direction.x > 0) { float xt = ((float)px + 1.f - headPosition.x) / direction.x; Vec3 tmp = headPosition + direction * 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 (direction.x < 0) { float xt = ((float)px - headPosition.x) / direction.x; Vec3 tmp = headPosition + direction * 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 (direction.y > 0) { float yt = ((float)py + 1.f - headPosition.y) / direction.y; Vec3 tmp = headPosition + direction * 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 (direction.y < 0) { float yt = ((float)py - headPosition.y) / direction.y; Vec3 tmp = headPosition + direction * 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 (direction.z > 0) { float zt = ((float)pz + 1.f - headPosition.z) / direction.z; Vec3 tmp = headPosition + direction * 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 (direction.z < 0) { float zt = ((float)pz - headPosition.z) / direction.z; Vec3 tmp = headPosition + direction * 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) { cs.lock(); delete target; target = 0; cs.unlock(); onTargetChange(); } break; } } void Entity::prepareTick(const Dimension* zDimension) {} void Entity::tick(const Dimension* zDimension) { if (time.isMeasuring()) { time.messungEnde(); if (movements.getEintragAnzahl() > 0) { MovementFrame currentFrame = movements.get(0); double seconds = time.getSekunden(); while (seconds > 0) { if (currentFrame.duration <= 0) { cs.lock(); movements.remove(0); cs.unlock(); if (movements.getEintragAnzahl() > 0) currentFrame = movements.get(0); else break; } double t = MIN(currentFrame.duration, seconds); // TODO: add collisin detection to reduce cheating capability location += (currentFrame.targetPosition - location) * (float)(t / currentFrame.duration); currentFrame.duration -= t; seconds -= t; if (currentFrame.duration <= 0) { location = currentFrame.targetPosition; } } if (currentFrame.duration > 0) movements.set(currentFrame, 0); } } time.messungStart(); } void Entity::api(Framework::StreamReader* zRequest, NetworkMessage* zResponse) {} 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); } float Entity::getMaxSpeed() const { return maxMovementSpeed; }