#include "Entity.h" #include "Dimension.h" #include "Game.h" #include "BlockType.h" #include "ItemSkill.h" #include "PlaceBlockUpdate.h" #include "EntityRemovedUpdate.h" #include "EntityChangedUpdate.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 ) { needUpdate = 0; Game::INSTANCE->requestWorldUpdate( new EntityChangedUpdate( id, location, currentDimensionId ) ); } } void Entity::api( Framework::StreamReader* zRequest, NetworkResponse* zResponse ) { // TODO: answer api requests } 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; }