1355 lines
34 KiB
C++
1355 lines
34 KiB
C++
/*
|
|
Copyright (c) 2013 yvt
|
|
|
|
This file is part of OpenSpades.
|
|
|
|
OpenSpades is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
OpenSpades is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with OpenSpades. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
#include "Player.h"
|
|
#include "PhysicsConstants.h"
|
|
#include "World.h"
|
|
#include "GameMap.h"
|
|
#include "IWorldListener.h"
|
|
#include "Weapon.h"
|
|
#include "../Core/Exception.h"
|
|
#include "GameMapWrapper.h"
|
|
#include "Grenade.h"
|
|
#include "../Core/Debug.h"
|
|
|
|
namespace spades {
|
|
namespace client {
|
|
|
|
Player::Player(World *w, int playerId,
|
|
WeaponType wType, int teamId,
|
|
Vector3 position,
|
|
IntVector3 color):
|
|
world(w){
|
|
SPADES_MARK_FUNCTION();
|
|
|
|
lastClimbTime = -100;
|
|
lastJumpTime = -100;
|
|
lastJump = false;
|
|
tool = ToolWeapon;
|
|
airborne = false;
|
|
wade = false;
|
|
this->position = position;
|
|
velocity = MakeVector3(0,0,0);
|
|
orientation = MakeVector3(1,0,0);
|
|
if(teamId) // quick hack for correct spawn orientation
|
|
orientation = MakeVector3(-1,0,0);
|
|
eye = MakeVector3(0,0,0);
|
|
moveDistance = 0.f;
|
|
moveSteps = 0;
|
|
|
|
this->playerId = playerId;
|
|
this->weapon = Weapon::CreateWeapon(wType, this);
|
|
this->teamId = teamId;
|
|
this->weapon->Reset();
|
|
this->color = color;
|
|
|
|
health = 100;
|
|
grenades = 3;
|
|
blockStocks = 50;
|
|
blockColor = IntVector3::Make(111, 111, 111);
|
|
|
|
nextSpadeTime = 0.f;
|
|
nextDigTime = 0.f;
|
|
nextGrenadeTime = 0.f;
|
|
nextBlockTime = 0.f;
|
|
firstDig = false;
|
|
|
|
pendingPlaceBlock = false;
|
|
|
|
blockCursorActive = false;
|
|
blockCursorDragging = false;
|
|
|
|
holdingGrenade = false;
|
|
reloadingServerSide = false;
|
|
canPending = false;
|
|
}
|
|
|
|
Player::~Player() {
|
|
SPADES_MARK_FUNCTION();
|
|
delete weapon;
|
|
}
|
|
|
|
void Player::SetInput(PlayerInput newInput) {
|
|
SPADES_MARK_FUNCTION();
|
|
|
|
if(!IsAlive())
|
|
return;
|
|
|
|
if(newInput.crouch != input.crouch) {
|
|
if(newInput.crouch)
|
|
position.z += 0.9f;
|
|
else
|
|
position.z -= 0.9f;
|
|
}
|
|
input = newInput;
|
|
}
|
|
|
|
void Player::SetWeaponInput(WeaponInput newInput){
|
|
SPADES_MARK_FUNCTION();
|
|
|
|
if(!IsAlive())
|
|
return;
|
|
|
|
if(input.sprint){
|
|
newInput.primary = false;
|
|
newInput.secondary = false;
|
|
}
|
|
if(tool == ToolSpade){
|
|
if(newInput.secondary)
|
|
newInput.primary = false;
|
|
if(newInput.secondary != weapInput.secondary){
|
|
if(newInput.secondary){
|
|
nextDigTime = world->GetTime() + 1.f;
|
|
firstDig = true;
|
|
}
|
|
}
|
|
}else if(tool == ToolGrenade) {
|
|
if(world->GetTime() < nextGrenadeTime){
|
|
newInput.primary = false;
|
|
}
|
|
if(grenades == 0){
|
|
newInput.primary = false;
|
|
}
|
|
if(weapInput.primary && holdingGrenade &&
|
|
GetGrenadeCookTime() < .15f) {
|
|
// pin is not pulled yet
|
|
newInput.primary = true;
|
|
}
|
|
if(newInput.primary != weapInput.primary){
|
|
if(!newInput.primary){
|
|
if(holdingGrenade){
|
|
nextGrenadeTime = world->GetTime() + .5f;
|
|
ThrowGrenade();
|
|
}
|
|
}else{
|
|
holdingGrenade = true;
|
|
grenadeTime = world->GetTime();
|
|
if(world->GetListener() &&
|
|
this == world->GetLocalPlayer())
|
|
// playing other's grenade sound
|
|
// is cheating
|
|
world->GetListener()->LocalPlayerPulledGrenadePin();
|
|
}
|
|
}
|
|
}else if(tool == ToolBlock){
|
|
if(world->GetTime() < nextBlockTime){
|
|
newInput.primary = false;
|
|
newInput.secondary = false;
|
|
}
|
|
if(newInput.secondary)
|
|
newInput.primary = false;
|
|
if(newInput.secondary != weapInput.secondary){
|
|
if(newInput.secondary){
|
|
if(IsBlockCursorActive()){
|
|
blockCursorDragging = true;
|
|
blockCursorDragPos = blockCursorPos;
|
|
}
|
|
}else {
|
|
if(IsBlockCursorActive() &&
|
|
IsBlockCursorDragging()){
|
|
std::vector<IntVector3> blocks = GetWorld()->CubeLine(blockCursorDragPos,
|
|
blockCursorPos, 256);
|
|
if(blocks.size() <= (int)blockStocks){
|
|
if(GetWorld()->GetListener() &&
|
|
this == world->GetLocalPlayer())
|
|
GetWorld()->GetListener()->LocalPlayerCreatedLineBlock(blockCursorDragPos, blockCursorPos);
|
|
//blockStocks -= blocks.size(); decrease when created
|
|
}
|
|
if(blockCursorActive){
|
|
nextBlockTime = world->GetTime() + GetToolSecondaryDelay();
|
|
}
|
|
}
|
|
blockCursorDragging = false;
|
|
blockCursorActive = false;
|
|
}
|
|
}
|
|
if(newInput.primary != weapInput.primary){
|
|
if(newInput.primary){
|
|
if(IsBlockCursorActive() && blockStocks > 0){
|
|
if(GetWorld()->GetListener() &&
|
|
this == world->GetLocalPlayer())
|
|
GetWorld()->GetListener()->LocalPlayerBlockAction(blockCursorPos, BlockActionCreate);
|
|
|
|
|
|
// blockStocks--; decrease when created
|
|
|
|
nextBlockTime = world->GetTime() + GetToolPrimaryDelay();
|
|
}else if(blockStocks > 0 && airborne && canPending &&
|
|
this == world->GetLocalPlayer()) {
|
|
pendingPlaceBlock = true;
|
|
pendingPlaceBlockPos = blockCursorPos;
|
|
}
|
|
|
|
blockCursorDragging = false;
|
|
blockCursorActive = false;
|
|
}
|
|
}
|
|
}else if(IsToolWeapon()){
|
|
weapon->SetShooting(newInput.primary);
|
|
}else{
|
|
SPAssert(false);
|
|
}
|
|
|
|
weapInput = newInput;
|
|
|
|
|
|
}
|
|
|
|
void Player::Reload() {
|
|
SPADES_MARK_FUNCTION();
|
|
if(health == 0){
|
|
// dead man cannot reload
|
|
return;
|
|
}
|
|
weapon->Reload();
|
|
if(this == world->GetLocalPlayer())
|
|
reloadingServerSide = true;
|
|
}
|
|
|
|
void Player::ReloadDone(int clip, int stock) {
|
|
reloadingServerSide = false;
|
|
weapon->ReloadDone(clip, stock);
|
|
}
|
|
|
|
void Player::Restock() {
|
|
SPADES_MARK_FUNCTION();
|
|
if(health == 0){
|
|
// dead man cannot restock
|
|
return;
|
|
}
|
|
|
|
weapon->Restock();
|
|
grenades = 3;
|
|
blockStocks = 50;
|
|
health = 100;
|
|
|
|
if(world->GetListener())
|
|
world->GetListener()->PlayerRestocked(this);
|
|
}
|
|
|
|
void Player::GotBlock() {
|
|
if(blockStocks < 50)
|
|
blockStocks++;
|
|
}
|
|
|
|
void Player::SetTool(spades::client::Player::ToolType t) {
|
|
SPADES_MARK_FUNCTION();
|
|
|
|
if(t == tool)
|
|
return;
|
|
tool = t;
|
|
holdingGrenade = false;
|
|
blockCursorActive = false;
|
|
blockCursorDragging = false;
|
|
|
|
reloadingServerSide = false;
|
|
|
|
WeaponInput inp;
|
|
SetWeaponInput(inp);
|
|
|
|
weapon->AbortReload();
|
|
|
|
if(world->GetListener())
|
|
world->GetListener()->PlayerChangedTool(this);
|
|
}
|
|
|
|
void Player::SetHeldBlockColor(spades::IntVector3 col){
|
|
blockColor = col;
|
|
}
|
|
|
|
void Player::SetPosition(const spades::Vector3 &v){
|
|
SPADES_MARK_FUNCTION();
|
|
|
|
position = v;
|
|
eye = v; // FIXME
|
|
|
|
}
|
|
|
|
void Player::SetOrientation(const spades::Vector3 &v) {
|
|
SPADES_MARK_FUNCTION();
|
|
|
|
orientation = v;
|
|
}
|
|
|
|
void Player::Turn(float longitude, float latitude){
|
|
SPADES_MARK_FUNCTION();
|
|
|
|
Vector3 o = GetFront();
|
|
float lng = atan2f(o.y, o.x);
|
|
float lat = atan2f(o.z, sqrtf(o.x*o.x+o.y*o.y));
|
|
|
|
lng += longitude;
|
|
lat += latitude;
|
|
|
|
if(lat < -M_PI * .49f)
|
|
lat = -M_PI * .49f;
|
|
if(lat > M_PI * .49f)
|
|
lat = M_PI * .49f;
|
|
|
|
o.x = cosf(lng) * cosf(lat);
|
|
o.y = sinf(lng) * cosf(lat);
|
|
o.z = sinf(lat);
|
|
SetOrientation(o);
|
|
}
|
|
|
|
void Player::SetHP(int hp,
|
|
HurtType type,
|
|
spades::Vector3 p) {
|
|
health = hp;
|
|
if(this == world->GetLocalPlayer()){
|
|
if(world->GetListener())
|
|
world->GetListener()->LocalPlayerHurt(type,
|
|
p.x != 0.f ||
|
|
p.y != 0.f ||
|
|
p.z != 0.f,
|
|
p);
|
|
}
|
|
}
|
|
|
|
void Player::Update(float dt) {
|
|
SPADES_MARK_FUNCTION();
|
|
|
|
MovePlayer(dt);
|
|
if(tool == ToolSpade){
|
|
if(weapInput.primary){
|
|
if(world->GetTime() > nextSpadeTime){
|
|
UseSpade();
|
|
nextSpadeTime = world->GetTime() + GetToolPrimaryDelay();
|
|
}
|
|
}else if(weapInput.secondary){
|
|
if(world->GetTime() > nextDigTime){
|
|
DigWithSpade();
|
|
nextDigTime = world->GetTime() + GetToolSecondaryDelay();
|
|
firstDig = false;
|
|
}
|
|
}
|
|
}else if(tool == ToolBlock){
|
|
GameMap::RayCastResult result;
|
|
result = GetWorld()->GetMap()->CastRay2(GetEye(),
|
|
GetFront(),
|
|
12);
|
|
canPending = false;
|
|
if(result.hit && (result.hitBlock + result.normal).z < 62 &&
|
|
(!OverlapsWithOneBlock(result.hitBlock + result.normal)) &&
|
|
BoxDistanceToBlock(result.hitBlock + result.normal) < 3.f &&
|
|
!pendingPlaceBlock){
|
|
blockCursorActive = true;
|
|
blockCursorPos = result.hitBlock + result.normal;
|
|
}else if(pendingPlaceBlock){
|
|
if(airborne == false || blockStocks <= 0){
|
|
pendingPlaceBlock = false;
|
|
}else if((!OverlapsWithOneBlock(pendingPlaceBlockPos)) &&
|
|
BoxDistanceToBlock(pendingPlaceBlockPos) < 3.f){
|
|
SPAssert(this == world->GetLocalPlayer());
|
|
|
|
if(GetWorld()->GetListener())
|
|
GetWorld()->GetListener()->LocalPlayerBlockAction(pendingPlaceBlockPos, BlockActionCreate);
|
|
|
|
pendingPlaceBlock = false;
|
|
// blockStocks--; decrease when created
|
|
|
|
nextBlockTime = world->GetTime() + GetToolPrimaryDelay();
|
|
}
|
|
}else{
|
|
canPending = result.hit && (result.hitBlock + result.normal).z < 62 &&
|
|
BoxDistanceToBlock(result.hitBlock + result.normal) < 3.f;
|
|
blockCursorActive = false;
|
|
blockCursorPos = result.hitBlock + result.normal;
|
|
}
|
|
}else if(tool == ToolWeapon){
|
|
}else if(tool == ToolGrenade){
|
|
if(holdingGrenade){
|
|
if(world->GetTime() - grenadeTime > 2.9f){
|
|
ThrowGrenade();
|
|
}
|
|
}
|
|
}
|
|
|
|
if(tool != ToolWeapon)
|
|
weapon->SetShooting(false);
|
|
if(weapon->FrameNext(dt)){
|
|
FireWeapon();
|
|
}
|
|
}
|
|
|
|
bool Player::RayCastApprox(spades::Vector3 start, spades::Vector3 dir){
|
|
Vector3 diff = position - start;
|
|
|
|
// |P-A| * cos(theta)
|
|
float c = Vector3::Dot(diff, dir);
|
|
|
|
// |P-A|^2
|
|
float sq = diff.GetPoweredLength();
|
|
|
|
// |P-A| * sin(theta)
|
|
float dist = sqrtf(sq - c * c);
|
|
|
|
return dist < 4.f;
|
|
}
|
|
|
|
void Player::FireWeapon() {
|
|
SPADES_MARK_FUNCTION();
|
|
|
|
Vector3 muzzle = GetEye();
|
|
muzzle += GetFront() * 0.01f;/*
|
|
muzzle += GetRight() * 0.4f;
|
|
muzzle -= GetUp() * 0.3f;*/
|
|
|
|
Vector3 right = GetRight();
|
|
Vector3 up = GetUp();
|
|
|
|
int pellets = weapon->GetPelletSize();
|
|
float spread = weapon->GetSpread();
|
|
GameMap *map = world->GetMap();
|
|
|
|
// pyspades takes destroying more than one block as a
|
|
// speed hack (shotgun does this)
|
|
bool blockDestroyed = false;
|
|
|
|
Vector3 dir2 = GetFront();
|
|
for(int i =0 ; i < pellets; i++){
|
|
// AoS 0.75's way (dir2 shouldn't be normalized!)
|
|
dir2.x += (GetRandom() * 2.f - 1.f) * spread;
|
|
dir2.y += (GetRandom() * 2.f - 1.f) * spread;
|
|
dir2.z += (GetRandom() * 2.f - 1.f) * spread;
|
|
Vector3 dir = dir2.Normalize();
|
|
|
|
// first do map raycast
|
|
GameMap::RayCastResult mapResult;
|
|
mapResult = map->CastRay2(muzzle,
|
|
dir,
|
|
500);
|
|
|
|
Player *hitPlayer = NULL;
|
|
float hitPlayerDistance = 0.f;
|
|
int hitFlag = 0;
|
|
|
|
for(int i = 0; i < world->GetNumPlayerSlots(); i++){
|
|
Player *other = world->GetPlayer(i);
|
|
if(other == this || other == NULL)
|
|
continue;
|
|
if(other == this || !other->IsAlive() ||
|
|
other->GetTeamId() >= 2)
|
|
continue;
|
|
if(!other->RayCastApprox(muzzle, dir))
|
|
continue;
|
|
|
|
HitBoxes hb = other->GetHitBoxes();
|
|
Vector3 hitPos;
|
|
|
|
if(hb.head.RayCast(muzzle, dir, &hitPos)) {
|
|
float dist = (hitPos - muzzle).GetLength();
|
|
if(hitPlayer == NULL ||
|
|
dist < hitPlayerDistance){
|
|
if(hitPlayer != other){
|
|
hitPlayer = other;
|
|
hitFlag = 0;
|
|
}
|
|
hitPlayerDistance = dist;
|
|
hitFlag = 1; // head
|
|
}
|
|
}
|
|
if(hb.torso.RayCast(muzzle, dir, &hitPos)) {
|
|
float dist = (hitPos - muzzle).GetLength();
|
|
if(hitPlayer == NULL ||
|
|
dist < hitPlayerDistance){
|
|
if(hitPlayer != other){
|
|
hitPlayer = other;
|
|
hitFlag = 0;
|
|
}
|
|
hitPlayerDistance = dist;
|
|
hitFlag = 2; // torso
|
|
}
|
|
}
|
|
for(int j = 0; j < 3 ;j++){
|
|
if(hb.limbs[j].RayCast(muzzle, dir, &hitPos)) {
|
|
float dist = (hitPos - muzzle).GetLength();
|
|
if(hitPlayer == NULL ||
|
|
dist < hitPlayerDistance){
|
|
if(hitPlayer != other){
|
|
hitPlayer = other;
|
|
hitFlag = 0;
|
|
}
|
|
hitPlayerDistance = dist;
|
|
if(j == 2)
|
|
hitFlag = 8; // arms
|
|
else
|
|
hitFlag = 4; // leg
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Vector3 finalHitPos;
|
|
finalHitPos = muzzle + dir * 128.f;
|
|
|
|
if(mapResult.hit && (mapResult.hitPos - muzzle).GetLength() < 128.f &&
|
|
(hitPlayer == NULL || (mapResult.hitPos - muzzle).GetLength() < hitPlayerDistance)){
|
|
IntVector3 outBlockCoord = mapResult.hitBlock;
|
|
// TODO: set correct ray distance
|
|
// FIXME: why ray casting twice?
|
|
|
|
finalHitPos = mapResult.hitPos;
|
|
|
|
if(outBlockCoord.x >= 0 && outBlockCoord.y >= 0 && outBlockCoord.z >= 0 &&
|
|
outBlockCoord.x < map->Width() && outBlockCoord.y < map->Height() &&
|
|
outBlockCoord.z < map->Depth()){
|
|
if(outBlockCoord.z < 62){
|
|
int x = outBlockCoord.x;
|
|
int y = outBlockCoord.y;
|
|
int z = outBlockCoord.z;
|
|
SPAssert(map->IsSolid(x, y, z));
|
|
|
|
Vector3 blockF = {x + .5f, y + .5f, z + .5f};
|
|
float distance = (blockF - muzzle).GetLength();
|
|
|
|
uint32_t color = map->GetColor(x, y, z);
|
|
int health = color >> 24;
|
|
health -= weapon->GetDamage(HitTypeBlock, distance);
|
|
if(health <= 0 && !blockDestroyed){
|
|
health = 0;
|
|
blockDestroyed = true;
|
|
//send destroy cmd
|
|
if(world->GetListener() &&
|
|
world->GetLocalPlayer() == this)
|
|
world->GetListener()->LocalPlayerBlockAction
|
|
(outBlockCoord, BlockActionTool);
|
|
|
|
}
|
|
color = (color & 0xffffff) | ((uint32_t)health << 24);
|
|
if(map->IsSolid(x, y, z))
|
|
map->Set(x, y, z, true, color);
|
|
|
|
if(world->GetListener())
|
|
world->GetListener()->BulletHitBlock(mapResult.hitPos,
|
|
mapResult.hitBlock,
|
|
mapResult.normal);
|
|
}
|
|
}
|
|
}else if(hitPlayer != NULL){
|
|
if(hitPlayerDistance < 128.f){
|
|
|
|
finalHitPos = muzzle + dir * hitPlayerDistance;
|
|
|
|
if(world->GetListener()){
|
|
if(hitFlag & 1)
|
|
world->GetListener()->BulletHitPlayer(hitPlayer,
|
|
HitTypeHead,
|
|
muzzle + dir * hitPlayerDistance,
|
|
this);
|
|
if(hitFlag & 2)
|
|
world->GetListener()->BulletHitPlayer(hitPlayer,
|
|
HitTypeTorso,
|
|
muzzle + dir * hitPlayerDistance,
|
|
this);
|
|
if(hitFlag & 4)
|
|
world->GetListener()->BulletHitPlayer(hitPlayer,
|
|
HitTypeLegs,
|
|
muzzle + dir * hitPlayerDistance,
|
|
this);
|
|
if(hitFlag & 8)
|
|
world->GetListener()->BulletHitPlayer(hitPlayer,
|
|
HitTypeArms,
|
|
muzzle + dir * hitPlayerDistance,
|
|
this);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(world->GetListener() && this != world->GetLocalPlayer())
|
|
world->GetListener()->AddBulletTracer(this,
|
|
muzzle, finalHitPos);
|
|
|
|
// one pellet done
|
|
}
|
|
|
|
// in AoS 0.75's way
|
|
Vector3 o = orientation;
|
|
Vector3 rec = weapon->GetRecoil();
|
|
float upLimit = Vector3::Dot(GetFront2D(), o);
|
|
upLimit -= 0.03f;
|
|
o += GetUp() * std::min(rec.y, std::max(0.f, upLimit));
|
|
o += GetRight() * rec.x * sinf(world->GetTime() * 2.f);
|
|
o = o.Normalize();
|
|
SetOrientation(o);
|
|
|
|
reloadingServerSide = false;
|
|
}
|
|
|
|
void Player::ThrowGrenade(){
|
|
SPADES_MARK_FUNCTION();
|
|
|
|
if(!holdingGrenade)
|
|
return;
|
|
grenades--;
|
|
|
|
Vector3 muzzle = GetEye() + GetFront() * 0.1f;
|
|
Vector3 vel = GetFront() * 1.f;
|
|
float fuse = world->GetTime() - grenadeTime;
|
|
fuse = 3.f - fuse;
|
|
|
|
if(health <= 0){
|
|
// drop, don't throw
|
|
vel = MakeVector3(0,0,0);
|
|
}
|
|
|
|
vel += GetVelocty();
|
|
|
|
if(this == world->GetLocalPlayer()){
|
|
Grenade *gren = new Grenade(world, muzzle, vel, fuse);
|
|
world->AddGrenade(gren);
|
|
if(world->GetListener())
|
|
world->GetListener()->PlayerThrownGrenade(this, gren);
|
|
}else{
|
|
// grenade packet will be sent by server
|
|
if(world->GetListener())
|
|
world->GetListener()->PlayerThrownGrenade(this, NULL);
|
|
}
|
|
|
|
holdingGrenade = false;
|
|
}
|
|
|
|
void Player::DigWithSpade(){
|
|
SPADES_MARK_FUNCTION();
|
|
|
|
IntVector3 outBlockCoord;
|
|
GameMap *map = world->GetMap();
|
|
Vector3 muzzle = GetEye(), dir = GetFront();
|
|
|
|
// TODO: set correct ray distance
|
|
// first do map raycast
|
|
GameMap::RayCastResult mapResult;
|
|
mapResult = map->CastRay2(muzzle,
|
|
dir,
|
|
256);
|
|
|
|
outBlockCoord = mapResult.hitBlock;
|
|
|
|
// TODO: set correct ray distance
|
|
if(mapResult.hit && BoxDistanceToBlock(mapResult.hitBlock + mapResult.normal) < 3.f &&
|
|
outBlockCoord.x >= 0 && outBlockCoord.y >= 0 && outBlockCoord.z >= 0 &&
|
|
outBlockCoord.x < map->Width() && outBlockCoord.y < map->Height() &&
|
|
outBlockCoord.z < map->Depth()){
|
|
if(outBlockCoord.z < 62){
|
|
int x = outBlockCoord.x;
|
|
int y = outBlockCoord.y;
|
|
int z = outBlockCoord.z;
|
|
SPAssert(map->IsSolid(x, y, z));
|
|
|
|
// send destroy command only for local cmd
|
|
if(this == world->GetLocalPlayer()) {
|
|
|
|
if(world->GetListener())
|
|
world->GetListener()->LocalPlayerBlockAction
|
|
(outBlockCoord, BlockActionDig);
|
|
|
|
|
|
}
|
|
|
|
if(world->GetListener())
|
|
world->GetListener()->PlayerHitBlockWithSpade(this,
|
|
mapResult.hitPos,
|
|
mapResult.hitBlock,
|
|
mapResult.normal);
|
|
}
|
|
}else{
|
|
if(world->GetListener())
|
|
world->GetListener()->PlayerMissedSpade(this);
|
|
}
|
|
}
|
|
|
|
void Player::UseSpade() {
|
|
SPADES_MARK_FUNCTION();
|
|
|
|
bool missed = true;
|
|
|
|
Vector3 muzzle = GetEye(), dir = GetFront();
|
|
|
|
IntVector3 outBlockCoord;
|
|
GameMap *map = world->GetMap();
|
|
// TODO: set correct ray distance
|
|
// first do map raycast
|
|
GameMap::RayCastResult mapResult;
|
|
mapResult = map->CastRay2(muzzle,
|
|
dir,
|
|
256);
|
|
|
|
Player *hitPlayer = NULL;
|
|
float hitPlayerDistance = 3.f;
|
|
int hitFlag = 0;
|
|
|
|
for(int i = 0; i < world->GetNumPlayerSlots(); i++){
|
|
Player *other = world->GetPlayer(i);
|
|
if(other == this || other == NULL)
|
|
continue;
|
|
if(other == this || !other->IsAlive() ||
|
|
other->GetTeamId() >= 2)
|
|
continue;
|
|
if(!other->RayCastApprox(muzzle, dir))
|
|
continue;
|
|
if((eye - other->GetEye()).GetChebyshevLength() >=
|
|
MELEE_DISTANCE_F)
|
|
continue;
|
|
|
|
Vector3 diff = other->GetEye() - eye;
|
|
Vector3 view;
|
|
view.x = Vector3::Dot(diff, GetRight());
|
|
view.y = Vector3::Dot(diff, GetUp());
|
|
view.z = Vector3::Dot(diff, GetFront());
|
|
|
|
if(view.z < 0.f)
|
|
continue;
|
|
|
|
view.x /= view.z;
|
|
view.y /= view.z;
|
|
view.z = 0.f;
|
|
|
|
if(view.GetChebyshevLength() < 5.f) {
|
|
hitPlayer = other;
|
|
hitFlag = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
outBlockCoord = mapResult.hitBlock;
|
|
if(mapResult.hit && BoxDistanceToBlock(mapResult.hitBlock + mapResult.normal) < 3.f &&
|
|
(hitPlayer == NULL) &&
|
|
outBlockCoord.x >= 0 && outBlockCoord.y >= 0 &&
|
|
outBlockCoord.z >= 0 &&
|
|
outBlockCoord.x < map->Width() &&
|
|
outBlockCoord.y < map->Height() &&
|
|
outBlockCoord.z < map->Depth()){
|
|
if(outBlockCoord.z < 62){
|
|
int x = outBlockCoord.x;
|
|
int y = outBlockCoord.y;
|
|
int z = outBlockCoord.z;
|
|
SPAssert(map->IsSolid(x, y, z));
|
|
missed = false;
|
|
|
|
uint32_t color = map->GetColor(x, y, z);
|
|
int health = color >> 24;
|
|
health -= 55;
|
|
if(health <= 0){
|
|
health = 0;
|
|
// send destroy command only for local cmd
|
|
if(this == world->GetLocalPlayer()) {
|
|
if(world->GetListener())
|
|
world->GetListener()->LocalPlayerBlockAction
|
|
(outBlockCoord, BlockActionTool);
|
|
}
|
|
}
|
|
color = (color & 0xffffff) | ((uint32_t)health << 24);
|
|
if(map->IsSolid(x, y, z))
|
|
map->Set(x, y, z, true, color);
|
|
|
|
if(world->GetListener())
|
|
world->GetListener()->PlayerHitBlockWithSpade(this,
|
|
mapResult.hitPos,
|
|
mapResult.hitBlock,
|
|
mapResult.normal);
|
|
}
|
|
}else if(hitPlayer != NULL){
|
|
|
|
|
|
if(world->GetListener()){
|
|
if(hitFlag)
|
|
world->GetListener()->BulletHitPlayer(hitPlayer,
|
|
HitTypeMelee,
|
|
hitPlayer->GetEye(),
|
|
this);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
if(missed){
|
|
if(world->GetListener())
|
|
world->GetListener()->PlayerMissedSpade(this);
|
|
}
|
|
}
|
|
|
|
Vector3 Player::GetFront() {
|
|
SPADES_MARK_FUNCTION_DEBUG();
|
|
return orientation;
|
|
}
|
|
|
|
Vector3 Player::GetFront2D() {
|
|
SPADES_MARK_FUNCTION_DEBUG();
|
|
return MakeVector3(orientation.x,
|
|
orientation.y,
|
|
0.f).Normalize();
|
|
}
|
|
|
|
Vector3 Player::GetRight() {
|
|
SPADES_MARK_FUNCTION_DEBUG();
|
|
return -Vector3::Cross(MakeVector3(0,0,-1),
|
|
GetFront2D()).Normalize();
|
|
}
|
|
|
|
Vector3 Player::GetLeft(){
|
|
SPADES_MARK_FUNCTION_DEBUG();
|
|
return -GetRight();
|
|
}
|
|
|
|
Vector3 Player::GetUp() {
|
|
SPADES_MARK_FUNCTION_DEBUG();
|
|
return Vector3::Cross(GetRight(), GetFront())
|
|
.Normalize();
|
|
}
|
|
|
|
bool Player::GetWade() {
|
|
SPADES_MARK_FUNCTION_DEBUG();
|
|
return GetOrigin().z > 62.f;
|
|
}
|
|
|
|
Vector3 Player::GetOrigin() {
|
|
SPADES_MARK_FUNCTION_DEBUG();
|
|
Vector3 v = eye;
|
|
float offset;
|
|
if(input.crouch){
|
|
offset = .45f;
|
|
}else{
|
|
offset = .9f;
|
|
}
|
|
v.z += offset;
|
|
v.z += .3f;
|
|
return v;
|
|
}
|
|
|
|
void Player::BoxClipMove(float fsynctics) {
|
|
SPADES_MARK_FUNCTION();
|
|
|
|
float f = fsynctics * 32.f;
|
|
float nx = f * velocity.x + position.x;
|
|
float ny = f * velocity.y + position.y;
|
|
bool climb = false;
|
|
float offset, m;
|
|
if(input.crouch){
|
|
offset = .45f;
|
|
m = .9f;
|
|
}else{
|
|
offset = .9f;
|
|
m = 1.35f;
|
|
}
|
|
|
|
float nz = position.z + offset;
|
|
|
|
float z;
|
|
GameMap *map = world->GetMap();
|
|
|
|
|
|
if(velocity.x < 0.f)
|
|
f = -0.45f;
|
|
else
|
|
f = 0.45f;
|
|
|
|
z = m;
|
|
|
|
while(z >= -1.36f &&
|
|
!map->ClipBox(nx + f, position.y - .45f, nz + z) &&
|
|
!map->ClipBox(nx + f, position.y + .45f, nz + z))
|
|
z -= 0.9f;
|
|
if(z < -1.36f)
|
|
position.x = nx;
|
|
else if(!(input.crouch) && orientation.z < 0.5f && !input.sprint){
|
|
z = 0.35f;
|
|
while(z >= -2.36f &&
|
|
!map->ClipBox(nx + f, position.y - .45f, nz + z) &&
|
|
!map->ClipBox(nx + f, position.y + .45f, nz + z))
|
|
z -= 0.9f;
|
|
if(z < -2.36f){
|
|
position.x = nx;
|
|
climb = true;
|
|
}else{
|
|
velocity.x = 0.f;
|
|
}
|
|
}else{
|
|
velocity.x = 0.f;
|
|
}
|
|
|
|
if(velocity.y < 0.f)
|
|
f = -0.45f;
|
|
else
|
|
f = 0.45f;
|
|
|
|
z = m;
|
|
|
|
while(z >= -1.36f &&
|
|
!map->ClipBox(position.x - .45f, ny + f, nz + z) &&
|
|
!map->ClipBox(position.x + .45f, ny + f, nz + z))
|
|
z -= 0.9f;
|
|
if(z < -1.36f)
|
|
position.y = ny;
|
|
else if(!(input.crouch) && orientation.z < 0.5f && !input.sprint &&
|
|
!climb){
|
|
z = 0.35f;
|
|
while(z >= -2.36f &&
|
|
!map->ClipBox(position.x - .45f, ny + f, nz + z) &&
|
|
!map->ClipBox(position.x + .45f, ny + f, nz + z))
|
|
z -= 0.9f;
|
|
if(z < -2.36f){
|
|
position.y = ny;
|
|
climb = true;
|
|
}else{
|
|
velocity.y = 0.f;
|
|
}
|
|
}else if(!climb){
|
|
velocity.y = 0.f;
|
|
}
|
|
|
|
if(climb){
|
|
velocity.x *= .5f;
|
|
velocity.y *= .5f;
|
|
lastClimbTime = world->GetTime();
|
|
nz -= 1.f;
|
|
m = -1.35f;
|
|
}else{
|
|
if(velocity.z < 0.f)
|
|
m = -m;
|
|
nz += velocity.z * fsynctics * 32.f;
|
|
}
|
|
|
|
airborne = true;
|
|
if(map->ClipBox(position.x - .45f, position.y - .45f, nz + m) ||
|
|
map->ClipBox(position.x - .45f, position.y + .45f, nz + m) ||
|
|
map->ClipBox(position.x + .45f, position.y - .45f, nz + m) ||
|
|
map->ClipBox(position.x + .45f, position.y + .45f, nz + m)) {
|
|
if(velocity.z >= 0.f){
|
|
wade = position.z > 61.f;
|
|
airborne = false;
|
|
}
|
|
velocity.z = 0.f;
|
|
}else{
|
|
position.z = nz - offset;
|
|
}
|
|
|
|
RepositionPlayer(position);
|
|
}
|
|
|
|
bool Player::IsOnGroundOrWade() {
|
|
return ((velocity.z >= 0.f && velocity.z < .017f) && !airborne);
|
|
}
|
|
|
|
void Player::MovePlayer(float fsynctics) {
|
|
if(input.jump && (!lastJump) &&
|
|
IsOnGroundOrWade()) {
|
|
velocity.z = -0.36f;
|
|
lastJump = true;
|
|
if(world->GetListener() && world->GetTime() > lastJumpTime + .1f){
|
|
world->GetListener()->PlayerJumped(this);
|
|
lastJumpTime = world->GetTime();
|
|
}
|
|
}else if(!input.jump){
|
|
lastJump = false;
|
|
}
|
|
|
|
float f = fsynctics;
|
|
if(airborne)
|
|
f *= 0.1f;
|
|
else if(input.crouch)
|
|
f *= 0.3f;
|
|
else if((weapInput.secondary && IsToolWeapon()) ||
|
|
input.sneak)
|
|
f *= 0.5f;
|
|
else if(input.sprint)
|
|
f *= 1.3f;
|
|
if((input.moveForward || input.moveBackward) &&
|
|
(input.moveRight || input.moveLeft))
|
|
f /= sqrtf(2.f);
|
|
|
|
Vector3 front = GetFront();
|
|
Vector3 left = GetLeft();
|
|
|
|
if(input.moveForward){
|
|
velocity.x += front.x * f;
|
|
velocity.y += front.y * f;
|
|
}else if(input.moveBackward){
|
|
velocity.x -= front.x * f;
|
|
velocity.y -= front.y * f;
|
|
}
|
|
if(input.moveLeft){
|
|
velocity.x += left.x * f;
|
|
velocity.y += left.y * f;
|
|
}else if(input.moveRight){
|
|
velocity.x -= left.x * f;
|
|
velocity.y -= left.y * f;
|
|
}
|
|
|
|
// this is a linear approximation that's
|
|
// done in pysnip
|
|
// accurate computation is not difficult
|
|
f = fsynctics + 1.f;
|
|
velocity.z += fsynctics;
|
|
velocity.z /= f; // air friction
|
|
|
|
if(wade)
|
|
f = fsynctics * 6.f + 1.f;
|
|
else if(!airborne)
|
|
f = fsynctics * 4.f + 1.f;
|
|
|
|
velocity.x /= f;
|
|
velocity.y /= f;
|
|
|
|
float f2 = velocity.z;
|
|
BoxClipMove(fsynctics);
|
|
|
|
// hit ground
|
|
if(velocity.z == 0.f && (f2 > FALL_SLOW_DOWN)) {
|
|
velocity.x *= .5f;
|
|
velocity.y *= .5f;
|
|
|
|
if(f2 > FALL_DAMAGE_VELOCITY){
|
|
f2 -= FALL_DAMAGE_VELOCITY;
|
|
if(world->GetListener()){
|
|
world->GetListener()->PlayerLanded(this, true);
|
|
}
|
|
}else{
|
|
if(world->GetListener()){
|
|
world->GetListener()->PlayerLanded(this, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(velocity.z >= 0.f && velocity.z < .017f &&
|
|
!input.sneak && !input.crouch &&
|
|
!(weapInput.secondary && IsToolWeapon())){
|
|
// count move distance
|
|
f = fsynctics * 32.f;
|
|
float dx = f * velocity.x;
|
|
float dy = f * velocity.y;
|
|
float dist = sqrtf(dx*dx+dy*dy);
|
|
moveDistance += dist * .3f;
|
|
|
|
bool madeFootstep = false;
|
|
while(moveDistance > 1.f){
|
|
moveSteps++;
|
|
moveDistance -= 1.f;
|
|
|
|
if(world->GetListener() && !madeFootstep){
|
|
world->GetListener()->PlayerMadeFootstep(this);
|
|
madeFootstep = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool Player::TryUncrouch(bool move) {
|
|
SPADES_MARK_FUNCTION();
|
|
|
|
float x1 = position.x + 0.45f;
|
|
float x2 = position.x - 0.45f;
|
|
float y1 = position.y + 0.45f;
|
|
float y2 = position.y - 0.45f;
|
|
float z1 = position.z + 2.25f;
|
|
float z2 = position.z - 1.35f;
|
|
|
|
GameMap *map = world->GetMap();
|
|
|
|
// lower feet
|
|
if(airborne &&
|
|
!(map->ClipBox(x1, y1, z1) ||
|
|
map->ClipBox(x2, y1, z1) ||
|
|
map->ClipBox(x1, y2, z1) ||
|
|
map->ClipBox(x2, y2, z1)))
|
|
return true;
|
|
else if(!(map->ClipBox(x1, y1, z2) ||
|
|
map->ClipBox(x2, y1, z2) ||
|
|
map->ClipBox(x1, y2, z2) ||
|
|
map->ClipBox(x2, y2, z2))){
|
|
if(move){
|
|
position.z -= 0.9f;
|
|
eye.z -= 0.9f;
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void Player::RepositionPlayer(const spades::Vector3 & pos2){
|
|
SPADES_MARK_FUNCTION();
|
|
|
|
eye = position = pos2;
|
|
float f = lastClimbTime - world->GetTime();
|
|
if(f > -.25f)
|
|
eye.z += (f + .25f) / .25f;
|
|
}
|
|
|
|
float Player::GetToolPrimaryDelay() {
|
|
SPADES_MARK_FUNCTION_DEBUG();
|
|
|
|
switch(tool){
|
|
case ToolWeapon:
|
|
return weapon->GetDelay();
|
|
case ToolBlock:
|
|
return .5f;
|
|
case ToolSpade:
|
|
return .2f;
|
|
case ToolGrenade:
|
|
return .5f;
|
|
default:
|
|
SPInvalidEnum("tool", tool);
|
|
}
|
|
}
|
|
|
|
float Player::GetToolSecondaryDelay() {
|
|
SPADES_MARK_FUNCTION_DEBUG();
|
|
|
|
switch(tool){
|
|
case ToolBlock:
|
|
return GetToolPrimaryDelay();
|
|
case ToolSpade:
|
|
return 1.f;
|
|
default:
|
|
SPInvalidEnum("tool", tool);
|
|
}
|
|
}
|
|
|
|
float Player::GetSpadeAnimationProgress() {
|
|
SPADES_MARK_FUNCTION_DEBUG();
|
|
|
|
SPAssert(tool == ToolSpade);
|
|
SPAssert(weapInput.primary);
|
|
return 1.f - (nextSpadeTime - world->GetTime())
|
|
/ GetToolPrimaryDelay();
|
|
}
|
|
|
|
float Player::GetDigAnimationProgress() {
|
|
SPADES_MARK_FUNCTION_DEBUG();
|
|
|
|
SPAssert(tool == ToolSpade);
|
|
SPAssert(weapInput.secondary);
|
|
return 1.f - (nextDigTime - world->GetTime())
|
|
/ GetToolSecondaryDelay();
|
|
}
|
|
|
|
float Player::GetTimeToNextGrenade() {
|
|
return nextGrenadeTime - world->GetTime();
|
|
}
|
|
|
|
void Player::KilledBy(KillType type,
|
|
Player *killer,
|
|
int respawnTime) {
|
|
SPADES_MARK_FUNCTION();
|
|
health = 0;
|
|
weapon->SetShooting(false);
|
|
|
|
// if local player is killed while cooking grenade,
|
|
// drop the live grenade.
|
|
if(this == world->GetLocalPlayer() &&
|
|
tool == ToolGrenade &&
|
|
holdingGrenade){
|
|
ThrowGrenade();
|
|
}
|
|
if(world->GetListener())
|
|
world->GetListener()->PlayerKilledPlayer(killer, this, type);
|
|
|
|
input = PlayerInput();
|
|
weapInput = WeaponInput();
|
|
this->respawnTime = world->GetTime() + respawnTime;
|
|
}
|
|
|
|
bool Player::IsAlive() {
|
|
return health > 0;
|
|
}
|
|
|
|
std::string Player::GetName() {
|
|
return world->GetPlayerPersistent(GetId()).name;
|
|
}
|
|
|
|
float Player::GetWalkAnimationProgress() {
|
|
return moveDistance * .5f +
|
|
(float)(moveSteps & 1) * .5f;
|
|
}
|
|
|
|
Player::HitBoxes Player::GetHitBoxes() {
|
|
SPADES_MARK_FUNCTION_DEBUG();
|
|
Player::HitBoxes hb;
|
|
|
|
Vector3 front = GetFront();
|
|
|
|
float yaw = atan2(front.y, front.x) + M_PI * .5f;
|
|
float pitch = -atan2(front.z, sqrt(front.x * front.x + front.y * front.y));
|
|
|
|
// lower axis
|
|
Matrix4 lower = Matrix4::Translate(GetOrigin());
|
|
lower = lower * Matrix4::Rotate(MakeVector3(0,0,1),
|
|
yaw);
|
|
|
|
Matrix4 torso;
|
|
|
|
if(input.crouch){
|
|
lower = lower * Matrix4::Translate(0, 0, -0.4);
|
|
// lower
|
|
hb.limbs[0] = AABB3(-.4f, -.15f, 0.5f,
|
|
0.3f, .3f, 0.5f);
|
|
hb.limbs[0] = lower * hb.limbs[0];
|
|
|
|
hb.limbs[1] = AABB3(.1f, -.15f, 0.5f,
|
|
0.3f, .3f, 0.5f);
|
|
hb.limbs[1] = lower * hb.limbs[1];
|
|
|
|
torso = lower * Matrix4::Translate(0, 0, -0.3);
|
|
|
|
// torso
|
|
hb.torso = AABB3(-.4f, -.15f, 0.1f,
|
|
.8f, .8f, .6f);
|
|
hb.torso = torso * hb.torso;
|
|
|
|
hb.limbs[2] = AABB3(-.6f, -.15f, 0.1f,
|
|
1.2f, .3f, .6f);
|
|
hb.limbs[2] = torso * hb.limbs[2];
|
|
|
|
// head
|
|
hb.head = AABB3(-.3f, -.3f, -0.45f,
|
|
.6f, .6f, 0.6f);
|
|
hb.head = Matrix4::Translate(0, 0, -0.15f) * hb.head;
|
|
hb.head = Matrix4::Rotate(MakeVector3(1,0,0),
|
|
pitch) * hb.head;
|
|
hb.head = Matrix4::Translate(0, 0, 0.15f) * hb.head;
|
|
hb.head = torso * hb.head;
|
|
}else{
|
|
// lower
|
|
hb.limbs[0] = AABB3(-.4f, -.15f, 0.f,
|
|
0.3f, .3f, 1.f);
|
|
hb.limbs[0] = lower * hb.limbs[0];
|
|
|
|
hb.limbs[1] = AABB3(.1f, -.15f, 0.f,
|
|
0.3f, .3f, 1.f);
|
|
hb.limbs[1] = lower * hb.limbs[1];
|
|
|
|
torso = lower * Matrix4::Translate(0, 0, -1.1);
|
|
|
|
// torso
|
|
hb.torso = AABB3(-.4f, -.15f, 0.1f,
|
|
.8f, .3f, .9f);
|
|
hb.torso = torso * hb.torso;
|
|
|
|
hb.limbs[2] = AABB3(-.6f, -.15f, 0.1f,
|
|
1.2f, .3f, .9f);
|
|
hb.limbs[2] = torso * hb.limbs[2];
|
|
|
|
// head
|
|
hb.head = AABB3(-.3f, -.3f, -0.5f,
|
|
.6f, .6f, 0.6f);
|
|
hb.head = Matrix4::Translate(0, 0, -0.1f) * hb.head;
|
|
hb.head = Matrix4::Rotate(MakeVector3(1,0,0),
|
|
pitch) * hb.head;
|
|
hb.head = Matrix4::Translate(0, 0, 0.1f) * hb.head;
|
|
hb.head = torso * hb.head;
|
|
}
|
|
|
|
return hb;
|
|
}
|
|
IntVector3 Player::GetColor() {
|
|
return world->GetTeam(teamId).color;
|
|
}
|
|
|
|
bool Player::IsCookingGrenade() {
|
|
return tool == ToolGrenade && holdingGrenade;
|
|
}
|
|
float Player::GetGrenadeCookTime() {
|
|
return world->GetTime() - grenadeTime;
|
|
}
|
|
|
|
void Player::SetWeaponType(WeaponType weap){
|
|
SPADES_MARK_FUNCTION_DEBUG();
|
|
if(this->weapon->GetWeaponType() == weap)
|
|
return;
|
|
delete this->weapon;
|
|
this->weapon = Weapon::CreateWeapon(weap, this);
|
|
}
|
|
|
|
void Player::SetTeam(int tId){
|
|
teamId = tId;
|
|
}
|
|
|
|
bool Player::IsReadyToUseTool() {
|
|
SPADES_MARK_FUNCTION_DEBUG();
|
|
switch(tool){
|
|
case ToolBlock:
|
|
return world->GetTime() > nextBlockTime &&
|
|
blockStocks > 0;
|
|
case ToolGrenade:
|
|
return world->GetTime() > nextGrenadeTime &&
|
|
grenades > 0;
|
|
case ToolSpade:
|
|
return true;
|
|
case ToolWeapon:
|
|
return weapon->IsReadyToShoot();
|
|
}
|
|
}
|
|
|
|
bool Player::IsToolSelectable(ToolType type) {
|
|
SPADES_MARK_FUNCTION_DEBUG();
|
|
switch(type){
|
|
case ToolSpade:
|
|
return true;
|
|
case ToolBlock:
|
|
return blockStocks > 0;
|
|
case ToolWeapon:
|
|
return weapon->GetAmmo() > 0 ||
|
|
weapon->GetStock() > 0;
|
|
case ToolGrenade:
|
|
return grenades > 0;
|
|
default:
|
|
SPAssert(false);
|
|
}
|
|
}
|
|
|
|
bool Player::OverlapsWith(const spades::AABB3 &aabb) {
|
|
SPADES_MARK_FUNCTION_DEBUG();
|
|
float offset, m;
|
|
if(input.crouch){
|
|
offset = .45f;
|
|
m = .9f;
|
|
}else{
|
|
offset = .9f;
|
|
m = 1.35f;
|
|
}
|
|
m -= .5f;
|
|
AABB3 playerBox(eye.x - .45f,
|
|
eye.y - .45f,
|
|
eye.z,
|
|
.9f, .9f, offset + m);
|
|
return aabb && playerBox;
|
|
}
|
|
|
|
bool Player::OverlapsWithOneBlock(spades::IntVector3 vec) {
|
|
SPADES_MARK_FUNCTION_DEBUG();
|
|
return OverlapsWith(AABB3(vec.x, vec.y, vec.z,
|
|
1, 1, 1));
|
|
}
|
|
|
|
#pragma mark - Block Construction
|
|
bool Player::IsBlockCursorActive(){
|
|
return tool == ToolBlock && blockCursorActive;
|
|
}
|
|
bool Player::IsBlockCursorDragging() {
|
|
return tool == ToolBlock && blockCursorDragging;
|
|
}
|
|
float Player::BoxDistanceToBlock(spades::IntVector3 v){
|
|
Vector3 e = {(float)v.x, (float)v.y, (float)v.z};
|
|
e += .5f;
|
|
|
|
return (e-eye).GetChebyshevLength();
|
|
}
|
|
}
|
|
}
|