/* 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 .
*
*/
// 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