mirror of
https://github.com/scummvm/scummvm.git
synced 2025-04-02 10:52:32 -04:00
680 lines
18 KiB
C++
680 lines
18 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program 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.
|
|
*
|
|
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
// Based on Phantasma code by Thomas Harte (2013),
|
|
// available at https://github.com/TomHarte/Phantasma/ (MIT)
|
|
|
|
#include "common/algorithm.h"
|
|
#include "common/hash-ptr.h"
|
|
|
|
#include "freescape/freescape.h"
|
|
#include "freescape/area.h"
|
|
#include "freescape/objects/global.h"
|
|
#include "freescape/sweepAABB.h"
|
|
|
|
namespace Freescape {
|
|
|
|
Object *Area::objectWithIDFromMap(ObjectMap *map, uint16 objectID) {
|
|
if (!map)
|
|
return nullptr;
|
|
if (!map->contains(objectID))
|
|
return nullptr;
|
|
return (*map)[objectID];
|
|
}
|
|
|
|
Object *Area::objectWithID(uint16 objectID) {
|
|
return objectWithIDFromMap(_objectsByID, objectID);
|
|
}
|
|
|
|
Object *Area::entranceWithID(uint16 objectID) {
|
|
return objectWithIDFromMap(_entrancesByID, objectID);
|
|
}
|
|
|
|
uint16 Area::getAreaID() {
|
|
return _areaID;
|
|
}
|
|
|
|
uint16 Area::getAreaFlags() {
|
|
return _areaFlags;
|
|
}
|
|
|
|
uint8 Area::getScale() {
|
|
return _scale;
|
|
}
|
|
|
|
Area::Area(uint16 areaID_, uint16 areaFlags_, ObjectMap *objectsByID_, ObjectMap *entrancesByID_) {
|
|
_areaID = areaID_;
|
|
_areaFlags = areaFlags_;
|
|
_objectsByID = objectsByID_;
|
|
_entrancesByID = entrancesByID_;
|
|
|
|
_scale = 0;
|
|
_skyColor = 255;
|
|
_groundColor = 255;
|
|
_usualBackgroundColor = 255;
|
|
_underFireBackgroundColor = 255;
|
|
_inkColor = 255;
|
|
_paperColor = 255;
|
|
|
|
_gasPocketRadius = 0;
|
|
|
|
// create a list of drawable objects only
|
|
for (auto &it : *_objectsByID) {
|
|
if (it._value->isDrawable()) {
|
|
_drawableObjects.push_back(it._value);
|
|
}
|
|
}
|
|
|
|
// sort so that those that are planar are drawn last
|
|
struct {
|
|
bool operator()(Object *object1, Object *object2) {
|
|
if (!object1->isPlanar() && object2->isPlanar())
|
|
return true;
|
|
if (object1->isPlanar() && !object2->isPlanar())
|
|
return false;
|
|
return object1->getObjectID() > object2->getObjectID();
|
|
};
|
|
} compareObjects;
|
|
|
|
Common::sort(_drawableObjects.begin(), _drawableObjects.end(), compareObjects);
|
|
_lastTick = 0;
|
|
}
|
|
|
|
Area::~Area() {
|
|
if (_entrancesByID) {
|
|
for (auto &it : *_entrancesByID) {
|
|
if (!_addedObjects.contains(it._value->getObjectID()))
|
|
delete it._value;
|
|
}
|
|
}
|
|
|
|
if (_objectsByID) {
|
|
for (auto &it : *_objectsByID) {
|
|
if (!_addedObjects.contains(it._value->getObjectID()))
|
|
delete it._value;
|
|
}
|
|
}
|
|
|
|
delete _entrancesByID;
|
|
delete _objectsByID;
|
|
}
|
|
|
|
ObjectArray Area::getSensors() {
|
|
ObjectArray sensors;
|
|
debugC(1, kFreescapeDebugMove, "Area name: %s", _name.c_str());
|
|
for (auto &it : *_objectsByID) {
|
|
if (it._value->getType() == kSensorType)
|
|
sensors.push_back(it._value);
|
|
}
|
|
return sensors;
|
|
}
|
|
|
|
void Area::show() {
|
|
debugC(1, kFreescapeDebugMove, "Area name: %s", _name.c_str());
|
|
for (auto &it : *_objectsByID)
|
|
debugC(1, kFreescapeDebugMove, "objID: %d, type: %d", it._value->getObjectID(), it._value->getType());
|
|
|
|
for (auto &it : *_entrancesByID)
|
|
debugC(1, kFreescapeDebugMove, "objID: %d, type: %d (entrance)", it._value->getObjectID(), it._value->getType());
|
|
}
|
|
|
|
void Area::loadObjects(Common::SeekableReadStream *stream, Area *global) {
|
|
int objectsByIDSize = stream->readUint32LE();
|
|
|
|
for (int i = 0; i < objectsByIDSize; i++) {
|
|
uint16 key = stream->readUint32LE();
|
|
uint32 flags = stream->readUint32LE();
|
|
float x = stream->readFloatLE();
|
|
float y = stream->readFloatLE();
|
|
float z = stream->readFloatLE();
|
|
Object *obj = nullptr;
|
|
if (!_objectsByID->contains(key))
|
|
addObjectFromArea(key, global);
|
|
|
|
obj = (*_objectsByID)[key];
|
|
assert(obj);
|
|
obj->setObjectFlags(flags);
|
|
obj->setOrigin(Math::Vector3d(x, y, z));
|
|
}
|
|
|
|
_colorRemaps.clear();
|
|
int colorRemapsSize = stream->readUint32LE();
|
|
|
|
for (int i = 0; i < colorRemapsSize; i++) {
|
|
int src = stream->readUint32LE();
|
|
int dst = stream->readUint32LE();
|
|
remapColor(src, dst);
|
|
}
|
|
}
|
|
|
|
void Area::saveObjects(Common::WriteStream *stream) {
|
|
stream->writeUint32LE(_objectsByID->size());
|
|
|
|
for (auto &it : *_objectsByID) {
|
|
Object *obj = it._value;
|
|
stream->writeUint32LE(it._key);
|
|
stream->writeUint32LE(obj->getObjectFlags());
|
|
stream->writeFloatLE(obj->getOrigin().x());
|
|
stream->writeFloatLE(obj->getOrigin().y());
|
|
stream->writeFloatLE(obj->getOrigin().z());
|
|
}
|
|
|
|
stream->writeUint32LE(_colorRemaps.size());
|
|
for (auto &it : _colorRemaps) {
|
|
stream->writeUint32LE(it._key);
|
|
stream->writeUint32LE(it._value);
|
|
}
|
|
}
|
|
|
|
void Area::remapColor(int index, int color) {
|
|
_colorRemaps[index] = color;
|
|
}
|
|
|
|
void Area::unremapColor(int index) {
|
|
_colorRemaps.clear(index);
|
|
}
|
|
|
|
void Area::resetAreaGroups() {
|
|
debugC(1, kFreescapeDebugMove, "Resetting groups from area: %s", _name.c_str());
|
|
if (_objectsByID) {
|
|
for (auto &it : *_objectsByID) {
|
|
Object *obj = it._value;
|
|
|
|
if (obj->getType() == ObjectType::kGroupType)
|
|
((Group *)obj)->reset();
|
|
}
|
|
}
|
|
}
|
|
|
|
void Area::resetArea() {
|
|
debugC(1, kFreescapeDebugMove, "Resetting objects from area: %s", _name.c_str());
|
|
_colorRemaps.clear();
|
|
if (_objectsByID) {
|
|
for (auto &it : *_objectsByID) {
|
|
Object *obj = it._value;
|
|
if (obj->isDestroyed())
|
|
obj->restore();
|
|
|
|
if (obj->isInitiallyInvisible())
|
|
obj->makeInvisible();
|
|
else
|
|
obj->makeVisible();
|
|
}
|
|
}
|
|
if (_entrancesByID) {
|
|
for (auto &it : *_entrancesByID) {
|
|
Object *obj = it._value;
|
|
if (obj->isDestroyed())
|
|
obj->restore();
|
|
|
|
if (obj->isInitiallyInvisible())
|
|
obj->makeInvisible();
|
|
else
|
|
obj->makeVisible();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void Area::draw(Freescape::Renderer *gfx, uint32 animationTicks, Math::Vector3d camera, Math::Vector3d direction) {
|
|
bool runAnimation = animationTicks != _lastTick;
|
|
assert(_drawableObjects.size() > 0);
|
|
ObjectArray planarObjects;
|
|
ObjectArray nonPlanarObjects;
|
|
Object *floor = nullptr;
|
|
Common::HashMap<Object *, float> sizes;
|
|
float offset = (gfx->_isAccelerated ? 1.0 : 2.0) / _scale;
|
|
|
|
for (auto &obj : _drawableObjects) {
|
|
if (!obj->isDestroyed() && !obj->isInvisible()) {
|
|
if (obj->getObjectID() == 0 && _groundColor < 255 && _skyColor < 255) {
|
|
floor = obj;
|
|
continue;
|
|
}
|
|
|
|
if (obj->getType() == ObjectType::kGroupType) {
|
|
drawGroup(gfx, (Group *)obj, runAnimation);
|
|
continue;
|
|
}
|
|
|
|
if (obj->isPlanar() && (obj->getType() != ObjectType::kSensorType))
|
|
planarObjects.push_back(obj);
|
|
else
|
|
nonPlanarObjects.push_back(obj);
|
|
}
|
|
}
|
|
|
|
if (floor) {
|
|
gfx->depthTesting(false);
|
|
floor->draw(gfx);
|
|
gfx->depthTesting(true);
|
|
}
|
|
|
|
Common::HashMap<Object *, float> offsetMap;
|
|
for (auto &planar : planarObjects)
|
|
offsetMap[planar] = 0;
|
|
|
|
for (auto &planar : planarObjects) {
|
|
Math::Vector3d centerPlanar = planar->_boundingBox.getMin() + planar->_boundingBox.getMax();
|
|
centerPlanar /= 2;
|
|
Math::Vector3d distance;
|
|
for (auto &object : nonPlanarObjects) {
|
|
if (object->_partOfGroup)
|
|
continue;
|
|
|
|
distance = object->_boundingBox.distance(centerPlanar);
|
|
if (distance.length() > 0.0001)
|
|
continue;
|
|
|
|
float sizeNonPlanar = object->_boundingBox.getSize().length();
|
|
if (sizes[planar] >= sizeNonPlanar)
|
|
continue;
|
|
|
|
sizes[planar] = sizeNonPlanar;
|
|
|
|
if (planar->getSize().x() == 0) {
|
|
if (object->getOrigin().x() >= centerPlanar.x())
|
|
offsetMap[planar] = -offset;
|
|
else
|
|
offsetMap[planar] = offset;
|
|
} else if (planar->getSize().y() == 0) {
|
|
if (object->getOrigin().y() >= centerPlanar.y())
|
|
offsetMap[planar] = -offset;
|
|
else
|
|
offsetMap[planar] = offset;
|
|
} else if (planar->getSize().z() == 0) {
|
|
if (object->getOrigin().z() >= centerPlanar.z())
|
|
offsetMap[planar] = -offset;
|
|
else
|
|
offsetMap[planar] = offset;
|
|
} else
|
|
; //It was not really planar?!
|
|
}
|
|
}
|
|
|
|
for (auto &planar : planarObjects) {
|
|
Math::Vector3d centerPlanar = planar->_boundingBox.getMin() + planar->_boundingBox.getMax();
|
|
centerPlanar /= 2;
|
|
Math::Vector3d distance;
|
|
for (auto &object : planarObjects) {
|
|
if (object == planar)
|
|
continue;
|
|
|
|
distance = object->_boundingBox.distance(centerPlanar);
|
|
if (distance.length() > 0)
|
|
continue;
|
|
|
|
if (planar->getSize().x() == 0) {
|
|
if (object->getSize().x() > 0)
|
|
continue;
|
|
} else if (planar->getSize().y() == 0) {
|
|
if (object->getSize().y() > 0)
|
|
continue;
|
|
} else if (planar->getSize().z() == 0) {
|
|
if (object->getSize().z() > 0)
|
|
continue;
|
|
} else
|
|
continue;
|
|
|
|
//debug("planar object %d collides with planar object %d", planar->getObjectID(), object->getObjectID());
|
|
if (offsetMap[planar] == offsetMap[object] && offsetMap[object] != 0) {
|
|
// Nothing to do?
|
|
} else if (offsetMap[planar] == offsetMap[object] && offsetMap[object] == 0) {
|
|
if (planar->getSize().x() == 0) {
|
|
if (object->getOrigin().x() < centerPlanar.x())
|
|
offsetMap[planar] = -offset;
|
|
else
|
|
offsetMap[planar] = offset;
|
|
} else if (planar->getSize().y() == 0) {
|
|
if (object->getOrigin().y() < centerPlanar.y())
|
|
offsetMap[planar] = -offset;
|
|
else
|
|
offsetMap[planar] = offset;
|
|
} else if (planar->getSize().z() == 0) {
|
|
if (object->getOrigin().z() < centerPlanar.z())
|
|
offsetMap[planar] = -offset;
|
|
else
|
|
offsetMap[planar] = offset;
|
|
} else
|
|
; //It was not really planar?!
|
|
}
|
|
}
|
|
}
|
|
|
|
// In theory, the ordering of the rendering should not matter,
|
|
// however, it seems that rendering the planar objects first
|
|
// triggers a bug in TinyGL where certain objects such as lines,
|
|
// are not rendered correctly. This is a workaround for that issue.
|
|
for (auto &obj : nonPlanarObjects) {
|
|
obj->draw(gfx);
|
|
}
|
|
|
|
for (auto &pair : offsetMap) {
|
|
pair._key->draw(gfx, pair._value);
|
|
}
|
|
|
|
_lastTick = animationTicks;
|
|
}
|
|
|
|
void Area::drawGroup(Freescape::Renderer *gfx, Group* group, bool runAnimation) {
|
|
if (runAnimation) {
|
|
group->run();
|
|
group->draw(gfx);
|
|
group->step();
|
|
} else
|
|
group->draw(gfx);
|
|
}
|
|
|
|
bool Area::hasActiveGroups() {
|
|
for (auto &obj : _drawableObjects) {
|
|
if (obj->getType() == kGroupType) {
|
|
Group *group = (Group *)obj;
|
|
if (group->isActive())
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
Object *Area::checkCollisionRay(const Math::Ray &ray, int raySize) {
|
|
float distance = 1.0;
|
|
float size = 16.0 * 8192.0; // TODO: check if this is the max size
|
|
Math::AABB boundingBox(ray.getOrigin(), ray.getOrigin());
|
|
Object *collided = nullptr;
|
|
for (auto &obj : _drawableObjects) {
|
|
if (obj->getType() == kLineType)
|
|
// If the line is not along an axis, the AABB is wildly inaccurate so we skip it
|
|
if (((GeometricObject *)obj)->isLineButNotStraight())
|
|
continue;
|
|
|
|
if (!obj->isDestroyed() && !obj->isInvisible()) {
|
|
GeometricObject *gobj = (GeometricObject *)obj;
|
|
Math::Vector3d collidedNormal;
|
|
float collidedDistance = sweepAABB(boundingBox, gobj->_boundingBox, raySize * ray.getDirection(), collidedNormal);
|
|
debugC(1, kFreescapeDebugMove, "reached obj id: %d with distance %f", obj->getObjectID(), collidedDistance);
|
|
if (collidedDistance >= 1.0)
|
|
continue;
|
|
|
|
if (collidedDistance == 0.0 && signbit(collidedDistance))
|
|
continue;
|
|
|
|
if (collidedDistance < distance || (ABS(collidedDistance - distance) <= 0.05 && gobj->getSize().length() < size)) {
|
|
collided = obj;
|
|
size = gobj->getSize().length();
|
|
distance = collidedDistance;
|
|
}
|
|
}
|
|
}
|
|
return collided;
|
|
}
|
|
|
|
ObjectArray Area::checkCollisions(const Math::AABB &boundingBox) {
|
|
ObjectArray collided;
|
|
for (auto &obj : _drawableObjects) {
|
|
if (!obj->isDestroyed() && !obj->isInvisible()) {
|
|
GeometricObject *gobj = (GeometricObject *)obj;
|
|
if (gobj->collides(boundingBox)) {
|
|
collided.push_back(gobj);
|
|
}
|
|
}
|
|
}
|
|
return collided;
|
|
}
|
|
|
|
bool Area::checkIfPlayerWasCrushed(const Math::AABB &boundingBox) {
|
|
for (auto &obj : _drawableObjects) {
|
|
if (!obj->isDestroyed() && !obj->isInvisible() && obj->getType() == kGroupType) {
|
|
Group *group = (Group *)obj;
|
|
if (group->collides(boundingBox)) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
Math::Vector3d Area::separateFromWall(const Math::Vector3d &_position) {
|
|
Math::Vector3d position = _position;
|
|
float sep = 8 / _scale;
|
|
for (auto &obj : _drawableObjects) {
|
|
if (!obj->isDestroyed() && !obj->isInvisible()) {
|
|
GeometricObject *gobj = (GeometricObject *)obj;
|
|
Math::Vector3d distance = gobj->_boundingBox.distance(position);
|
|
if (distance.length() > 0.0001)
|
|
continue;
|
|
|
|
position.z() = position.z() + sep;
|
|
distance = gobj->_boundingBox.distance(position);
|
|
if (distance.length() > 0.0001)
|
|
return position;
|
|
|
|
position = _position;
|
|
position.z() = position.z() - sep;
|
|
distance = gobj->_boundingBox.distance(position);
|
|
if (distance.length() > 0.0001)
|
|
return position;
|
|
|
|
position = _position;
|
|
position.x() = position.x() + sep;
|
|
distance = gobj->_boundingBox.distance(position);
|
|
if (distance.length() > 0.0001)
|
|
return position;
|
|
|
|
position = _position;
|
|
position.x() = position.x() - sep;
|
|
distance = gobj->_boundingBox.distance(position);
|
|
if (distance.length() > 0.0001)
|
|
return position;
|
|
}
|
|
}
|
|
return position;
|
|
}
|
|
|
|
Math::Vector3d Area::resolveCollisions(const Math::Vector3d &lastPosition_, const Math::Vector3d &newPosition_, int playerHeight) {
|
|
Math::Vector3d position = newPosition_;
|
|
Math::Vector3d lastPosition = lastPosition_;
|
|
|
|
float reductionHeight = 0.0;
|
|
// Ugly hack to fix the collisions in tight spaces in the stores and junk room
|
|
// for Castle Master
|
|
if (_name == " STORES " && _areaID == 62) {
|
|
reductionHeight = 0.3f;
|
|
} else if (_name == " JUNK ROOM " && _areaID == 61) {
|
|
reductionHeight = 0.3f;
|
|
}
|
|
|
|
Math::AABB boundingBox = createPlayerAABB(lastPosition, playerHeight, reductionHeight);
|
|
|
|
float epsilon = 1.5;
|
|
int i = 0;
|
|
while (true) {
|
|
float distance = 1.0;
|
|
Math::Vector3d normal;
|
|
Math::Vector3d direction = position - lastPosition;
|
|
|
|
for (auto &obj : _drawableObjects) {
|
|
if (!obj->isDestroyed() && !obj->isInvisible()) {
|
|
GeometricObject *gobj = (GeometricObject *)obj;
|
|
Math::Vector3d collidedNormal;
|
|
float collidedDistance = sweepAABB(boundingBox, gobj->_boundingBox, direction, collidedNormal);
|
|
if (collidedDistance < distance) {
|
|
distance = collidedDistance;
|
|
normal = collidedNormal;
|
|
}
|
|
}
|
|
}
|
|
position = lastPosition + distance * direction + epsilon * normal;
|
|
if (i > 1 || distance >= 1.0)
|
|
break;
|
|
i++;
|
|
}
|
|
return position;
|
|
}
|
|
|
|
bool Area::checkInSight(const Math::Ray &ray, float maxDistance) {
|
|
Math::Vector3d direction = ray.getDirection();
|
|
direction.normalize();
|
|
GeometricObject point(kCubeType,
|
|
0,
|
|
0,
|
|
Math::Vector3d(0, 0, 0),
|
|
Math::Vector3d(maxDistance / 30, maxDistance / 30, maxDistance / 30), // size
|
|
nullptr,
|
|
nullptr,
|
|
nullptr,
|
|
FCLInstructionVector(),
|
|
"");
|
|
|
|
for (int distanceMultiplier = 2; distanceMultiplier <= 10; distanceMultiplier++) {
|
|
Math::Vector3d origin = ray.getOrigin() + distanceMultiplier * (maxDistance / 10) * direction;
|
|
point.setOrigin(origin);
|
|
|
|
for (auto &obj : _drawableObjects) {
|
|
if (obj->getType() != kSensorType && !obj->isDestroyed() && !obj->isInvisible() && obj->_boundingBox.isValid() && point.collides(obj->_boundingBox)) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void Area::addObject(Object *obj) {
|
|
assert(obj);
|
|
int id = obj->getObjectID();
|
|
debugC(1, kFreescapeDebugParser, "Adding object %d to room %d", id, _areaID);
|
|
assert(!_objectsByID->contains(id));
|
|
(*_objectsByID)[id] = obj;
|
|
if (obj->isDrawable())
|
|
_drawableObjects.insert_at(0, obj);
|
|
|
|
_addedObjects[id] = obj;
|
|
}
|
|
|
|
void Area::removeObject(int16 id) {
|
|
assert(_objectsByID->contains(id));
|
|
for (uint i = 0; i < _drawableObjects.size(); i++) {
|
|
if (_drawableObjects[i]->getObjectID() == id) {
|
|
_drawableObjects.remove_at(i);
|
|
break;
|
|
}
|
|
}
|
|
_objectsByID->erase(id);
|
|
_addedObjects.erase(id);
|
|
}
|
|
|
|
void Area::addObjectFromArea(int16 id, Area *global) {
|
|
debugC(1, kFreescapeDebugParser, "Adding object %d to room structure in area %d", id, _areaID);
|
|
Object *obj = global->objectWithID(id);
|
|
if (!obj) {
|
|
assert(global->entranceWithID(id));
|
|
obj = global->entranceWithID(id);
|
|
obj = obj->duplicate();
|
|
obj->scale(_scale);
|
|
_addedObjects[id] = obj;
|
|
(*_entrancesByID)[id] = obj;
|
|
} else {
|
|
obj = obj->duplicate();
|
|
obj->scale(_scale);
|
|
(*_objectsByID)[id] = obj;
|
|
_addedObjects[id] = obj;
|
|
if (obj->isDrawable()) {
|
|
_drawableObjects.insert_at(0, obj);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Area::addGroupFromArea(int16 id, Area *global) {
|
|
debugC(1, kFreescapeDebugParser, "Adding group %d to room structure in area %d", id, _areaID);
|
|
Object *obj = global->objectWithID(id);
|
|
assert(obj);
|
|
assert(obj->getType() == ObjectType::kGroupType);
|
|
|
|
addObjectFromArea(id, global);
|
|
Group *group = (Group *)objectWithID(id);
|
|
for (auto &it : ((Group *)obj)->_objectIds) {
|
|
if (it == 0 || it == 0xffff)
|
|
break;
|
|
if (!global->objectWithID(it))
|
|
continue;
|
|
|
|
if (!objectWithID(it))
|
|
addObjectFromArea(it, global);
|
|
group->linkObject(objectWithID(it));
|
|
}
|
|
}
|
|
|
|
|
|
void Area::addFloor() {
|
|
int id = 0;
|
|
assert(!_objectsByID->contains(id));
|
|
Common::Array<uint8> *gColors = new Common::Array<uint8>;
|
|
for (int i = 0; i < 6; i++)
|
|
gColors->push_back(_groundColor);
|
|
|
|
int maxSize = 10000000 / 4;
|
|
Object *obj = (Object *)new GeometricObject(
|
|
ObjectType::kCubeType,
|
|
id,
|
|
0, // flags
|
|
Math::Vector3d(-maxSize, -3, -maxSize), // Position
|
|
Math::Vector3d(maxSize * 4, 3, maxSize * 4), // size
|
|
gColors,
|
|
nullptr,
|
|
nullptr,
|
|
FCLInstructionVector());
|
|
(*_objectsByID)[id] = obj;
|
|
_drawableObjects.insert_at(0, obj);
|
|
}
|
|
|
|
void Area::addStructure(Area *global) {
|
|
if (!global || !_entrancesByID->contains(255)) {
|
|
return;
|
|
}
|
|
GlobalStructure *rs = (GlobalStructure *)(*_entrancesByID)[255];
|
|
|
|
for (uint i = 0; i < rs->_structure.size(); i++) {
|
|
int16 id = rs->_structure[i];
|
|
if (id == 0)
|
|
continue;
|
|
|
|
addObjectFromArea(id, global);
|
|
}
|
|
}
|
|
|
|
void Area::changeObjectID(uint16 objectID, uint16 newObjectID) {
|
|
assert(!objectWithID(newObjectID));
|
|
Object *obj = objectWithID(objectID);
|
|
assert(obj);
|
|
obj->_objectID = newObjectID;
|
|
_addedObjects.erase(objectID);
|
|
_addedObjects[newObjectID] = obj;
|
|
|
|
(*_objectsByID).erase(objectID);
|
|
(*_objectsByID)[newObjectID] = obj;
|
|
}
|
|
|
|
|
|
bool Area::isOutside() {
|
|
return _skyColor < 255 && _groundColor < 255;
|
|
}
|
|
|
|
} // End of namespace Freescape
|