mirror of
https://github.com/scummvm/scummvm.git
synced 2025-04-02 10:52:32 -04:00
1925 lines
58 KiB
C++
1925 lines
58 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/>.
|
|
*
|
|
*/
|
|
|
|
#include "common/file.h"
|
|
#include "common/path.h"
|
|
#include "common/textconsole.h"
|
|
|
|
#include "tetraedge/tetraedge.h"
|
|
#include "tetraedge/game/application.h"
|
|
#include "tetraedge/game/billboard.h"
|
|
#include "tetraedge/game/syberia_game.h"
|
|
#include "tetraedge/game/in_game_scene.h"
|
|
#include "tetraedge/game/in_game_scene_xml_parser.h"
|
|
#include "tetraedge/game/character.h"
|
|
#include "tetraedge/game/characters_shadow.h"
|
|
#include "tetraedge/game/object3d.h"
|
|
#include "tetraedge/game/particle_xml_parser.h"
|
|
#include "tetraedge/game/scene_lights_xml_parser.h"
|
|
|
|
#include "tetraedge/te/te_bezier_curve.h"
|
|
#include "tetraedge/te/te_camera.h"
|
|
#include "tetraedge/te/te_core.h"
|
|
#include "tetraedge/te/te_free_move_zone.h"
|
|
#include "tetraedge/te/te_light.h"
|
|
#include "tetraedge/te/te_renderer.h"
|
|
#include "tetraedge/te/te_lua_script.h"
|
|
#include "tetraedge/te/te_lua_thread.h"
|
|
|
|
//#define TETRAEDGE_DEBUG_PATHFINDING
|
|
//#define TETRAEDGE_DEBUG_LIGHTS
|
|
|
|
namespace Tetraedge {
|
|
|
|
/*static*/
|
|
const int InGameScene::MAX_FIRE = 50;
|
|
const int InGameScene::MAX_SNOW = 250;
|
|
const int InGameScene::MAX_SMOKE = 350;
|
|
const float InGameScene::DUREE_MAX_FIRE = 32000.0f;
|
|
const float InGameScene::SCALE_FIRE = 0.1f;
|
|
const int InGameScene::MAX_FLAKE = 10;
|
|
const float InGameScene::DUREE_MIN_FLAKE = 3000.0f;
|
|
const float InGameScene::DUREE_MAX_FLAKE = 5000.0f;
|
|
const float InGameScene::SCALE_FLAKE = 0.1f;
|
|
const float InGameScene::DEPTH_MAX_FLAKE = 0.1f;
|
|
|
|
|
|
InGameScene::InGameScene() : _character(nullptr), _charactersShadow(nullptr),
|
|
_shadowLightNo(-1), _waitTime(-1.0), _shadowColor(0, 0, 0, 0x80), _shadowFov(20.0f),
|
|
_shadowFarPlane(1000), _shadowNearPlane(1), _maskAlpha(false),
|
|
_verticalScrollTime(1000000.0f), _verticalScrollPlaying(false) {
|
|
}
|
|
|
|
void InGameScene::activateAnchorZone(const Common::String &name, bool val) {
|
|
for (AnchorZone *zone : _anchorZones) {
|
|
if (zone->_name == name)
|
|
zone->_activated = val;
|
|
}
|
|
}
|
|
|
|
void InGameScene::addAnchorZone(const Common::String &s1, const Common::String &name, float radius) {
|
|
for (AnchorZone *zone : _anchorZones) {
|
|
if (zone->_name == name) {
|
|
zone->_radius = radius;
|
|
return;
|
|
}
|
|
}
|
|
|
|
assert(currentCamera());
|
|
currentCamera()->apply();
|
|
AnchorZone *zone = new AnchorZone();
|
|
zone->_name = name;
|
|
zone->_radius = radius;
|
|
zone->_activated = true;
|
|
|
|
if (s1.contains("Int")) {
|
|
TeButtonLayout *btn = hitObjectGui().buttonLayoutChecked(name);
|
|
TeVector3f32 pos = btn->position();
|
|
pos.x() += g_engine->getDefaultScreenWidth() / 2.0f;
|
|
pos.y() += g_engine->getDefaultScreenHeight() / 2.0f;
|
|
zone->_loc = currentCamera()->worldTransformationMatrix() * currentCamera()->transformPoint2Dto3D(pos);
|
|
} else {
|
|
if (s1.contains("Dummy")) {
|
|
Dummy d = dummy(name);
|
|
zone->_loc = d._position;
|
|
}
|
|
}
|
|
_anchorZones.push_back(zone);
|
|
}
|
|
|
|
bool InGameScene::addMarker(const Common::String &markerName, const Common::Path &imgPath, float x, float y, const Common::String &locType, const Common::String &markerVal, float anchorX, float anchorY) {
|
|
const TeMarker *marker = findMarker(markerName);
|
|
if (!marker) {
|
|
SyberiaGame *game = dynamic_cast<SyberiaGame *>(g_engine->getGame());
|
|
assert(game);
|
|
Application *app = g_engine->getApplication();
|
|
TeSpriteLayout *markerSprite = new TeSpriteLayout();
|
|
// Note: game checks paths here but seems to just use the original?
|
|
markerSprite->setName(markerName);
|
|
markerSprite->setAnchor(TeVector3f32(anchorX, anchorY, 0.0f));
|
|
if (!markerSprite->load(imgPath) && imgPath.baseName().hasSuffix(".anim"))
|
|
markerSprite->load(imgPath.append("cached"));
|
|
markerSprite->setSizeType(TeILayout::RELATIVE_TO_PARENT);
|
|
markerSprite->setPositionType(TeILayout::RELATIVE_TO_PARENT);
|
|
TeVector3f32 newPos;
|
|
if (locType == "PERCENT") {
|
|
TeVector3f32 parentSize;
|
|
//if (g_engine->gameType() == TetraedgeEngine::kSyberia)
|
|
parentSize = app->frontLayout().userSize();
|
|
//else
|
|
// parentSize = app->getMainWindow().size();
|
|
newPos.x() = parentSize.x() * (x / 100.0f);
|
|
newPos.y() = parentSize.y() * (y / 100.0f);
|
|
} else {
|
|
newPos.x() = x / g_engine->getDefaultScreenWidth();
|
|
newPos.y() = y / g_engine->getDefaultScreenHeight();
|
|
}
|
|
markerSprite->setPosition(newPos);
|
|
|
|
const TeVector3f32 winSize = app->getMainWindow().size();
|
|
float xscale = 1.0f;
|
|
float yscale = 1.0f;
|
|
|
|
// Originally this is only done in Syberia 2, but
|
|
// should be fine to calculate in Syberia 1, as long
|
|
// as the root layout is loaded.
|
|
TeLayout *bglayout = _bgGui.layoutChecked("background");
|
|
TeSpriteLayout *rootlayout = Game::findSpriteLayoutByName(bglayout, "root");
|
|
if (rootlayout && rootlayout->_tiledSurfacePtr && rootlayout->_tiledSurfacePtr->tiledTexture()) {
|
|
TeVector2s32 bgSize = rootlayout->_tiledSurfacePtr->tiledTexture()->totalSize();
|
|
xscale = 800.0f / bgSize._x;
|
|
yscale = 600.0f / bgSize._y;
|
|
}
|
|
|
|
if (g_engine->getCore()->fileFlagSystemFlag("definition") == "SD") {
|
|
markerSprite->setSize(TeVector3f32(xscale * 0.07f, yscale * (4.0f / ((winSize.y() / winSize.x()) * 4.0f)) * 0.07f, 0.0));
|
|
} else {
|
|
markerSprite->setSize(TeVector3f32(xscale * 0.04f, yscale * (4.0f / ((winSize.y() / winSize.x()) * 4.0f)) * 0.04f, 0.0));
|
|
}
|
|
markerSprite->setVisible(game->markersVisible());
|
|
markerSprite->_tiledSurfacePtr->_frameAnim.setLoopCount(-1);
|
|
markerSprite->play();
|
|
|
|
TeMarker newMarker;
|
|
newMarker._name = markerName;
|
|
newMarker._val = markerVal;
|
|
_markers.push_back(newMarker);
|
|
TeLayout *bg = game->forGui().layout("background");
|
|
if (bg)
|
|
bg->addChild(markerSprite);
|
|
_sprites.push_back(markerSprite);
|
|
} else {
|
|
setImagePathMarker(markerName, imgPath);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*static*/
|
|
float InGameScene::angularDistance(float a1, float a2) {
|
|
float result = a2 - a1;
|
|
if (result >= -M_PI && result > M_PI) {
|
|
result = result - (M_PI * 2);
|
|
} else {
|
|
result = result + (M_PI * 2);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
bool InGameScene::aroundAnchorZone(const AnchorZone *zone) {
|
|
if (!zone->_activated)
|
|
return false;
|
|
const TeVector3f32 charpos = _character->_model->position();
|
|
|
|
float xoff = charpos.x() - zone->_loc.x();
|
|
float zoff = charpos.z() - zone->_loc.z();
|
|
return sqrt(xoff * xoff + zoff * zoff) <= zone->_radius;
|
|
}
|
|
|
|
TeLayout *InGameScene::background() {
|
|
return _bgGui.layout("background");
|
|
}
|
|
|
|
Billboard *InGameScene::billboard(const Common::String &name) {
|
|
for (Billboard *billboard : _billboards) {
|
|
if (billboard->model()->name() == name)
|
|
return billboard;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
bool InGameScene::changeBackground(const Common::Path &name) {
|
|
TetraedgeFSNode node = g_engine->getCore()->findFile(name);
|
|
if (node.isReadable()) {
|
|
_bgGui.spriteLayoutChecked("root")->load(name);
|
|
if (g_engine->gameType() == TetraedgeEngine::kSyberia2)
|
|
_bgGui.spriteLayoutChecked("root")->play();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
Character *InGameScene::character(const Common::String &name) {
|
|
if (_character && _character->_model->name() == name)
|
|
return _character;
|
|
|
|
for (Character *c : _characters) {
|
|
if (c->_model->name() == name)
|
|
return c;
|
|
}
|
|
|
|
// WORKAROUND: Didn't find char, try again with case insensitive
|
|
// for "OScar" typo in scenes/ValTrain/19000.
|
|
for (Character *c : _characters) {
|
|
if (c->_model->name().compareToIgnoreCase(name) == 0)
|
|
return c;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void InGameScene::close() {
|
|
reset();
|
|
_loadedPath = "";
|
|
TeScene::close();
|
|
freeGeometry();
|
|
if (_character && _character->_model && !findKate()) {
|
|
models().push_back(_character->_model);
|
|
if (_character->_shadowModel[0]) {
|
|
models().push_back(_character->_shadowModel[0]);
|
|
models().push_back(_character->_shadowModel[1]);
|
|
}
|
|
}
|
|
_objects.clear();
|
|
for (TeFreeMoveZone *zone : _freeMoveZones)
|
|
delete zone;
|
|
_freeMoveZones.clear();
|
|
_hitObjects.clear();
|
|
for (TePickMesh2 *mesh : _clickMeshes)
|
|
delete mesh;
|
|
_clickMeshes.clear();
|
|
_bezierCurves.clear();
|
|
_dummies.clear();
|
|
freeSceneObjects();
|
|
}
|
|
|
|
void InGameScene::convertPathToMesh(TeFreeMoveZone *zone) {
|
|
TeIntrusivePtr<TeModel> model = new TeModel();
|
|
model->meshes().clear();
|
|
model->setMeshCount(1);
|
|
model->setName("shadowReceiving");
|
|
model->setPosition(zone->position());
|
|
model->setRotation(zone->rotation());
|
|
model->setScale(zone->scale());
|
|
uint64 nverticies = zone->freeMoveZoneVerticies().size();
|
|
TeMesh *mesh0 = model->meshes()[0].get();
|
|
mesh0->setConf(nverticies, nverticies, TeMesh::MeshMode_Triangles, 0, 0);
|
|
for (uint i = 0; i < nverticies; i++) {
|
|
mesh0->setIndex(i, i);
|
|
mesh0->setVertex(i, zone->freeMoveZoneVerticies()[i]);
|
|
mesh0->setNormal(i, TeVector3f32(0, 0, 1));
|
|
}
|
|
_zoneModels.push_back(model);
|
|
}
|
|
|
|
TeIntrusivePtr<TeBezierCurve> InGameScene::curve(const Common::String &curveName) {
|
|
for (TeIntrusivePtr<TeBezierCurve> &c : _bezierCurves) {
|
|
if (c->name() == curveName)
|
|
return c;
|
|
}
|
|
return TeIntrusivePtr<TeBezierCurve>();
|
|
}
|
|
|
|
void InGameScene::deleteAllCallback() {
|
|
for (auto &pair : _callbacks) {
|
|
for (auto *cb : pair._value) {
|
|
delete cb;
|
|
}
|
|
pair._value.clear();
|
|
}
|
|
_callbacks.clear();
|
|
}
|
|
|
|
void InGameScene::deleteMarker(const Common::String &markerName) {
|
|
if (!isMarker(markerName))
|
|
return;
|
|
|
|
for (uint i = 0; i < _markers.size(); i++) {
|
|
if (_markers[i]._name == markerName) {
|
|
_markers.remove_at(i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
Game *game = g_engine->getGame();
|
|
TeLayout *bg = game->forGui().layout("background");
|
|
if (!bg)
|
|
return;
|
|
for (Te3DObject2 *child : bg->childList()) {
|
|
if (child->name() == markerName) {
|
|
bg->removeChild(child);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void InGameScene::deserializeCam(Common::ReadStream &stream, TeIntrusivePtr<TeCamera> &cam) {
|
|
cam->setProjMatrixType(2);
|
|
cam->viewport(0, 0, _viewportSize.getX(), _viewportSize.getY());
|
|
// load name/position/rotation/scale
|
|
Te3DObject2::deserialize(stream, *cam);
|
|
cam->setFov(stream.readFloatLE());
|
|
cam->setAspectRatio(stream.readFloatLE());
|
|
// Original loads the second val then ignores it and sets 3000.
|
|
cam->setOrthoPlanes(stream.readFloatLE(), 3000.0);
|
|
stream.readFloatLE();
|
|
}
|
|
|
|
void InGameScene::deserializeModel(Common::ReadStream &stream, TeIntrusivePtr<TeModel> &model, TePickMesh2 *pickmesh) {
|
|
TeVector3f32 vec;
|
|
TeVector2f32 vec2;
|
|
TeQuaternion rot;
|
|
TeColor col;
|
|
Common::SharedPtr<TeMesh> mesh(TeMesh::makeInstance());
|
|
|
|
assert(pickmesh);
|
|
|
|
TeVector3f32::deserialize(stream, vec);
|
|
model->setPosition(vec);
|
|
pickmesh->setPosition(vec);
|
|
TeQuaternion::deserialize(stream, rot);
|
|
model->setRotation(rot);
|
|
pickmesh->setRotation(rot);
|
|
TeVector3f32::deserialize(stream, vec);
|
|
model->setScale(vec);
|
|
pickmesh->setScale(vec);
|
|
uint32 indexcount = stream.readUint32LE();
|
|
uint32 vertexcount = stream.readUint32LE();
|
|
|
|
if (indexcount > 100000 || vertexcount > 100000)
|
|
error("InGameScene::deserializeModel: Unxpected counts %d %d", indexcount, vertexcount);
|
|
|
|
mesh->setConf(vertexcount, indexcount, TeMesh::MeshMode_Triangles, 0, 0);
|
|
for (uint i = 0; i < indexcount; i++)
|
|
mesh->setIndex(i, stream.readUint32LE());
|
|
|
|
for (uint i = 0; i < vertexcount; i++) {
|
|
TeVector3f32::deserialize(stream, vec);
|
|
mesh->setVertex(i, vec);
|
|
}
|
|
for (uint i = 0; i < vertexcount; i++) {
|
|
TeVector3f32::deserialize(stream, vec);
|
|
mesh->setNormal(i, vec);
|
|
}
|
|
for (uint i = 0; i < vertexcount; i++) {
|
|
TeVector2f32::deserialize(stream, vec2);
|
|
mesh->setTextureUV(i, vec2);
|
|
}
|
|
for (uint i = 0; i < vertexcount; i++) {
|
|
col.deserialize(stream);
|
|
mesh->setColor(i, col);
|
|
}
|
|
|
|
pickmesh->setNbTriangles(indexcount / 3);
|
|
for (uint i = 0; i < indexcount; i++) {
|
|
vec = mesh->vertex(mesh->index(i));
|
|
pickmesh->verticies()[i] = vec;
|
|
}
|
|
model->addMesh(mesh);
|
|
}
|
|
|
|
void InGameScene::draw() {
|
|
if (currentCameraIndex() >= (int)cameras().size())
|
|
return;
|
|
|
|
currentCamera()->apply();
|
|
|
|
drawMask();
|
|
drawReflection();
|
|
|
|
#ifdef TETRAEDGE_DEBUG_PATHFINDING
|
|
if (_character && _character->curve()) {
|
|
_character->curve()->setVisible(true);
|
|
_character->curve()->draw();
|
|
}
|
|
|
|
for (TeFreeMoveZone *zone : _freeMoveZones) {
|
|
zone->setVisible(true);
|
|
zone->draw();
|
|
}
|
|
|
|
for (TePickMesh2 *mesh : _clickMeshes) {
|
|
mesh->setVisible(true);
|
|
mesh->draw();
|
|
}
|
|
#endif
|
|
|
|
g_engine->getRenderer()->updateGlobalLight();
|
|
for (uint i = 0; i < _lights.size(); i++)
|
|
_lights[i]->update(i);
|
|
|
|
TeCamera::restore();
|
|
|
|
drawKate();
|
|
|
|
TeScene::draw();
|
|
}
|
|
|
|
void InGameScene::drawKate() {
|
|
if (_rippleMasks.size())
|
|
error("TODO: Implement InGameScene::drawKate");
|
|
}
|
|
|
|
void InGameScene::drawMask() {
|
|
if (_masks.empty())
|
|
return;
|
|
|
|
TeIntrusivePtr<TeCamera> cam = currentCamera();
|
|
if (!cam)
|
|
return;
|
|
|
|
cam->apply();
|
|
|
|
TeRenderer *rend = g_engine->getRenderer();
|
|
if (!_maskAlpha)
|
|
rend->colorMask(false, false, false, false);
|
|
|
|
for (auto &mask : _masks)
|
|
mask->draw();
|
|
|
|
if (!_maskAlpha)
|
|
rend->colorMask(true, true, true, true);
|
|
}
|
|
|
|
void InGameScene::drawReflection() {
|
|
if (_rippleMasks.empty() || currentCameraIndex() >= (int)cameras().size())
|
|
return;
|
|
|
|
currentCamera()->apply();
|
|
if (!_maskAlpha)
|
|
g_engine->getRenderer()->colorMask(false, false, false, false);
|
|
|
|
for (uint i = _rippleMasks.size() - 1; i > 0; i--) {
|
|
_rippleMasks[i]->draw();
|
|
}
|
|
|
|
if (!_maskAlpha)
|
|
g_engine->getRenderer()->colorMask(true, true, true, true);
|
|
}
|
|
|
|
void InGameScene::drawPath() {
|
|
if (currentCameraIndex() >= (int)cameras().size())
|
|
return;
|
|
|
|
currentCamera()->apply();
|
|
g_engine->getRenderer()->disableZBuffer();
|
|
|
|
for (uint i = 0; i < _freeMoveZones.size(); i++)
|
|
_freeMoveZones[i]->draw();
|
|
|
|
g_engine->getRenderer()->enableZBuffer();
|
|
}
|
|
|
|
InGameScene::Dummy InGameScene::dummy(const Common::String &name) {
|
|
for (const Dummy &dummy : _dummies) {
|
|
if (dummy._name == name)
|
|
return dummy;
|
|
}
|
|
return Dummy();
|
|
}
|
|
|
|
bool InGameScene::findKate() {
|
|
for (auto &m : models()) {
|
|
if (m->name() == "Kate")
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
const InGameScene::TeMarker *InGameScene::findMarker(const Common::String &name) {
|
|
for (const TeMarker &marker : _markers) {
|
|
if (marker._name == name)
|
|
return ▮
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
const InGameScene::TeMarker *InGameScene::findMarkerByInt(const Common::String &val) {
|
|
for (const TeMarker &marker : _markers) {
|
|
if (marker._val == val)
|
|
return ▮
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
InGameScene::SoundStep InGameScene::findSoundStep(const Common::String &name) {
|
|
for (const auto &step : _soundSteps) {
|
|
if (step._key == name)
|
|
return step._value;
|
|
}
|
|
return SoundStep();
|
|
}
|
|
|
|
void InGameScene::freeGeometry() {
|
|
_loadedPath.set("");
|
|
_youkiManager.reset();
|
|
freeSceneObjects();
|
|
if (_character)
|
|
_character->setFreeMoveZone(nullptr);
|
|
for (Character *character : _characters)
|
|
character->setFreeMoveZone(nullptr);
|
|
for (TeFreeMoveZone *zone : _freeMoveZones)
|
|
delete zone;
|
|
_freeMoveZones.clear();
|
|
_bezierCurves.clear();
|
|
_dummies.clear();
|
|
cameras().clear();
|
|
models().clear();
|
|
_zoneModels.clear();
|
|
_masks.clear();
|
|
_shadowReceivingObjects.clear();
|
|
if (_charactersShadow)
|
|
_charactersShadow->destroy();
|
|
_sceneLights.clear();
|
|
if (_charactersShadow) {
|
|
delete _charactersShadow;
|
|
_charactersShadow = nullptr;
|
|
}
|
|
}
|
|
|
|
void InGameScene::freeSceneObjects() {
|
|
if (_character) {
|
|
_character->setCharLookingAt(nullptr);
|
|
_character->deleteAllCallback();
|
|
}
|
|
if (_characters.size() == 1) {
|
|
_characters[0]->deleteAllCallback();
|
|
}
|
|
|
|
SyberiaGame *game = dynamic_cast<SyberiaGame *>(g_engine->getGame());
|
|
assert(game);
|
|
game->unloadCharacters();
|
|
|
|
_characters.clear();
|
|
|
|
for (Object3D *obj : _object3Ds) {
|
|
obj->deleteLater();
|
|
}
|
|
_object3Ds.clear();
|
|
|
|
for (Billboard *bb : _billboards) {
|
|
bb->deleteLater();
|
|
}
|
|
_billboards.clear();
|
|
|
|
for (TeSpriteLayout *sprite : _sprites) {
|
|
sprite->deleteLater();
|
|
}
|
|
_sprites.clear();
|
|
|
|
// TODO: Clean up snows, waterCones, smokes, snowCones
|
|
|
|
_particles.clear();
|
|
TeParticle::deleteAll();
|
|
|
|
deleteAllCallback();
|
|
_markers.clear();
|
|
|
|
// TODO: Clean up randomAnims
|
|
|
|
for (RippleMask *rmask : _rippleMasks) {
|
|
delete rmask;
|
|
}
|
|
_rippleMasks.clear();
|
|
|
|
for (InGameScene::AnchorZone *zone : _anchorZones) {
|
|
delete zone;
|
|
}
|
|
_anchorZones.clear();
|
|
}
|
|
|
|
float InGameScene::getHeadHorizontalRotation(Character *cter, const TeVector3f32 &vec) {
|
|
TeVector3f32 pos = vec - cter->_model->position();
|
|
TeVector3f32 zvec = TeVector3f32(0, 0, 1.0f);
|
|
zvec.rotate(cter->_model->rotation());
|
|
float angle = atan2f(-pos.x(), pos.z()) - atan2f(-zvec.x(), zvec.z());
|
|
if (angle < -M_PI)
|
|
angle += (float)(M_PI * 2);
|
|
else if (angle > M_PI)
|
|
angle -= (float)(M_PI * 2);
|
|
return angle;
|
|
}
|
|
|
|
float InGameScene::getHeadVerticalRotation(Character *cter, const TeVector3f32 &vec) {
|
|
TeVector3f32 modelPos = cter->_model->position();
|
|
TeVector3f32 headWorldTrans = cter->_model->worldTransformationMatrix() * cter->lastHeadBoneTrans();
|
|
modelPos.y() = headWorldTrans.y();
|
|
TeVector3f32 offsetPos = vec - modelPos;
|
|
currentCamera()->apply();
|
|
float angle = atan2f(offsetPos.y(), TeVector2f32(offsetPos.x(), offsetPos.z()).length());
|
|
return angle;
|
|
}
|
|
|
|
Common::Path InGameScene::imagePathMarker(const Common::String &name) {
|
|
if (!isMarker(name))
|
|
return Common::Path();
|
|
Game *game = g_engine->getGame();
|
|
TeLayout *bg = game->forGui().layoutChecked("background");
|
|
for (Te3DObject2 *child : bg->childList()) {
|
|
TeSpriteLayout *spritelayout = dynamic_cast<TeSpriteLayout *>(child);
|
|
if (spritelayout && spritelayout->name() == name) {
|
|
return spritelayout->_tiledSurfacePtr->loadedPath();
|
|
}
|
|
}
|
|
return Common::Path();
|
|
}
|
|
|
|
void InGameScene::initScroll() {
|
|
_scrollOffset = TeVector2f32(0.5f, 0.0f);
|
|
}
|
|
|
|
bool InGameScene::isMarker(const Common::String &name) {
|
|
for (const TeMarker &marker : _markers) {
|
|
if (marker._name == name)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool InGameScene::isObjectBlocking(const Common::String &name) {
|
|
for (const Common::String &b: _blockingObjects) {
|
|
if (name == b)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
TeVector2f32 InGameScene::layerSize() {
|
|
TeLayout *bglayout = _bgGui.layout("background");
|
|
TeVector3f32 sz;
|
|
if (bglayout) {
|
|
TeLayout *rootlayout = Game::findSpriteLayoutByName(bglayout, "root");
|
|
if (!rootlayout)
|
|
error("InGameScene::layerSize: No root layout inside the background");
|
|
sz = rootlayout->size();
|
|
_scrollScale = TeVector2f32(sz.x(), sz.y());
|
|
} else {
|
|
sz = g_engine->getApplication()->getMainWindow().size();
|
|
}
|
|
return TeVector2f32(sz.x(), sz.y());
|
|
}
|
|
|
|
bool InGameScene::load(const TetraedgeFSNode &sceneNode) {
|
|
// Syberia 1 has loadActZones function contents inline.
|
|
loadActZones();
|
|
|
|
if (!_lights.empty()) {
|
|
g_engine->getRenderer()->disableAllLights();
|
|
for (uint i = 0; i < _lights.size(); i++) {
|
|
_lights[i]->disable(i);
|
|
}
|
|
_lights.clear();
|
|
}
|
|
_shadowLightNo = -1;
|
|
|
|
TeCore *core = g_engine->getCore();
|
|
const TetraedgeFSNode lightsNode(core->findFile(getLightsFileName()));
|
|
if (lightsNode.isReadable())
|
|
loadLights(lightsNode);
|
|
|
|
if (!sceneNode.exists())
|
|
return false;
|
|
|
|
close();
|
|
_loadedPath = sceneNode.getPath();
|
|
Common::ScopedPtr<Common::SeekableReadStream> scenefile(sceneNode.createReadStream());
|
|
if (!scenefile)
|
|
return false;
|
|
|
|
uint32 ncameras = scenefile->readUint32LE();
|
|
if (ncameras > 1024)
|
|
error("Improbable number of cameras %d", ncameras);
|
|
for (uint i = 0; i < ncameras; i++) {
|
|
TeIntrusivePtr<TeCamera> cam = new TeCamera();
|
|
deserializeCam(*scenefile, cam);
|
|
cameras().push_back(cam);
|
|
}
|
|
|
|
uint32 nobjects = scenefile->readUint32LE();
|
|
if (nobjects > 1024)
|
|
error("Improbable number of objects %d", nobjects);
|
|
for (uint i = 0; i < nobjects; i++) {
|
|
TeIntrusivePtr<TeModel> model = new TeModel();
|
|
const Common::String modelname = Te3DObject2::deserializeString(*scenefile);
|
|
model->setName(modelname);
|
|
const Common::String objname = Te3DObject2::deserializeString(*scenefile);
|
|
TePickMesh2 *pickmesh = new TePickMesh2();
|
|
deserializeModel(*scenefile, model, pickmesh);
|
|
if (modelname.contains("Clic")) {
|
|
//debug("Loaded clickMesh %s", modelname.c_str());
|
|
_hitObjects.push_back(model);
|
|
model->setVisible(false);
|
|
model->setColor(TeColor(0, 0xff, 0, 0xff));
|
|
models().push_back(model);
|
|
pickmesh->setName(modelname);
|
|
_clickMeshes.push_back(pickmesh);
|
|
} else {
|
|
delete pickmesh;
|
|
if (modelname.substr(0, 2) != "ZB") {
|
|
if (objname.empty()) {
|
|
debug("[InGameScene::load] Unknown type of object named : %s", modelname.c_str());
|
|
} else {
|
|
InGameScene::Object obj;
|
|
obj._name = objname;
|
|
obj._model = model;
|
|
_objects.push_back(obj);
|
|
model->setVisible(false);
|
|
models().push_back(model);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
uint32 nfreemovezones = scenefile->readUint32LE();
|
|
if (nfreemovezones > 1024)
|
|
error("Improbable number of free move zones %d", nfreemovezones);
|
|
for (uint i = 0; i < nfreemovezones; i++) {
|
|
TeFreeMoveZone *zone = new TeFreeMoveZone();
|
|
TeFreeMoveZone::deserialize(*scenefile, *zone, &_blockers, &_rectBlockers, &_actZones);
|
|
_freeMoveZones.push_back(zone);
|
|
zone->setVisible(false);
|
|
}
|
|
|
|
uint32 ncurves = scenefile->readUint32LE();
|
|
if (ncurves > 1024)
|
|
error("Improbable number of curves %d", ncurves);
|
|
for (uint i = 0; i < ncurves; i++) {
|
|
TeIntrusivePtr<TeBezierCurve> curve = new TeBezierCurve();
|
|
TeBezierCurve::deserialize(*scenefile, *curve);
|
|
curve->setVisible(true);
|
|
_bezierCurves.push_back(curve);
|
|
}
|
|
|
|
uint32 ndummies = scenefile->readUint32LE();
|
|
if (ndummies > 1024)
|
|
error("Improbable number of dummies %d", ndummies);
|
|
for (uint i = 0; i < ndummies; i++) {
|
|
InGameScene::Dummy dummy;
|
|
TeVector3f32 vec;
|
|
TeQuaternion rot;
|
|
dummy._name = Te3DObject2::deserializeString(*scenefile);
|
|
TeVector3f32::deserialize(*scenefile, vec);
|
|
dummy._position = vec;
|
|
TeQuaternion::deserialize(*scenefile, rot);
|
|
dummy._rotation = rot;
|
|
TeVector3f32::deserialize(*scenefile, vec);
|
|
dummy._scale = vec;
|
|
_dummies.push_back(dummy);
|
|
}
|
|
|
|
for (TeFreeMoveZone *zone : _freeMoveZones) {
|
|
convertPathToMesh(zone);
|
|
}
|
|
_charactersShadow = CharactersShadow::makeInstance();
|
|
_charactersShadow->create(this);
|
|
onMainWindowSizeChanged();
|
|
|
|
return true;
|
|
}
|
|
|
|
static Common::Path _sceneFileNameBase(const Common::String &zone, const Common::String &scene) {
|
|
Common::Path retval("scenes");
|
|
retval.joinInPlace(zone).joinInPlace(scene);
|
|
return retval;
|
|
}
|
|
|
|
static Common::Path _sceneFileNameBase() {
|
|
const Game *game = g_engine->getGame();
|
|
return _sceneFileNameBase(game->currentZone(), game->currentScene());
|
|
}
|
|
|
|
bool InGameScene::loadXml(const Common::String &zone, const Common::String &scene) {
|
|
_maskAlpha = false;
|
|
_zoneName = zone;
|
|
_sceneName = scene;
|
|
_blockers.clear();
|
|
_rectBlockers.clear();
|
|
TeFreeMoveZone::setCollisionSlide(false);
|
|
loadBlockers();
|
|
|
|
Common::Path xmlpath = _sceneFileNameBase(zone, scene).joinInPlace("Scene")
|
|
.appendInPlace(scene).appendInPlace(".xml");
|
|
TetraedgeFSNode node = g_engine->getCore()->findFile(xmlpath);
|
|
InGameSceneXmlParser parser(this);
|
|
parser.setAllowText();
|
|
|
|
Common::String fixedbuf;
|
|
if (g_engine->gameType() == TetraedgeEngine::kSyberia2 && scene == "GangcarVideo") {
|
|
//
|
|
// WORKAROUND: scenes/A1_RomHaut/GangcarVideo/SceneGangcarVideo.xml
|
|
// in Syberia 2 has an embedded comment, which is invalid XML.
|
|
// Patch the contents of the file before loading.
|
|
//
|
|
Common::ScopedPtr<Common::SeekableReadStream> xmlFile(node.createReadStream());
|
|
if (!xmlFile)
|
|
error("InGameScene::loadXml: Can't open %s", node.toString().c_str());
|
|
const int64 bufsize = xmlFile->size();
|
|
char *buf = new char[bufsize+1];
|
|
buf[bufsize] = '\0';
|
|
xmlFile->read(buf, bufsize);
|
|
fixedbuf = Common::String(buf);
|
|
delete [] buf;
|
|
size_t offset = fixedbuf.find("<!-- <rippleMask");
|
|
if (offset != Common::String::npos)
|
|
fixedbuf.replace(offset, 4, " "); // replace the <! at the start
|
|
offset = fixedbuf.find("texture=\"R11280-1-00.png\"/> -->");
|
|
if (offset != Common::String::npos)
|
|
fixedbuf.replace(offset + 29, 3, " "); // replace the > at the end
|
|
offset = fixedbuf.find("<!--<light ");
|
|
if (offset != Common::String::npos)
|
|
fixedbuf.replace(offset, 4, " "); // replace the <! at the start
|
|
parser.loadBuffer((const byte *)fixedbuf.c_str(), bufsize);
|
|
} else {
|
|
// Regular loading.
|
|
if (!node.loadXML(parser))
|
|
error("InGameScene::loadXml: Can't load %s", node.toString().c_str());
|
|
}
|
|
|
|
if (!parser.parse())
|
|
error("InGameScene::loadXml: Can't parse %s", node.toString().c_str());
|
|
|
|
// loadFlamme and loadSnowCustom are handled by the above.
|
|
|
|
_charactersShadow = CharactersShadow::makeInstance();
|
|
_charactersShadow->create(this);
|
|
|
|
for (uint i = 0; i < _lights.size(); i++)
|
|
_lights[i]->disable(i);
|
|
_lights.clear();
|
|
_shadowLightNo = -1;
|
|
|
|
TeCore *core = g_engine->getCore();
|
|
const TetraedgeFSNode lightsNode(core->findFile(getLightsFileName()));
|
|
if (lightsNode.isReadable())
|
|
loadLights(lightsNode);
|
|
|
|
Common::Path pxmlpath = _sceneFileNameBase(zone, scene).joinInPlace("particles.xml");
|
|
TetraedgeFSNode pnode = g_engine->getCore()->findFile(pxmlpath);
|
|
if (pnode.isReadable()) {
|
|
ParticleXmlParser pparser;
|
|
pparser._scene = this;
|
|
if (!pnode.loadXML(pparser))
|
|
error("InGameScene::loadXml: Can't load %s", pnode.toString().c_str());
|
|
if (!pparser.parse())
|
|
error("InGameScene::loadXml: Can't parse %s", pxmlpath.toString(Common::Path::kNativeSeparator).c_str());
|
|
}
|
|
|
|
TeMatrix4x4 camMatrix = currentCamera() ?
|
|
currentCamera()->worldTransformationMatrix() : TeMatrix4x4();
|
|
for (auto &particle : _particles) {
|
|
particle->setMatrix(camMatrix);
|
|
particle->realTimer().start();
|
|
particle->update(particle->startLoop());
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void InGameScene::loadActZones() {
|
|
_actZones.clear();
|
|
Common::File actzonefile;
|
|
if (actzonefile.open(getActZoneFileName())) {
|
|
if (Te3DObject2::loadAndCheckFourCC(actzonefile, "ACT0")) {
|
|
uint32 count = actzonefile.readUint32LE();
|
|
if (count > 1000000)
|
|
error("Improbable number of actzones %d", count);
|
|
_actZones.resize(count);
|
|
for (uint i = 0; i < _actZones.size(); i++) {
|
|
_actZones[i]._s1 = Te3DObject2::deserializeString(actzonefile);
|
|
_actZones[i]._s2 = Te3DObject2::deserializeString(actzonefile);
|
|
for (int j = 0; j < 4; j++)
|
|
TeVector2f32::deserialize(actzonefile, _actZones[i]._points[j]);
|
|
_actZones[i]._flag1 = (actzonefile.readByte() != 0);
|
|
_actZones[i]._flag2 = true;
|
|
}
|
|
} else {
|
|
warning("loadActZones: Incorrect header in %s", actzonefile.getName());
|
|
}
|
|
}
|
|
}
|
|
|
|
bool InGameScene::loadCamera(const Common::String &name) {
|
|
Common::Path p = _sceneFileNameBase().joinInPlace(name).appendInPlace(".xml");
|
|
TeCamera *cam = new TeCamera();
|
|
cam->loadXml(p);
|
|
// Original doesn't do this? but we seem to need it
|
|
cam->setName(name);
|
|
TeVector3f32 winSize = g_engine->getApplication()->getMainWindow().size();
|
|
cam->viewport(0, 0, winSize.x(), winSize.y());
|
|
cameras().push_back(TeIntrusivePtr<TeCamera>(cam));
|
|
return true;
|
|
}
|
|
|
|
bool InGameScene::loadCharacter(const Common::String &name) {
|
|
Character *c = character(name);
|
|
if (!c) {
|
|
c = new Character();
|
|
if (!c->loadModel(name, false)) {
|
|
delete c;
|
|
return false;
|
|
}
|
|
models().push_back(c->_model);
|
|
if (_character->_shadowModel[0]) {
|
|
models().push_back(c->_shadowModel[0]);
|
|
models().push_back(c->_shadowModel[1]);
|
|
}
|
|
_characters.push_back(c);
|
|
}
|
|
c->_model->setVisible(true);
|
|
return true;
|
|
}
|
|
|
|
bool InGameScene::loadFreeMoveZone(const Common::String &name, TeVector2f32 &gridSize) {
|
|
TeFreeMoveZone *zone = new TeFreeMoveZone();
|
|
zone->setName(name);
|
|
Common::Path p = _sceneFileNameBase().joinInPlace(name).appendInPlace(".bin");
|
|
zone->loadBin(p, &_blockers, &_rectBlockers, &_actZones, gridSize);
|
|
_freeMoveZones.push_back(zone);
|
|
zone->setVisible(false);
|
|
return true;
|
|
}
|
|
|
|
bool InGameScene::loadLights(const TetraedgeFSNode &node) {
|
|
SceneLightsXmlParser parser(&_lights);
|
|
|
|
if (!node.loadXML(parser))
|
|
error("InGameScene::loadLights: Can't load %s", node.toString().c_str());
|
|
if (!parser.parse())
|
|
error("InGameScene::loadLights: Can't parse %s", node.toString().c_str());
|
|
|
|
_shadowColor = parser.getShadowColor();
|
|
_shadowLightNo = parser.getShadowLightNo();
|
|
_shadowFarPlane = parser.getShadowFarPlane();
|
|
_shadowNearPlane = parser.getShadowNearPlane();
|
|
_shadowFov = parser.getShadowFov();
|
|
|
|
g_engine->getRenderer()->enableAllLights();
|
|
for (uint i = 0; i < _lights.size(); i++) {
|
|
//
|
|
// WORKAROUND: Some lights in Syberia 2 have 0 for all attenuation
|
|
// values, which causes textures to all be black. eg,
|
|
// scenes/A2_Sommet/25210/lights.xml, light 0.
|
|
// Correct them to have the default attenuation of 1, 0, 0.
|
|
//
|
|
_lights[i]->correctAttenuation();
|
|
_lights[i]->enable(i);
|
|
}
|
|
|
|
if (_shadowLightNo >= (int)_lights.size()) {
|
|
warning("Disabling scene shadows: invalid shadow light no.");
|
|
_shadowLightNo = -1;
|
|
}
|
|
|
|
#ifdef TETRAEDGE_DEBUG_LIGHTS
|
|
debug("--- Scene lights ---");
|
|
debug("Shadow: %s no:%d far:%.02f near:%.02f fov:%.02f", _shadowColor.dump().c_str(), _shadowLightNo, _shadowFarPlane, _shadowNearPlane, _shadowFov);
|
|
debug("Global: %s", TeLight::globalAmbient().dump().c_str());
|
|
for (uint i = 0; i < _lights.size(); i++) {
|
|
debug("%s", _lights[i]->dump().c_str());
|
|
}
|
|
debug("--- end lights ---");
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
void InGameScene::loadMarkers(const TetraedgeFSNode &node) {
|
|
_markerGui.load(node);
|
|
TeLayout *bg = _bgGui.layoutChecked("background");
|
|
TeSpriteLayout *root = Game::findSpriteLayoutByName(bg, "root");
|
|
bg->setRatioMode(TeILayout::RATIO_MODE_NONE);
|
|
root->addChild(bg);
|
|
}
|
|
|
|
bool InGameScene::loadObject(const Common::String &name) {
|
|
Object3D *obj = object3D(name);
|
|
if (!obj) {
|
|
obj = new Object3D();
|
|
if (!obj->loadModel(name)) {
|
|
warning("InGameScene::loadObject: Loading %s failed", name.c_str());
|
|
delete obj;
|
|
return false;
|
|
}
|
|
models().push_back(obj->model());
|
|
_object3Ds.push_back(obj);
|
|
}
|
|
obj->model()->setVisible(true);
|
|
return true;
|
|
}
|
|
|
|
bool InGameScene::loadObjectMaterials(const Common::String &name) {
|
|
TeImage img;
|
|
bool retval = false;
|
|
TeCore *core = g_engine->getCore();
|
|
for (auto &obj : _objects) {
|
|
// FIXME: This should probably only do something for the
|
|
// object where the model name matches? It won't find the file
|
|
// anyway so it probably works as-is but it's a bit wrong.
|
|
if (obj._name.empty())
|
|
continue;
|
|
|
|
Common::Path mpath = _loadedPath.join(name).join(obj._name + ".png");
|
|
if (img.load(core->findFile(mpath))) {
|
|
Te3DTexture *tex = Te3DTexture::makeInstance();
|
|
tex->load(img);
|
|
obj._model->meshes()[0]->defaultMaterial(tex);
|
|
retval = true;
|
|
}
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
bool InGameScene::loadObjectMaterials(const Common::Path &path, const Common::String &name) {
|
|
// Seems like this is never used?
|
|
error("InGameScene::loadObjectMaterials(%s, %s)", path.toString(Common::Path::kNativeSeparator).c_str(), name.c_str());
|
|
}
|
|
|
|
bool InGameScene::loadPlayerCharacter(const Common::String &name) {
|
|
if (_character == nullptr) {
|
|
_character = new Character();
|
|
if (!_character->loadModel(name, true)) {
|
|
_playerCharacterModel.release();
|
|
return false;
|
|
}
|
|
|
|
_playerCharacterModel = _character->_model;
|
|
|
|
bool kateFound = findKate();
|
|
|
|
if (g_engine->gameType() == TetraedgeEngine::kSyberia) {
|
|
if (!kateFound) {
|
|
models().push_back(_character->_model);
|
|
if (_character->_shadowModel[0]) {
|
|
models().push_back(_character->_shadowModel[0]);
|
|
models().push_back(_character->_shadowModel[1]);
|
|
}
|
|
}
|
|
} else {
|
|
if (kateFound) {
|
|
for (uint i = 0; i < models().size(); i++) {
|
|
if (models()[i] == _character->_model) {
|
|
models().remove_at(i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
models().push_back(_character->_model);
|
|
}
|
|
}
|
|
|
|
_character->_model->setVisible(true);
|
|
_character->setFreeMoveZone(nullptr);
|
|
return true;
|
|
}
|
|
|
|
bool InGameScene::loadCurve(const Common::String &name) {
|
|
TeCore *core = g_engine->getCore();
|
|
TetraedgeFSNode node = core->findFile(_sceneFileNameBase().joinInPlace(name).appendInPlace(".bin"));
|
|
if (!node.isReadable()) {
|
|
warning("[InGameScene::loadCurve] Can't open file : %s.", node.toString().c_str());
|
|
return false;
|
|
}
|
|
TeIntrusivePtr<TeBezierCurve> curve = new TeBezierCurve();
|
|
curve->loadBin(node);
|
|
_bezierCurves.push_back(curve);
|
|
return true;
|
|
}
|
|
|
|
bool InGameScene::loadDynamicLightBloc(const Common::String &name, const Common::String &texture, const Common::String &zone, const Common::String &scene) {
|
|
const Common::Path pdat = _sceneFileNameBase(zone, scene).joinInPlace(name).appendInPlace(".bin");
|
|
const Common::Path ptex = _sceneFileNameBase(zone, scene).joinInPlace(texture);
|
|
TetraedgeFSNode datNode = g_engine->getCore()->findFile(pdat);
|
|
TetraedgeFSNode texNode = g_engine->getCore()->findFile(ptex);
|
|
if (!datNode.isReadable()) {
|
|
warning("[InGameScene::loadDynamicLightBloc] Can't open file : %s.", pdat.toString('/').c_str());
|
|
return false;
|
|
}
|
|
|
|
Common::ScopedPtr<Common::SeekableReadStream> file(datNode.createReadStream());
|
|
|
|
TeModel *model = new TeModel();
|
|
model->setMeshCount(1);
|
|
model->setName(datNode.getPath().baseName());
|
|
|
|
// Read position/rotation/scale.
|
|
model->deserialize(*file, *model);
|
|
|
|
uint32 verts = file->readUint32LE();
|
|
uint32 tricount = file->readUint32LE();
|
|
if (verts > 100000 || tricount > 10000)
|
|
error("Improbable number of verts (%d) or triangles (%d)", verts, tricount);
|
|
|
|
TeMesh *mesh = model->meshes()[0].get();
|
|
mesh->setConf(verts, tricount * 3, TeMesh::MeshMode_Triangles, 0, 0);
|
|
|
|
for (uint i = 0; i < verts; i++) {
|
|
TeVector3f32 vec;
|
|
TeVector3f32::deserialize(*file, vec);
|
|
mesh->setVertex(i, vec);
|
|
mesh->setNormal(i, TeVector3f32(0, 0, 1));
|
|
}
|
|
for (uint i = 0; i < verts; i++) {
|
|
TeVector2f32 vec2;
|
|
TeVector2f32::deserialize(*file, vec2);
|
|
vec2.setY(1.0 - vec2.getY());
|
|
mesh->setTextureUV(i, vec2);
|
|
}
|
|
|
|
for (uint i = 0; i < tricount * 3; i++)
|
|
mesh->setIndex(i, file->readUint16LE());
|
|
|
|
file.reset();
|
|
|
|
if (texNode.exists()) {
|
|
TeIntrusivePtr<Te3DTexture> tex = Te3DTexture::makeInstance();
|
|
tex->load2(texNode, false);
|
|
mesh->defaultMaterial(tex);
|
|
} else if (texture.size()) {
|
|
warning("loadDynamicLightBloc: Failed to load texture %s", texture.c_str());
|
|
}
|
|
|
|
model->setVisible(false);
|
|
|
|
_zoneModels.push_back(TeIntrusivePtr<TeModel>(model));
|
|
return true;
|
|
}
|
|
|
|
bool InGameScene::loadLight(const Common::String &name, const Common::String &zone, const Common::String &scene) {
|
|
Common::Path datpath = _sceneFileNameBase(zone, scene).joinInPlace(name).appendInPlace(".bin");
|
|
TetraedgeFSNode datnode = g_engine->getCore()->findFile(datpath);
|
|
if (!datnode.isReadable()) {
|
|
warning("[InGameScene::loadLight] Can't open file : %s.", datpath.toString(Common::Path::kNativeSeparator).c_str());
|
|
return false;
|
|
}
|
|
|
|
Common::ScopedPtr<Common::SeekableReadStream> file(datnode.createReadStream());
|
|
SceneLight light;
|
|
light._name = name;
|
|
TeVector3f32::deserialize(*file, light._v1);
|
|
TeVector3f32::deserialize(*file, light._v2);
|
|
light._color.deserialize(*file);
|
|
light._f = file->readFloatLE();
|
|
|
|
_sceneLights.push_back(light);
|
|
return true;
|
|
}
|
|
|
|
bool InGameScene::loadMask(const Common::String &name, const Common::String &texture, const Common::String &zone, const Common::String &scene) {
|
|
TeCore *core = g_engine->getCore();
|
|
TetraedgeFSNode texnode = core->findFile(_sceneFileNameBase(zone, scene).joinInPlace(texture));
|
|
TetraedgeFSNode datnode = core->findFile(_sceneFileNameBase(zone, scene).joinInPlace(name).appendInPlace(".bin"));
|
|
if (!datnode.isReadable()) {
|
|
warning("[InGameScene::loadMask] Can't open file : %s.", datnode.toString().c_str());
|
|
return false;
|
|
}
|
|
TeModel *model = new TeModel();
|
|
model->setMeshCount(1);
|
|
model->setName(name);
|
|
|
|
Common::ScopedPtr<Common::SeekableReadStream> file(datnode.createReadStream());
|
|
|
|
// Load position, rotation, size.
|
|
Te3DObject2::deserialize(*file, *model, false);
|
|
|
|
uint32 verts = file->readUint32LE();
|
|
uint32 tricount = file->readUint32LE();
|
|
if (verts > 100000 || tricount > 10000)
|
|
error("Improbable number of verts (%d) or triangles (%d)", verts, tricount);
|
|
|
|
TeMesh *mesh = model->meshes()[0].get();
|
|
mesh->setConf(verts, tricount * 3, TeMesh::MeshMode_Triangles, 0, 0);
|
|
|
|
for (uint i = 0; i < verts; i++) {
|
|
TeVector3f32 vec;
|
|
TeVector3f32::deserialize(*file, vec);
|
|
mesh->setVertex(i, vec);
|
|
mesh->setNormal(i, TeVector3f32(0, 0, 1));
|
|
if (_maskAlpha) {
|
|
mesh->setColor(i, TeColor(255, 255, 255, 128));
|
|
}
|
|
}
|
|
|
|
for (uint i = 0; i < verts; i++) {
|
|
TeVector2f32 vec2;
|
|
TeVector2f32::deserialize(*file, vec2);
|
|
vec2.setY(1.0 - vec2.getY());
|
|
mesh->setTextureUV(i, vec2);
|
|
}
|
|
|
|
// For some reason this one has the indexes in reverse order :(
|
|
for (uint i = 0; i < tricount * 3; i += 3) {
|
|
mesh->setIndex(i + 2, file->readUint16LE());
|
|
mesh->setIndex(i + 1, file->readUint16LE());
|
|
mesh->setIndex(i, file->readUint16LE());
|
|
}
|
|
|
|
file.reset();
|
|
TeIntrusivePtr<Te3DTexture> tex = Te3DTexture::load2(texnode, !_maskAlpha);
|
|
|
|
if (tex) {
|
|
mesh->defaultMaterial(tex);
|
|
if (!_maskAlpha) {
|
|
mesh->materials()[0]._mode = TeMaterial::MaterialMode2;
|
|
}
|
|
|
|
_masks.push_back(model);
|
|
return true;
|
|
} else {
|
|
warning("Failed to load mask texture %s", texture.c_str());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool InGameScene::loadRBB(const Common::String &fname, const Common::String &zone, const Common::String &scene) {
|
|
warning("TODO: Implement InGameScene::loadRBB");
|
|
return true;
|
|
}
|
|
|
|
bool InGameScene::loadRippleMask(const Common::String &name, const Common::String &texture, const Common::String &zone, const Common::String &scene) {
|
|
warning("TODO: Implement InGameScene::loadRippleMask");
|
|
return true;
|
|
}
|
|
|
|
bool InGameScene::loadRObject(const Common::String &fname, const Common::String &zone, const Common::String &scene) {
|
|
warning("TODO: Implement InGameScene::loadRObject");
|
|
return true;
|
|
}
|
|
|
|
bool InGameScene::loadShadowMask(const Common::String &name, const Common::String &texture, const Common::String &zone, const Common::String &scene) {
|
|
warning("TODO: Implement InGameScene::loadShadowMask");
|
|
return true;
|
|
}
|
|
|
|
bool InGameScene::loadShadowReceivingObject(const Common::String &name, const Common::String &zone, const Common::String &scene) {
|
|
TetraedgeFSNode datnode = g_engine->getCore()->findFile(_sceneFileNameBase(zone, scene).joinInPlace(name).appendInPlace(".bin"));
|
|
if (!datnode.isReadable()) {
|
|
warning("[InGameScene::loadShadowReceivingObject] Can't open file : %s.", datnode.toString().c_str());
|
|
return false;
|
|
}
|
|
TeModel *model = new TeModel();
|
|
model->setMeshCount(1);
|
|
model->setName(name);
|
|
|
|
Common::ScopedPtr<Common::SeekableReadStream> file(datnode.createReadStream());
|
|
|
|
// Load position, rotation, size.
|
|
Te3DObject2::deserialize(*file, *model, false);
|
|
|
|
uint32 verts = file->readUint32LE();
|
|
uint32 tricount = file->readUint32LE();
|
|
if (verts > 100000 || tricount > 10000)
|
|
error("Improbable number of verts (%d) or triangles (%d)", verts, tricount);
|
|
|
|
TeMesh *mesh = model->meshes()[0].get();
|
|
mesh->setConf(verts, tricount * 3, TeMesh::MeshMode_Triangles, 0, 0);
|
|
|
|
for (uint i = 0; i < verts; i++) {
|
|
TeVector3f32 vec;
|
|
TeVector3f32::deserialize(*file, vec);
|
|
mesh->setVertex(i, vec);
|
|
mesh->setNormal(i, TeVector3f32(0, 0, 1));
|
|
}
|
|
|
|
// Indexes in reverse order :(
|
|
for (uint i = 0; i < tricount * 3; i += 3) {
|
|
mesh->setIndex(i + 2, file->readUint16LE());
|
|
mesh->setIndex(i + 1, file->readUint16LE());
|
|
mesh->setIndex(i, file->readUint16LE());
|
|
}
|
|
|
|
file.reset();
|
|
|
|
_shadowReceivingObjects.push_back(model);
|
|
return true;
|
|
}
|
|
|
|
bool InGameScene::loadZBufferObject(const Common::String &name, const Common::String &zone, const Common::String &scene) {
|
|
TetraedgeFSNode datnode = g_engine->getCore()->findFile(_sceneFileNameBase(zone, scene).joinInPlace(name).appendInPlace(".bin"));
|
|
if (!datnode.isReadable()) {
|
|
warning("[InGameScene::loadZBufferObject] Can't open file : %s.", datnode.toString().c_str());
|
|
return false;
|
|
}
|
|
TeModel *model = new TeModel();
|
|
model->setMeshCount(1);
|
|
model->setName(name);
|
|
|
|
Common::ScopedPtr<Common::SeekableReadStream> file(datnode.createReadStream());
|
|
|
|
// Load position, rotation, size.
|
|
Te3DObject2::deserialize(*file, *model, false);
|
|
|
|
uint32 verts = file->readUint32LE();
|
|
uint32 tricount = file->readUint32LE();
|
|
if (verts > 100000 || tricount > 10000)
|
|
error("Improbable number of verts (%d) or triangles (%d)", verts, tricount);
|
|
|
|
TeMesh *mesh = model->meshes()[0].get();
|
|
mesh->setConf(verts, tricount * 3, TeMesh::MeshMode_Triangles, 0, 0);
|
|
|
|
for (uint i = 0; i < verts; i++) {
|
|
TeVector3f32 vec;
|
|
TeVector3f32::deserialize(*file, vec);
|
|
mesh->setVertex(i, vec);
|
|
mesh->setNormal(i, TeVector3f32(0, 0, 1));
|
|
mesh->setColor(i, TeColor(128, 0, 255, 128));
|
|
}
|
|
|
|
for (uint i = 0; i < tricount * 3; i++) {
|
|
mesh->setIndex(i, file->readUint16LE());
|
|
}
|
|
|
|
_zoneModels.push_back(model);
|
|
return true;
|
|
}
|
|
|
|
Common::Path InGameScene::getLightsFileName() const {
|
|
return _sceneFileNameBase().joinInPlace("lights.xml");
|
|
}
|
|
|
|
Common::Path InGameScene::getActZoneFileName() const {
|
|
return _sceneFileNameBase().joinInPlace("actions.bin");
|
|
}
|
|
|
|
Common::Path InGameScene::getBlockersFileName() const {
|
|
return _sceneFileNameBase().joinInPlace("blockers.bin");
|
|
}
|
|
|
|
void InGameScene::loadBlockers() {
|
|
_blockers.clear();
|
|
_rectBlockers.clear();
|
|
const Common::Path blockersPath = getBlockersFileName();
|
|
if (!Common::File::exists(blockersPath))
|
|
return;
|
|
|
|
Common::File blockersfile;
|
|
if (!blockersfile.open(blockersPath)) {
|
|
warning("Couldn't open blockers file %s.", blockersPath.toString(Common::Path::kNativeSeparator).c_str());
|
|
return;
|
|
}
|
|
|
|
bool hasHeader = Te3DObject2::loadAndCheckFourCC(blockersfile, "BLK0");
|
|
if (!hasHeader)
|
|
blockersfile.seek(0);
|
|
|
|
uint32 nblockers = blockersfile.readUint32LE();
|
|
if (nblockers > 1024)
|
|
error("Improbable number of blockers %d", nblockers);
|
|
_blockers.resize(nblockers);
|
|
for (uint i = 0; i < nblockers; i++) {
|
|
_blockers[i]._s = Te3DObject2::deserializeString(blockersfile);
|
|
TeVector2f32::deserialize(blockersfile, _blockers[i]._pts[0]);
|
|
TeVector2f32::deserialize(blockersfile, _blockers[i]._pts[1]);
|
|
_blockers[i]._enabled = true;
|
|
}
|
|
|
|
if (hasHeader) {
|
|
uint32 nrectblockers = blockersfile.readUint32LE();
|
|
if (nrectblockers > 1024)
|
|
error("Improbable number of rectblockers %d", nrectblockers);
|
|
_rectBlockers.resize(nrectblockers);
|
|
for (uint i = 0; i < nrectblockers; i++) {
|
|
_rectBlockers[i]._s = Te3DObject2::deserializeString(blockersfile);
|
|
for (uint j = 0; j < 4l; j++) {
|
|
TeVector2f32::deserialize(blockersfile, _rectBlockers[i]._pts[j]);
|
|
}
|
|
_rectBlockers[i]._enabled = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
void InGameScene::loadBackground(const TetraedgeFSNode &node) {
|
|
_youkiManager.reset();
|
|
_bgGui.load(node);
|
|
TeLayout *bg = _bgGui.layout("background");
|
|
TeLayout *root = _bgGui.layout("root");
|
|
bg->setRatioMode(TeILayout::RATIO_MODE_NONE);
|
|
root->setRatioMode(TeILayout::RATIO_MODE_NONE);
|
|
TeCamera *wincam = g_engine->getApplication()->mainWindowCamera();
|
|
bg->disableAutoZ();
|
|
bg->setZPosition(wincam->orthoNearPlane());
|
|
|
|
for (const auto &layoutEntry : _bgGui.spriteLayouts()) {
|
|
AnimObject *animobj = new AnimObject();
|
|
animobj->_name = layoutEntry._key;
|
|
animobj->_layout = layoutEntry._value;
|
|
animobj->_layout->_tiledSurfacePtr->_frameAnim.onFinished().add(animobj, &AnimObject::onFinished);
|
|
if (animobj->_name != "root")
|
|
animobj->_layout->setVisible(false);
|
|
_animObjects.push_back(animobj);
|
|
}
|
|
}
|
|
|
|
bool InGameScene::loadBillboard(const Common::String &name) {
|
|
Billboard *b = billboard(name);
|
|
if (b)
|
|
return true;
|
|
b = new Billboard();
|
|
if (b->load(Common::Path(name))) {
|
|
_billboards.push_back(b);
|
|
return true;
|
|
} else {
|
|
delete b;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void InGameScene::loadInteractions(const TetraedgeFSNode &node) {
|
|
_hitObjectGui.load(node);
|
|
TeLayout *bgbackground = _bgGui.layoutChecked("background");
|
|
Game *game = g_engine->getGame();
|
|
TeSpriteLayout *root = game->findSpriteLayoutByName(bgbackground, "root");
|
|
TeLayout *background = _hitObjectGui.layoutChecked("background");
|
|
for (auto *child : background->childList()) {
|
|
TeButtonLayout *btn = dynamic_cast<TeButtonLayout *>(child);
|
|
if (btn)
|
|
btn->setDoubleValidationProtectionEnabled(false);
|
|
}
|
|
background->setRatioMode(TeILayout::RATIO_MODE_NONE);
|
|
root->addChild(background);
|
|
}
|
|
|
|
void InGameScene::moveCharacterTo(const Common::String &charName, const Common::String &curveName, float curveOffset, float curveEnd) {
|
|
Character *c = character(charName);
|
|
if (c != nullptr && c != _character) {
|
|
SyberiaGame *game = dynamic_cast<SyberiaGame *>(g_engine->getGame());
|
|
assert(game);
|
|
if (!game->_movePlayerCharacterDisabled) {
|
|
c->setCurveStartLocation(c->characterSettings()._cutSceneCurveDemiPosition);
|
|
TeIntrusivePtr<TeBezierCurve> crve = curve(curveName);
|
|
if (!curveName.empty() && !crve)
|
|
warning("moveCharacterTo: curve %s not found", curveName.c_str());
|
|
c->placeOnCurve(crve);
|
|
c->setCurveOffset(curveOffset);
|
|
const Common::String walkStartAnim = c->walkAnim(Character::WalkPart_Start);
|
|
if (walkStartAnim.empty()) {
|
|
c->setAnimation(c->walkAnim(Character::WalkPart_Loop), true);
|
|
} else {
|
|
c->setAnimation(c->walkAnim(Character::WalkPart_Start), false);
|
|
}
|
|
c->walkTo(curveEnd, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
void InGameScene::onMainWindowSizeChanged() {
|
|
TeVector3f32 winSize = g_engine->getApplication()->getMainWindow().size();
|
|
_viewportSize = TeVector2f32(winSize.x(), winSize.y());
|
|
Common::Array<TeIntrusivePtr<TeCamera>> &cams = cameras();
|
|
for (uint i = 0; i < cams.size(); i++) {
|
|
cams[i]->viewport(0, 0, _viewportSize.getX(), _viewportSize.getY());
|
|
}
|
|
}
|
|
|
|
Object3D *InGameScene::object3D(const Common::String &name) {
|
|
for (Object3D *obj : _object3Ds) {
|
|
if (obj->model()->name() == name)
|
|
return obj;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
TeFreeMoveZone *InGameScene::pathZone(const Common::String &name) {
|
|
for (TeFreeMoveZone *zone: _freeMoveZones) {
|
|
if (zone->name() == name)
|
|
return zone;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void InGameScene::playVerticalScrolling(float time) {
|
|
_verticalScrollTimer.start();
|
|
_verticalScrollTimer.stop();
|
|
_verticalScrollTimer.start();
|
|
_verticalScrollTime = time * 1000000.0f;
|
|
_verticalScrollPlaying = true;
|
|
}
|
|
|
|
void InGameScene::reset() {
|
|
for (auto *character : _characters)
|
|
character->setFreeMoveZone(nullptr);
|
|
_youkiManager.reset();
|
|
if (_character)
|
|
_character->setFreeMoveZone(nullptr);
|
|
freeSceneObjects();
|
|
_bgGui.unload();
|
|
unloadSpriteLayouts();
|
|
_markerGui.unload();
|
|
_hitObjectGui.unload();
|
|
}
|
|
|
|
TeLight *InGameScene::shadowLight() {
|
|
if (_shadowLightNo == -1 || (uint)_shadowLightNo >= _lights.size()) {
|
|
return nullptr;
|
|
}
|
|
return _lights[_shadowLightNo].get();
|
|
}
|
|
|
|
void InGameScene::setImagePathMarker(const Common::String &markerName, const Common::Path &path) {
|
|
if (!isMarker(markerName))
|
|
return;
|
|
|
|
Game *game = g_engine->getGame();
|
|
TeLayout *bg = game->forGui().layoutChecked("background");
|
|
|
|
for (Te3DObject2 *child : bg->childList()) {
|
|
if (child->name() == markerName) {
|
|
TeSpriteLayout *sprite = dynamic_cast<TeSpriteLayout *>(child);
|
|
if (sprite) {
|
|
sprite->load(path);
|
|
sprite->_tiledSurfacePtr->_frameAnim.setLoopCount(-1);
|
|
sprite->play();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void InGameScene::setPositionCharacter(const Common::String &charName, const Common::String &freeMoveZoneName, const TeVector3f32 &position) {
|
|
Character *c = character(charName);
|
|
if (!c) {
|
|
warning("[SetCharacterPosition] Character not found %s", charName.c_str());
|
|
} else if (c == _character && c->positionFlag()) {
|
|
c->setFreeMoveZoneName(freeMoveZoneName);
|
|
c->setPositionCharacter(position);
|
|
c->setPositionFlag(false);
|
|
c->setNeedsSomeUpdate(true);
|
|
} else {
|
|
c->stop();
|
|
TeFreeMoveZone *zone = pathZone(freeMoveZoneName);
|
|
if (!zone) {
|
|
warning("[SetCharacterPosition] PathZone not found %s", freeMoveZoneName.c_str());
|
|
for (TeFreeMoveZone *z : _freeMoveZones)
|
|
warning("zone: %s", z->name().c_str());
|
|
return;
|
|
}
|
|
TeIntrusivePtr<TeCamera> cam = currentCamera();
|
|
zone->setCamera(cam, false);
|
|
c->setFreeMoveZone(zone);
|
|
SoundStep step = findSoundStep(freeMoveZoneName);
|
|
c->setStepSound(step._stepSound1, step._stepSound2);
|
|
bool correctFlag = true;
|
|
const TeVector3f32 corrected = zone->correctCharacterPosition(position, &correctFlag, true);
|
|
c->_model->setPosition(corrected);
|
|
if (!correctFlag)
|
|
warning("[SetCharacterPosition] Warning : The character is not above the ground %s", charName.c_str());
|
|
}
|
|
}
|
|
|
|
void InGameScene::setStep(const Common::String &scene, const Common::String &step1, const Common::String &step2) {
|
|
SoundStep ss;
|
|
ss._stepSound1 = step1;
|
|
ss._stepSound2 = step2;
|
|
_soundSteps[scene] = ss;
|
|
}
|
|
|
|
void InGameScene::setVisibleMarker(const Common::String &markerName, bool val) {
|
|
if (!isMarker(markerName))
|
|
return;
|
|
|
|
Game *game = g_engine->getGame();
|
|
TeLayout *bg = game->forGui().layout("background");
|
|
if (!bg)
|
|
return;
|
|
|
|
for (Te3DObject2 *child : bg->childList()) {
|
|
if (child->name() == markerName) {
|
|
child->setVisible(val);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void InGameScene::unloadCharacter(const Common::String &name) {
|
|
if (_character && _character->_model->name() == name) {
|
|
_character->removeAnim();
|
|
_character->deleteAnim();
|
|
_character->deleteAllCallback();
|
|
if (_character->_model->anim())
|
|
_character->_model->anim()->stop(); // TODO: added this
|
|
_character->setFreeMoveZone(nullptr); // TODO: added this
|
|
_character->deleteLater();
|
|
_character = nullptr;
|
|
}
|
|
|
|
// NOTE: There may be multiple characters with the same
|
|
// model to delete here.
|
|
for (uint i = 0; i < _characters.size(); i++) {
|
|
Character *c = _characters[i];
|
|
if (c && c->_model->name() == name) {
|
|
c->removeAnim();
|
|
c->deleteAnim();
|
|
c->deleteAllCallback();
|
|
c->deleteLater();
|
|
if (c->_model->anim())
|
|
c->_model->anim()->stop(); // TODO: added this
|
|
c->setFreeMoveZone(nullptr); // TODO: added this
|
|
_characters.remove_at(i);
|
|
i--;
|
|
}
|
|
}
|
|
}
|
|
|
|
void InGameScene::unloadObject(const Common::String &name) {
|
|
for (uint i = 0; i < _object3Ds.size(); i++) {
|
|
if (_object3Ds[i]->model()->name() == name) {
|
|
// Remove from the scene models.
|
|
for (uint j = 0; j < models().size(); j++) {
|
|
if (models()[j] == _object3Ds[i]->model()) {
|
|
models().remove_at(j);
|
|
break;
|
|
}
|
|
}
|
|
_object3Ds[i]->deleteLater();
|
|
_object3Ds.remove_at(i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void InGameScene::unloadSpriteLayouts() {
|
|
for (auto *animobj : _animObjects) {
|
|
delete animobj;
|
|
}
|
|
_animObjects.clear();
|
|
}
|
|
|
|
void InGameScene::update() {
|
|
SyberiaGame *game = dynamic_cast<SyberiaGame *>(g_engine->getGame());
|
|
assert(game);
|
|
if (_bgGui.loaded()) {
|
|
_bgGui.layoutChecked("background")->setZPosition(0.0f);
|
|
}
|
|
if (_character) {
|
|
_character->setHasAnchor(false);
|
|
for (AnchorZone *zone : _anchorZones) {
|
|
if (aroundAnchorZone(zone)) {
|
|
TeVector2f32 headRot(getHeadHorizontalRotation(_character, zone->_loc),
|
|
getHeadVerticalRotation(_character, zone->_loc));
|
|
if (fabs(headRot.getX() * 180.0 / M_PI) > 90.0 || fabs(headRot.getY() * 180.0 / M_PI) > 45.0) {
|
|
_character->setHasAnchor(false);
|
|
_character->setLastHeadRotation(_character->headRotation());
|
|
} else {
|
|
_character->setHeadRotation(headRot);
|
|
_character->setHasAnchor(true);
|
|
}
|
|
}
|
|
}
|
|
if (_character->charLookingAt()) {
|
|
Character *targetc = _character->charLookingAt();
|
|
TeVector3f32 targetpos;
|
|
if (g_engine->gameType() == TetraedgeEngine::kSyberia)
|
|
targetpos = targetc->_model->position();
|
|
else
|
|
targetpos = targetc->_model->worldTransformationMatrix() * targetc->lastHeadBoneTrans();
|
|
|
|
//
|
|
// Note: The below general code for NPCs is different in Syberia 2,
|
|
// and uses c->charLookingAtOffset(), but the player look-at code
|
|
// is the same in both games and always adds 17 for "tall" characters.
|
|
//
|
|
|
|
if (_character->lookingAtTallThing())
|
|
targetpos.y() += 17;
|
|
TeVector2f32 headRot(getHeadHorizontalRotation(_character, targetpos),
|
|
getHeadVerticalRotation(_character, targetpos));
|
|
float hangle = headRot.getX() * 180.0f / M_PI;
|
|
if (hangle > 90.0f)
|
|
headRot.setX((float)M_PI_2);
|
|
else if (hangle < -90.0f)
|
|
headRot.setX((float)-M_PI_2);
|
|
_character->setHeadRotation(headRot);
|
|
_character->setHasAnchor(true);
|
|
}
|
|
}
|
|
for (Character *c : _characters) {
|
|
Character *targetc = c->charLookingAt();
|
|
if (targetc) {
|
|
TeVector3f32 targetpos;
|
|
if (g_engine->gameType() == TetraedgeEngine::kSyberia) {
|
|
targetpos = targetc->_model->position();
|
|
if (c->lookingAtTallThing())
|
|
targetpos.y() += 17;
|
|
} else {
|
|
targetpos = targetc->_model->worldTransformationMatrix() * targetc->lastHeadBoneTrans();
|
|
}
|
|
TeVector2f32 headRot(getHeadHorizontalRotation(c, targetpos),
|
|
getHeadVerticalRotation(c, targetpos));
|
|
float hangle = headRot.getX() * 180.0f / M_PI;
|
|
if (hangle > 90)
|
|
headRot.setX((float)M_PI_2);
|
|
else if (hangle < -90)
|
|
headRot.setX((float)-M_PI_2);
|
|
|
|
if (g_engine->gameType() == TetraedgeEngine::kSyberia2) {
|
|
if (c->lookingAtTallThing())
|
|
headRot.setY(c->charLookingAtOffset());
|
|
}
|
|
|
|
c->setHeadRotation(headRot);
|
|
c->setHasAnchor(true);
|
|
}
|
|
}
|
|
|
|
TeLuaGUI::StringMap<TeSpriteLayout *> &sprites = bgGui().spriteLayouts();
|
|
for (auto &sprite : sprites) {
|
|
if (_callbacks.contains(sprite._key)) {
|
|
error("TODO: handle sprite callback in InGameScene::update");
|
|
}
|
|
}
|
|
|
|
TeScene::update();
|
|
_youkiManager.update();
|
|
|
|
uint64 waitTime = _waitTimeTimer.timeFromLastTimeElapsed();
|
|
if (_waitTime != -1.0 && waitTime > _waitTime) {
|
|
_waitTime = -1.0;
|
|
_waitTimeTimer.stop();
|
|
bool resumed = false;
|
|
for (uint i = 0; i < game->yieldedCallbacks().size(); i++) {
|
|
SyberiaGame::YieldedCallback &yc = game->yieldedCallbacks()[i];
|
|
if (yc._luaFnName == "OnWaitFinished") {
|
|
TeLuaThread *thread = yc._luaThread;
|
|
game->yieldedCallbacks().remove_at(i);
|
|
thread->resume();
|
|
resumed = true;
|
|
}
|
|
}
|
|
if (!resumed)
|
|
game->luaScript().execute("OnWaitFinished");
|
|
}
|
|
|
|
// TODO: Update Flammes
|
|
|
|
// Original does this, but snowCustoms are never actually created?
|
|
//for (auto snow : _snowCustoms)
|
|
// snow->addFlake();
|
|
|
|
TeParticle::updateAll(1);
|
|
|
|
for (Object3D *obj : _object3Ds) {
|
|
if (obj->_translateTime >= 0) {
|
|
float time = MIN((float)(obj->_translateTimer.getTimeFromStart() / 1000000.0), obj->_translateTime);
|
|
TeVector3f32 trans = obj->_translateStart + (obj->_translateAmount * (time / obj->_translateTime));
|
|
obj->model()->setPosition(trans);
|
|
}
|
|
if (obj->_rotateTime >= 0) {
|
|
float time = MIN((float)(obj->_rotateTimer.getTimeFromStart() / 1000000.0), obj->_rotateTime);
|
|
TeVector3f32 rot = (obj->_rotateAmount * (time / obj->_rotateTime));
|
|
TeQuaternion rotq = TeQuaternion::fromEulerDegrees(rot);
|
|
obj->model()->setRotation(obj->_rotateStart * rotq);
|
|
}
|
|
}
|
|
}
|
|
|
|
void InGameScene::updateScroll() {
|
|
TeLayout *bg = _bgGui.layout("background");
|
|
if (!bg)
|
|
return;
|
|
|
|
TeSpriteLayout *root = Game::findSpriteLayoutByName(bg, "root");
|
|
if (!root)
|
|
error("No root layout in the background");
|
|
_scrollOffset = TeVector2f32();
|
|
TeIntrusivePtr<TeTiledTexture> rootTex = root->_tiledSurfacePtr->tiledTexture();
|
|
// During startup root texture is not yet loaded
|
|
if (!rootTex)
|
|
return;
|
|
const TeVector2s32 texSize = rootTex->totalSize();
|
|
if (texSize._x < 801) {
|
|
if (texSize._y < 601) {
|
|
_scrollOffset = TeVector2f32(0.5f, 0.0f);
|
|
updateViewport(0);
|
|
} else {
|
|
TeVector3f32 usersz = bg->userSize();
|
|
usersz.y() = 2.333333f;
|
|
bg->setSize(usersz);
|
|
//TeVector2f32 boundLayerSz = boundLayerSize();
|
|
layerSize();
|
|
|
|
float y1 = 300.0f / texSize._y;
|
|
float y2 = (texSize._y - 300.0f) / texSize._y;
|
|
|
|
if (_verticalScrollPlaying) {
|
|
float elapsed = _verticalScrollTimer.timeFromLastTimeElapsed();
|
|
_scrollOffset.setY(elapsed * (y2 - y1) / _verticalScrollTime + y1);
|
|
} else if (_character && _character->_model) {
|
|
TeIntrusivePtr<TeCamera> cam = currentCamera();
|
|
const TeMatrix4x4 camProjMatrix = cam->projectionMatrix();
|
|
TeMatrix4x4 camWorldMatrix = cam->worldTransformationMatrix();
|
|
camWorldMatrix.inverse();
|
|
const TeMatrix4x4 camProjWorld = camProjMatrix * camWorldMatrix;
|
|
TeVector3f32 charPos = camProjWorld * _character->_model->position();
|
|
_scrollOffset.setY(1.0f - (charPos.y() + 1.0f));
|
|
}
|
|
_scrollOffset.setX(0.5f);
|
|
_scrollOffset.setY(CLIP(_scrollOffset.getY(), y1, y2));
|
|
if (_scrollOffset.getY() >= y2 && _verticalScrollPlaying) {
|
|
_verticalScrollTimer.stop();
|
|
_verticalScrollPlaying = false;
|
|
}
|
|
root->setAnchor(TeVector3f32(0.5f, _scrollOffset.getY(), 0.5f));
|
|
updateViewport(2);
|
|
}
|
|
} else {
|
|
TeVector3f32 usersz = bg->userSize();
|
|
usersz.x() = texSize._x / 800.0f;
|
|
bg->setSize(usersz);
|
|
//TeVector2f32 boundLayerSz = boundLayerSize();
|
|
TeVector2f32 layerSz = layerSize();
|
|
float x1, x2;
|
|
if (g_engine->getApplication()->ratioStretched()) {
|
|
x1 = layerSz.getX() * 2;
|
|
const TeVector3f32 winSize = g_engine->getApplication()->getMainWindow().size();
|
|
x2 = winSize.x();
|
|
} else {
|
|
x1 = layerSz.getX() * 2;
|
|
x2 = layerSz.getX();
|
|
}
|
|
TeIntrusivePtr<TeCamera> cam = currentCamera();
|
|
if (cam) {
|
|
const TeMatrix4x4 camProjMatrix = cam->projectionMatrix();
|
|
TeMatrix4x4 camWorldMatrix = cam->worldTransformationMatrix();
|
|
camWorldMatrix.inverse();
|
|
const TeMatrix4x4 camProjWorld = camProjMatrix * camWorldMatrix;
|
|
const TeVector3f32 charPos = camProjWorld * _character->_model->position();
|
|
_scrollOffset.setX((charPos.x() + 1.0f) / 2.0f);
|
|
float xmin = x2 / x1;
|
|
float xmax = 1.0f - (x2 / x1);
|
|
_scrollOffset.setX(CLIP(_scrollOffset.getX(), xmin, xmax));
|
|
root->setAnchor(TeVector3f32(_scrollOffset.getX(), 0.5f, 0.5f));
|
|
TeLayout *forbg = g_engine->getGame()->forGui().layoutChecked("background");
|
|
forbg->setAnchor(TeVector3f32(_scrollOffset.getX(), 0.5f, 0.5f));
|
|
updateViewport(1);
|
|
// _globalScrollingType = 1; // This gets set but never used?
|
|
}
|
|
}
|
|
}
|
|
|
|
void InGameScene::updateViewport(int ival) {
|
|
const TeVector2f32 lsize = layerSize();
|
|
const TeVector2f32 offset((0.5f - _scrollOffset.getX()) * _scrollScale.getX(),
|
|
_scrollOffset.getY() * _scrollScale.getY());
|
|
const TeVector3f32 winSize = g_engine->getApplication()->getMainWindow().size();
|
|
int x = (winSize.x() - lsize.getX()) / 2.0f + offset.getX();
|
|
int y = (winSize.y() - lsize.getY()) / 2.0f;
|
|
for (auto &cam : cameras()) {
|
|
float aspectRatio = lsize.getX() / lsize.getY();
|
|
//cam->setSomething(ival);
|
|
cam->viewport(x, y, lsize.getX(), lsize.getY());
|
|
if (g_engine->getApplication()->ratioStretched()) {
|
|
aspectRatio = (aspectRatio / (winSize.x() / winSize.y())) * 1.333333f;
|
|
}
|
|
cam->setAspectRatio(aspectRatio);
|
|
}
|
|
}
|
|
|
|
void InGameScene::activateMask(const Common::String &name, bool val) {
|
|
for (auto &mask : _masks) {
|
|
if (mask->name() == name) {
|
|
mask->setVisible(val);
|
|
return;
|
|
}
|
|
}
|
|
warning("activateMask: Didn't find mask %s", name.c_str());
|
|
}
|
|
|
|
bool InGameScene::AnimObject::onFinished() {
|
|
SyberiaGame *game = dynamic_cast<SyberiaGame *>(g_engine->getGame());
|
|
assert(game);
|
|
for (uint i = 0; i < game->yieldedCallbacks().size(); i++) {
|
|
SyberiaGame::YieldedCallback &yc = game->yieldedCallbacks()[i];
|
|
if (yc._luaFnName == "OnFinishedAnim" && yc._luaParam == _name) {
|
|
TeLuaThread *thread = yc._luaThread;
|
|
game->yieldedCallbacks().remove_at(i);
|
|
if (thread) {
|
|
thread->resume();
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
game->luaScript().execute("OnFinishedAnim", _name);
|
|
return false;
|
|
}
|
|
|
|
void InGameScene::Flamme::initFire() {
|
|
_needsFires = true;
|
|
_fires.resize(MAX_FIRE);
|
|
}
|
|
|
|
InGameScene::Flamme::~Flamme() {
|
|
for (auto fire : _fires) {
|
|
if (fire) {
|
|
delete fire;
|
|
}
|
|
}
|
|
_fires.clear();
|
|
}
|
|
|
|
} // end namespace Tetraedge
|