Files
2026-02-02 04:50:13 +01:00

1563 lines
42 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* 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/debug.h"
#include "common/file.h"
#include "qdengine/qd_fwd.h"
#include "qdengine/parser/qdscr_parser.h"
#include "qdengine/parser/xml_tag_buffer.h"
#include "qdengine/system/graphics/gr_dispatcher.h"
#include "qdengine/qdcore/qd_setup.h"
#include "qdengine/qdcore/qd_camera.h"
#include "qdengine/qdcore/qd_game_object_animated.h"
#include "qdengine/qdcore/qd_game_dispatcher.h"
namespace QDEngine {
struct sPlane4f {
float A, B, C, D;
sPlane4f() { A = B = C = D = 0.0; }
sPlane4f(float a, float b, float c, float d) {
A = a, B = b, C = c, D = d;
}
sPlane4f(const Vect3f &a, const Vect3f &b, const Vect3f &c) {
// инициализация плоскости по трем точкам
A = (b.y - a.y) * (c.z - a.z) - (c.y - a.y) * (b.z - a.z);
B = (b.z - a.z) * (c.x - a.x) - (c.z - a.z) * (b.x - a.x);
C = (b.x - a.x) * (c.y - a.y) - (c.x - a.x) * (b.y - a.y);
GetNormal().normalize();
D = -A * a.x - B * a.y - C * a.z;
}
inline void Set(const Vect3f &a, const Vect3f &b, const Vect3f &c) {
// инициализация плоскости по трем точкам
A = (b.y - a.y) * (c.z - a.z) - (c.y - a.y) * (b.z - a.z);
B = (b.z - a.z) * (c.x - a.x) - (c.z - a.z) * (b.x - a.x);
C = (b.x - a.x) * (c.y - a.y) - (c.x - a.x) * (b.y - a.y);
GetNormal().normalize();
D = -A * a.x - B * a.y - C * a.z;
}
inline float GetDistance(const Vect3f &a) {
// расстояние от точки до плоскости
float t = A * a.x + B * a.y + C * a.z + D;
return t;
}
inline float GetCross(const Vect3f &a, const Vect3f &b) {
// поиск пересечения данной плоскости с прямой заданной двумя точками a и b
Vect3f v = a - b;
float t = A * v.x + B * v.y + C * v.z;
if (t == 0) return 0; // прямая и плоскость параллельны
t = (A * a.x + B * a.y + C * a.z + D) / t;
return t;
}
inline Vect3f &GetNormal() {
return *(Vect3f *)&A;
}
inline void GetReflectionVector(const Vect3f &in, Vect3f &out) {
// out - поиск отражение вектора от плоскости
out = in - 2 * dot(GetNormal(), in) * GetNormal();
}
};
const int32 IMPASSIBLE_CELL_CLR = 0x00FF0000;
const int32 SELECTED_CELL_CLR = 0x000000FF;
const int32 NORMAL_CELL_CLR = 0x00FFFFFF;
const int DASH_LEN = 2;
qdCamera *qdCamera::_current_camera = NULL;
#define CAMERA_WORLD_UP Vect3f(0, 1, 0)
#define CAMERA_AT_POINT Vect3f(0, 0, 0) // Point we're looking at
const float qdCamera::_NEAR_PLANE = 1;
const float qdCamera::_FAR_PLANE = 10000;
//qdCameraMode qdCamera::_default_mode;
qdCamera::qdCamera() : _m_fR(300.0f), _xAngle(45), _yAngle(0), _zAngle(0),
_GSX(0), _GSY(0), _grid(NULL),
_cellSX(32), _cellSY(32), _focus(1000.0f),
_gridCenter(0, 0, 0),
_redraw_mode(QDCAM_GRID_ZBUFFER),
_scrOffset(0, 0),
_current_mode_work_time(0.0f),
_current_mode_switch(false),
_current_object(NULL),
_default_object(NULL),
_scale_pow(1.0f),
_scale_z_offset(0.0f) {
set_grid_size(50, 50);
set_scr_size(640, 480);
set_scr_center(320, 240);
set_scr_center_initial(Vect2i(320, 240));
rotate_and_scale(_xAngle, _yAngle, _zAngle, 1, 1, 1);
_cycle_x = _cycle_y = false;
}
qdCamera::~qdCamera() {
if (_GSX) {
delete [] _grid;
}
}
void qdCamera::set_grid_size(int xs, int ys) {
if (_GSX == xs && _GSY == ys) return;
if (_GSX)
delete [] _grid;
_grid = new sGridCell[xs * ys];
_GSX = xs;
_GSY = ys;
}
void qdCamera::clear_grid() {
debugC(3, kDebugMovement, "qdCamera::clear_grid()");
int cnt = 0;
for (int i = 0; i < _GSY; i++) {
for (int j = 0; j < _GSX; j++) {
_grid[cnt++].clear();
}
}
}
float qdCamera::get_scale(const Vect3f &glCoord) const {
if ((_focus < 5000.0f) || (fabs(_scale_pow - 1) > 0.001)) {
Vect3f cameraCoord = global2camera_coord(glCoord);
float buf = cameraCoord.z + _scale_z_offset;
// Если координата отрицательна, то масштабирование происходит по линейному
// закону. Иначе по общему (степенному) закону.
if (buf > 0)
buf = exp(_scale_pow * log(buf));
float scale = (_focus / (buf + _focus));
if (scale < 0)
return 0;
return scale;
} else
return 1.0f;
}
const Vect2s qdCamera::scr2rscr(const Vect2s &v) const {
Vect2s res;
res.x = v.x - (_scrCenter.x - _scrOffset.x);
res.y = (_scrCenter.y - _scrOffset.y) - v.y;
return res;
}
const Vect2s qdCamera::rscr2scr(const Vect2s &v) const {
Vect2s res;
res.x = _scrCenter.x + v.x - _scrOffset.x;
res.y = _scrCenter.y - v.y - _scrOffset.y;
return res;
}
const Vect3f qdCamera::camera_coord2global(const Vect3f &v) const {
return TransformVector(v, MatrixInverse(_m_cam));
}
const Vect3f qdCamera::scr2global(const Vect2s &vScrPoint, float zInCameraCoord) const {
return rscr2global(scr2rscr(vScrPoint), zInCameraCoord);
}
const Vect3f qdCamera::rscr2global(const Vect2s rScrPoint, const float zInCameraCoord) const {
//Преобразование экран - координаты в системе камеры
Vect3f _t = rscr2camera_coord(rScrPoint, zInCameraCoord);
//Преобразование координаты в системе камеры - в координаты глобальные(в системе основной плоскости)
return camera_coord2global(_t);
}
const Vect3f qdCamera::global2camera_coord(const Vect3f &glCoord) const {
return TransformVector(glCoord, _m_cam);
}
const Vect3f qdCamera::rscr2camera_coord(const Vect2s &rScrPoint, float z) const {
float x = ((float)rScrPoint.x * (z + _focus)) / _focus;
float y = ((float)rScrPoint.y * (z + _focus)) / _focus;
return Vect3f(x, y, z);
}
const Vect2s qdCamera::camera_coord2rscr(const Vect3f &coord) const {
int16 sx = round(coord.x * _focus / (coord.z + _focus));
int16 sy = round(coord.y * _focus / (coord.z + _focus));
return Vect2s(sx, sy);
}
const Vect2s qdCamera::camera_coord2scr(const Vect3f &coord) const {
return rscr2scr(camera_coord2rscr(coord));
}
const Vect2s qdCamera::global2scr(const Vect3f &glCoord) const {
return camera_coord2scr(global2camera_coord(glCoord));
}
const Vect2s qdCamera::global2rscr(const Vect3f &glCoord) const {
return camera_coord2rscr(global2camera_coord(glCoord));
}
void qdCamera::set_R(const float r) {
_m_fR = r;
rotate_and_scale(_xAngle, _yAngle, _zAngle, 1, 1, 1);
}
bool qdCamera::line_cutting(Vect3f &b, Vect3f &e) const {
//положение по Z плоскости отсечения
const float D = -_focus * .9f;
if (b.z < D) { //первая лежит позади
if (e.z < D) //обе точки лежат позади
return false;
float k = (D - b.z) / (e.z - b.z);
b.z = D;
b.y = k * (e.y - b.y) + b.y;
b.x = k * (e.x - b.x) + b.x;
} else if (e.z < D) {
float k = (D - e.z) / (b.z - e.z);
e.z = D;
e.y = k * (b.y - e.y) + e.y;
e.x = k * (b.x - e.x) + e.x;
}
return true;
}
void qdCamera::rotate_and_scale(float XA, float YA, float ZA, float kX, float kY, float kZ) {
_xAngle = XA;
_yAngle = YA;
_zAngle = ZA;
MATRIX3D rot = RotateXMatrix(XA * (M_PI / 180.f));
rot = MatrixMult(RotateYMatrix(-YA * (M_PI / 180.f)), rot);
rot = MatrixMult(RotateZMatrix(-ZA * (M_PI / 180.f)), rot);
//точка, из которой мы сомотрим
const Vect3f camPos(0, 0, _m_fR);
//новая позиция камеры после поворота
Vect3f pos = TransformVector(camPos, rot);
//вычисляем, как измениться нормальный вектор камеры после поворота
Vect3f new_up = TransformVector(CAMERA_WORLD_UP, rot);
_m_cam = ViewMatrix(pos, CAMERA_AT_POINT, CAMERA_WORLD_UP, new_up);
}
const Vect3f qdCamera::rscr2plane_camera_coord(const Vect2s &scrPoint) const {
const int XSP = _cellSX * _GSX;
const int YSP = _cellSY * _GSY;
const float XSP05 = XSP * 0.5f;
const float YSP05 = YSP * 0.5f;
Vect3f p0 = global2camera_coord(Vect3f(-XSP05, -YSP05, 0) + _gridCenter);
Vect3f p1 = global2camera_coord(Vect3f(-XSP05, +YSP05, 0) + _gridCenter);
Vect3f p2 = global2camera_coord(Vect3f(+XSP05, +YSP05, 0) + _gridCenter);
sPlane4f plnT(p0, p1, p2);
Vect3f tlV((float)scrPoint.x, (float)scrPoint.y, _focus);
Vect3f tlP(0, 0, -_focus);
float t = -(plnT.A * tlP.x + plnT.B * tlP.y + plnT.C * tlP.z + plnT.D) /
(plnT.A * tlV.x + plnT.B * tlV.y + plnT.C * tlV.z);
float x = tlP.x + tlV.x * t;
float y = tlP.y + tlV.y * t;
float z = tlP.z + tlV.z * t;
return Vect3f(x, y, z);
}
const Vect3f qdCamera::scr2plane_camera_coord(const Vect2s &scrPoint) const {
return rscr2plane_camera_coord(scr2rscr(scrPoint));
}
const Vect3f qdCamera::scr2plane(const Vect2s &scrPoint) const {
return camera_coord2global(scr2plane_camera_coord(scrPoint));
}
const Vect3f qdCamera::rscr2plane(const Vect2s &rscrPoint) const {
return camera_coord2global(rscr2plane_camera_coord(rscrPoint));
}
const Vect2s qdCamera::plane2scr(const Vect3f &plnPoint) const {
return rscr2scr(plane2rscr(plnPoint));
}
const Vect2s qdCamera::plane2rscr(const Vect3f &plnPoint) const {
const float SMALL_VALUE = 0.0001f;
Vect3f res = global2camera_coord(plnPoint);
if (res.z < (SMALL_VALUE - _focus)) return Vect2s(0, 0);
int sx0 = round(res.x * _focus / (res.z + _focus));
int sy0 = round(res.y * _focus / (res.z + _focus));
return Vect2s(sx0, sy0);
}
const sGridCell *qdCamera::get_cell(float X, float Y) const {
int x = round(X - _gridCenter.x);
int y = round(Y - _gridCenter.y);
const int XSP = _cellSX * _GSX;
const int YSP = _cellSY * _GSY;
const int XSP05 = XSP / 2;
const int YSP05 = YSP / 2;
x += XSP05;
y += YSP05;
if (x < 0 || x >= XSP || y < 0 || y >= YSP) return 0;
x = x / _cellSX;
y = y / _cellSY;
return &_grid[y * _GSX + x];
}
const Vect2s qdCamera::get_cell_index(float X, float Y, bool grid_crop) const {
int x = round(X - _gridCenter.x);
int y = round(Y - _gridCenter.y);
const int XSP = _cellSX * _GSX;
const int YSP = _cellSY * _GSY;
const int XSP05 = XSP >> 1;
const int YSP05 = YSP >> 1;
x += XSP05;
y += YSP05;
if (grid_crop && (x < 0 || x >= XSP || y < 0 || y >= YSP))
return Vect2s(-1, -1);
return Vect2s(x / _cellSX, y / _cellSY);
}
const Vect2s qdCamera::get_cell_index(const Vect3f &v, bool grid_crop) const {
return get_cell_index(v.x, v.y, grid_crop);
}
const Vect3f qdCamera::get_cell_coords(int x_idx, int y_idx) const {
//float xx = (x_idx - (_GSX>>1)) * _cellSX + (_cellSX>>1) + _gridCenter.x;
//float yy = (y_idx - (_GSY>>1)) * _cellSY + (_cellSY>>1) + _gridCenter.y;
float xx = (x_idx - static_cast<float>(_GSX) / 2 + 0.5) * _cellSX + _gridCenter.x;
float yy = (y_idx - static_cast<float>(_GSY) / 2 + 0.5) * _cellSY + _gridCenter.y;
return Vect3f(xx, yy, _gridCenter.z);
}
const Vect3f qdCamera::get_cell_coords(const Vect2s &idxs) const {
return get_cell_coords(idxs.x, idxs.y);
}
void qdCamera::reset_all_select() {
int cnt = 0;
for (int i = 0; i < _GSY; i++) {
for (int j = 0; j < _GSX; j++) {
_grid[cnt++].deselect();
}
}
}
bool qdCamera::select_cell(int x, int y) {
const int XSP = _cellSX * _GSX;
const int YSP = _cellSY * _GSY;
const int XSP05 = XSP >> 1;
const int YSP05 = YSP >> 1;
x += XSP05 - _gridCenter.x;
y += YSP05 - _gridCenter.y;
if (x < 0 || x >= XSP || y < 0 || y >= YSP) return false;
x = x / _cellSX;
y = y / _cellSY;
_grid[y * _GSX + x].select();
return true;
}
bool qdCamera::deselect_cell(int x, int y) {
const int XSP = _cellSX * _GSX;
const int YSP = _cellSY * _GSY;
const int XSP05 = XSP >> 1;
const int YSP05 = YSP >> 1;
x += XSP05 - _gridCenter.x;
y += YSP05 - _gridCenter.y;
if (x < 0 || x >= XSP || y < 0 || y >= YSP) return false;
x = x / _cellSX;
y = y / _cellSY;
_grid[y * _GSX + x].deselect();
return true;
}
void qdCamera::load_script(const xml::tag *p) {
int x, y;
for (xml::tag::subtag_iterator it = p->subtags_begin(); it != p->subtags_end(); ++it) {
xml::tag_buffer buf(*it);
switch (it->ID()) {
case QDSCR_CAMERA_GRID_SIZE:
buf > x > y;
set_grid_size(x, y);
break;
case QDSCR_CAMERA_CELL_SIZE:
buf > _cellSX > _cellSY;
break;
case QDSCR_CAMERA_SCREEN_SIZE:
buf > x > y;
set_scr_size(x, y);
set_scr_center(x / 2, y / 2);
set_scr_center_initial(Vect2i(x / 2, y / 2));
break;
case QDSCR_CAMERA_SCREEN_OFFSET:
buf > x > y;
set_scr_offset(Vect2i(x, y));
break;
case QDSCR_CAMERA_SCREEN_CENTER:
buf > x > y;
set_scr_center_initial(Vect2i(x, y));
set_scr_center(x, y);
break;
case QDSCR_CAMERA_FOCUS:
buf > _focus;
break;
case QDSCR_CAMERA_ANGLES:
buf > _xAngle > _yAngle > _zAngle;
rotate_and_scale(_xAngle, _yAngle, _zAngle, 1, 1, 1);
break;
case QDSCR_CAMERA_GRID_CENTER: {
Vect3f v;
buf > v.x > v.y > v.z;
set_grid_center(v);
}
break;
case QDSCR_POS3D:
buf.get_float();
buf.get_float();
set_R(buf.get_float());
break;
case QDSCR_CAMERA_SCALE_POW:
buf > _scale_pow;
break;
case QDSCR_CAMERA_SCALE_Z_OFFSET:
buf > _scale_z_offset;
break;
}
}
rotate_and_scale(_xAngle, _yAngle, _zAngle, 1, 1, 1);
}
bool qdCamera::save_script(Common::WriteStream &fh, int indent) const {
for (int i = 0; i < indent; i++) {
fh.writeString("\t");
}
fh.writeString("<camera");
fh.writeString(Common::String::format(" camera_grid_size=\"%d %d\"", _GSX, _GSY));
fh.writeString(">\r\n");
for (int i = 0; i <= indent; i++) {
fh.writeString("\t");
}
fh.writeString(Common::String::format("<camera_cell_size>%d %d</camera_cell_size>\r\n", _cellSX, _cellSY));
for (int i = 0; i <= indent; i++) {
fh.writeString("\t");
}
fh.writeString(Common::String::format("<pos_3d>%ld %ld %f</pos_3d>\r\n", 0L, 0L, get_R()));
for (int i = 0; i <= indent; i++) {
fh.writeString("\t");
}
fh.writeString(Common::String::format("<camera_focus>%f</camera_focus>\r\n", _focus));
for (int i = 0; i <= indent; i++) {
fh.writeString("\t");
}
fh.writeString(Common::String::format("<camera_angles>%f %f %f</camera_angles>\r\n", _xAngle, _yAngle, _zAngle));
for (int i = 0; i <= indent; i++) {
fh.writeString("\t");
}
fh.writeString(Common::String::format("<camera_screen_size>%d %d</camera_screen_size>\r\n", _scrSize.x, _scrSize.y));
for (int i = 0; i <= indent; i++) {
fh.writeString("\t");
}
fh.writeString(Common::String::format("<camera_screen_offset>%d %d</camera_screen_offset>\r\n", _scrOffset.x, _scrOffset.y));
for (int i = 0; i <= indent; i++) {
fh.writeString("\t");
}
fh.writeString(Common::String::format("<camera_screen_center>%d %d</camera_screen_center>\r\n", _scrCenterInitial.x, _scrCenterInitial.y));
for (int i = 0; i <= indent; i++) {
fh.writeString("\t");
}
fh.writeString(Common::String::format("<camera_grid_center>%f %f %f</camera_grid_center>\r\n", _gridCenter.x, _gridCenter.y, _gridCenter.z));
for (int i = 0; i <= indent; i++) {
fh.writeString("\t");
}
fh.writeString(Common::String::format("<camera_scale_pow>%f</camera_scale_pow>\r\n", scale_pow()));
for (int i = 0; i <= indent; i++) {
fh.writeString("\t");
}
fh.writeString(Common::String::format("<camera_scale_z_offset>%f</camera_scale_z_offset>\r\n", scale_z_offset()));
for (int i = 0; i < indent; i++) {
fh.writeString("\t");
}
fh.writeString("</camera>\r\n");
return true;
}
const Vect2i qdCamera::screen_center_limit_x() const {
int x0, x1;
if (_scrSize.x < g_engine->_screenW) {
x0 = x1 = g_engine->_screenW / 2;
} else {
x0 = -_scrSize.x / 2 + g_engine->_screenW;
x1 = _scrSize.x / 2;
}
if (_cycle_x) {
x0 -= _scrSize.x;
x1 += _scrSize.x;
}
return Vect2i(x0, x1);
}
const Vect2i qdCamera::screen_center_limit_y() const {
int y0, y1;
if (_scrSize.y < g_engine->_screenH) {
y0 = y1 = g_engine->_screenH / 2;
} else {
y0 = -_scrSize.y / 2 + g_engine->_screenH;
y1 = _scrSize.y / 2;
}
if (_cycle_y) {
y0 -= _scrSize.y;
y1 += _scrSize.y;
}
return Vect2i(y0, y1);
}
void qdCamera::move_scr_center(int dxc, int dyc) {
_scrCenter.x += dxc;
_scrCenter.y += dyc;
clip_center_coords(_scrCenter.x, _scrCenter.y);
}
float qdCamera::scrolling_phase_x() const {
if (_scrSize.x <= g_engine->_screenW)
return 0.0f;
else
return float(_scrCenter.x * 2 + _scrSize.x - g_engine->_screenW * 2) / float(_scrSize.x - g_engine->_screenW) - 1.0f;
}
float qdCamera::scrolling_phase_y() const {
if (_scrSize.y <= g_engine->_screenH)
return 0.0f;
else
return float(_scrCenter.y * 2 + _scrSize.y - g_engine->_screenH * 2) / float(_scrSize.x - g_engine->_screenH) - 1.0f;
}
bool qdCamera::draw_grid() const {
if (_redraw_mode == QDCAM_GRID_NONE) return true;
int cnt = 0;
const int XSP = _cellSX * _GSX;
const int YSP = _cellSY * _GSY;
const float XSP05 = XSP / 2.f;
const float YSP05 = YSP / 2.f;
for (int i = 0; i < _GSY;++i) {
for (int j = 0; j < _GSX;++j) {
if (!_grid[cnt].is_walkable())
draw_cell(j, i, 0, 1, IMPASSIBLE_CELL_CLR);
if (_grid[cnt].is_selected() || _grid[cnt].check_attribute(sGridCell::CELL_OCCUPIED | sGridCell::CELL_PERSONAGE_OCCUPIED))
draw_cell(j, i, 0, 1, SELECTED_CELL_CLR);
++cnt;
}
}
if (_redraw_mode == QDCAM_GRID_ZBUFFER) {
for (int i = 0; i <= _GSX; i++) {
for (int j = 0; j < _GSY; j++) {
Vect3f begPoint(-XSP05 + i * _cellSX, -YSP05 + j * _cellSY, 0);
Vect3f endPoint(-XSP05 + i * _cellSX, -YSP05 + (j + 1) *_cellSY, 0);
begPoint = global2camera_coord(begPoint + _gridCenter);
endPoint = global2camera_coord(endPoint + _gridCenter);
if (line_cutting(begPoint, endPoint)) {
Vect2s b = camera_coord2scr(begPoint);
Vect2s e = camera_coord2scr(endPoint);
#ifdef _GR_ENABLE_ZBUFFER
grDispatcher::instance()->Line_z(b.x, b.y, begPoint.z, e.x, e.y, endPoint.z, NORMAL_CELL_CLR, DASH_LEN);
#else
grDispatcher::instance()->line(b.x, b.y, e.x, e.y, NORMAL_CELL_CLR, DASH_LEN);
#endif
}
}
}
for (int i = 0; i <= _GSY; i++) {
for (int j = 0; j < _GSX; j++) {
Vect3f begPoint(-XSP05 + j * _cellSX, -YSP05 + i * _cellSY, 0);
Vect3f endPoint(-XSP05 + (j + 1)*_cellSX, -YSP05 + i * _cellSY, 0);
begPoint = global2camera_coord(begPoint + _gridCenter);
endPoint = global2camera_coord(endPoint + _gridCenter);
if (line_cutting(begPoint, endPoint)) {
Vect2s b = camera_coord2scr(begPoint);
Vect2s e = camera_coord2scr(endPoint);
#ifdef _GR_ENABLE_ZBUFFER
grDispatcher::instance()->line_z(b.x, b.y, begPoint.z, e.x, e.y, endPoint.z, NORMAL_CELL_CLR, DASH_LEN);
#else
grDispatcher::instance()->line(b.x, b.y, e.x, e.y, NORMAL_CELL_CLR, DASH_LEN);
#endif
}
}
}
} else {
for (int i = 0; i <= _GSX; i++) {
Vect3f begPoint(-XSP05 + i * _cellSX, -YSP05, 0);
Vect3f endPoint(-XSP05 + i * _cellSX, +YSP05, 0);
begPoint = global2camera_coord(begPoint + _gridCenter);
endPoint = global2camera_coord(endPoint + _gridCenter);
if (line_cutting(begPoint, endPoint)) {
Vect2s b = camera_coord2scr(begPoint);
Vect2s e = camera_coord2scr(endPoint);
grDispatcher::instance()->line(b.x, b.y, e.x, e.y, NORMAL_CELL_CLR, DASH_LEN);
}
}
for (int i = 0; i <= _GSY; i++) {
Vect3f begPoint(-XSP05, -YSP05 + i * _cellSY, 0);
Vect3f endPoint(+XSP05, -YSP05 + i * _cellSY, 0);
begPoint = global2camera_coord(begPoint + _gridCenter);
endPoint = global2camera_coord(endPoint + _gridCenter);
if (line_cutting(begPoint, endPoint)) {
Vect2s b = camera_coord2scr(begPoint);
Vect2s e = camera_coord2scr(endPoint);
grDispatcher::instance()->line(b.x, b.y, e.x, e.y, NORMAL_CELL_CLR, DASH_LEN);
}
}
}
cnt = 0;
uint32 color;
for (int i = 0; i < _GSY; i++) {
for (int j = 0; j < _GSX; j++, cnt++) {
if (_grid[cnt].height() || _grid[cnt].attributes()) {
color = 0;
if (_grid[cnt].attributes() & sGridCell::CELL_IMPASSABLE)
color = grDispatcher::instance()->make_rgb(0xff, 0x00, 0x00);
else if (_grid[cnt].attributes() & sGridCell::CELL_PERSONAGE_OCCUPIED)
color = grDispatcher::instance()->make_rgb(0xff, 0xff, 0x00);
else if (_grid[cnt].height())
color = grDispatcher::instance()->make_rgb(0xff, 0xff, 0xff);
if (_grid[cnt].attributes() & sGridCell::CELL_OCCUPIED)
color = grDispatcher::instance()->make_rgb(0x00, 0x00, 0xff);
if (_grid[cnt].attributes() & sGridCell::CELL_PERSONAGE_PATH)
warning("path");
if (color)
draw_cell(j, i, _grid[cnt].height(), 1, color);
}
}
}
return true;
}
bool qdCamera::draw_cell(int x, int y, int z, int penWidth, uint32 color) const {
const int XSP = get_cell_sx() * get_grid_sx();
const int YSP = get_cell_sy() * get_grid_sy();
const float XSP05 = XSP * 0.5f;
const float YSP05 = YSP * 0.5f;
const int offset = 2;
Vect3f point0((float)(x * get_cell_sx() - XSP05 + offset), (float)(y * get_cell_sy() - YSP05 + offset), (float)z);
Vect3f point1((float)((x + 1)*get_cell_sx() - XSP05 - offset), (float)(y * get_cell_sy() - YSP05 + offset), (float)z);
Vect3f point2((float)((x + 1)*get_cell_sx() - XSP05 - offset), (float)((y + 1)*get_cell_sy() - YSP05 - offset), (float)z);
Vect3f point3((float)(x * get_cell_sx() - XSP05 + offset), (float)((y + 1)*get_cell_sy() - YSP05 - offset), (float)z);
point0 = global2camera_coord(point0 + _gridCenter);
point1 = global2camera_coord(point1 + _gridCenter);
point2 = global2camera_coord(point2 + _gridCenter);
point3 = global2camera_coord(point3 + _gridCenter);
if (_redraw_mode == QDCAM_GRID_ZBUFFER) {
if (line_cutting(point0, point1)) {
Vect2s p0 = camera_coord2scr(point0);
Vect2s p1 = camera_coord2scr(point1);
#ifdef _GR_ENABLE_ZBUFFER
grDispatcher::instance()->Line_z(p0.x, p0.y, point0.z, p1.x, p1.y, point1.z, color/*, DASH_LEN*/);
#else
grDispatcher::instance()->line(p0.x, p0.y, p1.x, p1.y, color/*, DASH_LEN*/);
#endif
}
if (line_cutting(point1, point2)) {
Vect2s p0 = camera_coord2scr(point1);
Vect2s p1 = camera_coord2scr(point2);
#ifdef _GR_ENABLE_ZBUFFER
grDispatcher::instance()->Line_z(p0.x, p0.y, point1.z, p1.x, p1.y, point2.z, color/*, DASH_LEN*/);
#else
grDispatcher::instance()->line(p0.x, p0.y, p1.x, p1.y, color/*, DASH_LEN*/);
#endif
}
if (line_cutting(point2, point3)) {
Vect2s p0 = camera_coord2scr(point2);
Vect2s p1 = camera_coord2scr(point3);
#ifdef _GR_ENABLE_ZBUFFER
grDispatcher::instance()->Line_z(p0.x, p0.y, point2.z, p1.x, p1.y, point3.z, color/*, DASH_LEN*/);
#else
grDispatcher::instance()->line(p0.x, p0.y, p1.x, p1.y, color/*, DASH_LEN*/);
#endif
}
if (line_cutting(point3, point0)) {
Vect2s p0 = camera_coord2scr(point3);
Vect2s p1 = camera_coord2scr(point0);
#ifdef _GR_ENABLE_ZBUFFER
grDispatcher::instance()->Line_z(p0.x, p0.y, point3.z, p1.x, p1.y, point0.z, color/*, DASH_LEN*/);
#else
grDispatcher::instance()->line(p0.x, p0.y, p1.x, p1.y, color/*, DASH_LEN*/);
#endif
}
} else {
if (line_cutting(point0, point1)) {
Vect2s p0 = camera_coord2scr(point0);
Vect2s p1 = camera_coord2scr(point1);
grDispatcher::instance()->line(p0.x, p0.y, p1.x, p1.y, color/*, DASH_LEN*/);
}
if (line_cutting(point1, point2)) {
Vect2s p0 = camera_coord2scr(point1);
Vect2s p1 = camera_coord2scr(point2);
grDispatcher::instance()->line(p0.x, p0.y, p1.x, p1.y, color/*, DASH_LEN*/);
}
if (line_cutting(point2, point3)) {
Vect2s p0 = camera_coord2scr(point2);
Vect2s p1 = camera_coord2scr(point3);
grDispatcher::instance()->line(p0.x, p0.y, p1.x, p1.y, color/*, DASH_LEN*/);
}
if (line_cutting(point3, point0)) {
Vect2s p0 = camera_coord2scr(point3);
Vect2s p1 = camera_coord2scr(point0);
grDispatcher::instance()->line(p0.x, p0.y, p1.x, p1.y, color/*, DASH_LEN*/);
}
}
return true;
}
void qdCamera::scale_grid(int sx, int sy, int csx, int csy) {
if (_GSX == sx && _GSY == sy) return;
sGridCell *new_grid = new sGridCell[sx * sy];
if (_GSX) {
if (_GSX >= sx && _GSY >= sy) {
int dx = _GSX / sx;
int dy = _GSY / sy;
sGridCell *new_p = new_grid;
sGridCell *old_p = _grid;
for (int i = 0; i < sy; i++) {
for (int j = 0; j < sx; j++) {
int attr_count = 0;
int height_sum = 0;
for (int y = 0; y < dy; y++) {
for (int x = 0; x < dx; x++) {
if (!old_p[j * dx + x + y * _GSX].is_walkable())
attr_count++;
height_sum += old_p[j * dx + x + y * _GSX].height();
}
}
if (attr_count >= dx * dy / 2)
new_p->make_impassable();
new_p->set_height(height_sum / (dx * dy));
new_p++;
}
old_p += _GSX * dy;
}
}
if (_GSX <= sx && _GSY <= sy) {
int dx = sx / _GSX;
int dy = sy / _GSY;
sGridCell *new_p = new_grid;
sGridCell *old_p = _grid;
for (int i = 0; i < _GSY; i++) {
for (int j = 0; j < _GSX; j++) {
for (int y = 0; y < dy; y++) {
for (int x = 0; x < dx; x++)
new_p[j * dx + x + y * sx] = *old_p;
}
old_p++;
}
new_p += sx * dy;
}
}
delete [] _grid;
}
_grid = new_grid;
_GSX = sx;
_GSY = sy;
_cellSX = csx;
_cellSY = csy;
}
void qdCamera::resize_grid(int sx, int sy) {
if (_GSX == sx && _GSY == sy) return;
sGridCell *new_grid = new sGridCell[sx * sy];
if (_GSX) {
int x0 = (sx - _GSX) / 2;
int y0 = (sy - _GSY) / 2;
for (int y = 0; y < _GSY; y++) {
for (int x = 0; x < _GSX; x++) {
if (x + x0 >= 0 && x + x0 < sx && y + y0 >= 0 && y + y0 < sy)
new_grid[x + x0 + (y + y0) * sx] = _grid[x + y * _GSX];
}
}
delete [] _grid;
}
_grid = new_grid;
_GSX = sx;
_GSY = sy;
}
bool qdCamera::set_grid_cell(const Vect2s &cell_pos, const sGridCell &cell) {
if (cell_pos.x >= 0 && cell_pos.x < _GSX && cell_pos.y >= 0 && cell_pos.y < _GSY) {
_grid[cell_pos.x + cell_pos.y * _GSX] = cell;
return true;
}
return false;
}
bool qdCamera::set_grid_cell_attributes(const Vect2s &cell_pos, int attr) {
if (cell_pos.x >= 0 && cell_pos.x < _GSX && cell_pos.y >= 0 && cell_pos.y < _GSY) {
_grid[cell_pos.x + cell_pos.y * _GSX].set_attributes(attr);
return true;
}
return false;
}
bool qdCamera::restore_grid_cell(const Vect2s cell_pos) {
if (cell_pos.x >= 0 && cell_pos.x < _GSX && cell_pos.y >= 0 && cell_pos.y < _GSY) {
sGridCell cl;
cl.make_impassable();
_grid[cell_pos.x + cell_pos.y * _GSX] = cl;
return true;
}
return false;
}
sGridCell *qdCamera::get_cell(const Vect2s &cell_pos) {
if (cell_pos.x >= 0 && cell_pos.x < _GSX && cell_pos.y >= 0 && cell_pos.y < _GSY) {
return &_grid[cell_pos.x + cell_pos.y * _GSX];
}
return NULL;
}
const sGridCell *qdCamera::get_cell(const Vect2s &cell_pos) const {
if (cell_pos.x >= 0 && cell_pos.x < _GSX && cell_pos.y >= 0 && cell_pos.y < _GSY) {
return &_grid[cell_pos.x + cell_pos.y * _GSX];
}
return NULL;
}
bool qdCamera::load_data(Common::SeekableReadStream &fh, int save_version) {
debugC(3, kDebugSave, " qdCamera::load_data(): before: %d", (int)fh.pos());
int x, y;
char flag;
_scrCenter.x = fh.readSint32LE();
_scrCenter.y = fh.readSint32LE();
x = fh.readSint32LE();
y = fh.readSint32LE();
_current_mode_work_time = fh.readFloatLE();
flag = fh.readByte();
_current_mode_switch = bool(flag);
if (x != _GSX || y != _GSY) return false;
if (!_current_mode.load_data(fh, save_version))
return false;
if (!_default_mode.load_data(fh, save_version))
return false;
flag = fh.readByte();
if (flag) {
qdNamedObjectReference ref;
if (!ref.load_data(fh, save_version))
return false;
_current_object = dynamic_cast<qdGameObjectAnimated *>(qdGameDispatcher::get_dispatcher()->get_named_object(&ref));
}
flag = fh.readByte();
if (flag) {
qdNamedObjectReference ref;
if (!ref.load_data(fh, save_version))
return false;
_default_object = dynamic_cast<qdGameObjectAnimated *>(qdGameDispatcher::get_dispatcher()->get_named_object(&ref));
}
debugC(3, kDebugSave, " qdCamera::load_data(): after: %d", (int)fh.pos());
return true;
}
bool qdCamera::save_data(Common::WriteStream &fh) const {
debugC(3, kDebugSave, " qdCamera::save_data(): before: %d", (int)fh.pos());
fh.writeSint32LE(_scrCenter.x);
fh.writeSint32LE(_scrCenter.y);
fh.writeSint32LE(_GSX);
fh.writeSint32LE(_GSY);
fh.writeFloatLE(_current_mode_work_time);
fh.writeByte(char(_current_mode_switch));
_current_mode.save_data(fh);
_default_mode.save_data(fh);
if (_current_object) {
fh.writeByte(char(1));
qdNamedObjectReference ref(_current_object);
ref.save_data(fh);
} else {
fh.writeByte(char(0));
}
if (_default_object) {
fh.writeByte(char(1));
qdNamedObjectReference ref(_default_object);
ref.save_data(fh);
} else {
fh.writeByte(char(0));
}
debugC(3, kDebugSave, " qdCamera::save_data(): after: %d", (int)fh.pos());
return true;
}
bool qdCamera::set_mode(const qdCameraMode &mode, qdGameObjectAnimated *object) {
_current_mode = mode;
_current_object = object;
_current_mode_work_time = 0.0f;
_current_mode_switch = _current_mode.smooth_switch();
return true;
}
bool qdCamera::quant(float dt) {
Vect2i last_pos = _scrCenter;
qdGameObjectAnimated *p = _current_object;
if (!p) p = _default_object;
if (p)
p->qdGameObject::update_screen_pos();
switch (_current_mode.camera_mode()) {
case qdCameraMode::MODE_CENTER_OBJECT:
if (p) {
Vect2i r = p->screen_pos() + _current_mode.center_offset();
int cx = _scrCenter.x + g_engine->_screenW / 2 - r.x;
int cy = _scrCenter.y + g_engine->_screenH / 2 - r.y;
clip_center_coords(cx, cy);
int dx = cx - _scrCenter.x;
int dy = cy - _scrCenter.y;
if (_current_mode_switch) {
Vect2f dr(dx, dy);
float dr0 = _current_mode.scrolling_speed() * dt;
if (dr.norm2() > dr0 * dr0)
dr.normalize(dr0);
else
_current_mode_switch = false;
move_scr_center(dr.xi(), dr.yi());
} else
move_scr_center(dx, dy);
}
break;
case qdCameraMode::MODE_OBJECT_ON_SCREEN:
if (p) {
Vect2s r = p->screen_pos();
float sz = p->radius();
int dx = 0;
int dy = 0;
if (r.x + sz + _current_mode.scrolling_distance() >= g_engine->_screenW) {
dx = g_engine->_screenW - (r.x + sz + _current_mode.scrolling_distance());
} else {
if (r.x - sz - _current_mode.scrolling_distance() < 0)
dx = -r.x + sz + _current_mode.scrolling_distance();
}
if (r.y + sz + _current_mode.scrolling_distance() >= g_engine->_screenH) {
dy = g_engine->_screenH - (r.y + sz + _current_mode.scrolling_distance());
} else {
if (r.y - sz - _current_mode.scrolling_distance() < 0)
dy = -r.y + sz + _current_mode.scrolling_distance();
}
if (_current_mode_switch) {
int cx = _scrCenter.x + dx;
int cy = _scrCenter.y + dy;
clip_center_coords(cx, cy);
dx = cx - _scrCenter.x;
dy = cy - _scrCenter.y;
Vect2f dr(dx, dy);
float dr0 = _current_mode.scrolling_speed() * dt;
if (dr.norm2() > dr0 * dr0)
dr.normalize(dr0);
else
_current_mode_switch = false;
move_scr_center(dr.xi(), dr.yi());
} else
move_scr_center(dx, dy);
}
break;
case qdCameraMode::MODE_FOLLOW_OBJECT:
if (p) {
Vect2s r = p->screen_pos() + _current_mode.center_offset();
int dx = -r.x + g_engine->_screenW / 2;
int dy = -r.y + g_engine->_screenH / 2;
if (dx || dy) {
Vect2f dr(dx, dy);
float dr0 = _current_mode.scrolling_speed() * dt;
if (dr.norm2() > dr0 * dr0)
dr.normalize(dr0);
move_scr_center(dr.xi(), dr.yi());
}
_current_mode_switch = false;
}
break;
case qdCameraMode::MODE_CENTER_OBJECT_WHEN_LEAVING:
if (p) {
Vect2s r = p->screen_pos() + _current_mode.center_offset();
float sz = p->radius();
int dx = 0;
int dy = 0;
if (r.x + sz + _current_mode.scrolling_distance() >= g_engine->_screenW) {
dx = g_engine->_screenW - (r.x + sz + _current_mode.scrolling_distance());
} else {
if (r.x - sz - _current_mode.scrolling_distance() < 0)
dx = -r.x + sz + _current_mode.scrolling_distance();
}
if (r.y + sz + _current_mode.scrolling_distance() >= g_engine->_screenH) {
dy = g_engine->_screenH - (r.y + sz + _current_mode.scrolling_distance());
} else {
if (r.y - sz - _current_mode.scrolling_distance() < 0)
dy = -r.y + sz + _current_mode.scrolling_distance();
}
if (dx || dy) {
int dx1 = -r.x + g_engine->_screenW / 2;
int dy1 = -r.y + g_engine->_screenH / 2;
Vect2f dr(dx1, dy1);
float dr0 = _current_mode.scrolling_speed() * dt;
if (dr.norm2() > dr0 * dr0)
dr.normalize(dr0);
move_scr_center(dr.xi(), dr.yi());
}
_current_mode_switch = false;
}
break;
default:
break;
}
if (p)
p->update_screen_pos();
clip_center_coords(_scrCenter.x, _scrCenter.y);
if (!_current_mode_switch)
_current_mode_work_time += dt;
if (_current_mode.has_work_time() && _current_mode_work_time > _current_mode.work_time())
set_mode(_default_mode, _default_object);
if (last_pos.x != _scrCenter.x || last_pos.y != _scrCenter.y)
return true;
return false;
}
bool qdCamera::set_grid_attributes(const Vect2s &center_pos, const Vect2s &size, int attr) {
int x0 = center_pos.x - size.x / 2;
int y0 = center_pos.y - size.y / 2;
int x1 = x0 + size.x;
int y1 = y0 + size.y;
if (x0 < 0) x0 = 0;
if (x1 > _GSX - 1) x1 = _GSX - 1;
if (y0 < 0) y0 = 0;
if (y1 > _GSY - 1) y1 = _GSY - 1;
sGridCell *cells = _grid + x0 + y0 * _GSX;
debugC(4, kDebugMovement, "qdCamera::set_grid_attributes() attr: %d at [%d, %d]", attr, x0, y0);
for (int y = y0; y < y1; y++) {
sGridCell *p = cells;
for (int x = x0; x < x1; x++, p++)
p->set_attribute(attr);
cells += _GSX;
}
return true;
}
bool qdCamera::drop_grid_attributes(const Vect2s &center_pos, const Vect2s &size, int attr) {
int x0 = center_pos.x - size.x / 2;
int y0 = center_pos.y - size.y / 2;
int x1 = x0 + size.x;
int y1 = y0 + size.y;
if (x0 < 0) x0 = 0;
if (x1 > _GSX - 1) x1 = _GSX - 1;
if (y0 < 0) y0 = 0;
if (y1 > _GSY - 1) y1 = _GSY - 1;
sGridCell *cells = _grid + x0 + y0 * _GSX;
for (int y = y0; y < y1; y++) {
sGridCell *p = cells;
for (int x = x0; x < x1; x++, p++)
p->drop_attribute(attr);
cells += _GSX;
}
return true;
}
bool qdCamera::set_grid_attributes(int attr) {
sGridCell *p = _grid;
for (int i = 0; i < _GSX * _GSY; i++, p++)
p->set_attribute(attr);
return true;
}
bool qdCamera::drop_grid_attributes(int attr) {
sGridCell *p = _grid;
for (int i = 0; i < _GSX * _GSY; i++, p++)
p->drop_attribute(attr);
return true;
}
bool qdCamera::check_grid_attributes(const Vect2s &center_pos, const Vect2s &size, int attr) const {
int x0 = center_pos.x - size.x / 2;
int y0 = center_pos.y - size.y / 2;
int x1 = x0 + size.x;
int y1 = y0 + size.y;
if (x0 < 0) x0 = 0;
if (x1 > _GSX - 1) x1 = _GSX - 1;
if (y0 < 0) y0 = 0;
if (y1 > _GSY - 1) y1 = _GSY - 1;
const sGridCell *cells = _grid + x0 + y0 * _GSX;
for (int y = y0; y < y1; y++) {
const sGridCell *p = cells;
for (int x = x0; x < x1; x++, p++) {
if (p->check_attribute(attr))
return true;
}
cells += _GSX;
}
return false;
}
int qdCamera::cells_num_with_exact_attributes(const Vect2s &center_pos, const Vect2s &size, int attr) const {
int x0 = center_pos.x - size.x / 2;
int y0 = center_pos.y - size.y / 2;
int x1 = x0 + size.x;
int y1 = y0 + size.y;
if (x0 < 0) x0 = 0;
if (x1 > _GSX - 1) x1 = _GSX - 1;
if (y0 < 0) y0 = 0;
if (y1 > _GSY - 1) y1 = _GSY - 1;
const sGridCell *cells = _grid + x0 + y0 * _GSX;
int ret = 0;
for (int y = y0; y < y1; y++) {
const sGridCell *p = cells;
for (int x = x0; x < x1; x++, p++) {
if (p->attributes() == (uint)attr)
ret++;
}
cells += _GSX;
}
return ret;
}
bool qdCamera::is_walkable(const Vect2s &center_pos, const Vect2s &size, bool ignore_personages) const {
int x0 = center_pos.x - size.x / 2;
int y0 = center_pos.y - size.y / 2;
int x1 = x0 + size.x;
int y1 = y0 + size.y;
if (x0 < 0) x0 = 0;
if (x1 > _GSX - 1) x1 = _GSX - 1;
if (y0 < 0) y0 = 0;
if (y1 > _GSY - 1) y1 = _GSY - 1;
const sGridCell *cells = _grid + x0 + y0 * _GSX;
debugC(3, kDebugMovement, "qdCamera::is_walkable(): attr: %d [%d, %d] size: [%d, %d], ignore_personages: %d", cells->attributes(), x0, y0, size.x, size.y, ignore_personages);
int attr = sGridCell::CELL_IMPASSABLE | sGridCell::CELL_OCCUPIED;
if (g_engine->_gameVersion > 20031206 && !ignore_personages) {
attr |= sGridCell::CELL_PERSONAGE_OCCUPIED;
}
for (int y = y0; y < y1; y++) {
const sGridCell *p = cells;
for (int x = x0; x < x1; x++, p++) {
debugC(3, kDebugMovement, "qdCamera::is_walkable(): attr %d at [%d, %d]", p->attributes(), x, y);
if (p->check_attribute(attr) && !p->check_attribute(sGridCell::CELL_SELECTED)) {
return false;
}
}
cells += _GSX;
}
return true;
}
bool qdCamera::clip_grid_line(Vect2s &v0, Vect2s &v1) const {
int x = 0, y = 0;
bool accept = false, done = false;
int outcodeOut;
int outcode0 = clip_out_code(v0);
int outcode1 = clip_out_code(v1);
do {
if (outcode0 == 0 && outcode1 == 0) {
accept = true;
done = true;
} else {
if ((outcode0 & outcode1) != 0)
done = true;
else {
if (outcode0)
outcodeOut = outcode0;
else
outcodeOut = outcode1;
if (clTOP & outcodeOut) {
x = v0.x + (v1.x - v0.x) * (_GSY - v0.y - 1) / (v1.y - v0.y);
y = _GSY - 1;
} else if (clBOTTOM & outcodeOut) {
x = v0.x + (v1.x - v0.x) * (-v0.y) / (v1.y - v0.y);
y = 0;
}
if (clRIGHT & outcodeOut) {
y = v0.y + (v1.y - v0.y) * (_GSX - v0.x - 1) / (v1.x - v0.x);
x = _GSX - 1;
} else if (clLEFT & outcodeOut) {
y = v0.y + (v1.y - v0.y) * (-v0.x) / (v1.x - v0.x);
x = 0;
}
if (outcodeOut == outcode0) {
v0.x = x;
v0.y = y;
outcode0 = clip_out_code(Vect2s(x, y));
} else {
v1.x = x;
v1.y = y;
outcode1 = clip_out_code(Vect2s(x, y));
}
}
}
} while (!done);
return accept;
}
bool qdCamera::init() {
_default_object = NULL;
_current_object = NULL;
_scrCenter = _scrCenterInitial;
set_mode(_default_mode);
return true;
}
void qdCamera::clip_center_coords(int &x, int &y) const {
Vect2i lim = screen_center_limit_x();
if (x < lim.x)
x = lim.x;
else if (x > lim.y)
x = lim.y;
lim = screen_center_limit_y();
if (y < lim.x)
y = lim.x;
else if (y > lim.y)
y = lim.y;
}
bool qdCamera::is_visible(const Vect2i &center_offs) const {
int sx = g_engine->_screenW / 2;
int sy = g_engine->_screenH / 2;
Vect2s pos = scr2rscr(Vect2s(sx, sy));
pos.x -= center_offs.x;
pos.y += center_offs.y;
if (pos.x < -_scrSize.x / 2 - sx || pos.x > _scrSize.x / 2 + sx || pos.y < -_scrSize.y / 2 - sy || pos.y > _scrSize.y / 2 + sy)
return false;
return true;
}
void qdCamera::cycle_coords(int &x, int &y) const {
Vect2s pos = scr2rscr(Vect2s(x, y));
if (_cycle_x) {
if (pos.x < -_scrSize.x / 2 + _scrOffset.x)
pos.x += _scrSize.x;
else if (pos.x > _scrSize.x / 2 + _scrOffset.x)
pos.x -= _scrSize.x;
}
if (_cycle_y) {
if (pos.y < -_scrSize.y / 2 + _scrOffset.y)
pos.y += _scrSize.y;
else if (pos.y > _scrSize.y / 2 + _scrOffset.y)
pos.y -= _scrSize.y;
}
pos = rscr2scr(pos);
x = pos.x;
y = pos.y;
}
bool qdCamera::set_grid_line_attributes(const Vect2s &start_pos, const Vect2s &end_pos, const Vect2s &size, int attr) {
if (start_pos.x == end_pos.x && start_pos.y == end_pos.y) {
set_grid_attributes(start_pos, size, attr);
return true;
}
Vect2f r(start_pos.x, start_pos.y);
int dx = end_pos.x - start_pos.x;
int dy = end_pos.y - start_pos.y;
Vect2f dr(dx, dy);
float d = (float)_cellSX / 3.0;
if (d < 0.5f) d = 0.5f;
dr.normalize(d);
if (abs(dx) > abs(dy)) {
int i = round(float(dx) / dr.x);
do {
set_grid_attributes(Vect2s(r.xi(), r.yi()), size, attr);
r += dr;
} while (--i >= 0);
} else {
int i = round(float(dy) / dr.y);
do {
set_grid_attributes(Vect2s(r.xi(), r.yi()), size, attr);
r += dr;
} while (--i >= 0);
}
return true;
}
bool qdCamera::drop_grid_line_attributes(const Vect2s &start_pos, const Vect2s &end_pos, const Vect2s &size, int attr) {
if (start_pos.x == end_pos.x && start_pos.y == end_pos.y) {
drop_grid_attributes(start_pos, size, attr);
return true;
}
Vect2f r(start_pos.x, start_pos.y);
int dx = end_pos.x - start_pos.x;
int dy = end_pos.y - start_pos.y;
Vect2f dr(dx, dy);
float d = (float)_cellSX / 3.0;
if (d < 0.5f) d = 0.5f;
dr.normalize(d);
if (abs(dx) > abs(dy)) {
int i = round(float(dx) / dr.x);
do {
drop_grid_attributes(Vect2s(r.xi(), r.yi()), size, attr);
r += dr;
} while (--i >= 0);
} else {
int i = round(float(dy) / dr.y);
do {
drop_grid_attributes(Vect2s(r.xi(), r.yi()), size, attr);
r += dr;
} while (--i >= 0);
}
return true;
}
bool qdCamera::check_grid_line_attributes(const Vect2s &start_pos, const Vect2s &end_pos, const Vect2s &size, int attr) const {
if (start_pos.x == end_pos.x && start_pos.y == end_pos.y)
return check_grid_attributes(start_pos, size, attr);
Vect2f r(start_pos.x, start_pos.y);
int dx = end_pos.x - start_pos.x;
int dy = end_pos.y - start_pos.y;
Vect2f dr(dx, dy);
float d = (float)_cellSX / 3.0;
if (d < 0.5f) d = 0.5f;
dr.normalize(d);
if (abs(dx) > abs(dy)) {
int i = round(float(dx) / dr.x);
do {
if (check_grid_attributes(Vect2s(r.xi(), r.yi()), size, attr))
return true;
r += dr;
} while (--i >= 0);
} else {
int i = round(float(dy) / dr.y);
do {
if (check_grid_attributes(Vect2s(r.xi(), r.yi()), size, attr))
return true;
r += dr;
} while (--i >= 0);
}
return false;
}
void qdCamera::dump_grid(const char *file_name) const {
Common::DumpFile fh;
fh.open(Common::Path(file_name));
fh.writeString(Common::String::format("%d x %d\n", _GSX, _GSY));
for (int i = 0; i < _GSY; i++) {
for (int j = 0; j < _GSX; j++) {
if (_grid[j + i * _GSX].attributes() < 10)
fh.writeString(" ");
fh.writeString(Common::String::format("%u ", _grid[j + i * _GSX].attributes()));
}
fh.writeString("\n");
}
fh.close();
}
bool qdCamera::can_change_mode() const {
if (_current_mode.has_work_time()) return false;
return true;
}
} // namespace QDEngine