#include "Entity.h" #include #include #include "BlockType.h" #include "Dimension.h" #include "EntityType.h" #include "Game.h" #include "ItemSkill.h" #include "ItemStack.h" #include "ItemType.h" ActionTarget::ActionTarget(Framework::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 || blockSide == NO_DIRECTION); } bool ActionTarget::isEntity(int entityId) const { return this->entityId == entityId; } bool ActionTarget::useItemSkillOnTarget( Entity* zActor, ItemSkill* zItemSkill, Item* zUsedItem) { if (entityId >= 0) { Entity* target = Game::INSTANCE->zEntity(entityId); if (target) { return zItemSkill->use(zActor, zUsedItem, target); } } else { Block* block = Game::INSTANCE->zRealBlockInstance( blockPos, zActor->getDimensionId()); if (block) { return zItemSkill->use(zActor, zUsedItem, block); } } return 0; } bool ActionTarget::interactItemSkillOnTarget( Entity* zActor, ItemSkill* zItemSkill, Item* zUsedItem) { if (zItemSkill) { if (entityId >= 0) { Entity* target = Game::INSTANCE->zEntity(entityId); if (target) return zItemSkill->interact(zActor, zUsedItem, target); } else { Block* block = Game::INSTANCE->zRealBlockInstance( blockPos, zActor->getDimensionId()); if (block) return zItemSkill->interact(zActor, zUsedItem, block); } } else { bool itemChanged = 0; if (entityId >= 0) { Block* block = Game::INSTANCE->zRealBlockInstance( blockPos, zActor->getDimensionId()); if (block) block->interact(zUsedItem, zActor, itemChanged); } else { Block* block = Game::INSTANCE->zRealBlockInstance( blockPos, zActor->getDimensionId()); if (block) block->interact(zUsedItem, zActor, itemChanged); } return itemChanged; } return 0; } bool ActionTarget::placeBlock(Entity* zActor, Item* zItem) { if (zActor->getStamina() > 0.2f) { if (zItem->canBePlacedAt(zActor->getDimensionId(), blockPos + getDirection(targetBlockSide))) { Block* block = zItem->zPlacedBlockType()->createBlockAt( blockPos + getDirection(targetBlockSide), zActor->getDimensionId(), zItem); if (block) { Game::INSTANCE->zDimension(zActor->getDimensionId()) ->placeBlock(block->getPos(), block); zItem->onPlaced(); zActor->setStamina(zActor->getStamina() - 0.2f); return 1; } } } return 0; } void ActionTarget::toMessage( const ActionTarget* zTarget, int dimensionId, NetworkMessage* zMsg) { if (zTarget) { if (zTarget->entityId >= 0) { Entity* zEntity = Game::INSTANCE->zEntity(zTarget->entityId); if (zEntity) { Framework::XML::Element* targetUIML = zEntity->getTargetUIML(); Framework::Text targetUIMLText = targetUIML ? targetUIML->toString() : Framework::Text(); targetUIML->release(); char* message = new char[8 + targetUIMLText.getLength()]; message[0] = 3; message[1] = 1; *(int*)(message + 2) = zTarget->entityId; *(short*)(message + 6) = (short)targetUIMLText.getLength(); memcpy(message + 8, targetUIMLText.getText(), targetUIMLText.getLength()); zMsg->setMessage(message, 8 + targetUIMLText.getLength()); } else { char* message = new char[2]; message[0] = 3; message[1] = 0; zMsg->setMessage(message, 2); } } else { Framework::XML::Element* targetUIML = 0; auto block = Game::INSTANCE->zBlockAt(zTarget->blockPos, dimensionId, 0); if (block.isA()) { targetUIML = block.getA()->getTargetUIML(); } else if (block.isB()) { targetUIML = Game::INSTANCE->zBlockType(block.getB())->getTargetUIML(); } Framework::Text targetUIMLText = targetUIML ? targetUIML->toString() : Framework::Text(); char* message = new char[18 + targetUIMLText.getLength() + 2]; 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; short len = (short)targetUIMLText.getLength(); *(short*)(message + 18) = len; memcpy(message + 20, targetUIMLText.getText(), len); zMsg->setMessage(message, 18 + len + 2); } } 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( int typeId, Framework::Vec3 location, int dimensionId, int entityId) : Inventory(location, dimensionId, true), chatSecurityLevel(0), lastChunkCenter(0, 0), lastSavedChunkCenter(Framework::Maybe::empty()), lastDimensionId(-1), maxMovementSpeed(0.f), speed(0, 0, 0), faceDir(1, 0, 0), target(0), typeId(typeId), removed(0), gravityMultiplier(1.f), jumpSpeed(0.f), id(entityId), placeBlockCooldown(0.0), movementFlags(0), collisionMap(0), collisionMapLength(0) {} void Entity::onDeath(Entity* zActor, Item* zUsedItem, ItemSkill* zUsedSkill) { if (!removed) { for (DropConfig* config : zType()->getDropConfigs()) { config->onObjectDestroyed(zActor, zUsedItem, zUsedSkill, this); } Dimension* dim = Game::INSTANCE->zDimension(dimensionId); if (dim) { Chunk* chunk = dim->zChunk(lastChunkCenter); if (chunk) { chunk->onEntityLeaves(this, 0); } dim->removeEntity(id); } removed = 1; } } bool Entity::useItem(int typeId, ItemStack* zStack, bool left) { if (left) { if (!zStack || !zStack->zItem() || zStack->zItem()->isUsable()) { cs.lock(); if (target) { ItemSkill* selected = zSkill(typeId); if (!selected) { selected = Game::INSTANCE->zItemType(typeId) ->createDefaultItemSkill(); selected->setItemTypeId(typeId); if (selected) skills.add(selected); } if (!selected) { selected = zSkill(ItemTypeEnum::PLAYER_HAND); selected->setItemTypeId(ItemTypeEnum::PLAYER_HAND); } bool result = target->useItemSkillOnTarget(this, selected, !zStack || zStack->getSize() > 1 ? 0 : (Item*)zStack->zItem()); cs.unlock(); return result; } cs.unlock(); } else { useItem(ItemTypeEnum::PLAYER_HAND, 0, left); } } else { if (zStack && zStack->zItem() && zStack->zItem()->isPlaceable() && zStack->getSize() > 0) { // place item cs.lock(); if (target) { if (placeBlockCooldown <= 0) { Item* item = zStack->extractFromStack(); bool result = target->placeBlock(this, item); if (item->getHp() > 0) { if (!zStack->addToStack( dynamic_cast(item->getThis()))) { ItemStack* newStack = new ItemStack(item, 1); addItems(newStack, NO_DIRECTION, 0); if (newStack->getSize()) { Game::INSTANCE->spawnItem( location, dimensionId, newStack); } } else { item->release(); } } else { item->release(); } if (result) { placeBlockCooldown = 1.0; } cs.unlock(); return result; } else { cs.unlock(); return 0; } } cs.unlock(); } if (zStack && zStack->zItem() && zStack->zItem()->isEatable() && zStack->getSize() > 0) { // eat item if (zStack->getSize() == 1) { return ((Item*)zStack->zItem())->applyFoodEffects(this); } else { if (zStack->zItem()->canApplyFoodEffectsFully(this)) { Item* item = zStack->extractFromStack(); item->applyFoodEffects(this); item->release(); return 1; } } } if (!zStack || !zStack->zItem() || zStack->zItem()->isUsable()) { cs.lock(); if (target) { ItemSkill* selected = zSkill(typeId); if (!selected) { selected = Game::INSTANCE->zItemType(typeId) ->createDefaultItemSkill(); selected->setItemTypeId(typeId); if (selected) skills.add(selected); } if (!selected) { selected = zSkill(ItemTypeEnum::PLAYER_HAND); selected->setItemTypeId(ItemTypeEnum::PLAYER_HAND); } bool result = target->interactItemSkillOnTarget(this, selected, !zStack || zStack->getSize() > 1 ? 0 : (Item*)zStack->zItem()); cs.unlock(); return result; } cs.unlock(); } else { useItem(ItemTypeEnum::PLAYER_HAND, 0, left); } } return 0; } void Entity::onTargetChange() {} bool Entity::interact(Item* zItem, Entity* zActor) { return false; } void Entity::calculateTarget(const Item* zItem) { Framework::Vec3 headPosition = location + faceOffset; int px = (int)floor(headPosition.x); int py = (int)floor(headPosition.y); int pz = (int)floor(headPosition.z); Framework::Vec3 direction = faceDir; direction.normalize(); Direction dir = BOTTOM; bool found = false; bool changed = false; while (true) { if (getDefaultBlock( Game::INSTANCE->zBlockAt( Framework::Vec3{px, py, pz}, dimensionId, 0)) ->isInteractable(zItem)) { found = true; if (!target || !target->isBlock({px, py, pz}, dir)) { changed = true; } break; } // collision to neighbor of current block if (direction.x > 0) { float xt = ((float)px + 1.f - headPosition.x) / direction.x; Framework::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; Framework::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; Framework::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; Framework::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; Framework::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; Framework::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) { changed = true; } break; } float distSq = Framework::Vec3((float)px, (float)py, (float)pz) .abstandSq(headPosition); Entity* zte = Game::INSTANCE->zDimension(dimensionId) ->zTarget(headPosition, direction, distSq); if (zte) { if (!target || !target->isEntity(zte->getId())) { cs.lock(); delete target; target = new ActionTarget(zte->getId()); cs.unlock(); onTargetChange(); } } else if (changed) { if (target && !found) { cs.lock(); delete target; target = 0; cs.unlock(); onTargetChange(); } else { cs.lock(); delete target; target = new ActionTarget({px, py, pz}, dir); cs.unlock(); onTargetChange(); } } } void Entity::notifyStatusBarObservers(NetworkMessage* msg) { statusBarObservable.notifyObservers(msg); } ItemSkill* Entity::zSkill(int itemType) { for (ItemSkill* skill : skills) { if (skill->getItemTypeId() == itemType) { return skill; } } return 0; } void Entity::calcBlockCollision(int& x, int& y, int& xl, int& yl) { Framework::Vec3 minXY = -boundingBox; Framework::Vec3 maxXY = boundingBox; Framework::Vec3 minXmaxY(minXY.x, maxXY.y, minXY.z); Framework::Vec3 maxXminY(maxXY.x, minXY.y, minXY.z); maxXY.z = minXY.z; minXY.rotateZ(rotation); maxXY.rotateZ(rotation); minXmaxY.rotateZ(rotation); maxXminY.rotateZ(rotation); minXY += location; maxXY += location; minXmaxY += location; maxXminY += location; x = (int)minXY.x; y = (int)minXY.y; if (x > (int)minXmaxY.x) x = (int)minXmaxY.x; if (x > (int)maxXminY.x) x = (int)maxXminY.x; if (x > (int)maxXY.x) x = (int)maxXY.x; if (y > (int)minXmaxY.y) y = (int)minXmaxY.y; if (y > (int)maxXminY.y) y = (int)maxXminY.y; if (y > (int)maxXY.y) y = (int)maxXY.y; xl = (int)minXY.x; if (xl < (int)minXmaxY.x) xl = (int)minXmaxY.x; if (xl < (int)maxXminY.x) xl = (int)maxXminY.x; if (xl < (int)maxXY.x) xl = (int)maxXY.x; yl = (int)minXY.y; if (yl < (int)minXmaxY.y) yl = (int)minXmaxY.y; if (yl < (int)maxXminY.y) yl = (int)maxXminY.y; if (yl < (int)maxXY.y) yl = (int)maxXY.y; xl = xl - x + 1; yl = yl - y + 1; if (collisionMapLength < xl * yl) { delete[] collisionMap; collisionMap = new bool[xl, yl]; collisionMapLength = xl * yl; } memset(collisionMap, 0, xl * yl * sizeof(bool)); int xi = (int)minXY.x - x; int yi = (int)minXY.y - y; collisionMap[xi + yi * xl] = 1; xi = (int)maxXY.x - x; yi = (int)maxXY.y - y; collisionMap[xi + yi * xl] = 1; xi = (int)minXmaxY.x - x; yi = (int)minXmaxY.y - y; collisionMap[xi + yi * xl] = 1; xi = (int)maxXminY.x - x; yi = (int)maxXminY.y - y; collisionMap[xi + yi * xl] = 1; auto dir = minXmaxY - minXY; float m = 0; if (abs(dir.x) >= abs(dir.y)) { if (dir.x != 0 && dir.y != 0) { m = dir.y / dir.x; } float yy = minXY.y; for (int xx = (int)minXY.x; xx < (int)minXmaxY.x; xx++) { int xi = xx - x; int yi = (int)yy - y; collisionMap[xi + yi * xl] = 1; yy += m; } } else { if (dir.x != 0 && dir.y != 0) { m = dir.x / dir.y; } float xx = minXY.x; for (int yy = (int)minXY.y; yy < (int)minXmaxY.y; yy++) { int xi = (int)xx - x; int yi = yy - y; collisionMap[xi + yi * xl] = 1; xx += m; } } dir = maxXminY - minXY; m = 0; if (abs(dir.x) >= abs(dir.y)) { if (dir.x != 0 && dir.y != 0) { m = dir.y / dir.x; } float yy = minXY.y; for (int xx = (int)minXY.x; xx < (int)maxXminY.x; xx++) { int xi = xx - x; int yi = (int)yy - y; collisionMap[xi + yi * xl] = 1; yy += m; } } else { if (dir.x != 0 && dir.y != 0) { m = dir.x / dir.y; } float xx = minXY.x; for (int yy = (int)minXY.y; yy < (int)maxXminY.y; yy++) { int xi = (int)xx - x; int yi = yy - y; collisionMap[xi + yi * xl] = 1; xx += m; } } dir = maxXY - maxXminY; m = 0; if (abs(dir.x) >= abs(dir.y)) { if (dir.x != 0 && dir.y != 0) { m = dir.y / dir.x; } float yy = maxXminY.y; for (int xx = (int)maxXminY.x; xx < (int)maxXY.x; xx++) { int xi = xx - x; int yi = (int)yy - y; collisionMap[xi + yi * xl] = 1; yy += m; } } else { if (dir.x != 0 && dir.y != 0) { m = dir.x / dir.y; } float xx = maxXminY.x; for (int yy = (int)maxXminY.y; yy < (int)maxXY.y; yy++) { int xi = (int)xx - x; int yi = yy - y; collisionMap[xi + yi * xl] = 1; xx += m; } } dir = maxXY - minXmaxY; m = 0; if (abs(dir.x) >= abs(dir.y)) { if (dir.x != 0 && dir.y != 0) { m = dir.y / dir.x; } float yy = minXmaxY.y; for (int xx = (int)minXmaxY.x; xx < (int)maxXY.x; xx++) { int xi = xx - x; int yi = (int)yy - y; collisionMap[xi + yi * xl] = 1; yy += m; } } else { if (dir.x != 0 && dir.y != 0) { m = dir.x / dir.y; } float xx = minXmaxY.x; for (int yy = (int)minXmaxY.y; yy < (int)maxXY.y; yy++) { int xi = (int)xx - x; int yi = yy - y; collisionMap[xi + yi * xl] = 1; xx += m; } } for (int yy = 0; yy < yl; yy++) { int min = xl; int max = -1; for (int xx = 0; xx < xl; xx++) { if (collisionMap[xx + yy * xl]) { if (xx < min) min = xx; if (xx > max) max = xx; } } for (int xx = min; xx <= max; xx++) { collisionMap[xx + yy * xl] = 1; } } } bool Entity::isCollidingWithBlock(const Dimension* zDimension) { int x, y, xl, yl; calcBlockCollision(x, y, xl, yl); bool result = isCollidingWithBlock(zDimension, x, y, xl, yl); return result; } bool Entity::isCollidingWithBlock( const Dimension* zDimension, int x, int y, int xl, int yl) { for (int yy = 0; yy < yl; yy++) { for (int xx = 0; xx < xl; xx++) { if (collisionMap[xx + yy * xl]) { for (int zz = (int)(location.z - boundingBox.z); zz <= (int)(location.z + boundingBox.z); zz++) { const Block* block = zDimension->zBlockOrDefault( Framework::Vec3(x + xx, y + yy, zz)); if (!block->isPassable()) { return true; } } } } } return false; } void Entity::prepareTick(const Dimension* zDimension, double seconds) {} void Entity::tick(const Dimension* zDimension, double seconds) { if (removed) return; if (placeBlockCooldown > 0) { placeBlockCooldown -= seconds; } bool changed = 0; float rotSpeed = 0.f; if (movementFlags & MovementFlags::ROTATE_LEFT) { rotSpeed += 3.f * (float)seconds; } if (movementFlags & MovementFlags::ROTATE_RIGHT) { rotSpeed -= 3.f * (float)seconds; } if (movementFlags & MovementFlags::ROTATE_TO_FACE) { float rot = Framework::Vec2(0, -1).angle({faceDir.x, faceDir.y}); rotSpeed = rot - rotation; } if (rotSpeed != 0.f) { rotation += rotSpeed; if (isCollidingWithBlock(zDimension)) { rotation -= rotSpeed; } else { changed = 1; } while (rotation > 2.f * (float)PI) { rotation -= 2.f * (float)PI; } while (rotation < 0.f) { rotation += 2.f * (float)PI; } } Framework::Vec3 moveDir(0.f, 0.f, 0.f); if (movementFlags & MovementFlags::WALK_FORWARD) { moveDir.x += cosf(rotation - (float)PI / 2.f); moveDir.y += sinf(rotation - (float)PI / 2.f); } if (movementFlags & MovementFlags::WALK_BACKWARD) { moveDir.x -= cosf(rotation - (float)PI / 2.f); moveDir.y -= sinf(rotation - (float)PI / 2.f); } if (movementFlags & MovementFlags::WALK_LEFT) { moveDir.x += cosf(rotation + (float)PI); moveDir.y += sinf(rotation + (float)PI); } if (movementFlags & MovementFlags::WALK_RIGHT) { moveDir.x += cosf(rotation); moveDir.y += sinf(rotation); } if (movementFlags & MovementFlags::FLYING && movementFlags & MovementFlags::JUMPING) { moveDir.z += 1.f; movementFlags &= ~MovementFlags::GROUND_CONTACT; } if (movementFlags & MovementFlags::FLYING && movementFlags & MovementFlags::SNEAKING) { moveDir.z -= 1.f; } float staminaCost = 0.f; if (moveDir.x != 0.f || moveDir.y != 0.f) { moveDir.normalize(); moveDir *= maxMovementSpeed * (float)seconds; if (movementFlags & MovementFlags::SPRINTING) { moveDir *= 2.f; } if (movementFlags & MovementFlags::SNEAKING && !(movementFlags & MovementFlags::FLYING)) { moveDir *= 0.5f; } staminaCost = moveDir.getLength() / (maxMovementSpeed * 10); if (stamina < staminaCost) { staminaCost = stamina; moveDir *= stamina / staminaCost; } } if (!(movementFlags & MovementFlags::FLYING) && movementFlags & MovementFlags::JUMPING && movementFlags & MovementFlags::GROUND_CONTACT) { if (stamina > staminaCost + 0.02f) { staminaCost += 0.02f; speed.z += jumpSpeed; movementFlags &= ~MovementFlags::GROUND_CONTACT; } } if (!(movementFlags & MovementFlags::FLYING)) { speed.z -= zDimension->getGravity() * gravityMultiplier * (float)seconds; } moveDir += speed * (float)seconds; bool staminaChanged = staminaCost != 0.f; stamina -= staminaCost; if (stamina <= maxStamina - (float)seconds) { if (getThirst() > 0 && getHunger() > 0) { // TODO: modify regen rate based on hunger/thirst stamina += (float)seconds; staminaChanged = 1; setHunger(hunger - (float)seconds / 5.f); setThirst(thirst - (float)seconds / 2.5f); } } if (staminaChanged) { setStamina(stamina); // notify observers } if (moveDir.x != 0.f || moveDir.y != 0.f || moveDir.z != 0.f) { while (abs(moveDir.x) > 1.f) { if (moveDir.x > 0.f) { location.x += 1.f; if (isCollidingWithBlock(zDimension)) { moveDir.x = 0.f; speed.x = 0.f; location.x -= 1.f; } else { moveDir.x -= 1.f; changed = 1; } } else { location.x -= 1.f; if (isCollidingWithBlock(zDimension)) { moveDir.x = 0.f; speed.x = 0.f; location.x += 1.f; } else { moveDir.x += 1.f; changed = 1; } } } if (moveDir.x != 0.f) { location.x += moveDir.x; if (isCollidingWithBlock(zDimension)) { location.x -= moveDir.x; speed.x = 0.f; } else { changed = 1; } } while (abs(moveDir.y) > 1.f) { if (moveDir.y > 0.f) { location.y += 1.f; if (isCollidingWithBlock(zDimension)) { moveDir.y = 0.f; location.y -= 1.f; speed.y = 0.f; } else { moveDir.y -= 1.f; changed = 1; } } else { location.y -= 1.f; if (isCollidingWithBlock(zDimension)) { moveDir.y = 0.f; location.y += 1.f; speed.y = 0.f; } else { moveDir.y += 1.f; changed = 1; } } } if (moveDir.y != 0.f) { location.y += moveDir.y; if (isCollidingWithBlock(zDimension)) { location.y -= moveDir.y; speed.y = 0.f; } else { changed = 1; } } while (abs(moveDir.z) > 1.f) { if (moveDir.z > 0.f) { location.z += 1.f; if (isCollidingWithBlock(zDimension)) { moveDir.z = 0.f; location.z -= 1.f; speed.z = 0.f; } else { moveDir.z -= 1.f; changed = 1; } } else { location.z -= 1.f; if (isCollidingWithBlock(zDimension)) { moveDir.z = 0.f; location.z += 1.f; onFall(speed.z); speed.z = 0.f; movementFlags |= MovementFlags::GROUND_CONTACT; } else { moveDir.z += 1.f; changed = 1; } } } if (moveDir.z != 0.f) { location.z += moveDir.z; if (isCollidingWithBlock(zDimension)) { if (speed.z < 0.f) { onFall(speed.z); speed.z = 0.f; movementFlags |= MovementFlags::GROUND_CONTACT; } else { changed = 1; } location.z -= moveDir.z; } } } if (movementFlags & MovementFlags::GROUND_CONTACT) { if (speed.x != 0) { if (speed.x > (float)seconds) { speed.x -= (float)seconds; } else if (speed.x < -(float)seconds) { speed.x += (float)seconds; } else { speed.x = 0; } } if (speed.y != 0) { if (speed.y > (float)seconds) { speed.y -= (float)seconds; } else if (speed.y < -(float)seconds) { speed.y += (float)seconds; } else { speed.y = 0; } } } Framework::Punkt chunkCenter = Game::INSTANCE->getChunkCenter((int)location.x, (int)location.y); Chunk* zCurrentChunk = 0; if (dimensionId != lastDimensionId || chunkCenter != lastChunkCenter) { Dimension* lastDimension = Game::INSTANCE->zDimension(lastDimensionId); Dimension* currentDimension = Game::INSTANCE->zDimension(dimensionId); zCurrentChunk = currentDimension ? currentDimension->zChunk(chunkCenter) : 0; Chunk* zLastChunk = lastDimension ? lastDimension->zChunk(lastChunkCenter) : 0; if (lastDimensionId != -1) { if (zLastChunk) { zLastChunk->onEntityLeaves( this, lastDimensionId == dimensionId ? zCurrentChunk : 0); } } if (zCurrentChunk) { zCurrentChunk->onEntityEnters( this, lastDimensionId == dimensionId ? zLastChunk : 0); } lastDimensionId = dimensionId; lastChunkCenter = chunkCenter; } if (changed) { if (!zCurrentChunk) { Dimension* currentDimension = Game::INSTANCE->zDimension(dimensionId); zCurrentChunk = currentDimension ? currentDimension->zChunk(chunkCenter) : 0; } if (zCurrentChunk) { NetworkMessage* msg = new NetworkMessage(); msg->sendEntityMovement(this, (float)seconds); zCurrentChunk->notifyObservers(msg); } } } void Entity::api(Framework::StreamReader* zRequest, NetworkMessage* zResponse, Entity* zSource) { char type; zRequest->lese(&type, 1); switch (type) { case 0: // request status bar state { char len; zRequest->lese(&len, 1); char* guiId = new char[(int)len + 1]; zRequest->lese(guiId, len); guiId[(int)len] = 0; int processor; zRequest->lese((char*)&processor, 4); zResponse->addressUIElement(guiId, processor); statusBarObservable.addObserver(zSource, guiId, processor); char* msg = new char[33]; msg[0] = 0; *(float*)(msg + 1) = getMaxHP(); *(float*)(msg + 5) = getCurrentHP(); *(float*)(msg + 9) = getMaxStamina(); *(float*)(msg + 13) = getStamina(); *(float*)(msg + 17) = getMaxHunger(); *(float*)(msg + 21) = getHunger(); *(float*)(msg + 25) = getMaxThirst(); *(float*)(msg + 29) = getThirst(); zResponse->setMessage(msg, 33); delete[] guiId; break; } case 1: // remove status bar observer { char len; zRequest->lese(&len, 1); char* guiId = new char[(int)len + 1]; zRequest->lese(guiId, len); guiId[(int)len] = 0; int processor; zRequest->lese((char*)&processor, 4); statusBarObservable.removeObserver(zSource, guiId, processor); delete[] guiId; break; } case 2: // TODO: component request break; } } void Entity::onFall(float collisionSpeed) { if (collisionSpeed > 20) { setHP(this, 0, 0, getCurrentHP() - (collisionSpeed - 20.f) / 2.5f); } } Framework::XML::Element* Entity::getTargetUIML() const { return new Framework::XML::Element( Framework::Text( "") + Game::INSTANCE->zEntityType(typeId)->getName() + ""); } void Entity::setChatSecurityLevel(int level) { chatSecurityLevel = level; } void Entity::setPosition(Framework::Vec3 pos) { location = pos; } void Entity::takeDamage( Entity* zSource, Item* zUsedItem, ItemSkill* zUsedSkill, float damage) { currentHP -= damage; if (currentHP <= 0) { currentHP = 0; onDeath(zSource, zUsedItem, zUsedSkill); } } void Entity::setHP( Entity* zActor, Item* zUsedItem, ItemSkill* zUsedSkill, float hp) { currentHP = MIN(MAX(hp, 0), maxHP); NetworkMessage* msg = new NetworkMessage(); char* message = new char[9]; message[0] = 1; *(float*)(message + 1) = getMaxHP(); *(float*)(message + 5) = getCurrentHP(); msg->setMessage(message, 9); notifyStatusBarObservers(msg); if (currentHP == 0) { onDeath(zActor, zUsedItem, zUsedSkill); } } void Entity::setStamina(float stamina) { this->stamina = MIN(MAX(stamina, 0), maxStamina); NetworkMessage* msg = new NetworkMessage(); char* message = new char[9]; message[0] = 2; *(float*)(message + 1) = getMaxStamina(); *(float*)(message + 5) = getStamina(); msg->setMessage(message, 9); notifyStatusBarObservers(msg); } void Entity::setHunger(float hunger) { this->hunger = MIN(MAX(hunger, 0), maxHunger); NetworkMessage* msg = new NetworkMessage(); char* message = new char[9]; message[0] = 3; *(float*)(message + 1) = getMaxHunger(); *(float*)(message + 5) = getHunger(); msg->setMessage(message, 9); notifyStatusBarObservers(msg); } void Entity::setThirst(float thirst) { this->thirst = MIN(MAX(thirst, 0), maxThirst); NetworkMessage* msg = new NetworkMessage(); char* message = new char[9]; message[0] = 4; *(float*)(message + 1) = getMaxThirst(); *(float*)(message + 5) = getThirst(); msg->setMessage(message, 9); notifyStatusBarObservers(msg); } void Entity::setGravityMultiplier(float multiplier) { gravityMultiplier = multiplier; } 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; } float Entity::getJumpSpeed() const { return jumpSpeed; } void Entity::setJumpSpeed(float speed) { jumpSpeed = speed; } bool Entity::isRemoved() const { return removed; } const EntityType* Entity::zType() const { return Game::INSTANCE->zEntityType(typeId); } const ActionTarget* Entity::zTarget() const { return target; } int Entity::getId() const { return id; } bool Entity::hasDefaultModel() const { return 1; } ModelInfo* Entity::zSpecialModel() const { return 0; } float Entity::getMaxSpeed() const { return maxMovementSpeed; } bool Entity::isMoving() const { return movementFlags > 0; } int Entity::getChatSecurityLevel() const { return chatSecurityLevel; } Framework::Maybe Entity::getLastSavedChunkCenter() const { return lastSavedChunkCenter; } void Entity::setLastSavedChunkCenter(Framework::Punkt pos) { lastSavedChunkCenter = Framework::Maybe::of(pos); } void Entity::setRemoved() { removed = true; } double Entity::getHitDistance( Framework::Vec3 rayOrigin, Framework::Vec3 rayDirection) const { Framework::Vec3 rotatedRayOrigin = Framework::Vec3(rayOrigin).rotateZ(-rotation); Framework::Vec3 rotatedRayDirection = Framework::Vec3(rayDirection).rotateZ(-rotation); rotatedRayDirection.normalize(); if (rotatedRayDirection.x != 0) { float d; if (rotatedRayDirection.x > 0) { float border = getPosition().x - boundingBox.x; d = (border - rotatedRayOrigin.x) / rotatedRayDirection.x; } else if (rotatedRayDirection.x < 0) { float border = getPosition().x + boundingBox.x; d = (border - rotatedRayOrigin.x) / rotatedRayDirection.x; } if (d > 0) { Framework::Vec3 hitPoint = rotatedRayOrigin + rotatedRayDirection * d; if (hitPoint.y >= getPosition().y - boundingBox.y && hitPoint.y <= getPosition().y + boundingBox.y && hitPoint.z >= getPosition().z - boundingBox.z && hitPoint.z <= getPosition().z + boundingBox.z) { return d; } } } if (rotatedRayDirection.y != 0) { float d; if (rotatedRayDirection.y > 0) { float border = getPosition().y - boundingBox.y; d = (border - rotatedRayOrigin.y) / rotatedRayDirection.y; } else if (rotatedRayDirection.y < 0) { float border = getPosition().y + boundingBox.y; d = (border - rotatedRayOrigin.y) / rotatedRayDirection.y; } if (d > 0) { Framework::Vec3 hitPoint = rotatedRayOrigin + rotatedRayDirection * d; if (hitPoint.x >= getPosition().x - boundingBox.x && hitPoint.x <= getPosition().x + boundingBox.x && hitPoint.z >= getPosition().z - boundingBox.z && hitPoint.z <= getPosition().z + boundingBox.z) { return d; } } } if (rotatedRayDirection.z != 0) { float d; if (rotatedRayDirection.z > 0) { float border = getPosition().z - boundingBox.z; d = (border - rotatedRayOrigin.z) / rotatedRayDirection.z; } else if (rotatedRayDirection.z < 0) { float border = getPosition().z + boundingBox.z; d = (border - rotatedRayOrigin.z) / rotatedRayDirection.z; } if (d > 0) { Framework::Vec3 hitPoint = rotatedRayOrigin + rotatedRayDirection * d; if (hitPoint.x >= getPosition().x - boundingBox.x && hitPoint.x <= getPosition().x + boundingBox.x && hitPoint.y >= getPosition().y - boundingBox.y && hitPoint.y <= getPosition().y + boundingBox.y) { return d; } } } return NAN; } float Entity::getRotation() const { return rotation; }