400 lines
9.2 KiB
C++
400 lines
9.2 KiB
C++
/*
|
|
Copyright (c) 2013 yvt
|
|
based on code of pysnip (c) Mathias Kaerlev 2011-2012.
|
|
|
|
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 "World.h"
|
|
#include "GameMap.h"
|
|
#include "GameMapWrapper.h"
|
|
#include "../Core/IStream.h"
|
|
#include "../Core/FileManager.h"
|
|
#include "../Core/Debug.h"
|
|
#include "Player.h"
|
|
#include "Weapon.h"
|
|
#include "Grenade.h"
|
|
#include "../Core/Debug.h"
|
|
#include "IGameMode.h"
|
|
#include <math.h>
|
|
#include <stdlib.h>
|
|
#include "IWorldListener.h"
|
|
|
|
namespace spades {
|
|
namespace client {
|
|
|
|
World::World(){
|
|
SPADES_MARK_FUNCTION();
|
|
|
|
listener = NULL;
|
|
|
|
map = NULL;
|
|
mapWrapper = NULL;
|
|
/*
|
|
IStream *stream = FileManager::OpenForReading("Maps/burbs.vxl");
|
|
map = GameMap::Load(stream);
|
|
delete stream;
|
|
|
|
mapWrapper = new GameMapWrapper(map);
|
|
mapWrapper->Rebuild();*/
|
|
|
|
localPlayerIndex = -1;
|
|
for(int i = 0; i < 32; i++){
|
|
players.push_back((Player *)NULL);
|
|
playerPersistents.push_back(PlayerPersistent());
|
|
}
|
|
/*
|
|
Player *pl = new Player(this, 0, WeaponType::SMG_WEAPON,
|
|
0, MakeVector3(256,256,5),
|
|
"Deuce", IntVector3::Make(0,255,0));
|
|
|
|
pl->SetOrientation(MakeVector3(0, 1, 0));
|
|
|
|
|
|
players[0] = pl;*/
|
|
|
|
localPlayerIndex = 0;
|
|
|
|
time = 0.f;
|
|
mode = NULL;
|
|
|
|
}
|
|
World::~World() {
|
|
SPADES_MARK_FUNCTION();
|
|
|
|
for(std::list<Grenade *>::iterator it = grenades.begin();
|
|
it != grenades.end(); it++)
|
|
delete *it;
|
|
for(size_t i = 0; i < players.size(); i++)
|
|
if(players[i])
|
|
delete players[i];
|
|
if(mode){
|
|
delete mode;
|
|
}
|
|
if(map){
|
|
delete mapWrapper;
|
|
map->Release();
|
|
}
|
|
}
|
|
|
|
void World::Advance(float dt) {
|
|
SPADES_MARK_FUNCTION();
|
|
|
|
for(size_t i = 0; i < players.size(); i++)
|
|
if(players[i])
|
|
players[i]->Update(dt);
|
|
|
|
std::vector<std::list<Grenade *>::iterator> removedGrenades;
|
|
for(std::list<Grenade *>::iterator it = grenades.begin();
|
|
it != grenades.end(); it++){
|
|
Grenade *g = *it;
|
|
if(g->Update(dt)){
|
|
removedGrenades.push_back(it);
|
|
}
|
|
}
|
|
for(size_t i = 0; i < removedGrenades.size(); i++)
|
|
grenades.erase(removedGrenades[i]);
|
|
|
|
time += dt;
|
|
}
|
|
|
|
void World::SetMap(spades::client::GameMap *newMap){
|
|
if(map == newMap)
|
|
return;
|
|
|
|
if(map){
|
|
map->Release();
|
|
delete mapWrapper;
|
|
}
|
|
|
|
map = newMap;
|
|
if(map){
|
|
map->AddRef();
|
|
mapWrapper = new GameMapWrapper(map);
|
|
mapWrapper->Rebuild();
|
|
}
|
|
}
|
|
|
|
void World::AddGrenade(spades::client::Grenade *g){
|
|
SPADES_MARK_FUNCTION_DEBUG();
|
|
|
|
grenades.push_back(g);
|
|
}
|
|
|
|
std::vector<Grenade *> World::GetAllGrenades() {
|
|
SPADES_MARK_FUNCTION_DEBUG();
|
|
|
|
std::vector<Grenade *> g;
|
|
for(std::list<Grenade *>::iterator it = grenades.begin();
|
|
it != grenades.end(); it++){
|
|
g.push_back(*it);
|
|
}
|
|
return g;
|
|
}
|
|
|
|
void World::SetPlayer(int i,
|
|
spades::client::Player *p){
|
|
SPADES_MARK_FUNCTION();
|
|
SPAssert(i >= 0);
|
|
SPAssert( i < (int)players.size());
|
|
if(players[i] == p)
|
|
return;
|
|
if(players[i])
|
|
delete players[i];
|
|
players[i] = p;
|
|
if(listener)
|
|
listener->PlayerObjectSet(i);
|
|
}
|
|
|
|
void World::SetMode(spades::client::IGameMode *m) {
|
|
if(mode == m)
|
|
return;
|
|
if(mode)
|
|
delete mode;
|
|
mode = m;
|
|
}
|
|
|
|
void World::CreateBlock(spades::IntVector3 pos,
|
|
spades::IntVector3 color) {
|
|
if(map->IsSolid(pos.x, pos.y, pos.z))
|
|
return;
|
|
mapWrapper->AddBlock(pos.x, pos.y, pos.z,
|
|
color.x |
|
|
(color.y << 8) |
|
|
(color.z << 16) |
|
|
(100UL << 24));
|
|
}
|
|
void World::DestroyBlock(std::vector<spades::IntVector3> pos){
|
|
std::vector<CellPos> cells;
|
|
for(size_t i = 0; i < pos.size(); i++){
|
|
const IntVector3& p = pos[i];
|
|
if(p.z >= 62 || p.z < 0 || p.x < 0 || p.y < 0 ||
|
|
p.x >= map->Width() || p.y >= map->Height())
|
|
continue;
|
|
if(!map->IsSolid(p.x, p.y, p.z))
|
|
continue;
|
|
cells.push_back(CellPos(p.x,p.y,p.z));
|
|
}
|
|
|
|
cells = mapWrapper->RemoveBlocks(cells);
|
|
|
|
for(size_t i =0 ; i < cells.size(); i++){
|
|
CellPos& p = cells[i];
|
|
map->Set(p.x, p.y, p.z, false, 0);
|
|
}
|
|
|
|
std::vector<IntVector3> cells2;
|
|
for(size_t i =0 ; i < cells.size(); i++){
|
|
cells2.push_back(IntVector3::Make(cells[i].x,
|
|
cells[i].y,
|
|
cells[i].z));
|
|
}
|
|
|
|
if(listener)
|
|
listener->BlocksFell(cells2);
|
|
}
|
|
|
|
World::PlayerPersistent& World::GetPlayerPersistent(int index) {
|
|
SPAssert(index >= 0);
|
|
SPAssert(index < players.size());
|
|
return playerPersistents[index];
|
|
}
|
|
|
|
std::vector<IntVector3> World::CubeLine(spades::IntVector3 v1,
|
|
spades::IntVector3 v2,
|
|
int maxLength) {
|
|
SPADES_MARK_FUNCTION_DEBUG();
|
|
|
|
IntVector3 c = v1;
|
|
IntVector3 d = v2 - v1;
|
|
long ixi, iyi, izi, dx, dy, dz, dxi, dyi, dzi;
|
|
std::vector<IntVector3> ret;
|
|
|
|
int VSID = map->Width();
|
|
SPAssert(VSID == map->Height());
|
|
|
|
int MAXZDIM = map->Depth();
|
|
|
|
if (d.x < 0) ixi = -1;
|
|
else ixi = 1;
|
|
if (d.y < 0) iyi = -1;
|
|
else iyi = 1;
|
|
if (d.z < 0) izi = -1;
|
|
else izi = 1;
|
|
|
|
if ((abs(d.x) >= abs(d.y)) && (abs(d.x) >= abs(d.z)))
|
|
{
|
|
dxi = 1024; dx = 512;
|
|
dyi = (long)(!d.y ? 0x3fffffff/VSID : abs(d.x*1024/d.y));
|
|
dy = dyi/2;
|
|
dzi = (long)(!d.z ? 0x3fffffff/VSID : abs(d.x*1024/d.z));
|
|
dz = dzi/2;
|
|
}
|
|
else if (abs(d.y) >= abs(d.z))
|
|
{
|
|
dyi = 1024; dy = 512;
|
|
dxi = (long)(!d.x ? 0x3fffffff/VSID : abs(d.y*1024/d.x));
|
|
dx = dxi/2;
|
|
dzi = (long)(!d.z ? 0x3fffffff/VSID : abs(d.y*1024/d.z));
|
|
dz = dzi/2;
|
|
}
|
|
else
|
|
{
|
|
dzi = 1024; dz = 512;
|
|
dxi = (long)(!d.x ? 0x3fffffff/VSID : abs(d.z*1024/d.x));
|
|
dx = dxi/2;
|
|
dyi = (long)(!d.y ? 0x3fffffff/VSID : abs(d.z*1024/d.y));
|
|
dy = dyi/2;
|
|
}
|
|
if (ixi >= 0) dx = dxi-dx;
|
|
if (iyi >= 0) dy = dyi-dy;
|
|
if (izi >= 0) dz = dzi-dz;
|
|
|
|
while (1)
|
|
{
|
|
ret.push_back(c);
|
|
|
|
if(ret.size() == (size_t)maxLength)
|
|
break;
|
|
|
|
if(c.x == v2.x &&
|
|
c.y == v2.y &&
|
|
c.z == v2.z)
|
|
break;
|
|
|
|
if ((dz <= dx) && (dz <= dy))
|
|
{
|
|
c.z += izi;
|
|
if (c.z < 0 || c.z >= MAXZDIM)
|
|
break;
|
|
dz += dzi;
|
|
}
|
|
else
|
|
{
|
|
if (dx < dy)
|
|
{
|
|
c.x += ixi;
|
|
if ((unsigned long)c.x >= VSID)
|
|
break;
|
|
dx += dxi;
|
|
}
|
|
else
|
|
{
|
|
c.y += iyi;
|
|
if ((unsigned long)c.y >= VSID)
|
|
break;
|
|
dy += dyi;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
World::WeaponRayCastResult World::WeaponRayCast(spades::Vector3 startPos,
|
|
spades::Vector3 dir,
|
|
Player *exclude) {
|
|
WeaponRayCastResult result;
|
|
Player *hitPlayer = NULL;
|
|
float hitPlayerDistance = 0.f;
|
|
hitTag_t hitFlag = hit_None;
|
|
|
|
for(int i = 0; i < (int)players.size(); i++){
|
|
Player *p = players[i];
|
|
if(p == NULL || p == exclude)
|
|
continue;
|
|
if(p->GetTeamId() >= 2 || !p->IsAlive())
|
|
continue;
|
|
if(!p->RayCastApprox(startPos, dir))
|
|
continue;
|
|
|
|
Player::HitBoxes hb = p->GetHitBoxes();
|
|
Vector3 hitPos;
|
|
|
|
if(hb.head.RayCast(startPos, dir, &hitPos)) {
|
|
float dist = (hitPos - startPos).GetLength();
|
|
if(hitPlayer == NULL ||
|
|
dist < hitPlayerDistance){
|
|
if(hitPlayer != p){
|
|
hitPlayer = p;
|
|
hitFlag = hit_None;
|
|
}
|
|
hitPlayerDistance = dist;
|
|
hitFlag |= hit_Head;
|
|
}
|
|
}
|
|
if(hb.torso.RayCast(startPos, dir, &hitPos)) {
|
|
float dist = (hitPos - startPos).GetLength();
|
|
if(hitPlayer == NULL ||
|
|
dist < hitPlayerDistance){
|
|
if(hitPlayer != p){
|
|
hitPlayer = p;
|
|
hitFlag = hit_None;
|
|
}
|
|
hitPlayerDistance = dist;
|
|
hitFlag |= hit_Torso;
|
|
}
|
|
}
|
|
for(int j = 0; j < 3 ;j++){
|
|
if(hb.limbs[j].RayCast(startPos, dir, &hitPos)) {
|
|
float dist = (hitPos - startPos).GetLength();
|
|
if(hitPlayer == NULL ||
|
|
dist < hitPlayerDistance){
|
|
if(hitPlayer != p){
|
|
hitPlayer = p;
|
|
hitFlag = hit_None;
|
|
}
|
|
hitPlayerDistance = dist;
|
|
if(j == 2) {
|
|
hitFlag |= hit_Arms;
|
|
} else {
|
|
hitFlag |= hit_Legs;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// map raycast
|
|
GameMap::RayCastResult res2;
|
|
res2 = map->CastRay2(startPos, dir, 256);
|
|
|
|
if(res2.hit && (hitPlayer == NULL ||
|
|
(res2.hitPos - startPos).GetLength() <
|
|
hitPlayerDistance)){
|
|
result.hit = true;
|
|
result.startSolid = res2.startSolid;
|
|
result.player = NULL;
|
|
result.hitFlag = hit_None;
|
|
result.blockPos = res2.hitBlock;
|
|
result.hitPos = res2.hitPos;
|
|
}else if(hitPlayer) {
|
|
result.hit = true;
|
|
result.startSolid = false; // FIXME: startSolid for player
|
|
result.player = hitPlayer;
|
|
result.hitPos = startPos + dir * hitPlayerDistance;
|
|
result.hitFlag = hitFlag;
|
|
}else{
|
|
result.hit = false;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
}
|
|
}
|