Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,318 @@
/* 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/>.
*
*/
#ifndef QDENGINE_QDCORE_QD_ANIMATION_H
#define QDENGINE_QDCORE_QD_ANIMATION_H
#include "qdengine/parser/xml_fwd.h"
#include "qdengine/system/graphics/gr_screen_region.h"
#include "qdengine/system/graphics/gr_tile_animation.h"
#include "qdengine/qdcore/qd_resource.h"
#include "qdengine/qdcore/qd_animation_frame.h"
#include "qdengine/qdcore/qd_named_object.h"
namespace QDEngine {
class qdAnimationInfo;
const int QD_ANIMATION_FLAG_REFERENCE = 0x01;
const int QD_ANIMATION_FLAG_LOOP = 0x04;
const int QD_ANIMATION_FLAG_FLIP_HORIZONTAL = 0x08;
const int QD_ANIMATION_FLAG_FLIP_VERTICAL = 0x10;
const int QD_ANIMATION_FLAG_BLACK_FON = 0x20;
const int QD_ANIMATION_FLAG_SUPPRESS_ALPHA = 0x40;
const int QD_ANIMATION_FLAG_CROP = 0x80;
const int QD_ANIMATION_FLAG_COMPRESS = 0x100;
const int QD_ANIMATION_FLAG_TILE_COMPRESS = 0x200;
enum qdAnimationStatus {
QD_ANIMATION_STOPPED = 0,
QD_ANIMATION_PLAYING,
QD_ANIMATION_PAUSED,
QD_ANIMATION_END_PLAYING
};
//! Анимация.
class qdAnimation : public qdNamedObject, public qdResource {
public:
qdAnimation();
qdAnimation(const qdAnimation &anm);
~qdAnimation();
qdAnimation &operator = (const qdAnimation &anm);
int named_object_type() const {
return QD_NAMED_OBJECT_ANIMATION;
}
const qdAnimationFrame *get_cur_frame() const;
const qdAnimationFrame *get_cur_frame(float &scale) const;
qdAnimationFrame *get_cur_frame();
void set_cur_frame(int number);
int get_cur_frame_number() const;
qdAnimationFrame *get_frame(int number);
const qdAnimationFrame *get_scaled_frame(int number, int scale_index) const;
int num_frames() const {
return _num_frames;
}
float length() const {
return _length;
}
float cur_time() const {
return _cur_time;
}
void set_time(float tm) {
_cur_time = tm;
}
float cur_time_rel() const {
if (_length > 0.01f)
return _cur_time / _length;
return 0.0f;
}
void set_time_rel(float tm) {
if (tm < 0.0f) tm = 0.0f;
if (tm > 0.99f) tm = 0.99f;
_cur_time = _length * tm;
}
void advance_time(float tm);
void init_size();
int size_x() const {
return _sx;
}
int size_y() const {
return _sy;
}
int picture_size_x() const;
int picture_size_y() const;
bool is_playing() const {
return (_status == QD_ANIMATION_PLAYING ||
_status == QD_ANIMATION_END_PLAYING);
}
int status() const {
return _status;
}
bool is_finished() const {
return _is_finished;
}
bool need_stop() const {
return _status == QD_ANIMATION_END_PLAYING;
}
void start() {
_status = QD_ANIMATION_PLAYING;
_is_finished = false;
_cur_time = 0.0f;
}
void stop() {
_status = QD_ANIMATION_STOPPED;
_is_finished = true;
}
void pause() {
_status = QD_ANIMATION_PAUSED;
}
void resume() {
_status = QD_ANIMATION_PLAYING;
}
void quant(float dt);
void redraw(int x, int y, int z, int mode = 0) const;
void redraw(int x, int y, int z, float scale, int mode = 0) const;
void redraw_rot(int x, int y, int z, float angle, int mode = 0) const;
void redraw_rot(int x, int y, int z, float angle, const Vect2f &scale, int mode = 0) const;
void draw_mask(int x, int y, int z, uint32 mask_color, int mask_alpha, int mode = 0) const;
void draw_mask(int x, int y, int z, uint32 mask_color, int mask_alpha, float scale, int mode = 0) const;
void draw_mask_rot(int x, int y, int z, float angle, uint32 mask_color, int mask_alpha, int mode = 0) const;
void draw_mask_rot(int x, int y, int z, float angle, uint32 mask_color, int mask_alpha, const Vect2f &scale, int mode = 0) const;
void draw_contour(int x, int y, uint32 color) const;
void draw_contour(int x, int y, uint32 color, float scale) const;
bool hit(int x, int y) const;
bool hit(int x, int y, float scale) const;
bool add_frame(qdAnimationFrame *p, qdAnimationFrame *insert_pos = 0, bool insert_after = true);
bool remove_frame(int number);
bool remove_frame_range(int number0, int number1);
bool reverse_frame_range(int number0, int number1);
void load_script(const xml::tag *p);
bool save_script(Common::WriteStream &fh, int indent = 0) const;
const Common::Path qda_file() const {
return _qda_file;
}
void qda_set_file(const Common::Path &fname);
bool qda_load(const Common::Path &fname);
bool load_resources();
void free_resources();
bool scale(float coeff_x, float coeff_y);
bool crop();
bool undo_crop();
Vect2i remove_edges();
bool compress();
bool uncompress();
bool tileCompress(grTileCompressionMethod method = TILE_UNCOMPRESSED, int tolerance = 0);
qdAnimationFrameList &frames_list() {
return _frames;
};
void create_reference(qdAnimation *p, const qdAnimationInfo *inf = NULL) const;
bool is_reference(const qdAnimation *p) const {
if (p->check_flag(QD_ANIMATION_FLAG_REFERENCE) && p->_parent == this) return true;
return false;
}
void clear() {
stop();
_frames_ptr = &_frames;
_parent = NULL;
}
bool is_empty() const {
return (_frames_ptr->empty());
}
//! Возвращает область экрана, занимаемую анимацией.
/**
Координаты области - смещение от центра анимации.
В mode задаются повороты анимации по горизонтали и вертикали
(QD_ANIMATION_FLAG_FLIP_HORIZONTAL, QD_ANIMATION_FLAG_FLIP_VERTICAL)
*/
grScreenRegion screen_region(int mode = 0, float scale = 1.0f) const;
const qdAnimation *parent() const {
return _parent;
}
// qdResource
bool load_resource();
bool free_resource();
//! Устанавливает имя файла, в котором хранятся данные ресурса.
void set_resource_file(const Common::Path &file_name) {
qda_set_file(file_name);
}
//! Возвращает имя файла, в котором хранится анимация.
const Common::Path resource_file() const {
if (qda_file().empty()) {
if (!check_flag(QD_ANIMATION_FLAG_REFERENCE) && !_frames.empty()) {
if (_frames.front()->has_file())
return _frames.front()->file();
else
return NULL;
} else
return NULL;
} else
return qda_file();
}
#ifdef __QD_DEBUG_ENABLE__
uint32 resource_data_size() const;
#endif
//! Загрузка данных из сэйва.
bool load_data(Common::SeekableReadStream &fh, int save_version);
//! Запись данных в сэйв.
bool save_data(Common::WriteStream &fh) const;
bool add_scale(float value);
bool create_scaled_frames();
const Std::vector<float> &scales() const {
if (check_flag(QD_ANIMATION_FLAG_REFERENCE) && _parent) return _parent->_scales;
else return _scales;
}
void clear_scales() {
_scales.clear();
}
const grTileAnimation *tileAnimation() const {
if (check_flag(QD_ANIMATION_FLAG_REFERENCE) && _parent)
return _parent->_tileAnimation;
else
return _tileAnimation;
}
static Common::String flag2str(int fl, bool truncate = false, bool icon = false);
static Common::String status2str(int fl, bool truncate = false);
private:
int _sx;
int _sy;
enum {
qda_version = 104
};
float _length;
float _cur_time;
float _playback_speed;
int _num_frames;
const qdAnimationFrameList *_frames_ptr;
qdAnimationFrameList _frames;
const qdAnimationFrameList *_scaled_frames_ptr;
qdAnimationFrameList _scaled_frames;
Std::vector<float> _scales;
grTileAnimation *_tileAnimation;
int _status;
bool _is_finished;
Common::Path _qda_file;
const qdAnimation *_parent;
int get_scale_index(float &scale_value) const;
bool copy_frames(const qdAnimation &anm);
void clear_frames();
};
} // namespace QDEngine
#endif // QDENGINE_QDCORE_QD_ANIMATION_H

View File

@@ -0,0 +1,75 @@
/* 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 "qdengine/qd_fwd.h"
#include "qdengine/qdcore/qd_animation_frame.h"
namespace QDEngine {
qdAnimationFrame::qdAnimationFrame() : _start_time(0.0f),
_length(0.0f) {
}
qdAnimationFrame::qdAnimationFrame(const qdAnimationFrame &frm) : qdSprite(frm),
_start_time(frm._start_time),
_length(frm._length) {
}
qdAnimationFrame::~qdAnimationFrame() {
free();
}
qdAnimationFrame &qdAnimationFrame::operator = (const qdAnimationFrame &frm) {
if (this == &frm) return *this;
*static_cast<qdSprite *>(this) = frm;
_start_time = frm._start_time;
_length = frm._length;
return *this;
}
qdAnimationFrame *qdAnimationFrame::clone() const {
return new qdAnimationFrame(*this);
}
void qdAnimationFrame::qda_load(Common::SeekableReadStream *fh, int version) {
/*int32 fl = */fh->readSint32LE();
_start_time = fh->readFloatLE();
_length = fh->readFloatLE();
qdSprite::qda_load(fh, version);
}
bool qdAnimationFrame::load_resources() {
if (!load()) return false;
return true;
}
void qdAnimationFrame::free_resources() {
free();
}
} // namespace QDEngine

View File

@@ -0,0 +1,72 @@
/* 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/>.
*
*/
#ifndef QDENGINE_QDCORE_QD_ANIMATION_FRAME_H
#define QDENGINE_QDCORE_QD_ANIMATION_FRAME_H
#include "qdengine/qdcore/qd_sprite.h"
namespace QDEngine {
//! Кадр анимации.
class qdAnimationFrame : public qdSprite {
public:
qdAnimationFrame();
qdAnimationFrame(const qdAnimationFrame &frm);
~qdAnimationFrame();
qdAnimationFrame &operator = (const qdAnimationFrame &frm);
qdAnimationFrame *clone() const;
float start_time() const {
return _start_time;
}
float end_time() const {
return _start_time + _length;
}
float length() const {
return _length;
}
void set_start_time(float tm) {
_start_time = tm;
}
void set_length(float tm) {
_length = tm;
}
virtual void qda_load(class Common::SeekableReadStream *fh, int version = 100);
bool load_resources();
void free_resources();
private:
float _start_time;
float _length;
};
typedef Std::list<qdAnimationFrame *> qdAnimationFrameList;
} // namespace QDEngine
#endif // QDENGINE_QDCORE_QD_ANIMATION_FRAME_H

View File

@@ -0,0 +1,128 @@
/* 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 "qdengine/qd_fwd.h"
#include "qdengine/xmath.h"
#include "qdengine/parser/xml_tag_buffer.h"
#include "qdengine/parser/qdscr_parser.h"
#include "qdengine/qdcore/qd_game_scene.h"
#include "qdengine/qdcore/qd_game_dispatcher.h"
#include "qdengine/qdcore/qd_named_object.h"
#include "qdengine/qdcore/qd_animation_info.h"
#include "qdengine/qdcore/qd_animation.h"
namespace QDEngine {
qdAnimationInfo::qdAnimationInfo() : _speed(0.0f), _animation_speed(1.0f) {
}
qdAnimationInfo::qdAnimationInfo(const qdAnimationInfo &p) : qdNamedObject(p),
_speed(p._speed),
_animation_speed(p._animation_speed) {
}
qdAnimationInfo::~qdAnimationInfo() {
}
void qdAnimationInfo::load_script(const xml::tag *p) {
int fl;
for (xml::tag::subtag_iterator it = p->subtags_begin(); it != p->subtags_end(); ++it) {
switch (it->ID()) {
case QDSCR_SPEED:
xml::tag_buffer(*it) > _speed;
break;
case QDSCR_ANIMATION_SPEED:
xml::tag_buffer(*it) > _animation_speed;
break;
case QDSCR_ANIMATION:
set_animation_name(it->data());
break;
case QDSCR_FLAG:
xml::tag_buffer(*it) > fl;
set_flag(fl);
break;
}
}
}
bool qdAnimationInfo::save_script(Common::WriteStream &fh, int indent) const {
Common::String res;
if (flags()) {
if (debugChannelSet(-1, kDebugLog))
res += Common::String::format(" flags=\"%s\"", qdAnimation::flag2str(flags()).c_str());
else
res += Common::String::format(" flags=\"%d\"", flags());
}
if (_speed > 0.01f)
res += Common::String::format(" speed=\"%f\"", _speed);
if (_animation_speed != 1.0f)
res += Common::String::format(" animation_speed=\"%f\"", _animation_speed);
if (animation_name())
res += Common::String::format(" animation=\"%s\"", qdscr_XML_string(animation_name()));
if (!res.empty()) {
for (int i = 0; i < indent; i++)
fh.writeString("\t");
fh.writeString("<animation_info");
fh.writeString(res);
fh.writeString("/>\r\n");
}
return true;
}
qdAnimationInfo &qdAnimationInfo::operator = (const qdAnimationInfo &p) {
if (this == &p)
return *this;
*static_cast<qdNamedObject *>(this) = p;
_speed = p._speed;
_animation_speed = p._animation_speed;
return *this;
}
qdAnimation *qdAnimationInfo::animation() const {
if (animation_name()) {
if (qdGameScene *p = static_cast<qdGameScene *>(owner(QD_NAMED_OBJECT_SCENE))) {
if (qdAnimation *anm = p->get_animation(animation_name()))
return anm;
}
if (qdGameDispatcher *p = qd_get_game_dispatcher())
return p->get_animation(animation_name());
}
return NULL;
}
} // namespace QDEngine

View File

@@ -0,0 +1,85 @@
/* 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/>.
*
*/
#ifndef QDENGINE_QDCORE_QD_ANIMATION_INFO_H
#define QDENGINE_QDCORE_QD_ANIMATION_INFO_H
#include "qdengine/parser/xml_fwd.h"
#include "qdengine/qdcore/qd_named_object.h"
namespace QDEngine {
class qdAnimation;
//! Информация об анимации.
class qdAnimationInfo : public qdNamedObject {
public:
qdAnimationInfo();
qdAnimationInfo(const qdAnimationInfo &p);
~qdAnimationInfo();
qdAnimationInfo &operator = (const qdAnimationInfo &p);
int named_object_type() const {
return QD_NAMED_OBJECT_ANIMATION_INFO;
}
float speed() const {
return _speed;
}
void set_speed(float sp) {
_speed = sp;
}
float animation_speed() const {
return _animation_speed;
}
void set_animation_speed(float sp) {
_animation_speed = sp;
}
qdAnimation *animation() const;
const char *animation_name() const {
return name();
}
void load_script(const xml::tag *p);
bool save_script(Common::WriteStream &fh, int indent = 0) const;
void set_animation_name(const char *name) {
set_name(name);
}
void free_animation_name() {
set_name(0);
}
private:
float _speed;
float _animation_speed;
};
typedef Std::vector<qdAnimationInfo> qdAnimationInfoVector;
} // namespace QDEngine
#endif // QDENGINE_QDCORE_QD_ANIMATION_INFO_H

View File

@@ -0,0 +1,451 @@
/* 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 "qdengine/qd_fwd.h"
#include "qdengine/parser/xml_tag_buffer.h"
#include "qdengine/parser/qdscr_parser.h"
#include "qdengine/qdcore/qd_animation_set.h"
#include "qdengine/qdcore/qd_game_dispatcher.h"
namespace QDEngine {
qdAnimationSet::qdAnimationSet() {
_start_angle = 0.0f;
}
qdAnimationSet::qdAnimationSet(const qdAnimationSet &set) : qdNamedObject(set),
_start_angle(set._start_angle),
_animations(set._animations),
_walk_sound_frequency(set._walk_sound_frequency),
_static_animations(set._static_animations),
_start_animations(set._start_animations),
_stop_animations(set._stop_animations),
_turn_animation(set._turn_animation) {
_turn_animation.set_owner(this);
for (int i = 0; i < size(); i++) {
_animations[i].set_owner(this);
_static_animations[i].set_owner(this);
_static_animations[i].set_owner(this);
_stop_animations[i].set_owner(this);
}
}
qdAnimationSet::~qdAnimationSet() {
_animations.clear();
_static_animations.clear();
_start_animations.clear();
_stop_animations.clear();
}
qdAnimationSet &qdAnimationSet::operator = (const qdAnimationSet &set) {
if (this == &set) return *this;
*static_cast<qdNamedObject *>(this) = set;
_start_angle = set._start_angle;
_animations = set._animations;
_static_animations = set._static_animations;
_start_animations = set._stop_animations;
_stop_animations = set._stop_animations;
_walk_sound_frequency = set._walk_sound_frequency;
_turn_animation = set._turn_animation;
_turn_animation.set_owner(this);
for (int i = 0; i < size(); i++) {
_animations[i].set_owner(this);
_static_animations[i].set_owner(this);
_start_animations[i].set_owner(this);
_stop_animations[i].set_owner(this);
}
return *this;
}
void qdAnimationSet::resize(int sz) {
_animations.resize(sz);
_static_animations.resize(sz);
_start_animations.resize(sz);
_stop_animations.resize(sz);
_walk_sound_frequency.resize(sz, 1);
for (int i = 0; i < size(); i++) {
_animations[i].set_owner(this);
_static_animations[i].set_owner(this);
}
}
qdAnimationInfo *qdAnimationSet::get_animation_info(int index) {
if (index >= 0 && index < size())
return &_animations[index];
return 0;
}
int qdAnimationSet::get_angle_index(float direction_angle, int dir_count) {
if (direction_angle < 0.0f)
direction_angle += 2.0f * M_PI;
int index = round(direction_angle * float(dir_count) / (2.0f * M_PI));
if (index >= dir_count) index -= dir_count;
if (index < 0) index += dir_count;
return index;
}
float qdAnimationSet::get_index_angle(int index, int dir_count) {
return index * 2.f * M_PI / dir_count;
}
float qdAnimationSet::get_index_angle(int direction_index) const {
return get_index_angle(direction_index, size()) + _start_angle;
}
int qdAnimationSet::get_angle_index(float direction_angle) const {
return get_angle_index(direction_angle - _start_angle, size());
}
qdAnimationInfo *qdAnimationSet::get_animation_info(float direction_angle) {
int index = get_angle_index(direction_angle);
return get_animation_info(index);
}
qdAnimationInfo *qdAnimationSet::get_static_animation_info(int index) {
if (index >= 0 && index < size())
return &_static_animations[index];
return 0;
}
qdAnimationInfo *qdAnimationSet::get_static_animation_info(float direction_angle) {
int index = get_angle_index(direction_angle);
return get_static_animation_info(index);
}
qdAnimationInfo *qdAnimationSet::get_start_animation_info(int index) {
if (index >= 0 && index < size())
return &_start_animations[index];
return 0;
}
qdAnimationInfo *qdAnimationSet::get_start_animation_info(float direction_angle) {
int index = get_angle_index(direction_angle);
return get_start_animation_info(index);
}
qdAnimationInfo *qdAnimationSet::get_stop_animation_info(int index) {
if (index >= 0 && index < size())
return &_stop_animations[index];
return 0;
}
qdAnimationInfo *qdAnimationSet::get_stop_animation_info(float direction_angle) {
int index = get_angle_index(direction_angle);
return get_stop_animation_info(index);
}
qdAnimation *qdAnimationSet::get_turn_animation() const {
return _turn_animation.animation();
}
void qdAnimationSet::load_script(const xml::tag *p) {
int index = 0;
for (xml::tag::subtag_iterator it = p->subtags_begin(); it != p->subtags_end(); ++it) {
switch (it->ID()) {
case QDSCR_NAME:
set_name(it->data());
break;
case QDSCR_FLAG:
set_flag(xml::tag_buffer(*it).get_int());
break;
case QDSCR_SIZE:
resize(xml::tag_buffer(*it).get_int());
break;
case QDSCR_ANIMATION_INFO:
if (index < size())
_animations[index].load_script(&*it);
else if (index < size() * 2)
_static_animations[index - size()].load_script(&*it);
else if (index < size() * 3)
_start_animations[index - size() * 2].load_script(&*it);
else
_stop_animations[index - size() * 3].load_script(&*it);
index++;
break;
case QDSCR_ANIMATION_SET_TURN:
_turn_animation.set_animation_name(it->data());
break;
case QDSCR_ANIMATION_SET_START_ANGLE:
xml::tag_buffer(*it) > _start_angle;
break;
case QDSCR_OBJECT_STATE_WALK_SOUND_FREQUENCY: {
xml::tag_buffer buf(*it);
_walk_sound_frequency.resize(it->data_size());
for (int i = 0; i < it->data_size(); i++)
buf > _walk_sound_frequency[i];
}
break;
}
}
}
bool qdAnimationSet::save_script(Common::WriteStream &fh, int indent) const {
for (int i = 0; i < indent; i++) {
fh.writeString("\t");
}
fh.writeString(Common::String::format("<animation_set name=\"%s\"", qdscr_XML_string(name())));
if (_turn_animation.animation_name()) {
fh.writeString(Common::String::format(" animation_turn=\"%s\"", qdscr_XML_string(_turn_animation.animation_name())));
}
fh.writeString(Common::String::format(" size=\"%d\"", size()));
if (fabs(_start_angle) > FLT_EPS) {
fh.writeString(Common::String::format(" start_angle=\"%f\"", _start_angle));
}
fh.writeString(">\r\n");
for (auto &it : _animations) {
it.save_script(fh, indent + 1);
}
for (auto &it : _static_animations) {
it.save_script(fh, indent + 1);
}
for (auto &it : _start_animations) {
it.save_script(fh, indent + 1);
}
for (auto &it : _stop_animations) {
it.save_script(fh, indent + 1);
}
if (_walk_sound_frequency.size()) {
for (int i = 0; i <= indent; i++) {
fh.writeString("\t");
}
fh.writeString(Common::String::format("<walk_sound_frequency>%u", _walk_sound_frequency.size()));
for (uint i = 0; i < _walk_sound_frequency.size(); i++) {
fh.writeString(Common::String::format(" %f", _walk_sound_frequency[i]));
}
fh.writeString("</walk_sound_frequency>\r\n");
}
for (int i = 0; i < indent; i++) {
fh.writeString("\t");
}
fh.writeString("</animation_set>\r\n");
return true;
}
bool qdAnimationSet::load_animations(const qdNamedObject *res_owner) {
if (qdGameDispatcher *dp = qdGameDispatcher::get_dispatcher()) {
for (auto &it : _animations) {
if (qdAnimation *p = it.animation()) {
dp->load_resource(p, res_owner);
}
}
for (auto &it : _static_animations) {
if (qdAnimation *p = it.animation()) {
dp->load_resource(p, res_owner);
}
}
for (auto &it : _start_animations) {
if (qdAnimation *p = it.animation()) {
dp->load_resource(p, res_owner);
}
}
for (auto &it : _stop_animations) {
if (qdAnimation *p = it.animation()) {
dp->load_resource(p, res_owner);
}
}
if (qdAnimation *p = _turn_animation.animation())
dp->load_resource(p, res_owner);
return true;
}
return false;
}
bool qdAnimationSet::free_animations(const qdNamedObject *res_owner) {
if (qdGameDispatcher *dp = qdGameDispatcher::get_dispatcher()) {
for (auto &it : _animations) {
if (qdAnimation *p = it.animation()) {
dp->release_resource(p, res_owner);
}
}
for (auto &it : _static_animations) {
if (qdAnimation *p = it.animation()) {
dp->release_resource(p, res_owner);
}
}
for (auto &it : _start_animations) {
if (qdAnimation *p = it.animation()) {
dp->release_resource(p, res_owner);
}
}
for (auto &it : _stop_animations) {
if (qdAnimation *p = it.animation()) {
dp->release_resource(p, res_owner);
}
}
if (qdAnimation *p = _turn_animation.animation())
dp->release_resource(p, res_owner);
return true;
}
return false;
}
bool qdAnimationSet::scale_animations(float coeff_x, float coeff_y) {
bool res = true;
for (auto &it : _animations) {
qdAnimation *p = it.animation();
if (p)
if (!p->scale(coeff_x, coeff_y)) res = false;
}
for (auto &it : _static_animations) {
qdAnimation *p = it.animation();
if (p)
if (!p->scale(coeff_x, coeff_y)) res = false;
}
for (auto &it : _start_animations) {
qdAnimation *p = it.animation();
if (p)
if (!p->scale(coeff_x, coeff_y)) res = false;
}
for (auto &it : _stop_animations) {
qdAnimation *p = it.animation();
if (p)
if (!p->scale(coeff_x, coeff_y)) res = false;
}
if (qdAnimation *p = _turn_animation.animation())
if (!p->scale(coeff_x, coeff_y)) res = false;
return res;
}
bool qdAnimationSet::register_resources(const qdNamedObject *res_owner) {
if (qdGameDispatcher *dp = qdGameDispatcher::get_dispatcher()) {
for (auto &it : _animations) {
if (qdAnimation *p = it.animation())
dp->register_resource(p, res_owner);
}
for (auto &it : _static_animations) {
if (qdAnimation *p = it.animation())
dp->register_resource(p, res_owner);
}
for (auto &it : _start_animations) {
if (qdAnimation *p = it.animation())
dp->register_resource(p, res_owner);
}
for (auto &it : _stop_animations) {
if (qdAnimation *p = it.animation())
dp->register_resource(p, res_owner);
}
if (qdAnimation *p = _turn_animation.animation())
dp->register_resource(p, res_owner);
return true;
}
return false;
}
bool qdAnimationSet::unregister_resources(const qdNamedObject *res_owner) {
if (qdGameDispatcher *dp = qdGameDispatcher::get_dispatcher()) {
for (auto &it : _animations) {
if (qdAnimation *p = it.animation())
dp->unregister_resource(p, res_owner);
}
for (auto &it : _static_animations) {
if (qdAnimation *p = it.animation())
dp->unregister_resource(p, res_owner);
}
for (auto &it : _start_animations) {
if (qdAnimation *p = it.animation())
dp->unregister_resource(p, res_owner);
}
for (auto &it : _stop_animations) {
if (qdAnimation *p = it.animation())
dp->unregister_resource(p, res_owner);
}
if (qdAnimation *p = _turn_animation.animation())
dp->unregister_resource(p, res_owner);
return true;
}
return false;
}
float qdAnimationSet::adjust_angle(float angle) const {
int dir = get_angle_index(angle);
if (dir == -1) return 0.0f;
return get_index_angle(dir);
}
float qdAnimationSet::walk_sound_frequency(int direction_index) const {
if (direction_index < 0 || direction_index >= (int)_walk_sound_frequency.size())
return 1;
else
return _walk_sound_frequency[direction_index];
}
float qdAnimationSet::walk_sound_frequency(float direction_angle) const {
int index = get_angle_index(direction_angle);
return walk_sound_frequency(index);
}
void qdAnimationSet::set_walk_sound_frequency(int direction_index, float freq) {
assert(direction_index >= 0);
if (direction_index >= (int)_walk_sound_frequency.size())
_walk_sound_frequency.resize(direction_index + 1, 1);
_walk_sound_frequency[direction_index] = freq;
}
} // namespace QDEngine

View File

@@ -0,0 +1,128 @@
/* 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/>.
*
*/
#ifndef QDENGINE_QDCORE_QD_ANIMATION_SET_H
#define QDENGINE_QDCORE_QD_ANIMATION_SET_H
#include "qdengine/parser/xml_fwd.h"
#include "qdengine/qdcore/qd_named_object.h"
#include "qdengine/qdcore/qd_animation.h"
#include "qdengine/qdcore/qd_animation_info.h"
namespace QDEngine {
class qdAnimationSet : public qdNamedObject {
public:
qdAnimationSet();
qdAnimationSet(const qdAnimationSet &set);
~qdAnimationSet();
qdAnimationSet &operator = (const qdAnimationSet &set);
int named_object_type() const {
return QD_NAMED_OBJECT_ANIMATION_SET;
}
int size() const {
return _animations.size();
}
void resize(int sz);
//! выдает индекс походки и остановки, соответствующих углу
int get_angle_index(float direction_angle) const;
//! выдает угол походки и остановки, соответствующих индексу
float get_index_angle(int direction_index) const;
//! Возвращает ближайшее к angle направление, для которого есть анимация.
float adjust_angle(float angle) const;
qdAnimationInfo *get_animation_info(int index = 0);
qdAnimationInfo *get_animation_info(float direction_angle);
qdAnimationInfo *get_static_animation_info(int index = 0);
qdAnimationInfo *get_static_animation_info(float direction_angle);
qdAnimationInfo *get_start_animation_info(int index = 0);
qdAnimationInfo *get_start_animation_info(float direction_angle);
qdAnimationInfo *get_stop_animation_info(int index = 0);
qdAnimationInfo *get_stop_animation_info(float direction_angle);
qdAnimation *get_turn_animation() const;
qdAnimationInfo *get_turn_animation_info() {
return &_turn_animation;
}
void set_turn_animation(const char *animation_name) {
_turn_animation.set_animation_name(animation_name);
}
float walk_sound_frequency(int direction_index) const;
float walk_sound_frequency(float direction_angle) const;
void set_walk_sound_frequency(int direction_index, float freq);
bool load_animations(const qdNamedObject *res_owner);
bool free_animations(const qdNamedObject *res_owner);
//! Регистрация ресурсов набора в диспетчере ресурсов.
bool register_resources(const qdNamedObject *res_owner);
//! Отмена регистрации ресурсов набора в диспетчере ресурсов.
bool unregister_resources(const qdNamedObject *res_owner);
bool scale_animations(float coeff_x, float coeff_y);
void load_script(const xml::tag *p);
bool save_script(Common::WriteStream &fh, int indent = 0) const;
float start_angle() const {
return _start_angle;
}
void set_start_angle(float v) {
_start_angle = v;
}
private:
//! Угол, соответствующий первому направлению в походке.
/**
Если нулевой - первое направление вправо.
*/
float _start_angle;
qdAnimationInfoVector _animations;
qdAnimationInfoVector _static_animations;
qdAnimationInfoVector _start_animations;
qdAnimationInfoVector _stop_animations;
/// анимация поворота, полный оборот начиная с направления вправо
qdAnimationInfo _turn_animation;
//! Коэффициенты для частоты звука походки.
Std::vector<float> _walk_sound_frequency;
static int get_angle_index(float direction_angle, int dir_count);
static float get_index_angle(int index, int dir_count);
};
} // namespace QDEngine
#endif // QDENGINE_QDCORE_QD_ANIMATION_SET_H

View File

@@ -0,0 +1,42 @@
/* 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 "qdengine/qd_fwd.h"
#include "qdengine/qdcore/qd_animation_set_info.h"
#include "qdengine/qdcore/qd_game_dispatcher.h"
#include "qdengine/qdcore/qd_game_scene.h"
namespace QDEngine {
qdAnimationSet *qdAnimationSetInfo::animation_set() const {
if (name()) {
if (qdGameScene *p = static_cast<qdGameScene *>(owner(QD_NAMED_OBJECT_SCENE))) {
if (qdAnimationSet *set = p->get_animation_set(name()))
return set;
}
if (qdGameDispatcher *p = qdGameDispatcher::get_dispatcher())
return p->get_animation_set(name());
}
return NULL;
}
} // namespace QDEngine

View File

@@ -0,0 +1,48 @@
/* 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/>.
*
*/
#ifndef QDENGINE_QDCORE_QD_ANIMATION_SET_INFO_H
#define QDENGINE_QDCORE_QD_ANIMATION_SET_INFO_H
#include "qdengine/qdcore/qd_named_object.h"
namespace QDEngine {
class qdAnimationSet;
//! Информация о наборе анимаций.
class qdAnimationSetInfo : public qdNamedObject {
public:
qdAnimationSetInfo() { }
~qdAnimationSetInfo() { }
int named_object_type() const {
return QD_NAMED_OBJECT_ANIMATION_SET_INFO;
}
qdAnimationSet *animation_set() const;
};
} // namespace QDEngine
#endif // QDENGINE_QDCORE_QD_ANIMATION_SET_INFO_H

View File

@@ -0,0 +1,243 @@
/* 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/system.h"
#include "qdengine/qd_fwd.h"
#include "qdengine/qdcore/qd_animation_set.h"
#include "qdengine/qdcore/qd_animation_set_preview.h"
#include "qdengine/qdcore/qd_camera.h"
namespace QDEngine {
qdAnimationSetPreview::qdAnimationSetPreview(qdAnimationSet *p) :
_graph_d(0),
_animation_set(p),
_preview_mode(VIEW_WALK_ANIMATIONS) {
_animation = new qdAnimation;
_camera = new qdCamera;
_camera->set_focus(2000.0f);
_camera_angle = 45;
_start_time = 0;
_back_color = 0x000000FF;
_grid_color = 0x00FF00FF;
set_direction(0);
_cell_size = 100;
_personage_height = float(_animation->size_y());
p->load_animations(NULL);
}
qdAnimationSetPreview::~qdAnimationSetPreview() {
_animation_set->free_animations(NULL);
delete _animation;
delete _camera;
}
void qdAnimationSetPreview::set_graph_dispatcher(grDispatcher *p) {
_graph_d = p;
set_screen(Vect2s(0, 0), Vect2s(p->get_SizeX(), p->get_SizeY()));
}
void qdAnimationSetPreview::start() {
_start_time = g_system->getMillis();
_animation->start();
_cell_offset = 0.0f;
}
void qdAnimationSetPreview::quant() {
int time = g_system->getMillis();
float tm = float(time - _start_time) / 1000.0f;
_start_time = time;
if (tm >= 0.3f) return;
quant(tm);
}
void qdAnimationSetPreview::quant(float tm) {
_animation->quant(tm);
_cell_offset -= _personage_speed * tm;
while (_cell_offset <= -float(_cell_size)) _cell_offset += float(_cell_size);
}
void qdAnimationSetPreview::redraw() {
grDispatcher *gp = grDispatcher::set_instance(_graph_d);
grDispatcher::instance()->fill(_back_color);
redraw_grid();
Vect2s v = _camera->global2scr(Vect3f(0.0f, 0.0f, _personage_height / 2.0f));
float scale = _camera->get_scale(Vect3f(0.0f, 0.0f, _personage_height / 2.0f));
_animation->redraw(v.x, v.y, scale);
Vect2s v0 = _camera->global2scr(Vect3f(0.0f, 0.0f, _personage_height));
Vect2s v1 = _camera->global2scr(Vect3f(0.0f, 0.0f, 0.0f));
const int rect_sz = 4;
grDispatcher::instance()->rectangle(v.x - rect_sz / 2, v.y - rect_sz / 2, rect_sz, rect_sz, _grid_color, _grid_color, GR_FILLED);
const int line_sz = 10;
grDispatcher::instance()->line(v0.x - line_sz, v0.y, v0.x + line_sz, v0.y, _grid_color);
grDispatcher::instance()->line(v1.x - line_sz, v1.y, v1.x + line_sz, v1.y, _grid_color);
grDispatcher::instance()->line(v0.x, v0.y, v1.x, v1.y, _grid_color);
grDispatcher::instance()->rectangle(_screen_offset.x, _screen_offset.y, _screen_size.x, _screen_size.y, _grid_color, 0, GR_OUTLINED);
grDispatcher::instance()->flush();
grDispatcher::set_instance(gp);
}
bool qdAnimationSetPreview::set_direction(int dir) {
bool result = false;
_animation->clear();
_personage_speed = 0.0f;
_direction = dir;
_cell_offset = 0.0f;
float angle = _animation_set->get_index_angle(_direction) * 180.0f / M_PI;
_camera->rotate_and_scale(_camera_angle, 0, angle, 1.0f, 1.0f, 1.0f);
qdAnimationInfo *p = NULL;
if (_preview_mode == VIEW_WALK_ANIMATIONS)
p = _animation_set->get_animation_info(dir);
else if (_preview_mode == VIEW_STATIC_ANIMATIONS)
p = _animation_set->get_static_animation_info(dir);
else if (_preview_mode == VIEW_START_ANIMATIONS)
p = _animation_set->get_start_animation_info(dir);
else if (_preview_mode == VIEW_STOP_ANIMATIONS)
p = _animation_set->get_stop_animation_info(dir);
if (p) {
qdAnimation *ap = p->animation();
if (ap) {
ap->create_reference(_animation, p);
result = true;
}
_personage_speed = p->speed();
}
_animation->start();
return result;
}
void qdAnimationSetPreview::set_cell_size(int sz) {
_cell_size = sz;
_animation->start();
_cell_offset = 0.0f;
}
void qdAnimationSetPreview::set_screen(Vect2s offs, Vect2s size) {
if (!_graph_d) return;
_screen_offset = offs;
_screen_size = size;
_camera->set_scr_size(size.x, size.y);
_camera->set_scr_center(offs.x + size.x / 2, offs.y + size.y * 3 / 4);
_graph_d->setClip(offs.x, offs.y, offs.x + size.x, offs.y + size.y);
}
void qdAnimationSetPreview::redraw_grid() {
float size = 0;
Vect2f p = _camera->scr2plane(_screen_offset);
if (fabs(p.x) > size) size = fabs(p.x);
if (fabs(p.y) > size) size = fabs(p.y);
p = _camera->scr2plane(_screen_offset + _screen_size);
if (fabs(p.x) > size) size = fabs(p.x);
if (fabs(p.y) > size) size = fabs(p.y);
p = _camera->scr2plane(Vect2s(_screen_offset.x + _screen_size.x, _screen_offset.y));
if (fabs(p.x) > size) size = fabs(p.x);
if (fabs(p.y) > size) size = fabs(p.y);
p = _camera->scr2plane(Vect2s(_screen_offset.x, _screen_offset.y + _screen_size.y));
if (fabs(p.x) > size) size = fabs(p.x);
if (fabs(p.y) > size) size = fabs(p.y);
int sz = round(size) + _cell_size;
sz -= sz % _cell_size;
for (int i = -sz; i <= sz; i += _cell_size) {
int dx = round(_cell_offset);
Vect3f v00 = _camera->global2camera_coord(Vect3f(i + dx, -sz, 0));
Vect3f v10 = _camera->global2camera_coord(Vect3f(i + dx, sz, 0));
if (_camera->line_cutting(v00, v10)) {
Vect2s v0 = _camera->camera_coord2scr(v00);
Vect2s v1 = _camera->camera_coord2scr(v10);
grDispatcher::instance()->line(v0.x, v0.y, v1.x, v1.y, _grid_color, 0);
}
v00 = _camera->global2camera_coord(Vect3f(-sz + dx, i, 0));
v10 = _camera->global2camera_coord(Vect3f(sz + dx, i, 0));
if (_camera->line_cutting(v00, v10)) {
Vect2s v0 = _camera->camera_coord2scr(v00);
Vect2s v1 = _camera->camera_coord2scr(v10);
grDispatcher::instance()->line(v0.x, v0.y, v1.x, v1.y, _grid_color, 0);
}
/*
v0 = _camera->global2scr(Vect3f(-sz + dx,i,0));
v1 = _camera->global2scr(Vect3f(sz + dx,i,0));
grDispatcher::instance()->line(v0.x,v0.y,v1.x,v1.y,_grid_color,0);
*/
}
}
void qdAnimationSetPreview::set_camera_angle(int ang) {
_camera_angle = ang;
float angle = _animation_set->get_index_angle(_direction) * 180.0f / M_PI;
_camera->rotate_and_scale(_camera_angle, 0, angle, 1.0f, 1.0f, 1.0f);
}
float qdAnimationSetPreview::camera_focus() {
return _camera->get_focus();
}
void qdAnimationSetPreview::set_camera_focus(float f) {
_camera->set_focus(f);
}
bool qdAnimationSetPreview::set_phase(float phase) {
if (!_animation->is_empty()) {
if (!_animation->is_playing())
_animation->start();
_cell_offset = 0.0f;
_animation->set_time(0.0f);
quant(_animation->length() * phase);
return true;
}
return false;
}
} // namespace QDEngine

View File

@@ -0,0 +1,150 @@
/* 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/>.
*
*/
#ifndef QDENGINE_QDCORE_ANIMATION_SET_PREVIEW_H
#define QDENGINE_QDCORE_ANIMATION_SET_PREVIEW_H
#include "qdengine/system/graphics/gr_dispatcher.h"
namespace QDEngine {
class qdCamera;
class qdAnimation;
class qdAnimationSet;
//! Превью походки.
class qdAnimationSetPreview {
public:
enum preview_mode_t {
VIEW_WALK_ANIMATIONS,
VIEW_STATIC_ANIMATIONS,
VIEW_START_ANIMATIONS,
VIEW_STOP_ANIMATIONS
};
qdAnimationSetPreview(qdAnimationSet *p);
~qdAnimationSetPreview();
preview_mode_t preview_mode() const {
return _preview_mode;
}
void set_preview_mode(preview_mode_t md) {
_preview_mode = md;
}
void set_screen(Vect2s offs, Vect2s size);
uint32 back_color() const {
return _back_color;
}
void set_back_color(uint32 col) {
_back_color = col;
}
uint32 grid_color() const {
return _grid_color;
}
void set_grid_color(uint32 col) {
_grid_color = col;
}
int camera_angle() const {
return _camera_angle;
}
void set_camera_angle(int ang);
float camera_focus();
void set_camera_focus(float f);
int cell_size() const {
return _cell_size;
}
void set_cell_size(int sz);
float personage_speed() const {
return _personage_speed;
}
void set_personage_speed(float sp) {
_personage_speed = sp;
}
float personage_height() const {
return _personage_height;
}
void set_personage_height(float h) {
_personage_height = h;
}
int direction() const {
return _direction;
}
bool set_direction(int dir);
bool set_phase(float phase);
void set_graph_dispatcher(grDispatcher *p);
void start();
void quant();
void quant(float tm);
void redraw();
const qdAnimation *cur_animation() const {
return _animation;
}
private:
int _start_time;
int _direction;
//float speed_;
float _personage_speed;
float _personage_height;
qdAnimation *_animation;
qdAnimationSet *_animation_set;
qdCamera *_camera;
int _camera_angle;
grDispatcher *_graph_d;
uint32 _back_color;
uint32 _grid_color;
int _cell_size;
float _cell_offset;
Vect2s _screen_offset;
Vect2s _screen_size;
preview_mode_t _preview_mode;
void redraw_grid();
};
} // namespace QDEngine
#endif // QDENGINE_QDCORE_ANIMATION_SET_PREVIEW_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,505 @@
/* 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/>.
*
*/
#ifndef QDENGINE_QDCORE_QD_CAMERA_H
#define QDENGINE_QDCORE_QD_CAMERA_H
#include "qdengine/qdcore/qd_d3dutils.h"
#include "qdengine/qdcore/qd_camera_mode.h"
namespace Common {
class WriteStream;
}
namespace QDEngine {
class sGridCell {
public:
//! Атрибуты
enum cell_attribute_t {
//! ячейка выделена
CELL_SELECTED = 0x01,
//! ячейка непроходима
CELL_IMPASSABLE = 0x02,
//! ячейка занята объектом
CELL_OCCUPIED = 0x04,
//! ячейка занята персонажем
CELL_PERSONAGE_OCCUPIED = 0x08,
//! по ячейке проходит путь персонажа
CELL_PERSONAGE_PATH = 0x10
};
sGridCell() {
_attributes = CELL_IMPASSABLE;
}
sGridCell(uint16 atr, int16 h) {
_attributes = atr;
}
~sGridCell() { }
bool is_walkable() const {
return !check_attribute(CELL_IMPASSABLE);
}
void make_impassable() {
set_attribute(CELL_IMPASSABLE);
}
void make_walkable() {
drop_attribute(CELL_IMPASSABLE);
}
void select() {
set_attribute(CELL_SELECTED);
}
void deselect() {
drop_attribute(CELL_SELECTED);
}
void toggle_select() {
toggle_attribute(CELL_SELECTED);
}
bool is_selected() const {
return check_attribute(CELL_SELECTED);
}
int16 height() const {
return 0;
}
void set_height(int16 h) { }
void set_attribute(uint32 attr) {
_attributes |= attr;
}
void toggle_attribute(uint32 attr) {
_attributes ^= attr;
}
void drop_attribute(uint32 attr) {
_attributes &= ~attr;
}
bool check_attribute(uint32 attr) const {
if (_attributes & attr) return true;
else return false;
}
uint32 attributes() const {
return _attributes;
}
void set_attributes(uint32 attr) {
_attributes = attr;
}
bool clear() {
_attributes = 0;
return true;
}
private:
byte _attributes;
};
enum qdCameraRedrawMode {
QDCAM_GRID_ABOVE,
QDCAM_GRID_ZBUFFER,
QDCAM_GRID_NONE
};
class qdCamera {
public:
qdCamera();
~qdCamera();
// Устанавливет параметры клетки с координатами cell_pos
// по параметрам клетки cell
bool set_grid_cell(const Vect2s &cell_pos, const sGridCell &cell);
bool set_grid_cell_attributes(const Vect2s &cell_pos, int attr);
//! Устанавливает атрибуты для клеток из прямоугольника на сетке с ценром center_pos и размерами size.
bool set_grid_attributes(const Vect2s &center_pos, const Vect2s &size, int attr);
//! Очищает атрибуты для клеток из прямоугольника на сетке с ценром center_pos и размерами size.
bool drop_grid_attributes(const Vect2s &center_pos, const Vect2s &size, int attr);
//! Возвращает true, если в прямоугольнике на сетке ЕСТЬ ХОТЯ БЫ ОДНА ячейка с атрибутами attr.
bool check_grid_attributes(const Vect2s &center_pos, const Vect2s &size, int attr) const;
//! Возвращает количество ячеек в заданной области, имеющих именно аттрибуты attr
int cells_num_with_exact_attributes(const Vect2s &center_pos, const Vect2s &size, int attr) const;
//! Устанавливает атрибуты для линии клеток.
bool set_grid_line_attributes(const Vect2s &start_pos, const Vect2s &end_pos, const Vect2s &size, int attr);
//! Очищает атрибуты для линии клеток.
bool drop_grid_line_attributes(const Vect2s &start_pos, const Vect2s &end_pos, const Vect2s &size, int attr);
//! Возвращает true, если в линии на сетке есть хотя бы одна ячейка с атрибутами attr.
bool check_grid_line_attributes(const Vect2s &start_pos, const Vect2s &end_pos, const Vect2s &size, int attr) const;
bool is_walkable(const Vect2s &center_pos, const Vect2s &size, bool ignore_personages = false) const;
//! Устанавливает атрибуты attr для всех клеток сетки.
bool set_grid_attributes(int attr);
//! Очищает атрибуты attr для всех клеток сетки.
bool drop_grid_attributes(int attr);
sGridCell *get_cell(const Vect2s &cell_pos);
const sGridCell *get_cell(const Vect2s &cell_pos) const;
// Восстанавливает параметры клетки (сейчас - делает ее непроходимой
// с нулевой высотой)
bool restore_grid_cell(const Vect2s cell_pos);
void cycle_coords(int &x, int &y) const;
void set_redraw_mode(int md) const {
_redraw_mode = md;
}
int get_redraw_mode() const {
return _redraw_mode;
}
void set_grid_size(int xs, int ys);
void scale_grid(int sx, int sy, int csx, int csy);
void resize_grid(int sx, int sy);
int get_grid_sx() const {
return _GSX;
}
int get_grid_sy() const {
return _GSY;
}
const sGridCell *get_grid() const {
return _grid;
}
int get_cell_sx() const {
return _cellSX;
}
int get_cell_sy() const {
return _cellSY;
}
void set_cell_size(int csx, int csy) {
_cellSX = csx;
_cellSY = csy;
}
void clear_grid();
// rotateAndScaling
void rotate_and_scale(float XA, float YA, float ZA, float kX, float kY, float kZ);
float get_focus() const {
return _focus;
}
void set_focus(float focus) {
_focus = focus;
}
void set_R(const float r);
float get_R() const {
return _m_fR;
}
float get_x_angle() const {
return _xAngle;
}
float get_y_angle() const {
return _yAngle;
}
float get_z_angle() const {
return _zAngle;
}
inline void set_scr_size(int xs, int ys) {
_scrSize.x = xs;
_scrSize.y = ys;
}
const Vect2i &get_scr_size() const {
return _scrSize;
}
// getScrSizeX
int get_scr_sx() const {
return _scrSize.x;
}
// getScrSizeY
int get_scr_sy() const {
return _scrSize.y;
}
void set_scr_center(int xc, int yc) {
_scrCenter.x = xc;
_scrCenter.y = yc;
}
const Vect2i &get_scr_center() const {
return _scrCenter;
}
int get_scr_center_x() const {
return _scrCenter.x;
}
int get_scr_center_y() const {
return _scrCenter.y;
}
const Vect2i screen_center_limit_x() const;
const Vect2i screen_center_limit_y() const;
const Vect2i &get_scr_offset() const {
return _scrOffset;
}
void set_scr_offset(const Vect2i &offs) {
_scrOffset = offs;
}
const Vect2i &get_scr_center_initial() const {
return _scrCenterInitial;
}
void set_scr_center_initial(const Vect2i &v) {
_scrCenterInitial = v;
}
void move_scr_center(int dxc, int dyc);
float scrolling_phase_x() const;
float scrolling_phase_y() const;
const Vect3f scr2plane(const Vect2s &scrPoint) const;
const Vect3f rscr2plane(const Vect2s &rscrPoint) const;
const Vect3f scr2plane_camera_coord(const Vect2s &scrPoint) const;
const Vect3f rscr2plane_camera_coord(const Vect2s &scrPoint) const;
const Vect2s plane2scr(const Vect3f &plnPoint) const;
const Vect2s plane2rscr(const Vect3f &plnPoint) const;
const Vect3f rscr2camera_coord(const Vect2s &rScrPoint, float z) const;
const Vect2s camera_coord2rscr(const Vect3f &coord) const;
const Vect2s rscr2scr(const Vect2s &v) const;
const Vect2s scr2rscr(const Vect2s &v) const;
const Vect2s camera_coord2scr(const Vect3f &coord) const;
const Vect3f scr2global(const Vect2s &vScrPoint, float zInCameraCoord) const;
const Vect3f rscr2global(const Vect2s rScrPoint, const float zInCameraCoord) const;
const Vect3f global2camera_coord(const Vect3f &glCoord) const;
const Vect3f camera_coord2global(const Vect3f &v) const;
const Vect2s global2scr(const Vect3f &glCoord) const;
const Vect2s global2rscr(const Vect3f &glCoord) const;
//обрезание отрезка по плоскости отсечения камеры
//возвращает false, если отрезок вообеще невидим
bool line_cutting(Vect3f &b, Vect3f &e) const;
//! Обрезка линиии, так чтобы она за сетку не выходила.
/**
Все координаты - сеточные, т.е. индексы ячеек сетки.
*/
bool clip_grid_line(Vect2s &v0, Vect2s &v1) const;
// getKScale
float get_scale(const Vect3f &glCoord) const;
const Vect3f &get_grid_center() const {
return _gridCenter;
}
void set_grid_center(const Vect3f &v) {
_gridCenter = v;
}
const sGridCell *get_cell(float X, float Y) const;
const Vect2s get_cell_index(float X, float Y, bool grid_crop = true) const;
const Vect2s get_cell_index(const Vect3f &v, bool grid_crop = true) const;
const Vect3f get_cell_coords(int x_idx, int y_idx) const;
const Vect3f get_cell_coords(const Vect2s &idxs) const;
void reset_all_select();
//принимает глобальные координаты
bool select_cell(int x, int y);
//принимает глобальные координаты
bool deselect_cell(int x, int y);
void load_script(const xml::tag *p);
bool save_script(Common::WriteStream &fh, int indent = 0) const;
//! Загрузка данных из сэйва.
bool load_data(Common::SeekableReadStream &fh, int save_version);
//! Запись данных в сэйв.
bool save_data(Common::WriteStream &fh) const;
//! Инициализация данных, вызывается при старте и перезапуске игры.
bool init();
bool draw_grid() const;
bool draw_cell(int x, int y, int z, int penWidth, uint32 color) const;
//! Установка текущего режима камеры.
/**
Если объект нулевой, то берется объект по-умолчанию.
*/
bool set_mode(const qdCameraMode &mode, qdGameObjectAnimated *object = NULL);
//! Возвращает true, если в данный момент можно менять режим камеры.
bool can_change_mode() const;
//! Установка объекта, за которым камера следит по умолчанию.
void set_default_object(qdGameObjectAnimated *p) {
_default_object = p;
}
//! Обсчет логики (движения камеры и т.д), параметр - время в секундах.
bool quant(float dt);
//! Возвращает false, если вся рабочая область камеры находится за пределами экрана.
/**
Параметр - дополнительное смещение центра рабочей области камеры.
*/
bool is_visible(const Vect2i &center_offs = Vect2i(0, 0)) const;
void set_cycle(bool cx, bool cy) {
_cycle_x = cx;
_cycle_y = cy;
}
void dump_grid(const char *file_name) const;
//! Параметры функции масштабирования
float scale_pow() const {
return _scale_pow;
}
void set_scale_pow(float sp) {
_scale_pow = sp;
}
float scale_z_offset() const {
return _scale_z_offset;
};
void set_scale_z_offset(float szo) {
_scale_z_offset = szo;
};
bool need_perspective_correction() const {
return (fabs(_scale_pow - 1) > 0.001 || fabs(_scale_z_offset) > 0.001);
}
//! Возвращает режим работы камеры по умолчанию.
const qdCameraMode &default_mode() const {
return _default_mode;
}
//! Установка режима работы камеры по умолчанию.
void set_default_mode(const qdCameraMode &mode) {
_default_mode = mode;
}
static qdCamera *current_camera() {
return _current_camera;
}
static void set_current_camera(qdCamera *p) {
_current_camera = p;
}
MATRIX3D const &get_view_matrix() const {
return _m_cam;
}
private:
MATRIX3D _m_cam;
float _m_fR;
float _xAngle, _yAngle, _zAngle;
int _GSX, _GSY;
sGridCell *_grid;
bool _cycle_x;
bool _cycle_y;
int _cellSX, _cellSY;
float _focus;//! расстояние до ближней плоскости отсечения
// расстояние от центра рабочей области до
// верхнего левого угла окна
Vect2i _scrCenter;
// начальное расстояние от центра рабочей области до
// верхнего левого угла окна
Vect2i _scrCenterInitial;
// размер рабочей области
Vect2i _scrSize;
// смещение рабочей области
Vect2i _scrOffset;
// Координаты центра сетки
Vect3f _gridCenter;
mutable int _redraw_mode;
//! Текущий режим работы камеры.
qdCameraMode _current_mode;
//! Время в секуднах с установки текущего режима.
float _current_mode_work_time;
bool _current_mode_switch;
//! Объект, за которым следит камера.
qdGameObjectAnimated *_current_object;
//! Объект, за которым камера следит по умолчанию.
qdGameObjectAnimated *_default_object;
//! Режим работы камеры по умолчанию.
qdCameraMode _default_mode;
//! Параметры функции ускоренного масштабирования из get_scale()
float _scale_pow;
float _scale_z_offset;
static qdCamera *_current_camera;
static const float _NEAR_PLANE; //ближная плоскость отсечения
static const float _FAR_PLANE; //дальняя
enum {
clLEFT = 1,
clRIGHT = 2,
clBOTTOM = 4,
clTOP = 8
};
inline int clip_out_code(const Vect2s &v) const {
int code = 0;
if (v.y >= _GSY)
code |= clTOP;
else if (v.y < 0)
code |= clBOTTOM;
if (v.x >= _GSX)
code |= clRIGHT;
else if (v.x < 0)
code |= clLEFT;
return code;
}
void clip_center_coords(int &x, int &y) const;
};
inline Vect3f To3D(const Vect2f &v) {
return Vect3f(v.x, v.y, 0);
}
} // namespace QDEngine
#endif // QDENGINE_QDCORE_QD_CAMERA_H

View File

@@ -0,0 +1,133 @@
/* 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/stream.h"
#include "qdengine/qd_fwd.h"
#include "qdengine/parser/xml_tag_buffer.h"
#include "qdengine/parser/qdscr_parser.h"
#include "qdengine/qdcore/qd_camera_mode.h"
namespace QDEngine {
qdCameraMode::qdCameraMode() : _camera_mode(MODE_UNASSIGNED),
_work_time(0.0f),
_scrolling_speed(100.0f),
_scrolling_distance(100),
_smooth_switch(false),
_center_offset(0, 0) {
}
bool qdCameraMode::load_script(const xml::tag *p) {
for (xml::tag::subtag_iterator it = p->subtags_begin(); it != p->subtags_end(); ++it) {
xml::tag_buffer buf(*it);
switch (it->ID()) {
case QDSCR_TYPE:
set_camera_mode(camera_mode_t(buf.get_int()));
break;
case QDSCR_TIME:
set_work_time(buf.get_float());
break;
case QDSCR_CAMERA_SCROLLING_SPEED:
set_scrolling_speed(buf.get_float());
break;
case QDSCR_CAMERA_SCROLLING_DIST:
set_scrolling_distance(buf.get_int());
break;
case QDSCR_CAMERA_SMOOTH_SWITCH:
_smooth_switch = (buf.get_int()) ? true : false;
break;
case QDSCR_CAMERA_SCREEN_CENTER:
buf > _center_offset.x > _center_offset.y;
break;
}
}
return true;
}
bool qdCameraMode::save_script(Common::WriteStream &fh, int indent) const {
for (int i = 0; i < indent; i++) {
fh.writeString("\t");
}
fh.writeString(Common::String::format("<camera_mode type=\"%d\"", (int)camera_mode()));
fh.writeString(Common::String::format(" scrolling_speed=\"%f\"", _scrolling_speed));
fh.writeString(Common::String::format(" scrolling_dist=\"%d\"", _scrolling_distance));
if (_center_offset.x || _center_offset.y) {
fh.writeString(Common::String::format(" camera_screen_center=\"%d %d\"", _center_offset.x, _center_offset.y));
}
if (has_work_time()) {
fh.writeString(Common::String::format(" time=\"%f\"", work_time()));
}
if (smooth_switch()) {
fh.writeString(" smooth_switch=\"1\"");
}
fh.writeString("/>\r\n");
return true;
}
bool qdCameraMode::load_data(Common::SeekableReadStream &fh, int save_version) {
debugC(4, kDebugSave, " qdCameraMode::load_data(): before: %d", (int)fh.pos());
/*int mode = */fh.readSint32LE();
_work_time = fh.readFloatLE();
_scrolling_speed = fh.readFloatLE();
_scrolling_distance = fh.readSint32LE();
_center_offset.x = fh.readSint32LE();
_center_offset.y = fh.readSint32LE();
char switch_flag = fh.readByte();
_smooth_switch = (switch_flag) ? true : false;
debugC(4, kDebugSave, " qdCameraMode::load_data(): after: %d", (int)fh.pos());
return true;
}
bool qdCameraMode::save_data(Common::WriteStream &fh) const {
debugC(4, kDebugSave, " qdCameraMode::save_data(): before: %d", (int)fh.pos());
fh.writeSint32LE((int)_camera_mode);
fh.writeFloatLE(_work_time);
fh.writeFloatLE(_scrolling_speed);
fh.writeSint32LE(_scrolling_distance);
fh.writeSint32LE(_center_offset.x);
fh.writeSint32LE(_center_offset.y);
if (_smooth_switch) {
fh.writeByte(1);
} else {
fh.writeByte(0);
}
debugC(4, kDebugSave, " qdCameraMode::save_data(): after: %d", (int)fh.pos());
return true;
}
} // namespace QDEngine

View File

@@ -0,0 +1,152 @@
/* 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/>.
*
*/
#ifndef QDENGINE_QDCORE_QD_CAMERA_MODE_H
#define QDENGINE_QDCORE_QD_CAMERA_MODE_H
#include "qdengine/xmath.h"
#include "qdengine/parser/xml_fwd.h"
namespace Common {
class SeekableReadStream;
class WriteStream;
}
namespace QDEngine {
class qdGameObjectState;
class qdGameObjectAnimated;
//! Режим работы камеры.
class qdCameraMode {
public:
qdCameraMode();
~qdCameraMode() { }
//! идентификаторы режимов камеры
enum camera_mode_t {
//! камера не выпускает объект с экрана
MODE_OBJECT_ON_SCREEN,
//! камера жестко привязана к объекту
MODE_CENTER_OBJECT,
//! камера все время центруется на объекте
MODE_FOLLOW_OBJECT,
//! когда объект подходит к краю экрана камера плавно на нем центруется
MODE_CENTER_OBJECT_WHEN_LEAVING,
//! режим не установлен
MODE_UNASSIGNED
};
void set_camera_mode(camera_mode_t mode) {
_camera_mode = mode;
}
camera_mode_t camera_mode() const {
return _camera_mode;
}
void set_work_time(float tm) {
_work_time = tm;
}
float work_time() const {
return _work_time;
}
bool has_work_time() const {
return _work_time > 0.001f;
}
void set_scrolling_speed(float v) {
_scrolling_speed = v;
}
float scrolling_speed() const {
return _scrolling_speed;
}
void set_scrolling_distance(int dist) {
_scrolling_distance = dist;
}
int scrolling_distance() const {
return _scrolling_distance;
}
bool smooth_switch() const {
return _smooth_switch;
}
void set_smooth_switch(bool v) {
_smooth_switch = v;
}
const Vect2i &center_offset() const {
return _center_offset;
}
void set_center_offset(const Vect2i &offs) {
_center_offset = offs;
}
bool load_script(const xml::tag *p);
bool save_script(Common::WriteStream &fh, int indent = 0) const;
//! Загрузка данных из сэйва.
bool load_data(Common::SeekableReadStream &fh, int save_version);
//! Запись данных в сэйв.
bool save_data(Common::WriteStream &fh) const;
private:
//! Идентификатор режима.
camera_mode_t _camera_mode;
//! Время работы режима (в секундах).
/**
По истечении этого времени камера переключается
на активного персонажа (если он есть).
Если это время нулевое, то сменить режим можно будет
только в принудительном порядке из триггера.
*/
float _work_time;
//! Скорость, с которой камера скроллируется (в пикселах в секунду).
float _scrolling_speed;
//! Расстояние от центра объекта до края экрана, при котором включается скроллинг.
/**
В режимах, когда камера не дает объекту выйти за пределы экрана или
центруется на объекте когда он близко к краю экрана.
Задается в пикселах.
*/
int _scrolling_distance;
//! Смещение центра экрана в пикселах.
/**
Позволяет задавать режим, когда камера, центруясь на объекте,
держит его в определенной точке экрана.
*/
Vect2i _center_offset;
//! Плавное переключение в режим, если true.
bool _smooth_switch;
};
} // namespace QDEngine
#endif // QDENGINE_QDCORE_QD_CAMERA_MODE_H

View File

@@ -0,0 +1,541 @@
/* 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 "qdengine/qdengine.h"
#include "qdengine/qd_fwd.h"
#include "qdengine/parser/qdscr_parser.h"
#include "qdengine/parser/xml_tag_buffer.h"
#include "qdengine/qdcore/qd_rnd.h"
#include "qdengine/qdcore/qd_condition.h"
#include "qdengine/qdcore/qd_game_dispatcher.h"
namespace Common {
class WriteStream;
}
namespace QDEngine {
bool qdCondition::_successful_click = false;
bool qdCondition::_successful_object_click = false;
qdCondition::qdCondition() : _type(CONDITION_FALSE), _is_inversed(false), _is_in_group(false) {
}
qdCondition::qdCondition(qdCondition::ConditionType tp) : _is_inversed(false), _is_in_group(false) {
set_type(tp);
}
qdCondition::qdCondition(const qdCondition &cnd) : _type(cnd._type),
_owner(cnd._owner),
_data(cnd._data),
_objects(cnd._objects),
_is_inversed(cnd._is_inversed),
_is_in_group(false) {
}
qdCondition &qdCondition::operator = (const qdCondition &cnd) {
if (this == &cnd) return *this;
_type = cnd._type;
_owner = cnd._owner;
_data = cnd._data;
_objects = cnd._objects;
_is_inversed = cnd._is_inversed;
return *this;
}
qdCondition::~qdCondition() {
}
void qdCondition::set_type(ConditionType tp) {
_type = tp;
switch (_type) {
case CONDITION_TRUE:
case CONDITION_FALSE:
break;
case CONDITION_MOUSE_CLICK:
case CONDITION_PERSONAGE_ACTIVE:
case CONDITION_MOUSE_ZONE_CLICK:
_data.resize(1);
_objects.resize(1);
init_data(0, qdConditionData::DATA_STRING);
break;
case CONDITION_MOUSE_OBJECT_CLICK:
_data.resize(2);
_objects.resize(2);
init_data(0, qdConditionData::DATA_STRING);
init_data(1, qdConditionData::DATA_STRING);
break;
case CONDITION_OBJECT_IN_ZONE:
_data.resize(2);
_objects.resize(2);
init_data(0, qdConditionData::DATA_STRING);
init_data(1, qdConditionData::DATA_STRING);
break;
case CONDITION_PERSONAGE_WALK_DIRECTION:
case CONDITION_PERSONAGE_STATIC_DIRECTION:
_data.resize(2);
_objects.resize(1);
init_data(0, qdConditionData::DATA_STRING);
init_data(1, qdConditionData::DATA_FLOAT, 1);
break;
case CONDITION_TIMER:
_data.resize(2);
init_data(0, qdConditionData::DATA_FLOAT, 2);
init_data(1, qdConditionData::DATA_INT, 2);
break;
case CONDITION_MOUSE_DIALOG_CLICK:
break;
case CONDITION_MINIGAME_STATE:
_data.resize(2);
_objects.resize(1);
init_data(0, qdConditionData::DATA_STRING);
init_data(1, qdConditionData::DATA_STRING);
break;
case CONDITION_OBJECT_STATE:
case CONDITION_OBJECT_PREV_STATE:
_data.resize(2);
_objects.resize(2);
init_data(0, qdConditionData::DATA_STRING);
init_data(1, qdConditionData::DATA_STRING);
break;
case CONDITION_OBJECT_NOT_IN_STATE:
inverse();
_type = CONDITION_OBJECT_STATE;
_data.resize(2);
_objects.resize(2);
init_data(0, qdConditionData::DATA_STRING);
init_data(1, qdConditionData::DATA_STRING);
break;
case CONDITION_MOUSE_OBJECT_ZONE_CLICK:
_data.resize(2);
_objects.resize(2);
init_data(0, qdConditionData::DATA_STRING);
init_data(1, qdConditionData::DATA_STRING);
break;
case CONDITION_OBJECT_STATE_WAS_ACTIVATED:
_data.resize(2);
_objects.resize(2);
init_data(0, qdConditionData::DATA_STRING);
init_data(1, qdConditionData::DATA_STRING);
break;
case CONDITION_OBJECT_STATE_WAS_NOT_ACTIVATED:
inverse();
_type = CONDITION_OBJECT_STATE_WAS_ACTIVATED;
_data.resize(2);
_objects.resize(2);
init_data(0, qdConditionData::DATA_STRING);
init_data(1, qdConditionData::DATA_STRING);
break;
case CONDITION_OBJECTS_DISTANCE:
_data.resize(3);
_objects.resize(2);
init_data(0, qdConditionData::DATA_STRING);
init_data(1, qdConditionData::DATA_STRING);
init_data(2, qdConditionData::DATA_FLOAT, 1);
break;
case CONDITION_OBJECT_STATE_WAITING:
_data.resize(2);
_objects.resize(2);
init_data(0, qdConditionData::DATA_STRING);
init_data(1, qdConditionData::DATA_STRING);
break;
case CONDITION_OBJECT_STATE_ANIMATION_PHASE:
_data.resize(3);
_objects.resize(2);
init_data(0, qdConditionData::DATA_STRING);
init_data(1, qdConditionData::DATA_STRING);
init_data(2, qdConditionData::DATA_FLOAT, 2);
break;
case CONDITION_STATE_TIME_GREATER_THAN_VALUE:
_data.resize(1);
_objects.resize(1);
init_data(0, qdConditionData::DATA_FLOAT, 1);
break;
case CONDITION_STATE_TIME_GREATER_THAN_STATE_TIME:
_objects.resize(2);
break;
case CONDITION_STATE_TIME_IN_INTERVAL:
_data.resize(1);
_objects.resize(1);
init_data(0, qdConditionData::DATA_FLOAT, 2);
break;
case CONDITION_COUNTER_GREATER_THAN_VALUE:
case CONDITION_COUNTER_LESS_THAN_VALUE:
_data.resize(1);
_objects.resize(1);
init_data(0, qdConditionData::DATA_INT, 1);
break;
case CONDITION_COUNTER_GREATER_THAN_COUNTER:
_objects.resize(2);
break;
case CONDITION_COUNTER_IN_INTERVAL:
_data.resize(1);
_objects.resize(1);
init_data(0, qdConditionData::DATA_INT, 2);
break;
case CONDITION_OBJECT_ON_PERSONAGE_WAY:
_data.resize(1);
_objects.resize(2);
init_data(0, qdConditionData::DATA_FLOAT, 1);
break;
case CONDITION_KEYPRESS:
_data.resize(1);
init_data(0, qdConditionData::DATA_INT, 1);
break;
case CONDITION_ANY_PERSONAGE_IN_ZONE:
_objects.resize(1);
break;
case CONDITION_MOUSE_RIGHT_CLICK:
_objects.resize(1);
break;
case CONDITION_MOUSE_RIGHT_OBJECT_CLICK:
_objects.resize(2);
break;
case CONDITION_MOUSE_RIGHT_ZONE_CLICK:
_objects.resize(1);
break;
case CONDITION_MOUSE_RIGHT_OBJECT_ZONE_CLICK:
_objects.resize(2);
break;
case CONDITION_OBJECT_HIDDEN:
_objects.resize(1);
break;
case CONDITION_MOUSE_HOVER:
_objects.resize(1);
break;
case CONDITION_MOUSE_OBJECT_HOVER:
_objects.resize(2);
break;
case CONDITION_MOUSE_HOVER_ZONE:
_objects.resize(1);
break;
case CONDITION_MOUSE_OBJECT_HOVER_ZONE:
_objects.resize(2);
break;
case CONDITION_MOUSE_CLICK_FAILED:
case CONDITION_MOUSE_OBJECT_CLICK_FAILED:
case CONDITION_MOUSE_CLICK_EVENT:
case CONDITION_MOUSE_RIGHT_CLICK_EVENT:
break;
case CONDITION_MOUSE_OBJECT_CLICK_EVENT:
case CONDITION_MOUSE_RIGHT_OBJECT_CLICK_EVENT:
case CONDITION_MOUSE_STATE_PHRASE_CLICK:
_objects.resize(1);
break;
case CONDITION_OBJECT_IS_CLOSER:
_objects.resize(3);
break;
case CONDITION_ANIMATED_OBJECT_IDLE_GREATER_THAN_VALUE:
_objects.resize(1);
_data.resize(1);
init_data(0, qdConditionData::DATA_INT, 1);
break;
case CONDITION_ANIMATED_OBJECTS_INTERSECTIONAL_BOUNDS:
_objects.resize(2);
break;
}
}
bool qdCondition::put_value(int idx, const char *str) {
assert(idx >= 0 && idx < (int)_data.size());
return _data[idx].put_string(str);
}
bool qdCondition::put_value(int idx, int val, int val_index) {
assert(idx >= 0 && idx < (int)_data.size());
return _data[idx].put_int(val, val_index);
}
bool qdCondition::put_value(int idx, float val, int val_index) {
assert(idx >= 0 && idx < (int)_data.size());
return _data[idx].put_float(val, val_index);
}
bool qdCondition::get_value(int idx, const char *&str) const {
assert(idx >= 0 && idx < (int)_data.size());
if (_data[idx].get_string()) {
str = _data[idx].get_string();
return true;
}
return false;
}
bool qdCondition::get_value(int idx, int &val, int val_index) const {
assert(idx >= 0 && idx < (int)_data.size());
val = _data[idx].get_int(val_index);
return true;
}
bool qdCondition::get_value(int idx, float &val, int val_index) const {
assert(idx >= 0 && idx < (int)_data.size());
val = _data[idx].get_float(val_index);
return true;
}
bool qdCondition::load_script(const xml::tag *p) {
int data_idx = 0;
for (xml::tag::subtag_iterator it = p->subtags_begin(); it != p->subtags_end(); ++it) {
switch (it->ID()) {
case QDSCR_CONDITION_DATA_INT:
case QDSCR_CONDITION_DATA_FLOAT:
case QDSCR_CONDITION_DATA_STRING:
if (data_idx < (int)_data.size())
_data[data_idx++].load_script(&*it);
break;
case QDSCR_CONDITION_INVERSE:
if (xml::tag_buffer(*it).get_int())
inverse(true);
else
inverse(false);
break;
case QDSCR_CONDITION_OBJECT:
if (const xml::tag *tp = it->search_subtag(QDSCR_ID)) {
int object_idx = xml::tag_buffer(*tp).get_int();
if (object_idx >= 0 && object_idx < (int)_objects.size())
_objects[object_idx].load_script(&*it);
}
break;
}
}
return true;
}
bool qdCondition::save_script(Common::WriteStream &fh, int indent) const {
for (int i = 0; i < indent; i++) {
fh.writeString("\t");
}
if (debugChannelSet(-1, kDebugLog)) {
fh.writeString(Common::String::format("<condition type=\"%s\"", type2str(_type)));
} else {
fh.writeString(Common::String::format("<condition type=\"%d\"", _type));
}
if (is_inversed()) {
fh.writeString(" condition_inverse=\"1\"");
}
fh.writeString(">\r\n");
for (auto &it : _data) {
it.save_script(fh, indent + 1);
}
for (uint i = 0; i < _objects.size(); i++) {
if (_objects[i].object())
_objects[i].save_script(fh, indent + 1, i);
}
for (int i = 0; i < indent; i++) {
fh.writeString("\t");
}
fh.writeString("</condition>\r\n");
return true;
}
void qdCondition::quant(float dt) {
debugC(9, kDebugQuant, "qdCondition::quant(%f)", dt);
if (_type == CONDITION_TIMER) {
float period, timer;
if (!get_value(TIMER_PERIOD, period, 0)) return;
if (!get_value(TIMER_PERIOD, timer, 1)) return;
timer += dt;
put_value(TIMER_PERIOD, timer, 1);
if (timer >= period) {
debugC(3, kDebugQuant, "qdCondition::quant() timer >= period");
timer -= period;
put_value(TIMER_PERIOD, timer, 1);
int rnd;
if (!get_value(TIMER_RND, rnd)) return;
int state = 1;
if (rnd && qd_rnd(100 - rnd))
state = 0;
put_value(TIMER_RND, state, 1);
} else
put_value(TIMER_RND, 0, 1);
}
}
bool qdCondition::load_data(Common::SeekableReadStream &fh, int save_version) {
debugC(5, kDebugSave, " qdCondition::load_data(): before: %d", (int)fh.pos());
if (_type == CONDITION_TIMER) {
int state;
float timer;
timer = fh.readFloatLE();
state = fh.readSint32LE();
if (!put_value(TIMER_PERIOD, timer, 1)) return false;
if (!put_value(TIMER_RND, state, 1)) return false;
}
debugC(5, kDebugSave, " qdCondition::load_data(): after: %d", (int)fh.pos());
return true;
}
bool qdCondition::save_data(Common::WriteStream &fh) const {
debugC(5, kDebugSave, " qdCondition::save_data(): before: %d", (int)fh.pos());
if (_type == CONDITION_TIMER) {
float timer;
if (!get_value(TIMER_PERIOD, timer, 1)) {
return false;
}
int state;
if (!get_value(TIMER_RND, state, 1)) {
return false;
}
fh.writeFloatLE(timer);
fh.writeSint32LE(state);
}
debugC(5, kDebugSave, " qdCondition::save_data(): after: %d", (int)fh.pos());
return true;
}
bool qdCondition::check() {
bool result = false;
if (qdGameDispatcher *dp = qdGameDispatcher::get_dispatcher()) {
if (dp->check_condition(this))
result = !_is_inversed;
else
result = _is_inversed;
}
if (result) {
if (is_click_condition())
_successful_click = true;
else if (is_object_click_condition())
_successful_object_click = true;
}
return result;
}
bool qdCondition::put_object(int idx, qdNamedObject *obj) {
assert(idx >= 0 && idx < (int)_objects.size());
_objects[idx].set_object(obj);
return true;
}
const qdNamedObject *qdCondition::get_object(int idx) {
if (idx >= 0 && idx < (int)_objects.size()) {
if (!_objects[idx].object())
_objects[idx].find_object();
return _objects[idx].object();
}
return NULL;
}
bool qdCondition::init() {
if (_type == CONDITION_TIMER) {
if (!put_value(TIMER_PERIOD, 0.0f, 1)) return false;
if (!put_value(TIMER_RND, 0, 1)) return false;
}
return true;
}
const char *types[] = {
"CONDITION_TRUE",
"CONDITION_FALSE",
"CONDITION_MOUSE_CLICK",
"CONDITION_MOUSE_OBJECT_CLICK",
"CONDITION_OBJECT_IN_ZONE",
"CONDITION_PERSONAGE_WALK_DIRECTION",
"CONDITION_PERSONAGE_STATIC_DIRECTION",
"CONDITION_TIMER",
"CONDITION_MOUSE_DIALOG_CLICK",
"CONDITION_MINIGAME_STATE",
"CONDITION_OBJECT_STATE",
"CONDITION_MOUSE_ZONE_CLICK",
"CONDITION_MOUSE_OBJECT_ZONE_CLICK",
"CONDITION_OBJECT_STATE_WAS_ACTIVATED",
"CONDITION_OBJECT_STATE_WAS_NOT_ACTIVATED",
"CONDITION_OBJECT_NOT_IN_STATE",
"CONDITION_OBJECTS_DISTANCE",
"CONDITION_PERSONAGE_ACTIVE",
"CONDITION_OBJECT_STATE_WAITING",
"CONDITION_OBJECT_STATE_ANIMATION_PHASE",
"CONDITION_OBJECT_PREV_STATE",
"CONDITION_STATE_TIME_GREATER_THAN_VALUE",
"CONDITION_STATE_TIME_GREATER_THAN_STATE_TIME",
"CONDITION_STATE_TIME_IN_INTERVAL",
"CONDITION_COUNTER_GREATER_THAN_VALUE",
"CONDITION_COUNTER_LESS_THAN_VALUE",
"CONDITION_COUNTER_GREATER_THAN_COUNTER",
"CONDITION_COUNTER_IN_INTERVAL",
"CONDITION_OBJECT_ON_PERSONAGE_WAY",
"CONDITION_KEYPRESS",
"CONDITION_ANY_PERSONAGE_IN_ZONE",
"CONDITION_MOUSE_RIGHT_CLICK",
"CONDITION_MOUSE_RIGHT_OBJECT_CLICK",
"CONDITION_MOUSE_RIGHT_ZONE_CLICK",
"CONDITION_MOUSE_RIGHT_OBJECT_ZONE_CLICK",
"CONDITION_OBJECT_HIDDEN",
"CONDITION_MOUSE_HOVER",
"CONDITION_MOUSE_OBJECT_HOVER",
"CONDITION_MOUSE_HOVER_ZONE",
"CONDITION_MOUSE_OBJECT_HOVER_ZONE",
"CONDITION_MOUSE_CLICK_FAILED",
"CONDITION_MOUSE_OBJECT_CLICK_FAILED",
"CONDITION_MOUSE_CLICK_EVENT",
"CONDITION_MOUSE_OBJECT_CLICK_EVENT",
"CONDITION_MOUSE_RIGHT_CLICK_EVENT",
"CONDITION_MOUSE_RIGHT_OBJECT_CLICK_EVENT",
"CONDITION_MOUSE_STATE_PHRASE_CLICK",
"CONDITION_OBJECT_IS_CLOSER",
"CONDITION_ANIMATED_OBJECT_IDLE_GREATER_THAN_VALUE",
"CONDITION_ANIMATED_OBJECTS_INTERSECTIONAL_BOUNDS",
};
const char *qdCondition::type2str(uint type) {
if (type >= ARRAYSIZE(types))
return "???";
return types[type];
}
} // namespace QDEngine

View File

@@ -0,0 +1,598 @@
/* 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/>.
*
*/
#ifndef QDENGINE_QDCORE_QD_CONDITION_H
#define QDENGINE_QDCORE_QD_CONDITION_H
#include "qdengine/parser/xml_fwd.h"
#include "qdengine/qdcore/qd_named_object.h"
#include "qdengine/qdcore/qd_condition_data.h"
#include "qdengine/qdcore/qd_condition_object_reference.h"
namespace Common {
class WriteStream;
}
namespace QDEngine {
//! Условие.
/**
Структура данных по типам условий.
Индексы для доступа к объектам (если они используются в условии)
совпадают с индексами для доступа к данным об именах объектов.
CONDITION_TRUE
CONDITION_FALSE
Данные: отсутствуют
CONDITION_MOUSE_CLICK
Данные:
string
Объекты:
qdGameObject
Индескы данных:
OBJECT_NAME
Имя объекта, по которому надо кликнуть.
Если пустое - проверяется клик по объекту, которому принадлежит
состояние, в котором выставлено это условие.
CONDITION_MOUSE_OBJECT_CLICK
Данные:
string, string
Объекты:
qdGameObject, qdGameObject
Индескы данных:
OBJECT_NAME, MOUSE_OBJECT_NAME
Имя объекта, по которому надо кликнуть
и имя объекта, который должен быть на мыши.
CONDITION_OBJECT_IN_ZONE
Данные:
string, string
Объекты:
qdGameObject, qdGridZone
Индескы данных:
OBJECT_NAME, ZONE_NAME
Имя объекта и имя зоны на сетке,
в которую он должен попасть.
CONDITION_PERSONAGE_WALK_DIRECTION
CONDITION_PERSONAGE_STATIC_DIRECTION
Данные:
string, float[1]
Объекты:
qdGameObjectMoving
Индескы данных:
PERSONAGE_NAME, DIRECTION_ANGLE
Имя персонажа и угол направления в радианах.
Угол задается так же, как и направления в походках
(т.е. от направления направо против часовой стрелки)
CONDITION_TIMER
Данные:
float[2], int[2]
Индескы данных:
TIMER_PERIOD, TIMER_RND
Период и текущее время таймера в секундах,
вероятность выполнения условия по истечении времени
таймера в процентах и текущее состояние условия.
CONDITION_MOUSE_DIALOG_CLICK
Данные: отсутствуют
CONDITION_MINIGAME_STATE
Данные:
string, string
Объекты:
qdMiniGame
Индескы данных:
MINIGAME_NAME, MINIGAME_STATE_NAME
Имя мини-игры и имя состояния, в котором она должна находиться
для выполнения условия.
CONDITION_OBJECT_STATE
Данные:
string, string
Объекты:
qdGameObjectAnimated, qdGameObjectState
Индескы данных:
OBJECT_NAME, OBJECT_STATE_NAME
Имя объекта и имя состояния, в котором он должен находиться
для выполнения условия.
CONDITION_MOUSE_ZONE_CLICK
Данные:
string
Объекты:
qdGridZone
Индескы данных:
CLICK_ZONE_NAME
Имя зоны на сетке,
в которую надо кликнуть.
CONDITION_MOUSE_OBJECT_ZONE_CLICK
Данные:
string, string
Объекты:
qdGridZone, qdGameObject
Индескы данных:
CLICK_ZONE_NAME, MOUSE_OBJECT_NAME
Имя зоны на сетке, в которую надо кликнуть
и имя объекта, который должен быть на мыши.
CONDITION_OBJECT_STATE_WAS_ACTIVATED
CONDITION_OBJECT_STATE_WAS_NOT_ACTIVATED
Данные:
string, string
Объекты:
qdGameObjectAnimated, qdGameObjectState
Индескы данных:
OBJECT_NAME, OBJECT_STATE_NAME
Имя объекта и имя состояния.
CONDITION_OBJECT_NOT_IN_STATE
см. CONDITION_OBJECT_STATE
CONDITION_OBJECTS_DISTANCE
Данные:
string, string, float
Объекты:
qdGameObject, qdGameObject
Индескы данных:
OBJECT_NAME, OBJECT2_NAME, OBJECTS_DISTANCE
Имена объектов и расстояние по сетке.
CONDITION_PERSONAGE_ACTIVE
Данные:
string
Объекты:
qdGameObjectMoving
Индескы данных:
PERSONAGE_NAME
Имя персонажа, который должен быть активным
для выполнения условия.
CONDITION_OBJECT_STATE_WAITING
Данные:
string, string
Объекты:
qdGameObjectAnimated, qdGameObjectState
Индескы данных:
OBJECT_NAME, OBJECT_STATE_NAME
Имя объекта и имя состояния.
CONDITION_OBJECT_STATE_ANIMATION_PHASE
Данные:
string, string, float[2]
Объекты:
qdGameObjectAnimated, qdGameObjectState
Индескы данных:
OBJECT_NAME, OBJECT_STATE_NAME, ANIMATION_PHASE
Имя объекта, имя состояния и диапазон фазы анимации.
CONDITION_OBJECT_PREV_STATE
Данные:
string, string
Объекты:
qdGameObjectAnimated, qdGameObjectState
Индескы данных:
OBJECT_NAME, OBJECT_STATE_NAME
Имя объекта и имя состояния.
CONDITION_STATE_TIME_GREATER_THAN_VALUE
Данные:
float
Время работы состояния.
Объекты:
qdGameObjectState
CONDITION_STATE_TIME_GREATER_THAN_STATE_TIME
Данные: отсутствуют
Объекты:
qdGameObjectState, qdGameObjectState
Состояния, время работы которых сравнивается.
CONDITION_STATE_TIME_IN_INTERVAL
Данные:
float[2]
Интервал, в котором должно находиться ремя работы состояния.
Объекты:
qdGameObjectState
CONDITION_COUNTER_GREATER_THAN_VALUE
CONDITION_COUNTER_LESS_THAN_VALUE
Данные:
int
Число, с которым сравнивается значение счетчика.
Объекты:
qdCounter
CONDITION_COUNTER_GREATER_THAN_COUNTER
Данные: отсутствуют
Объекты:
qdCounter, qdCounter
Счетчики, значения которых сравнивается.
CONDITION_COUNTER_IN_INTERVAL
Данные:
int[2]
Интервал, в котором должно находиться значение счетчика.
Объекты:
qdCounter
CONDITION_OBJECT_ON_PERSONAGE_WAY
Данные:
float[1]
Максимальное расстояние между объектам и персонажем по сетке.
Объекты:
qdGameObjectMoving, qdGameObject
CONDITION_KEYPRESS
Данные:
int[1]
vkey кнопки.
CONDITION_ANY_PERSONAGE_IN_ZONE
Объекты:
qdGridZone
CONDITION_MOUSE_RIGHT_CLICK
Объекты:
qdGameObject
Индескы данных:
OBJECT_NAME
Объект, по которому надо кликнуть.
Если нулевой - проверяется клик по объекту, которому принадлежит
состояние, в котором выставлено это условие.
CONDITION_MOUSE_RIGHT_OBJECT_CLICK
Объекты:
qdGameObject, qdGameObject
Индескы данных:
OBJECT_NAME, MOUSE_OBJECT_NAME
Объект, по которому надо кликнуть
и объект, который должен быть на мыши.
CONDITION_MOUSE_RIGHT_ZONE_CLICK
Объекты:
qdGridZone
Индескы данных:
CLICK_ZONE_NAME
Зона на сетке,
в которую надо кликнуть.
CONDITION_MOUSE_RIGHT_OBJECT_ZONE_CLICK
Объекты:
qdGridZone, qdGameObject
Индескы данных:
CLICK_ZONE_NAME, MOUSE_OBJECT_NAME
Зона на сетке, в которую надо кликнуть
и объект, который должен быть на мыши.
CONDITION_OBJECT_HIDDEN
Объекты:
qdGameObject
Объект, который должен быть скрыт
CONDITION_MOUSE_HOVER
Объекты:
qdGameObject
Объект, на который наводится мышь
CONDITION_MOUSE_OBJECT_HOVER
Объекты:
qdGameObject, qdGameObject
Объект, на который наводится мышь,
объект который на мыши
CONDITION_MOUSE_HOVER_ZONE
Объекты:
qdGridZone
Зона, на которую наводится мышь
CONDITION_MOUSE_OBJECT_HOVER_ZONE
Объекты:
qdGridZone, qdGameObject
Зона, на которую наводится мышь,
объект который на мыши
CONDITION_MOUSE_CLICK_FAILED
CONDITION_MOUSE_OBJECT_CLICK_FAILED
CONDITION_MOUSE_CLICK_EVENT
//CONDITION_MOUSE_OBJECT_CLICK_EVENT
CONDITION_MOUSE_RIGHT_CLICK_EVENT
//CONDITION_MOUSE_RIGHT_OBJECT_CLICK_EVENT
Данных нет.
CONDITION_MOUSE_OBJECT_CLICK_EVENT
CONDITION_MOUSE_RIGHT_OBJECT_CLICK_EVENT
Объекты:
qdGameObject
Объект, которым надо кликнуть для выполнения условия.
Если нулевй, то достаточно клика любым объектом.
CONDITION_MOUSE_STATE_PHRASE_CLICK
Объекты:
qdGameObjecState
Состояние, по фразе которого надо кликнуть.
CONDITION_OBJECT_IS_CLOSER
Объекты:
qdGameObject, qdGameObject, qdGameObject
Расстояние от первого объекта до второго должно быть меньше,
чем расстояние от первого до третьего.
CONDITION_ANIMATED_OBJECT_IDLE_GREATER_THAN_VALUE
Объекты:
qdGameObjectAnimated, int
Анимированный объект, который простаивает, и порог времени,
который должен быть превышен временем ожидания заданного объекта.
CONDITION_ANIMATED_OBJECTS_INTERSECTIONAL_BOUNDS
Объекты:
qdGameObjectAnimated, qdGameObjectAnimated
Два объекта, баунды которых пероверяются на пересечение.
*/
class qdCondition {
public:
//! Типы условий.
enum ConditionType {
//! всегда true
CONDITION_TRUE,
//! всегда false
CONDITION_FALSE,
//! клик мышью по объекту
CONDITION_MOUSE_CLICK,
//! клик объектом на мыши по объекту
CONDITION_MOUSE_OBJECT_CLICK,
//! персонаж или объект в определенном месте сетки
CONDITION_OBJECT_IN_ZONE,
//! персонаж движется в определенном направлении
CONDITION_PERSONAGE_WALK_DIRECTION, // 5
//! персонаж стоит и ориентирован в определенном направлении
CONDITION_PERSONAGE_STATIC_DIRECTION,
//! условие выпоняется с некоторой периодичностью
CONDITION_TIMER,
//! клик мышью по фразе в диалоге
CONDITION_MOUSE_DIALOG_CLICK,
//! мини-игра находится в определенном состоянии
CONDITION_MINIGAME_STATE,
//! объект находится в определенном состоянии
CONDITION_OBJECT_STATE, // 10
//! клик мышью по зоне на сетке сцены
CONDITION_MOUSE_ZONE_CLICK,
//! клик объектом по зоне на сетке сцены
CONDITION_MOUSE_OBJECT_ZONE_CLICK,
//! состояние объекта активировалось
CONDITION_OBJECT_STATE_WAS_ACTIVATED,
//! состояние объекта не активировалось
CONDITION_OBJECT_STATE_WAS_NOT_ACTIVATED,
//! объект не находится в определенном состоянии
CONDITION_OBJECT_NOT_IN_STATE, // 15
//! расстояние по сетке между двумя объектами меньше заданного
CONDITION_OBJECTS_DISTANCE,
//! персонаж активен
CONDITION_PERSONAGE_ACTIVE,
//! состояние объекта ждет активации
CONDITION_OBJECT_STATE_WAITING,
//! состояние в определенной фазе анимации
CONDITION_OBJECT_STATE_ANIMATION_PHASE,
//! предыдущее состояние объекта
CONDITION_OBJECT_PREV_STATE, // 20
//! время работы состояния больше значения
CONDITION_STATE_TIME_GREATER_THAN_VALUE,
//! время работы одного состояния больше времени работы другого
CONDITION_STATE_TIME_GREATER_THAN_STATE_TIME,
//! время работы состояния в интервале
CONDITION_STATE_TIME_IN_INTERVAL,
//! значние счетчика больше числа
CONDITION_COUNTER_GREATER_THAN_VALUE,
//! значние счетчика меньше числа
CONDITION_COUNTER_LESS_THAN_VALUE, // 25
//! значние счетчика больше значения другого счетчика
CONDITION_COUNTER_GREATER_THAN_COUNTER,
//! значние счетчика в интервале
CONDITION_COUNTER_IN_INTERVAL,
//! объект на пути персонажа
CONDITION_OBJECT_ON_PERSONAGE_WAY,
//! кнопка нажата
CONDITION_KEYPRESS,
//! в зоне хотя бы один персонаж
CONDITION_ANY_PERSONAGE_IN_ZONE, // 30
//! правый клик мыши по объекту
CONDITION_MOUSE_RIGHT_CLICK,
//! правый клик объектом на мыши по объекту
CONDITION_MOUSE_RIGHT_OBJECT_CLICK,
//! правый клик мышью по зоне на сетке сцены
CONDITION_MOUSE_RIGHT_ZONE_CLICK,
//! правый клик объектом по зоне на сетке сцены
CONDITION_MOUSE_RIGHT_OBJECT_ZONE_CLICK,
//! объект скрыт
CONDITION_OBJECT_HIDDEN, // 35
//! мышь наведена на объект
CONDITION_MOUSE_HOVER,
//! мышь наведена на объект, на мыши объект
CONDITION_MOUSE_OBJECT_HOVER,
//! мышь наведена на зону
CONDITION_MOUSE_HOVER_ZONE,
//! мышь наведена на зону, на мыши объект
CONDITION_MOUSE_OBJECT_HOVER_ZONE,
//! неудачный клик мыши
CONDITION_MOUSE_CLICK_FAILED, // 40
//! неудачный клик объектом на мыши
CONDITION_MOUSE_OBJECT_CLICK_FAILED,
//! был клик мыши
CONDITION_MOUSE_CLICK_EVENT,
//! был клик объектом на мыши
CONDITION_MOUSE_OBJECT_CLICK_EVENT,
//! был клик правой кнопкой мыши
CONDITION_MOUSE_RIGHT_CLICK_EVENT,
//! был клик правой кнопкой объектом на мыши
CONDITION_MOUSE_RIGHT_OBJECT_CLICK_EVENT, // 45
//! клик по фразе определённого состояния
CONDITION_MOUSE_STATE_PHRASE_CLICK,
//! расстояние до одного объекта меньше чем до другого
CONDITION_OBJECT_IS_CLOSER,
//! время простоя больше заданного порога
CONDITION_ANIMATED_OBJECT_IDLE_GREATER_THAN_VALUE,
//! баунды двух объектов пересекаются
CONDITION_ANIMATED_OBJECTS_INTERSECTIONAL_BOUNDS,
};
//! Индексы для обращения к данным в условиях.
enum ConditionDataIndex {
OBJECT_NAME = 0,
MOUSE_OBJECT_NAME = 1,
ZONE_NAME = 1,
DIRECTION_ANGLE = 1,
PERSONAGE_NAME = 0,
TIMER_PERIOD = 0,
TIMER_RND = 1,
MINIGAME_NAME = 0,
MINIGAME_STATE_NAME = 1,
OBJECT_STATE_NAME = 1,
CLICK_ZONE_NAME = 0,
OBJECT2_NAME = 1,
OBJECTS_DISTANCE = 2,
ANIMATION_PHASE = 2,
STATE_TIME = 2
};
qdCondition();
qdCondition(ConditionType tp);
qdCondition(const qdCondition &cnd);
~qdCondition();
qdCondition &operator = (const qdCondition &cnd);
ConditionType type() const {
return _type;
}
void set_type(ConditionType tp);
static const char *type2str(uint type);
bool put_value(int idx, const char *str);
bool is_click_condition() const {
return (_type == CONDITION_MOUSE_CLICK || _type == CONDITION_MOUSE_DIALOG_CLICK || _type == CONDITION_MOUSE_ZONE_CLICK);
}
bool is_object_click_condition() const {
return (_type == CONDITION_MOUSE_OBJECT_CLICK || _type == CONDITION_MOUSE_OBJECT_ZONE_CLICK);
}
// val_index - индекс, по которому пропишется значение
// в qdConditionDataInt::_data или qdConditionDataFloat::_data
bool put_value(int idx, int val, int val_index = 0);
bool put_value(int idx, float val, int val_index = 0);
bool get_value(int idx, const char *&str) const;
bool get_value(int idx, int &val, int val_index = 0) const;
bool get_value(int idx, float &val, int val_index = 0) const;
bool put_object(int idx, qdNamedObject *obj);
const qdNamedObject *get_object(int idx) ;
const qdNamedObject *owner() const {
return _owner;
}
void set_owner(const qdNamedObject *p) {
_owner = p;
}
bool load_script(const xml::tag *p);
bool save_script(Common::WriteStream &fh, int indent = 0) const;
void quant(float dt);
//! Загрузка данных из сэйва.
bool load_data(Common::SeekableReadStream &fh, int save_version);
//! Запись данных в сэйв.
bool save_data(Common::WriteStream &fh) const;
//! Инициализация условия, вызывается при старте и перезапуске игры.
bool init();
bool is_inversed() const {
return _is_inversed;
}
void inverse(bool inverse_mode = true) {
_is_inversed = inverse_mode;
}
bool check();
bool is_in_group() const {
return _is_in_group;
}
void add_group_reference() {
_is_in_group = true;
}
void remove_group_reference() {
_is_in_group = false;
}
static bool successful_click() {
return _successful_click;
}
static bool successful_object_click() {
return _successful_object_click;
}
static void clear_successful_clicks() {
_successful_click = _successful_object_click = false;
}
private:
ConditionType _type;
const qdNamedObject *_owner = nullptr;
typedef Std::vector<qdConditionData> data_container_t;
data_container_t _data;
typedef Std::vector<qdConditionObjectReference> objects_container_t;
objects_container_t _objects;
bool _is_inversed;
bool _is_in_group;
static bool _successful_click;
static bool _successful_object_click;
bool init_data(int data_index, qdConditionData::data_t data_type, int data_size = 0) {
assert(data_index >= 0 && data_index < (int)_data.size());
_data[data_index].set_type(data_type);
if (data_size)
_data[data_index].alloc_data(data_size);
return true;
}
};
} // namespace QDEngine
#endif // QDENGINE_QDCORE_QD_CONDITION_H

View File

@@ -0,0 +1,127 @@
/* 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 "qdengine/qd_fwd.h"
#include "qdengine/parser/xml_tag_buffer.h"
#include "qdengine/parser/qdscr_parser.h"
#include "qdengine/qdcore/qd_condition_data.h"
namespace QDEngine {
qdConditionData::qdConditionData() : _type(DATA_STRING) {
}
qdConditionData::qdConditionData(data_t data_type, int data_size) : _type(data_type) {
if (data_size)
alloc_data(data_size);
}
qdConditionData::qdConditionData(const qdConditionData &data) : _type(data._type),
_data(data._data) {
}
qdConditionData::~qdConditionData() {
}
qdConditionData &qdConditionData::operator = (const qdConditionData &data) {
if (this == &data) return *this;
_type = data._type;
_data = data._data;
return *this;
}
bool qdConditionData::alloc_data(int size) {
switch (_type) {
case DATA_INT:
size *= sizeof(int);
break;
case DATA_FLOAT:
size *= sizeof(float);
break;
case DATA_STRING:
size++;
break;
}
if ((int)_data.size() < size)
_data.resize(size);
return true;
}
bool qdConditionData::load_script(const xml::tag *p) {
switch (_type) {
case DATA_INT: {
xml::tag_buffer buf(*p);
for (int i = 0; i < p->data_size(); i++)
put_int(buf.get_int(), i);
}
break;
case DATA_FLOAT: {
xml::tag_buffer buf(*p);
for (int i = 0; i < p->data_size(); i++)
put_float(buf.get_float(), i);
}
break;
case DATA_STRING:
put_string(p->data());
break;
}
return true;
}
bool qdConditionData::save_script(Common::WriteStream &fh, int indent) const {
for (int i = 0; i < indent; i++) {
fh.writeString("\t");
}
switch (_type) {
case DATA_INT:
fh.writeString(Common::String::format("<condition_data_int>%lu", _data.size() / sizeof(int32)));
for (uint i = 0; i < _data.size() / sizeof(int32); i++) {
fh.writeString(Common::String::format(" %d", get_int(i)));
}
fh.writeString("</condition_data_int>\r\n");
break;
case DATA_FLOAT:
fh.writeString(Common::String::format("<condition_data_float>%lu", _data.size() / sizeof(float)));
for (uint i = 0; i < _data.size() / sizeof(float); i++) {
fh.writeString(Common::String::format(" %f", get_float(i)));
}
fh.writeString("</condition_data_float>\r\n");
break;
case DATA_STRING:
fh.writeString("<condition_data_string>");
if (!_data.empty()) {
fh.writeString(Common::String::format("%s", qdscr_XML_string(&*_data.begin())));
}
fh.writeString("</condition_data_string>\r\n");
break;
}
return true;
}
} // namespace QDEngine

View File

@@ -0,0 +1,107 @@
/* 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/>.
*
*/
#ifndef QDENGINE_QDCORE_QD_CONDITION_DATA_H
#define QDENGINE_QDCORE_QD_CONDITION_DATA_H
#include "common/std/vector.h"
#include "qdengine/parser/xml_fwd.h"
namespace QDEngine {
class qdConditionData {
public:
enum data_t {
DATA_INT,
DATA_FLOAT,
DATA_STRING
};
qdConditionData();
qdConditionData(data_t data_type, int data_size = 0);
qdConditionData(const qdConditionData &data);
~qdConditionData();
qdConditionData &operator = (const qdConditionData &data);
data_t type() const {
return _type;
}
void set_type(data_t tp) {
_type = tp;
}
int get_int(int index = 0) const {
return reinterpret_cast<const int32 *>(&*_data.begin())[index];
}
bool put_int(int value, int index = 0) {
if (static_cast<int>(_data.size()) >= static_cast<int>((index - 1) * sizeof(int32))) {
reinterpret_cast<int32 *>(&*_data.begin())[index] = value;
return true;
}
return false;
}
float get_float(int index = 0) const {
return reinterpret_cast<const float *>(&*_data.begin())[index];
}
bool put_float(float value, int index = 0) {
if (static_cast<int>(_data.size()) >=
static_cast<int>((index - 1) * sizeof(float))) {
reinterpret_cast<float *>(&*_data.begin())[index] = value;
return true;
}
return false;
}
const char *get_string() const {
if (!_data.empty())
return &*_data.begin();
else
return NULL;
}
bool put_string(const char *str) {
if (alloc_data(strlen(str) + 1)) {
Common::strlcpy(&*_data.begin(), str, _data.size());
return true;
}
return false;
}
bool load_script(const xml::tag *p);
bool save_script(Common::WriteStream &fh, int indent = 0) const;
bool alloc_data(int size);
private:
data_t _type;
Std::vector<char> _data;
};
} // namespace QDEngine
#endif // QDENGINE_QDCORE_QD_CONDITION_DATA_H

View File

@@ -0,0 +1,107 @@
/* 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 "qdengine/qd_fwd.h"
#include "qdengine/parser/qdscr_parser.h"
#include "qdengine/parser/xml_tag_buffer.h"
#include "qdengine/qdcore/qd_condition_group.h"
namespace QDEngine {
qdConditionGroup::qdConditionGroup(conditions_mode_t md) : _conditions_mode(md) {
}
qdConditionGroup::qdConditionGroup(const qdConditionGroup &cg) : _conditions_mode(cg._conditions_mode), _conditions(cg._conditions) {
}
qdConditionGroup::~qdConditionGroup() {
}
qdConditionGroup &qdConditionGroup::operator = (const qdConditionGroup &cg) {
if (this == &cg) return *this;
_conditions_mode = cg._conditions_mode;
_conditions = cg._conditions;
return *this;
}
bool qdConditionGroup::add_condition(int condition_id) {
conditions_container_t::iterator it = Common::find(_conditions.begin(), _conditions.end(), condition_id);
if (it != _conditions.end())
return false;
_conditions.push_back(condition_id);
return true;
}
bool qdConditionGroup::remove_condition(int condition_id) {
for (conditions_container_t::iterator it = _conditions.begin(); it != _conditions.end(); ++it) {
if (*it > condition_id)
(*it)--;
}
conditions_container_t::iterator it1 = Common::find(_conditions.begin(), _conditions.end(), condition_id);
if (it1 != _conditions.end())
return false;
_conditions.erase(it1);
return true;
}
bool qdConditionGroup::load_script(const xml::tag *p) {
_conditions.reserve(p->data_size());
if (const xml::tag *tp = p->search_subtag(QDSCR_TYPE))
_conditions_mode = conditions_mode_t(xml::tag_buffer(*tp).get_int());
xml::tag_buffer buf(*p);
for (int i = 0; i < p->data_size(); i++)
_conditions.push_back(buf.get_int());
return true;
}
bool qdConditionGroup::save_script(Common::WriteStream &fh, int indent) const {
for (int i = 0; i < indent; i++) {
fh.writeString("\t");
}
fh.writeString("<condition_group");
if (debugChannelSet(-1, kDebugLog))
fh.writeString(Common::String::format(" type=\"%s\"", _conditions_mode == CONDITIONS_AND ? "CONDITIONS_AND" : "CONDITIONS_OR"));
else
fh.writeString(Common::String::format(" type=\"%d\"", (int)_conditions_mode));
fh.writeString(">");
fh.writeString(Common::String::format("%u", _conditions.size()));
for (auto &it : _conditions) {
fh.writeString(Common::String::format(" %d", it));
}
fh.writeString("</condition_group>\r\n");
return true;
}
} // namespace QDEngine

View File

@@ -0,0 +1,84 @@
/* 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/>.
*
*/
#ifndef QDENGINE_QDCORE_QD_CONDITION_GROUP_H
#define QDENGINE_QDCORE_QD_CONDITION_GROUP_H
#include "qdengine/parser/xml_fwd.h"
namespace QDEngine {
//! Группа условий.
class qdConditionGroup {
public:
//! Режим проверки условий.
enum conditions_mode_t {
//! "И" - должны выполниться все условия.
CONDITIONS_AND,
//! "ИЛИ" - достаточно выполнения одного из условий.
CONDITIONS_OR
};
explicit qdConditionGroup(conditions_mode_t md = CONDITIONS_AND);
qdConditionGroup(const qdConditionGroup &cg);
qdConditionGroup &operator = (const qdConditionGroup &cg);
~qdConditionGroup();
typedef Std::vector<int> conditions_container_t;
typedef conditions_container_t::const_iterator conditions_iterator_t;
conditions_iterator_t conditions_begin() const {
return _conditions.begin();
}
conditions_iterator_t conditions_end() const {
return _conditions.end();
}
int conditions_size() const {
return _conditions.size();
}
conditions_mode_t conditions_mode() const {
return _conditions_mode;
}
void set_conditions_mode(conditions_mode_t mode) {
_conditions_mode = mode;
}
bool add_condition(int condition_id);
bool remove_condition(int condition_id);
bool load_script(const xml::tag *p);
bool save_script(Common::WriteStream &fh, int indent = 0) const;
private:
conditions_mode_t _conditions_mode;
conditions_container_t _conditions;
};
} // namespace QDEngine
#endif // QDENGINE_QDCORE_QD_CONDITION_GROUP_H

View File

@@ -0,0 +1,91 @@
/* 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 "qdengine/qd_fwd.h"
#include "qdengine/parser/xml_tag.h"
#include "qdengine/parser/qdscr_parser.h"
#include "qdengine/qdcore/qd_game_dispatcher.h"
#include "qdengine/qdcore/qd_named_object_indexer.h"
#include "qdengine/qdcore/qd_condition_object_reference.h"
namespace QDEngine {
qdConditionObjectReference::qdConditionObjectReference() : _object(NULL) {
}
qdConditionObjectReference::qdConditionObjectReference(const qdConditionObjectReference &ref) :
_object(ref._object) {
}
qdConditionObjectReference::~qdConditionObjectReference() {
}
qdConditionObjectReference &qdConditionObjectReference::operator = (const qdConditionObjectReference &ref) {
if (this == &ref) return *this;
_object = ref._object;
return *this;
}
void qdConditionObjectReference::set_object(qdNamedObject *p) {
_object = p;
}
bool qdConditionObjectReference::find_object() {
return false;
}
bool qdConditionObjectReference::load_script(const xml::tag *p) {
for (xml::tag::subtag_iterator it = p->subtags_begin(); it != p->subtags_end(); ++it) {
switch (it->ID()) {
case QDSCR_NAMED_OBJECT: {
qdNamedObjectReference &ref = qdNamedObjectIndexer::instance().add_reference((qdNamedObject *&)_object);
ref.load_script(&*it);
}
break;
}
}
return true;
}
bool qdConditionObjectReference::save_script(Common::WriteStream &fh, int indent, int id) const {
for (int i = 0; i < indent; i++) {
fh.writeString("\t");
}
fh.writeString(Common::String::format("<condition_object ID=\"%d\">\r\n", id));
if (_object) {
qdNamedObjectReference ref(_object);
ref.save_script(fh, indent + 1);
}
for (int i = 0; i < indent; i++) {
fh.writeString("\t");
}
fh.writeString("</condition_object>\r\n");
return true;
}
} // namespace QDEngine

View File

@@ -0,0 +1,66 @@
/* 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/>.
*
*/
#ifndef QDENGINE_QDCORE_QD_CONDITION_OBJECT_REFERENCE_H
#define QDENGINE_QDCORE_QD_CONDITION_OBJECT_REFERENCE_H
#include "qdengine/parser/xml_fwd.h"
#include "qdengine/qdcore/qd_named_object_reference.h"
namespace QDEngine {
class qdNamedObject;
class qdConditionObjectReference {
public:
qdConditionObjectReference();
qdConditionObjectReference(const qdConditionObjectReference &ref);
~qdConditionObjectReference();
qdConditionObjectReference &operator = (const qdConditionObjectReference &ref);
void set_object(qdNamedObject *p);
bool find_object();
const qdNamedObject *object() const {
return _object;
}
bool is_empty() const {
if (_object) return true;
else return false;
}
//! Загрузка данных из скрипта.
bool load_script(const xml::tag *p);
//! Запись данных в скрипт.
bool save_script(Common::WriteStream &fh, int indent = 0, int id = 0) const;
private:
qdNamedObject *_object;
};
} // namespace QDEngine
#endif // QDENGINE_QDCORE_QD_CONDITION_OBJECT_REFERENCE_H

View File

@@ -0,0 +1,310 @@
/* 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/stream.h"
#include "qdengine/qd_fwd.h"
#include "qdengine/parser/xml_tag_buffer.h"
#include "qdengine/parser/qdscr_parser.h"
#include "qdengine/qdcore/qd_conditional_object.h"
#include "qdengine/qdcore/qd_game_scene.h"
namespace QDEngine {
qdConditionalObject::qdConditionalObject() : _conditions_mode(CONDITIONS_OR) {
}
qdConditionalObject::qdConditionalObject(const qdConditionalObject &obj) : qdNamedObject(obj),
_conditions_mode(obj._conditions_mode),
_conditions(obj._conditions),
_condition_groups(obj._condition_groups) {
}
qdConditionalObject::~qdConditionalObject() {
}
qdConditionalObject &qdConditionalObject::operator = (const qdConditionalObject &obj) {
if (this == &obj) return *this;
*static_cast<qdNamedObject *>(this) = obj;
_conditions_mode = obj._conditions_mode;
_conditions = obj._conditions;
_condition_groups = obj._condition_groups;
return *this;
}
int qdConditionalObject::add_condition(const qdCondition *p) {
_conditions.push_back(*p);
_conditions.back().set_owner(this);
return _conditions.size() - 1;
}
bool qdConditionalObject::update_condition(int num, const qdCondition &p) {
assert(num >= 0 && num < (int)_conditions.size());
qdCondition &cond = _conditions[num];
cond = p;
cond.set_owner(this);
return true;
}
bool qdConditionalObject::check_conditions() {
qdCondition::clear_successful_clicks();
if (!_conditions.empty()) {
switch (conditions_mode()) {
case CONDITIONS_AND:
for (conditions_container_t::iterator it = _conditions.begin(); it != _conditions.end(); ++it) {
if (!it->is_in_group()) {
if (!it->check())
return false;
}
}
for (condition_groups_container_t::iterator it = _condition_groups.begin(); it != _condition_groups.end(); ++it) {
if (!check_group_conditions(*it))
return false;
}
return true;
case CONDITIONS_OR:
for (conditions_container_t::iterator it = _conditions.begin(); it != _conditions.end(); ++it) {
if (!it->is_in_group()) {
if (it->check())
return true;
}
}
for (condition_groups_container_t::iterator it = _condition_groups.begin(); it != _condition_groups.end(); ++it) {
if (check_group_conditions(*it))
return true;
}
return false;
}
}
return true;
}
bool qdConditionalObject::remove_conditon(int idx) {
assert(idx >= 0 && idx < (int)_conditions.size());
_conditions.erase(_conditions.begin() + idx);
for (condition_groups_container_t::iterator it = _condition_groups.begin(); it != _condition_groups.end(); ++it)
it->remove_condition(idx);
return true;
}
bool qdConditionalObject::load_conditions_script(const xml::tag *p) {
int count = 0;
int gr_count = 0;
for (xml::tag::subtag_iterator it = p->subtags_begin(); it != p->subtags_end(); ++it) {
switch (it->ID()) {
case QDSCR_CONDITION:
count++;
break;
case QDSCR_CONDITION_GROUP:
gr_count++;
break;
}
}
if (count) _conditions.resize(count);
conditions_container_t::iterator ict = _conditions.begin();
if (gr_count) _condition_groups.resize(gr_count);
condition_groups_container_t::iterator igt = _condition_groups.begin();
for (xml::tag::subtag_iterator it = p->subtags_begin(); it != p->subtags_end(); ++it) {
switch (it->ID()) {
case QDSCR_CONDITION:
if (const xml::tag *tp = it->search_subtag(QDSCR_TYPE)) {
qdCondition *cp = &*ict;
cp->set_type(qdCondition::ConditionType(xml::tag_buffer(*tp).get_int()));
cp->load_script(&*it);
cp->set_owner(this);
}
++ict;
break;
case QDSCR_CONDITION_GROUP:
if (const xml::tag *tp = it->search_subtag(QDSCR_TYPE))
igt->set_conditions_mode(qdConditionGroup::conditions_mode_t(xml::tag_buffer(*tp).get_int()));
igt->load_script(&*it);
++igt;
break;
case QDSCR_CONDITIONS_MODE:
set_conditions_mode(ConditionsMode((xml::tag_buffer(*it).get_int())));
break;
}
}
for (uint i = 0; i < _conditions.size(); i++) {
if (is_condition_in_group(i))
_conditions[i].add_group_reference();
}
return true;
}
bool qdConditionalObject::save_conditions_script(Common::WriteStream &fh, int indent) const {
if (_conditions.size()) {
for (auto &it : _conditions) {
it.save_script(fh, indent + 1);
}
for (int i = 0; i <= indent; i++) {
fh.writeString("\t");
}
if (debugChannelSet(-1, kDebugLog))
fh.writeString(Common::String::format("<conditions_mode>%s</conditions_mode>\r\n", _conditions_mode == CONDITIONS_AND ? "CONDITIONS_AND" : "CONDITIONS_OR"));
else
fh.writeString(Common::String::format("<conditions_mode>%d</conditions_mode>\r\n", _conditions_mode));
}
for (auto &it : _condition_groups) {
it.save_script(fh, indent + 1);
}
return true;
}
void qdConditionalObject::conditions_quant(float dt) {
for (auto &it : _conditions) {
it.quant(dt);
}
}
bool qdConditionalObject::load_data(Common::SeekableReadStream &fh, int save_version) {
debugC(4, kDebugSave, " qdConditionalObject::load_data(): before: %d", (int)fh.pos());
if (!qdNamedObject::load_data(fh, save_version))
return false;
for (auto &it : _conditions)
it.load_data(fh, save_version);
debugC(4, kDebugSave, " qdConditionalObject::load_data(): after: %d", (int)fh.pos());
return true;
}
bool qdConditionalObject::save_data(Common::WriteStream &fh) const {
debugC(4, kDebugSave, " qdConditionalObject::save_data(): before: %d", (int)fh.pos());
if (!qdNamedObject::save_data(fh)) {
return false;
}
for (auto &it : _conditions)
it.save_data(fh);
debugC(4, kDebugSave, " qdConditionalObject::save_data(): after: %d", (int)fh.pos());
return true;
}
bool qdConditionalObject::check_group_conditions(const qdConditionGroup &gr) {
switch (gr.conditions_mode()) {
case qdConditionGroup::CONDITIONS_AND:
for (qdConditionGroup::conditions_iterator_t it = gr.conditions_begin(); it != gr.conditions_end(); ++it) {
if (!_conditions[*it].check())
return false;
}
return true;
case qdConditionGroup::CONDITIONS_OR:
for (qdConditionGroup::conditions_iterator_t it = gr.conditions_begin(); it != gr.conditions_end(); ++it) {
if (_conditions[*it].check())
return true;
}
return false;
}
return true;
}
bool qdConditionalObject::is_condition_in_group(int condition_idx) const {
for (condition_groups_container_t::const_iterator it = _condition_groups.begin(); it != _condition_groups.end(); ++it) {
if (Common::find(it->conditions_begin(), it->conditions_end(), condition_idx) != it->conditions_end())
return true;
}
return false;
}
int qdConditionalObject::add_condition_group(const qdConditionGroup *p) {
_condition_groups.push_back(*p);
return _condition_groups.size() - 1;
}
bool qdConditionalObject::update_condition_group(int num, const qdConditionGroup &p) {
assert(num >= 0 && num < (int)_condition_groups.size());
qdConditionGroup &gr = _condition_groups[num];
gr = p;
for (uint i = 0; i < _conditions.size(); i++) {
if (is_condition_in_group(i))
_conditions[i].add_group_reference();
else
_conditions[i].remove_group_reference();
}
return true;
}
bool qdConditionalObject::remove_conditon_group(int idx) {
assert(idx >= 0 && idx < (int)_condition_groups.size());
_condition_groups.erase(_condition_groups.begin() + idx);
for (uint i = 0; i < _conditions.size(); i++) {
if (is_condition_in_group(i))
_conditions[i].add_group_reference();
else
_conditions[i].remove_group_reference();
}
return true;
}
bool qdConditionalObject::init() {
bool result = true;
for (uint i = 0; i < _conditions.size(); i++) {
if (!_conditions[i].init())
result = false;
}
return result;
}
bool qdConditionalObject::trigger_can_start() const {
if (const qdGameScene *p = static_cast<const qdGameScene *>(owner(QD_NAMED_OBJECT_SCENE)))
return p->is_active();
return true;
}
} // namespace QDEngine

View File

@@ -0,0 +1,163 @@
/* 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/>.
*
*/
#ifndef QDENGINE_QDCORE_CONDITIONAL_OBJECT_H
#define QDENGINE_QDCORE_CONDITIONAL_OBJECT_H
#include "qdengine/parser/xml_fwd.h"
#include "qdengine/qdcore/qd_condition.h"
#include "qdengine/qdcore/qd_condition_group.h"
#include "qdengine/qdcore/qd_named_object.h"
namespace Common {
class WriteStream;
}
namespace QDEngine {
//! Объект с условиями.
class qdConditionalObject : public qdNamedObject {
public:
qdConditionalObject();
qdConditionalObject(const qdConditionalObject &obj);
~qdConditionalObject();
qdConditionalObject &operator = (const qdConditionalObject &obj);
//! Режим проверки условий.
enum ConditionsMode {
//! "И" - должны выполниться все условия.
CONDITIONS_AND,
//! "ИЛИ" - достаточно выполнения одного из условий.
CONDITIONS_OR
};
typedef Std::vector<qdCondition> conditions_container_t;
typedef Std::vector<qdConditionGroup> condition_groups_container_t;
//! Возвращает true, если условия выполняются.
virtual bool check_conditions();
//! Добавляет условие. Возвращает поярдковый номер,-1 в случае ошибки.
int add_condition(const qdCondition *p);
//! Обновляет условие условие с порядковым номером num.
bool update_condition(int num, const qdCondition &p);
//! Удаляет условие.
bool remove_conditon(int idx);
//! Возвращает указатель на условие.
const qdCondition *get_condition(int idx = 0) const {
return &*(_conditions.begin() + idx);
}
//! Возвращает количество условий.
int conditions_count() const {
return _conditions.size();
}
//! Добавляет группу условий. Возвращает поярдковый номер,-1 в случае ошибки.
int add_condition_group(const qdConditionGroup *p);
//! Обновляет группу условий с порядковым номером num.
bool update_condition_group(int num, const qdConditionGroup &p);
//! Удаляет группу условий.
bool remove_conditon_group(int idx);
//! Возвращает указатель на группу условий.
const qdConditionGroup *get_condition_group(int idx = 0) const {
return &*(_condition_groups.begin() + idx);
}
//! Возвращает количество групп условий.
int condition_groups_count() const {
return _condition_groups.size();
}
//! Возвращает режим проверки условий.
ConditionsMode conditions_mode() const {
return _conditions_mode;
}
//! Устанавливает режим проверки условий.
void set_conditions_mode(ConditionsMode m) {
_conditions_mode = m;
}
//! Возвращает true, если список условий не пустой.
bool has_conditions() const {
return !_conditions.empty();
}
//! Обсчет логики условий, dt - время в секундах.
void conditions_quant(float dt);
//! Инициализация объекта, вызывается при старте и перезепуске игры.
virtual bool init();
//! Загрузка данных из сэйва.
bool load_data(Common::SeekableReadStream &fh, int save_version);
//! Запись данных в сэйв.
bool save_data(Common::WriteStream &fh) const;
bool is_condition_in_group(int condition_idx) const;
//! Варианты старта триггера.
enum trigger_start_mode {
//! запуск не удался
TRIGGER_START_FAILED,
//! включить активный режим у триггера
TRIGGER_START_ACTIVATE,
//! включить ждущий режим у триггера
TRIGGER_START_WAIT
};
//! Активация триггера.
virtual trigger_start_mode trigger_start() = 0;
//! Возвращает true, если триггер может быть активирован при выполнении его внутренних условий.
/**
Объект должен принадлежать активной сцене (игровые объекты, зоны на сетке, музыка)
или не зависеть от нее (сцены, мини-игры и т.д.).
*/
virtual bool trigger_can_start() const;
protected:
//! Загрузка данных из скрипта.
bool load_conditions_script(const xml::tag *p);
//! Запись данных в скрипт.
bool save_conditions_script(Common::WriteStream &fh, int indent = 0) const;
private:
//! Логика проверки условий - И/ИЛИ.
ConditionsMode _conditions_mode;
//! Условия.
conditions_container_t _conditions;
//! Группы условий.
condition_groups_container_t _condition_groups;
bool check_group_conditions(const qdConditionGroup &gr);
};
} // namespace QDEngine
#endif // QDENGINE_QDCORE_CONDITIONAL_OBJECT_H

View File

@@ -0,0 +1,355 @@
/* 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 "qdengine/qd_fwd.h"
#include "qdengine/parser/qdscr_parser.h"
#include "qdengine/parser/xml_tag_buffer.h"
#include "qdengine/qdcore/qd_contour.h"
namespace QDEngine {
qdContour::qdContour(qdContourType tp) : _contour_type(tp),
_size(0, 0),
_mask_pos(0, 0) {
}
qdContour::qdContour(const qdContour &ct) : _contour_type(ct._contour_type),
_size(ct._size),
_contour(ct._contour),
_mask(ct._mask) {
_mask_pos = ct._mask_pos;
}
qdContour::~qdContour() {
_contour.clear();
}
qdContour &qdContour::operator = (const qdContour &ct) {
if (this == &ct) return *this;
_contour_type = ct._contour_type;
_size = ct._size;
_mask_pos = ct._mask_pos;
_mask = ct._mask;
_contour = ct._contour;
return *this;
}
void qdContour::add_contour_point(const Vect2s &pt) {
_contour.push_back(pt);
}
void qdContour::insert_contour_point(const Vect2s &pt, int insert_pos) {
if (insert_pos < (int)_contour.size()) {
if (insert_pos < 0) insert_pos = 0;
_contour.insert(_contour.begin() + insert_pos, pt);
} else {
_contour.push_back(pt);
}
}
bool qdContour::remove_contour_point(int pos) {
if (pos >= 0 && pos < (int)_contour.size()) {
_contour.erase(_contour.begin() + pos);
return true;
}
return false;
}
bool qdContour::update_contour_point(const Vect2s &pt, int pos) {
if (pos >= 0 && pos < (int)_contour.size()) {
_contour[pos] = pt;
return true;
}
return false;
}
void qdContour::createMaskOld(int x0, int y0, int x1, int y1) {
_mask.resize(_size.x * _size.y);
Common::fill(_mask.begin(), _mask.end(), 0);
Std::vector<int> intersections;
intersections.reserve(_contour.size());
Std::vector<byte>::iterator it = _mask.begin();
for (int y = y0; y <= y1; y++) {
byte *ptr = &*it;
for (uint i = 0; i < _contour.size(); i++) {
Vect2s p0 = _contour[i];
Vect2s p1 = (i < _contour.size() - 1) ? _contour[i + 1] : _contour[0];
if (p0.y != p1.y) {
if (((p0.y << 2) <= (y << 2) + 2 && (p1.y << 2) >= (y << 2) + 2) || ((p0.y << 2) >= (y << 2) + 2 && (p1.y << 2) <= (y << 2) + 2))
intersections.push_back((y - p0.y) * (p1.x - p0.x) / (p1.y - p0.y) + p0.x);
}
}
if (intersections.size() > 1) {
Common::sort(intersections.begin(), intersections.end());
for (uint i = 0; i < intersections.size() - 1; i += 2) {
int xl = intersections[i];
int xr = intersections[i + 1];
if (xr > xl) {
byte *p = ptr + xl - x0;
for (int col = xl; col <= xr; col++)
*p++ = true;
}
}
}
intersections.clear();
for (uint i = 0; i < _contour.size(); i++) {
Vect2s p0 = _contour[i];
Vect2s p1 = (i < _contour.size() - 1) ? _contour[i + 1] : _contour[0];
if (p0.y != p1.y) {
if (((p0.y << 2) <= (y << 2) - 2 && (p1.y << 2) >= (y << 2) - 2) || ((p0.y << 2) >= (y << 2) - 2 && (p1.y << 2) <= (y << 2) - 2))
intersections.push_back((y - p0.y) * (p1.x - p0.x) / (p1.y - p0.y) + p0.x);
}
}
if (intersections.size() > 1) {
Common::sort(intersections.begin(), intersections.end());
for (uint i = 0; i < intersections.size() - 1; i += 2) {
int xl = intersections[i];
int xr = intersections[i + 1];
if (xr > xl) {
byte *p = ptr + xl - x0;
for (int col = xl; col <= xr; col++)
*p++ = true;
}
}
}
intersections.clear();
it += _size.x;
}
}
bool qdContour::update_contour() {
if (_contour_type != CONTOUR_POLYGON) return false;
if (_contour.empty())
return false;
int x0 = _contour[0].x;
int x1 = _contour[0].x;
int y0 = _contour[0].y;
int y1 = _contour[0].y;
for (uint i = 0; i < _contour.size(); i++) {
if (_contour[i].x < x0) x0 = _contour[i].x;
if (_contour[i].x > x1) x1 = _contour[i].x;
if (_contour[i].y < y0) y0 = _contour[i].y;
if (_contour[i].y > y1) y1 = _contour[i].y;
}
_size = Vect2s(x1 - x0 + 1, y1 - y0 + 1);
_mask_pos = Vect2s(x0 + _size.x / 2, y0 + _size.y / 2);
if (g_engine->_gameVersion <= 20050101)
createMaskOld(x0, y0, x1, y1);
return true;
}
bool qdContour::is_inside(const Vect2s &pos) const {
switch (_contour_type) {
case CONTOUR_RECTANGLE:
if (pos.x >= -_size.x / 2 && pos.x < _size.x / 2 && pos.y >= -_size.y / 2 && pos.y < _size.y / 2)
return true;
break;
case CONTOUR_CIRCLE:
if (pos.x * pos.x + pos.y * pos.y <= _size.x * _size.x)
return true;
break;
case CONTOUR_POLYGON: {
if (g_engine->_gameVersion > 20050101) {
Vect2s p = pos;
int intersections_lt0 = 0;
int intersections_gt0 = 0;
int intersections_lt1 = 0;
int intersections_gt1 = 0;
for (uint i = 0; i < _contour.size(); i ++) {
Vect2s p0 = _contour[i];
Vect2s p1 = (i < _contour.size() - 1) ? _contour[i + 1] : _contour[0];
if (p0.y != p1.y) {
if ((p0.y < p.y && p1.y >= p.y) || (p0.y >= p.y && p1.y < p.y)) {
if (p0.x < p.x && p1.x < p.x)
intersections_lt0++;
else if (p0.x > p.x && p1.x > p.x)
intersections_gt0++;
else {
int x = (p.y - p0.y) * (p1.x - p0.x) / (p1.y - p0.y) + p0.x;
if (x == p.x)
return true;
else if (x > p.x)
intersections_gt0++;
else
intersections_lt0++;
}
}
if ((p0.y <= p.y && p1.y > p.y) || (p0.y > p.y && p1.y <= p.y)) {
if (p0.x < p.x && p1.x < p.x)
intersections_lt1++;
else if (p0.x > p.x && p1.x > p.x)
intersections_gt1++;
else {
int x = (p.y - p0.y) * (p1.x - p0.x) / (p1.y - p0.y) + p0.x;
if (x == p.x)
return true;
else if (x > p.x)
intersections_gt1++;
else
intersections_lt1++;
}
}
}
}
return ((intersections_lt0 & 1) && intersections_gt0 != 0) ||
((intersections_lt1 & 1) && intersections_gt1 != 0);
} else {
Vect2s p = pos - _mask_pos;
p.x += _size.x / 2;
p.y += _size.y / 2;
if (p.x >= 0 && p.x < _size.x && p.y >= 0 && p.y < _size.y) {
if (_mask[p.x + p.y * _size.x])
return true;
}
}
break;
}
}
return false;
}
bool qdContour::save_script(Common::WriteStream &fh, int indent) const {
if (_contour_type == CONTOUR_POLYGON) {
for (int i = 0; i < indent; i++) {
fh.writeString("\t");
}
fh.writeString(Common::String::format("<contour_polygon>%d", contour_size() * 2));
for (int j = 0; j < contour_size(); j++) {
fh.writeString(Common::String::format(" %d %d", _contour[j].x, _contour[j].y));
}
fh.writeString("</contour_polygon>\r\n");
return true;
}
if (_contour_type == CONTOUR_RECTANGLE) {
for (int i = 0; i < indent; i++) {
fh.writeString("\t");
}
fh.writeString(Common::String::format("<contour_rect>%d %d</contour_rect>\r\n", _size.x, _size.y));
return true;
}
if (_contour_type == CONTOUR_CIRCLE) {
for (int i = 0; i < indent; i++) {
fh.writeString("\t");
}
fh.writeString(Common::String::format("<contour_circle>%d</contour_circle>\r\n", _size.x));
return true;
}
return false;
}
bool qdContour::load_script(const xml::tag *p) {
int i;
Vect2s v;
xml::tag_buffer buf(*p);
switch (_contour_type) {
case CONTOUR_RECTANGLE:
buf > v.x > v.y;
_size = Vect2s(v);
return true;
case CONTOUR_CIRCLE:
_size.x = _size.y = buf.get_short();
return true;
case CONTOUR_POLYGON:
_contour.reserve(p->data_size() / 2);
for (i = 0; i < p->data_size(); i += 2) {
buf > v.x > v.y;
add_contour_point(v);
}
update_contour();
return true;
}
return false;
}
void qdContour::divide_contour(int shift) {
Std::vector<Vect2s>::iterator _itr = _contour.begin(), _end = _contour.end();
for (; _itr != _end; ++_itr) {
Vect2s &v = *_itr;
v.x >>= shift;
v.y >>= shift;
}
}
void qdContour::mult_contour(int shift) {
Std::vector<Vect2s>::iterator _itr = _contour.begin(), _end = _contour.end();
for (; _itr != _end; ++_itr) {
Vect2s &v = *_itr;
v.x <<= shift;
v.y <<= shift;
}
}
void qdContour::shift_contour(int dx, int dy) {
Std::vector<Vect2s>::iterator _itr = _contour.begin(), _end = _contour.end();
for (; _itr != _end; ++_itr) {
Vect2s &v = *_itr;
v.x += dx;
v.y += dy;
}
}
bool qdContour::can_be_closed() const {
if (_contour_type != CONTOUR_POLYGON)
return false;
return (_contour.size() > 2);
}
bool qdContour::is_contour_empty() const {
if (_contour_type == CONTOUR_POLYGON)
return (_contour.size() < 3);
return false;
}
} // namespace QDEngine

View File

@@ -0,0 +1,173 @@
/* 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/>.
*
*/
#ifndef QDENGINE_QDCORE_QD_CONTOUR_H
#define QDENGINE_QDCORE_QD_CONTOUR_H
#include "qdengine/parser/xml_fwd.h"
#include "qdengine/xmath.h"
namespace Common {
class WriteStream;
}
namespace QDEngine {
//! Контур.
/**
Используется для задания масок на статических объектах,
зон на сетке и формы элементов GUI.
*/
class qdContour {
public:
enum qdContourType {
//! Прямоугольник
CONTOUR_RECTANGLE,
//! Окружность
CONTOUR_CIRCLE,
//! Произвольный многоугольник
CONTOUR_POLYGON
};
qdContour(qdContourType tp = CONTOUR_POLYGON);
qdContour(const qdContour &ct);
virtual ~qdContour();
qdContour &operator = (const qdContour &ct);
//! Возвращает тип контура.
qdContourType contour_type() const {
return _contour_type;
}
//! Устанавливает тип контура.
void set_contour_type(qdContourType tp) {
_contour_type = tp;
}
//! Возвращает горизонтальный размер контура.
int size_x() const {
return _size.x;
}
//! Возвращает вертикальный размер контура.
int size_y() const {
return _size.y;
}
//! Возвращает true, если точка с координатами pos лежит внутри контура.
bool is_inside(const Vect2s &pos) const;
//! Запись данных в скрипт.
virtual bool save_script(Common::WriteStream &fh, int indent = 0) const;
//! Чтение данных из скрипта.
virtual bool load_script(const xml::tag *p);
//! Добавляет точку в контур.
/**
pt - координаты точки
*/
void add_contour_point(const Vect2s &pt);
//! Вставляет точку в контур.
/**
pt - координаты точки, insert_pos - номер точки, перед которой добавится новая.
*/
void insert_contour_point(const Vect2s &pt, int insert_pos = 0);
//! Удаляет точку номер pos из контура.
bool remove_contour_point(int pos);
//! Присваивает точке номер pos контура координаты pt.
bool update_contour_point(const Vect2s &pt, int pos);
//! Удаляет все точки из контура.
void clear_contour() {
_contour.clear();
}
//! Делит координаты контура на 2 в степени shift.
void divide_contour(int shift = 1);
//! Умножает координаты контура на 2 в степени shift.
void mult_contour(int shift = 1);
//! Добавляет к соответствующим координатам dx, dy.
void shift_contour(int dx, int dy);
//! Возвращает координаты точки контура номер pos.
const Vect2s &get_point(int pos) const {
return _contour[pos];
}
void createMaskOld(int x0, int y0, int x1, int y1);
const byte *maskData() const {
return &*_mask.begin();
}
//! Возвращает размеры маски.
const Vect2s &mask_size() const {
return _size;
}
//! Возвращает координаты центра маски.
const Vect2s &mask_pos() const {
return _mask_pos;
}
//! Возвращает количество точек в контуре.
int contour_size() const {
return _contour.size();
}
//! Возвращает массив точек контура.
const Std::vector<Vect2s> &get_contour() const {
return _contour;
}
bool is_mask_empty() const {
return _contour.empty();
}
bool update_contour();
// можно ли замкнуть текущий контур.
// для типов контура CONTOUR_CIRCLE и CONTOUR_RECTANGLE
// всегда возвращается false
bool can_be_closed() const;
//! Возвращает true, если контур пустой.
bool is_contour_empty() const;
private:
//! Тип контура.
qdContourType _contour_type;
//! Размеры контура.
Vect2s _size;
Vect2s _mask_pos;
Std::vector<byte> _mask;
//! Контур.
/**
Произвольный замкнутый контур. Задается для CONTOUR_POLYGON.
*/
Std::vector<Vect2s> _contour;
};
} // namespace QDEngine
#endif // QDENGINE_QDCORE_QD_CONTOUR_H

View File

@@ -0,0 +1,520 @@
/* 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 "qdengine/qd_fwd.h"
#include "qdengine/xmath.h"
#include "qdengine/parser/qdscr_parser.h"
#include "qdengine/parser/xml_tag_buffer.h"
#include "qdengine/qdcore/qd_coords_animation.h"
#include "qdengine/qdcore/qd_game_object_animated.h"
#include "qdengine/qdcore/qd_game_object_moving.h"
namespace QDEngine {
qdCoordsAnimation::qdCoordsAnimation() : _status(false),
_is_finished(false),
_type(CA_INTERPOLATE_COORDS),
_animation_phase(0.0f),
_speed(100.0f),
_start_object(NULL),
_cur_point(0) {
}
qdCoordsAnimation::qdCoordsAnimation(const qdCoordsAnimation &anm) : qdNamedObject(anm),
_status(false),
_is_finished(false),
_type(anm._type),
_animation_phase(anm._animation_phase),
_speed(anm._speed),
_start_object(anm._start_object),
_start_object_ref(anm.start_object_ref()),
_points(anm._points),
_cur_point(0) {
}
qdCoordsAnimation &qdCoordsAnimation::operator = (const qdCoordsAnimation &anm) {
qdNamedObject::operator=(anm);
_status = false;
_is_finished = false;
_type = anm._type;
_animation_phase = anm._animation_phase;
_speed = anm._speed;
// Внутри функции устанавливается и _start_object_ref
set_start_object(anm.start_object());
_points = anm._points;
_cur_point = 0;
return *this;
}
qdCoordsAnimation::~qdCoordsAnimation() {
_points.clear();
}
void qdCoordsAnimation::set_start_object(const qdGameObject *p_obj) {
_start_object = p_obj;
if (NULL != p_obj) {
qdNamedObjectReference ref(p_obj);
_start_object_ref = ref;
} else
_start_object_ref.clear();
}
void qdCoordsAnimation::calc_paths() const {
for (size_t i = 1; i < _points.size(); i++)
_points[i].calc_path(_points[i - 1]);
}
void qdCoordsAnimation::add_point(const qdCoordsAnimationPoint *p) {
_points.push_back(*p);
calc_paths();
}
void qdCoordsAnimation::insert_point(const qdCoordsAnimationPoint *p, int insert_pos) {
_points.insert(_points.begin() + insert_pos, *p);
calc_paths();
}
void qdCoordsAnimation::remove_point(int num) {
assert(0 <= num && num < (int)_points.size());
_points.erase(_points.begin() + num);
if (_cur_point >= (int)_points.size())
_cur_point = _points.size() - 1;
calc_paths();
}
void qdCoordsAnimation::clear() {
if (!_points.empty())
set_cur_point(0);
_cur_point = -1;
_points.clear();
clear_flags();
}
void qdCoordsAnimation::start() const {
_cur_point = 0;
if (!_points.empty()) {
qdGameObjectAnimated *p = object();
if (!p) return;
// Объект будет двигаться по шаблону =>
// он выходит из состояния следования
qdGameObjectMoving *obj = dynamic_cast<qdGameObjectMoving *>(p);
if (NULL != obj)
obj->set_follow_condition(qdGameObjectMoving::FOLLOW_DONE);
// Если траектория должна выполняться относительно текущего положения (или
// от центра заданного объекта), то считаем дельту
if (true == check_flag(QD_COORDS_ANM_RELATIVE_FLAG)) {
if (NULL != _start_object)
_del = _points[0].dest_pos() - _start_object->R();
else _del = _points[0].dest_pos() - p->R();
} else {
_del.x = 0;
_del.y = 0;
_del.z = 0;
};
if (check_flag(QD_COORDS_ANM_OBJECT_START_FLAG))
_start_point.set_dest_pos(p->R());
else {
_start_point.set_dest_pos(_points[0].dest_pos() - _del);
// Задана коорд. анимация с перемещением в точку и задан угол =>
// устанавливаем направление для движущегося объекта
qdGameObjectMoving *mov_obj = dynamic_cast<qdGameObjectMoving *>(obj);
if ((NULL != mov_obj) &&
(qdCoordsAnimationPoint::NO_DIRECTION != _points[0].direction_angle()))
mov_obj->set_direction(_points[0].direction_angle());
}
_points[0].calc_path(_start_point, _del);
_points[0].start();
_status = true;
p->set_pos(cur_pos());
}
_is_finished = false;
}
void qdCoordsAnimation::stop() const {
_status = false;
}
void qdCoordsAnimation::quant(float dt) const {
if (!_status || !_points.size()) return;
if (_type == CA_INTERPOLATE_COORDS) {
float path = _speed * dt;
while (_points[_cur_point].move(path)) {
if (++_cur_point >= (int)_points.size()) {
_is_finished = true;
if (!check_flag(QD_COORDS_ANM_LOOP_FLAG)) {
_cur_point --;
stop();
break;
}
_start_point.set_dest_pos(_points[_points.size() - 1].dest_pos() - _del);
// Расстояние считаем, переместившись в глобальне координаты, так как _points в глобальных
_points[0].calc_path(_start_point, _del);
_cur_point = 0;
}
_points[_cur_point].start();
}
qdGameObjectAnimated *obj = object();
if (obj) obj->set_pos(cur_pos());
} else { // _type == CA_WALK
qdGameObjectAnimated *obj = object();
if (obj->named_object_type() != QD_NAMED_OBJECT_MOVING_OBJ) return;
qdGameObjectMoving *p = static_cast<qdGameObjectMoving *>(obj);
if (p->is_in_position(_points[_cur_point].dest_pos() - _del)) {
if (++_cur_point >= (int)_points.size()) {
_is_finished = true;
if (!check_flag(QD_COORDS_ANM_LOOP_FLAG)) {
stop();
return;
}
_start_point.set_dest_pos(_points[_points.size() - 1].dest_pos() - _del);
// Расстояние считаем, переместившись в глобальне координаты
_points[0].calc_path(_start_point, _del);
_cur_point = 0;
}
_points[_cur_point].start();
}
if (!p->is_moving())
p->move(_points[_cur_point].dest_pos() - _del, _points[_cur_point].direction_angle());
}
}
void qdCoordsAnimation::load_script(const xml::tag *p) {
int v;
for (xml::tag::subtag_iterator it = p->subtags_begin(); it != p->subtags_end(); ++it) {
switch (it->ID()) {
case QDSCR_NAME:
set_name(it->data());
break;
case QDSCR_COORDS_ANIMATION_POINT: {
qdCoordsAnimationPoint fp;
fp.load_script(&*it);
add_point(&fp);
}
break;
case QDSCR_TYPE:
xml::tag_buffer(*it) > v;
set_type((qdCoordsAnimationType)v);
break;
case QDSCR_FLAG:
xml::tag_buffer(*it) > v;
set_flag(v);
break;
case QDSCR_SPEED:
xml::tag_buffer(*it) > _speed;
break;
case QDSCR_ANIMATION_PHASE:
xml::tag_buffer(*it) > _animation_phase;
break;
case QDSCR_NAMED_OBJECT:
_start_object_ref.load_script(&*it);
break;
}
}
}
bool qdCoordsAnimation::save_script(Common::WriteStream &fh, int indent) const {
for (int i = 0; i < indent; i++) {
fh.writeString("\t");
}
fh.writeString("<coords_animation");
if (name()) {
fh.writeString(Common::String::format(" name=\"%s\"", qdscr_XML_string(name())));
} else {
fh.writeString(" name=\" \"");
}
if (debugChannelSet(-1, kDebugLog))
fh.writeString(Common::String::format(" type=\"%s\"", _type == CA_INTERPOLATE_COORDS ? "CA_INTERPOLATE_COORDS" : "CA_WALK"));
else
fh.writeString(Common::String::format(" type=\"%d\"", (int)_type));
fh.writeString(Common::String::format(" speed=\"%f\"", _speed));
fh.writeString(Common::String::format(" animation_phase=\"%f\"", _animation_phase));
fh.writeString(">\r\n");
for (auto &it: _points) {
it.save_script(fh, indent + 1);
}
if (flags()) {
for (int i = 0; i <= indent; i++) {
fh.writeString("\t");
}
if (debugChannelSet(-1, kDebugLog))
fh.writeString(Common::String::format("<flag>%s</flag>\r\n", flag2str(flags()).c_str()));
else
fh.writeString(Common::String::format("<flag>%d</flag>\r\n", flags()));
}
if (NULL != _start_object) {
_start_object_ref.save_script(fh, indent + 1);
}
for (int i = 0; i < indent; i++) {
fh.writeString("\t");
}
fh.writeString("</coords_animation>\r\n");
return true;
}
Vect3f qdCoordsAnimation::cur_pos() const {
// В cur_pos() учитываем дельту относительных координат
qdCoordsAnimationPoint p0;
if (0 != _cur_point) {
p0 = _points[_cur_point - 1];
p0.set_dest_pos(p0.dest_pos() - _del); // Перемещаемся в относительные координаты
} else p0 = _start_point;
qdCoordsAnimationPoint p1 = _points[_cur_point];
p1.set_dest_pos(p1.dest_pos() - _del); // Перемещаемся в относительные координаты
Vect3f v = p0.dest_pos() + p1.passed_path() * (p1.dest_pos() - p0.dest_pos());
return v;
}
qdGameObjectAnimated *qdCoordsAnimation::object() const {
if (owner() && owner()->named_object_type() == QD_NAMED_OBJECT_OBJ_STATE) {
qdNamedObject *p = owner()->owner();
if (p && (p->named_object_type() == QD_NAMED_OBJECT_ANIMATED_OBJ || p->named_object_type() == QD_NAMED_OBJECT_MOVING_OBJ))
return static_cast<qdGameObjectAnimated *>(p);
}
return 0;
}
bool qdCoordsAnimation::set_cur_point(int point_num) const {
if (!_points.size()) return false;
start();
if (_type == CA_WALK) {
if (!object() || object()->named_object_type() != QD_NAMED_OBJECT_MOVING_OBJ) return false;
qdGameObjectMoving *p = static_cast<qdGameObjectMoving *>(object());
p->set_pos(cur_pos());
p->drop_flag(QD_OBJ_MOVING_FLAG);
p->move(_points[_cur_point].dest_pos());
p->skip_movement();
for (int i = 0; i < point_num; i++) {
if (++_cur_point >= (int)_points.size()) {
if (!check_flag(QD_COORDS_ANM_LOOP_FLAG)) {
stop();
return false;
}
_start_point.set_dest_pos(_points[_points.size() - 1].dest_pos());
_points[0].calc_path(_start_point);
_cur_point = 0;
}
_points[_cur_point].start();
if (!p->move(_points[_cur_point].dest_pos())) return false;
if (!p->skip_movement()) return false;
}
return true;
} else {
if (!object() || _speed < 0.01f) return false;
qdGameObjectAnimated *p = object();
p->get_animation()->set_time_rel(animation_phase());
p->set_pos(cur_pos());
for (int i = 0; i < point_num; i++) {
if (++_cur_point >= (int)_points.size()) {
if (!check_flag(QD_COORDS_ANM_LOOP_FLAG)) {
stop();
if (p->named_object_type() == QD_NAMED_OBJECT_MOVING_OBJ)
static_cast<qdGameObjectMoving *>(p)->adjust_z();
return false;
}
_start_point.set_dest_pos(_points[_points.size() - 1].dest_pos());
_points[0].calc_path(_start_point);
_cur_point = 0;
}
_points[_cur_point].start();
p->set_pos(_points[_cur_point].dest_pos());
p->get_animation()->advance_time(_points[_cur_point].path_length() / _speed);
}
if (p->named_object_type() == QD_NAMED_OBJECT_MOVING_OBJ)
static_cast<qdGameObjectMoving * >(p)->adjust_z();
return true;
}
/*
const float dt = 0.005f;
while(_cur_point < point_num || _points[_cur_point].passed_path() < 0.99f){
quant(dt);
p->quant(dt);
}
return true;
}
*/
return false;
}
bool qdCoordsAnimation::reset_cur_point() const {
return set_cur_point(_cur_point);
}
void qdCoordsAnimation::set_time_rel(float tm) {
assert(tm >= 0.0f && tm <= 1.0f);
if (!_points.size()) return;
int pt = tm * float(_points.size() - 1);
set_cur_point(pt);
}
bool qdCoordsAnimation::load_data(Common::SeekableReadStream &fh, int save_version) {
debugC(4, kDebugSave, " qdCoordsAnimation::load_data(): before: %d", (int)fh.pos());
if (!qdNamedObject::load_data(fh, save_version)) return false;
int v;
v = fh.readSint32LE();
_status = (v) ? true : false;
v = fh.readSint32LE();
_is_finished = (v) ? true : false;
_cur_point = fh.readSint32LE();
v = fh.readSint32LE();
if ((int)_points.size() != v) return false;
if (save_version >= 101) {
_del.x = fh.readFloatLE();
_del.y = fh.readFloatLE();
_del.z = fh.readFloatLE();
} else {
_del = Vect3f(0, 0, 0);
}
for (auto &it : _points)
it.load_data(fh, save_version);
_start_point.load_data(fh, save_version);
Vect3f vec;
vec.x = fh.readFloatLE();
vec.y = fh.readFloatLE();
vec.z = fh.readFloatLE();
_start_point.set_dest_pos(vec);
debugC(4, kDebugSave, " qdCoordsAnimation::load_data(): after: %d", (int)fh.pos());
return true;
}
bool qdCoordsAnimation::save_data(Common::WriteStream &fh) const {
debugC(4, kDebugSave, " qdCoordsAnimation::save_data(): before: %d", (int)fh.pos());
if (!qdNamedObject::save_data(fh)) return false;
fh.writeSint32LE(static_cast<int>(_status));
fh.writeSint32LE(static_cast<int>(_is_finished));
fh.writeSint32LE(_cur_point);
fh.writeUint32LE(_points.size());
fh.writeFloatLE(_del.x);
fh.writeFloatLE(_del.y);
fh.writeFloatLE(_del.z);
for (qdCoordsAnimationPointVector::const_iterator it = _points.begin(); it != _points.end(); ++it)
it->save_data(fh);
_start_point.save_data(fh);
fh.writeFloatLE(_start_point.dest_pos().x);
fh.writeFloatLE(_start_point.dest_pos().y);
fh.writeFloatLE(_start_point.dest_pos().z);
debugC(4, kDebugSave, " qdCoordsAnimation::save_data(): after: %d", (int)fh.pos());
return true;
}
#define defFlag(x) { x, #x }
struct FlagsList {
int f;
const char *s;
} static flagList[] = {
defFlag(QD_COORDS_ANM_OBJECT_START_FLAG),
defFlag(QD_COORDS_ANM_LOOP_FLAG),
defFlag(QD_COORDS_ANM_RELATIVE_FLAG),
};
Common::String qdCoordsAnimation::flag2str(int fl) const {
Common::String res;
for (int i = 0; i < ARRAYSIZE(flagList); i++) {
if (fl & flagList[i].f) {
if (!res.empty())
res += " | ";
res += flagList[i].s;
fl &= ~flagList[i].f;
}
}
if (fl)
res += Common::String::format(" | %x", fl);
return res;
}
} // namespace QDEngine

View File

@@ -0,0 +1,165 @@
/* 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/>.
*
*/
#ifndef QDENGINE_QDCORE_QD_COORDS_ANIMATION_H
#define QDENGINE_QDCORE_QD_COORDS_ANIMATION_H
#include "qdengine/parser/xml_fwd.h"
#include "qdengine/qdcore/qd_named_object.h"
#include "qdengine/qdcore/qd_coords_animation_point.h"
#include "qdengine/qdcore/qd_named_object_reference.h"
namespace QDEngine {
const int QD_COORDS_ANM_OBJECT_START_FLAG = 0x01;
const int QD_COORDS_ANM_LOOP_FLAG = 0x02;
const int QD_COORDS_ANM_RELATIVE_FLAG = 0x04; // Флаг перемещения траектории в текущую точку нахождения объекта
class qdCoordsAnimation : public qdNamedObject {
public:
enum qdCoordsAnimationType {
CA_INTERPOLATE_COORDS,
CA_WALK
};
qdCoordsAnimation();
qdCoordsAnimation(const qdCoordsAnimation &anm);
~qdCoordsAnimation();
qdCoordsAnimation &operator = (const qdCoordsAnimation &anm);
int named_object_type() const {
return QD_NAMED_OBJECT_COORDS_ANIMATION;
}
float speed() const {
return _speed;
}
void set_speed(float sp) {
_speed = sp;
}
const qdGameObject *start_object() const {
return _start_object;
}
void set_start_object(const qdGameObject *p_obj);
const qdNamedObjectReference &start_object_ref() const {
return _start_object_ref;
}
bool is_playing() const {
return _status;
}
bool is_finished() const {
return _is_finished;
}
qdCoordsAnimationType type() const {
return _type;
}
void set_type(qdCoordsAnimationType tp) {
_type = tp;
}
float animation_phase() const {
return _animation_phase;
}
void set_animation_phase(float p) {
_animation_phase = p;
}
void start() const;
void stop() const;
void quant(float dt) const;
bool reset_cur_point() const;
bool set_cur_point(int point_num) const;
int get_cur_point() const {
return _cur_point;
}
void set_time_rel(float tm);
void add_point(const qdCoordsAnimationPoint *p);
void insert_point(const qdCoordsAnimationPoint *p, int insert_pos);
void remove_point(int num);
void clear();
qdCoordsAnimationPoint *get_point(int index = 0) {
return &_points[index];
};
const qdCoordsAnimationPoint *get_point(int index = 0) const {
return &_points[index];
};
int size() const {
return _points.size();
}
void load_script(const xml::tag *p);
bool save_script(Common::WriteStream &fh, int indent = 0) const;
bool is_empty() const {
if (_points.empty()) return true;
return false;
}
//! Загрузка данных из сэйва.
bool load_data(Common::SeekableReadStream &fh, int save_version);
//! Запись данных в сэйв.
bool save_data(Common::WriteStream &fh) const;
Common::String flag2str(int flags) const;
private:
qdCoordsAnimationType _type;
qdCoordsAnimationPointVector _points;
float _animation_phase;
float _speed;
// Для относительного перемещения персонажа (QD_COORDS_ANM_RELATIVE_FLAG):
// объект, относительно коориднат которого будет работать траектория движения (если не задано, то работаем
// относительно координат объекта-владельца координатной анимации)
const qdGameObject *_start_object;
qdNamedObjectReference _start_object_ref;
mutable bool _status;
mutable bool _is_finished;
mutable qdCoordsAnimationPoint _start_point;
mutable int _cur_point;
Vect3f cur_pos() const;
class qdGameObjectAnimated *object() const;
void calc_paths() const;
// Дельта (вектор) смещения координат анимации
mutable Vect3f _del;
};
//typedef Std::list<qdCoordsAnimation*> qdCoordsAnimationList;
} // namespace QDEngine
#endif // QDENGINE_QDCORE_QD_COORDS_ANIMATION_H

View File

@@ -0,0 +1,93 @@
/* 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 "qdengine/qd_fwd.h"
#include "qdengine/xmath.h"
#include "qdengine/parser/xml_tag_buffer.h"
#include "qdengine/parser/qdscr_parser.h"
#include "qdengine/qdcore/qd_coords_animation_point.h"
namespace QDEngine {
const float qdCoordsAnimationPoint::NO_DIRECTION = -1.f;
qdCoordsAnimationPoint::qdCoordsAnimationPoint() : _pos(0, 0, 0),
_direction_angle(NO_DIRECTION) {
_path_length = _passed_path_length = 0.0f;
}
qdCoordsAnimationPoint::~qdCoordsAnimationPoint() {
}
void qdCoordsAnimationPoint::load_script(const xml::tag *p) {
for (xml::tag::subtag_iterator it = p->subtags_begin(); it != p->subtags_end(); ++it) {
switch (it->ID()) {
case QDSCR_DEST_POS:
xml::tag_buffer(*it) > _pos.x > _pos.y > _pos.z;
break;
case QDSCR_OBJECT_DIRECTION:
xml::tag_buffer(*it) > _direction_angle;
break;
}
}
}
bool qdCoordsAnimationPoint::save_script(Common::WriteStream &fh, int indent) const {
for (int i = 0; i < indent; i++) {
fh.writeString("\t");
}
fh.writeString("<coords_animation_point");
fh.writeString(Common::String::format(" dest_pos=\"%f %f %f\"", _pos.x, _pos.y, _pos.z));
if (_direction_angle >= 0.0f) {
fh.writeString(Common::String::format(" object_direction=\"%f\"", _direction_angle));
}
fh.writeString("/>\r\n");
return true;
}
void qdCoordsAnimationPoint::calc_path(const qdCoordsAnimationPoint &p, const Vect3f &shift) const {
Vect3f dr = _pos - shift - p.dest_pos();
_path_length = dr.norm();
}
float qdCoordsAnimationPoint::passed_path() const {
if (_path_length < 0.01f) return 1.0f;
return _passed_path_length / _path_length;
}
bool qdCoordsAnimationPoint::load_data(Common::SeekableReadStream &fh, int save_version) {
_path_length = fh.readFloatLE();
_passed_path_length = fh.readFloatLE();
return true;
}
bool qdCoordsAnimationPoint::save_data(Common::WriteStream &fh) const {
fh.writeFloatLE(_path_length);
fh.writeFloatLE(_passed_path_length);
return true;
}
} // namespace QDEngine

View File

@@ -0,0 +1,91 @@
/* 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/>.
*
*/
#ifndef QDENGINE_QDCORE_QD_COORDS_ANIMATION_POINT_H
#define QDENGINE_QDCORE_QD_COORDS_ANIMATION_POINT_H
#include "qdengine/parser/xml_fwd.h"
namespace QDEngine {
class qdCoordsAnimationPoint {
public:
qdCoordsAnimationPoint();
~qdCoordsAnimationPoint();
static const float NO_DIRECTION;
const Vect3f &dest_pos() const {
return _pos;
}
void set_dest_pos(const Vect3f &r) {
_pos = r;
}
float direction_angle() const {
return _direction_angle;
}
void set_direction_angle(float ang) {
_direction_angle = ang;
}
void start() const {
_passed_path_length = 0.0f;
}
bool move(float &path) const {
_passed_path_length += path;
if (_passed_path_length >= _path_length) {
path = _passed_path_length - _path_length;
return true;
}
return false;
}
void calc_path(const qdCoordsAnimationPoint &p, const Vect3f &shift = Vect3f(0, 0, 0)) const;
float passed_path() const;
float path_length() const {
return _path_length;
}
void load_script(const xml::tag *p);
bool save_script(Common::WriteStream &fh, int indent = 0) const;
//! Загрузка данных из сэйва.
bool load_data(Common::SeekableReadStream &fh, int save_version);
//! Запись данных в сэйв.
bool save_data(Common::WriteStream &fh) const;
private:
Vect3f _pos;
float _direction_angle;
mutable float _path_length;
mutable float _passed_path_length;
};
typedef Std::vector<qdCoordsAnimationPoint> qdCoordsAnimationPointVector;
} // namespace QDEngine
#endif // QDENGINE_QDCORE_QD_COORDS_ANIMATION_POINT_H

View File

@@ -0,0 +1,308 @@
/* 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/stream.h"
#include "qdengine/qdengine.h"
#include "qdengine/qd_fwd.h"
#include "qdengine/parser/xml_tag_buffer.h"
#include "qdengine/parser/qdscr_parser.h"
#include "qdengine/qdcore/qd_counter.h"
#include "qdengine/qdcore/qd_game_object_state.h"
namespace QDEngine {
qdCounterElement::qdCounterElement() : _state(NULL),
_last_state_status(false),
_increment_value(true) {
}
qdCounterElement::~qdCounterElement() {
}
qdCounterElement::qdCounterElement(const qdGameObjectState *p, bool inc_value) : _state(p),
_state_reference(p),
_last_state_status(false),
_increment_value(inc_value) {
}
bool qdCounterElement::init() {
// if(!_state){
_state = dynamic_cast<const qdGameObjectState *>(_state_reference.object());
if (!_state) {
debugC(3, kDebugLog, "qdCounterElement::init() failed");
return false;
}
// }
_last_state_status = false;
return true;
}
bool qdCounterElement::quant() {
if (_state) {
bool result = false;
bool status = _state->is_active();
if (status && !_last_state_status)
result = true;
_last_state_status = status;
return result;
}
return false;
}
bool qdCounterElement::load_script(const xml::tag *p) {
for (xml::tag::subtag_iterator it = p->subtags_begin(); it != p->subtags_end(); ++it) {
xml::tag_buffer buf(*it);
switch (it->ID()) {
case QDSCR_NAMED_OBJECT:
_state_reference.load_script(&*it);
break;
case QDSCR_COUNTER_INC_VALUE:
_increment_value = (xml::tag_buffer(*it).get_int()) ? true : false;
break;
}
}
return true;
}
bool qdCounterElement::save_script(Common::WriteStream &fh, int indent) const {
for (int i = 0; i < indent; i++) {
fh.writeString("\t");
}
fh.writeString("<counter_element");
if (!_increment_value) {
fh.writeString(" inc_value=\"0\"");
}
fh.writeString(">\r\n");
if (_state) {
qdNamedObjectReference ref(_state);
ref.save_script(fh, indent + 1);
}
for (int i = 0; i < indent; i++) {
fh.writeString("\t");
}
fh.writeString("</counter_element>\r\n");
return true;
}
bool qdCounterElement::load_data(Common::SeekableReadStream &fh, int save_version) {
char v;
v = fh.readByte();
_last_state_status = v;
return true;
}
bool qdCounterElement::save_data(Common::WriteStream &fh) const {
fh.writeByte(_last_state_status);
return true;
}
qdCounter::qdCounter() : _value(0),
_value_limit(0) {
}
qdCounter::~qdCounter() {
}
void qdCounter::set_value(int value) {
_value = value;
if (_value_limit > 0 && _value >= _value_limit)
_value = 0;
if (check_flag(POSITIVE_VALUE) && _value < 0)
_value = 0;
}
void qdCounter::add_value(int value_delta) {
_value += value_delta;
if (_value_limit > 0 && _value >= _value_limit)
_value = 0;
if (check_flag(POSITIVE_VALUE) && _value < 0)
_value = 0;
}
bool qdCounter::add_element(const qdGameObjectState *p, bool inc_value) {
element_container_t::const_iterator it = Common::find(_elements.begin(), _elements.end(), p);
if (it != _elements.end())
return false;
_elements.push_back(qdCounterElement(p, inc_value));
return true;
}
bool qdCounter::remove_element(const qdGameObjectState *p) {
element_container_t::iterator it = Common::find(_elements.begin(), _elements.end(), p);
if (it != _elements.end()) {
_elements.erase(it);
return true;
}
return false;
}
bool qdCounter::remove_element(int idx) {
assert(idx >= 0 && idx < (int)_elements.size());
_elements.erase(_elements.begin() + idx);
return true;
}
void qdCounter::quant() {
int value_change = 0;
for (element_container_t::iterator it = _elements.begin(); it != _elements.end(); ++it) {
if (it->quant()) {
if (it->increment_value())
value_change++;
else
value_change--;
}
}
_value += value_change;
if (_value_limit > 0 && _value >= _value_limit)
_value = 0;
if (check_flag(POSITIVE_VALUE) && _value < 0)
_value = 0;
}
bool qdCounter::load_script(const xml::tag *p) {
int num_elements = 0;
for (xml::tag::subtag_iterator it = p->subtags_begin(); it != p->subtags_end(); ++it) {
xml::tag_buffer buf(*it);
switch (it->ID()) {
case QDSCR_COUNTER_ELEMENT:
num_elements++;
break;
}
}
_elements.reserve(num_elements);
for (xml::tag::subtag_iterator it = p->subtags_begin(); it != p->subtags_end(); ++it) {
xml::tag_buffer buf(*it);
switch (it->ID()) {
case QDSCR_NAME:
set_name(it->data());
break;
case QDSCR_FLAG:
set_flag(xml::tag_buffer(*it).get_int());
break;
case QDSCR_COUNTER_ELEMENT: {
qdCounterElement el;
el.load_script(&*it);
_elements.push_back(el);
}
break;
case QDSCR_COUNTER_LIMIT:
xml::tag_buffer(*it) > _value_limit;
break;
}
}
return true;
}
bool qdCounter::save_script(Common::WriteStream &fh, int indent) const {
for (int i = 0; i < indent; i++) {
fh.writeString("\t");
}
fh.writeString("<counter");
fh.writeString(Common::String::format(" name=\"%s\"", qdscr_XML_string(name())));
if (_value_limit) {
fh.writeString(Common::String::format(" limit=\"%d\"", _value_limit));
}
if (flags()) {
fh.writeString(Common::String::format(" flags=\"%d\"", flags()));
}
fh.writeString(">\r\n");
for (auto &it : _elements) {
it.save_script(fh, indent + 1);
}
for (int i = 0; i < indent; i++) {
fh.writeString("\t");
}
fh.writeString("</counter>\r\n");
return true;
}
bool qdCounter::load_data(Common::SeekableReadStream &fh, int save_version) {
debugC(3, kDebugSave, " qdCounter::load_data(): before: %d", (int)fh.pos());
int sz;
_value = fh.readSint32LE();
sz = fh.readSint32LE();
if (sz != (int)_elements.size())
return false;
for (auto &it : _elements)
it.load_data(fh, save_version);
debugC(3, kDebugSave, " qdCounter::load_data(): after: %d", (int)fh.pos());
return true;
}
bool qdCounter::save_data(Common::WriteStream &fh) const {
debugC(3, kDebugSave, " qdCounter::save_data(): before: %d", (int)fh.pos());
fh.writeSint32LE(_value);
fh.writeSint32LE(_elements.size());
for (auto &it : _elements) {
it.save_data(fh);
}
debugC(3, kDebugSave, " qdCounter::save_data(): after: %d", (int)fh.pos());
return true;
}
void qdCounter::init() {
for (element_container_t::iterator it = _elements.begin(); it != _elements.end(); ++it)
it->init();
_value = 0;
}
} // namespace QDEngine

View File

@@ -0,0 +1,141 @@
/* 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/>.
*
*/
#ifndef QDENGINE_QDCORE_QD_COUNTER_H
#define QDENGINE_QDCORE_QD_COUNTER_H
#include "qdengine/parser/xml_fwd.h"
#include "qdengine/qdcore/qd_named_object_reference.h"
namespace Common {
class WriteStream;
}
namespace QDEngine {
class qdGameObjectState;
class qdCounterElement {
public:
qdCounterElement();
~qdCounterElement();
explicit qdCounterElement(const qdGameObjectState *p, bool inc_value = true);
bool operator == (const qdGameObjectState *p) const {
return _state == p;
}
const qdGameObjectState *state() const {
return _state;
}
bool increment_value() const {
return _increment_value;
}
bool init();
bool quant();
bool load_script(const xml::tag *p);
bool save_script(Common::WriteStream &fh, int indent = 0) const;
//! Загрузка данных из сэйва.
bool load_data(Common::SeekableReadStream &fh, int save_version);
//! Запись данных в сэйв.
bool save_data(Common::WriteStream &fh) const;
private:
qdNamedObjectReference _state_reference;
const qdGameObjectState *_state;
bool _last_state_status;
bool _increment_value;
};
//! Счетчик состояний.
class qdCounter : public qdNamedObject {
public:
qdCounter();
~qdCounter();
//! Флаги счетчика.
enum {
//! принимает только положительные значения
POSITIVE_VALUE = 0x01
};
int named_object_type() const {
return QD_NAMED_OBJECT_COUNTER;
}
int value() const {
return _value;
}
void set_value(int value);
void add_value(int value_delta);
int value_limit() const {
return _value_limit;
}
void set_value_limit(int limit) {
_value_limit = limit;
}
typedef Std::vector<qdCounterElement> element_container_t;
const element_container_t &elements() const {
return _elements;
}
//! Добавление состояния. Возвращает false, если оно уже добавлено ранее.
bool add_element(const qdGameObjectState *p, bool inc_value = true);
bool remove_element(const qdGameObjectState *p);
bool remove_element(int idx);
void quant();
void init();
bool load_script(const xml::tag *p);
bool save_script(Common::WriteStream &fh, int indent = 0) const;
//! Загрузка данных из сэйва.
bool load_data(Common::SeekableReadStream &fh, int save_version);
//! Запись данных в сэйв.
bool save_data(Common::WriteStream &fh) const;
protected:
private:
//! Состояния, которые при включении изменяют значение счетчика.
element_container_t _elements;
//! Текущее значение счетчика.
int _value;
//! Предельное значение, по достижении которого счетчик скидывается в ноль.
/**
Если меньше или равно нулю - не учитывается.
*/
int _value_limit;
};
} // namespace QDEngine
#endif // QDENGINE_QDCORE_QD_COUNTER_H

View File

@@ -0,0 +1,575 @@
/* 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/>.
*
*/
// Note: Must Define D3D_OVERLOADS to get C++ version of MATRIX3D
#include "qdengine/qdcore/qd_d3dutils.h"
//набор своих функций, потому что те, что представлены в xMath.h
//не удовлетворяют потребностям
//я порой такой требовательный, что самому страшно.
namespace QDEngine {
namespace vector_helpers {
inline VALUE3D
SquareMagnitude(const Vect3f &v) {
return v.x * v.x + v.y * v.y + v.z * v.z;
}
inline VALUE3D
Magnitude(const Vect3f &v) {
return (VALUE3D) sqrt(SquareMagnitude(v));
}
inline Vect3f
Normalize(const Vect3f &v) {
return v / Magnitude(v);
}
inline VALUE3D DotProduct(const Vect3f &v1, const Vect3f &v2) {
return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
}
inline Vect3f
CrossProduct(const Vect3f &v1, const Vect3f &v2) {
Vect3f result;
result[0] = v1[1] * v2[2] - v1[2] * v2[1];
result[1] = v1[2] * v2[0] - v1[0] * v2[2];
result[2] = v1[0] * v2[1] - v1[1] * v2[0];
return result;
}
//угол между векторами лежащими в плоскости ХОУ
//иначе ее применять НЕЛЬЗЯ
float VectorAngle(const Vect3f &v1, const Vect3f &v2) {
return float(atan2(v2.y, v2.x) - atan2(v1.y, v1.x));
}
}//vector_helpers
/*
**-----------------------------------------------------------------------------
** Name: ZeroMatrix
** Purpose: sets D3D matrix to all 0's
**-----------------------------------------------------------------------------
*/
MATRIX3D
ZeroMatrix() {
MATRIX3D ret;
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
ret(i, j) = 0.0f;
}
}
return ret;
} // end ZeroMatrix
/*
**-----------------------------------------------------------------------------
** Name: IdentityMatrix
** Purpose: sets D3D matrix to Identiy (1's on diagonal, zero's elsewhere)
**-----------------------------------------------------------------------------
*/
MATRIX3D
IdentityMatrix() {
MATRIX3D ret;
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
ret(i, j) = 0.0f;
}
ret(i, i) = 1.0f;
}
return ret;
} // end IdentityMatrix
/*
**-----------------------------------------------------------------------------
** Name: ProjectionMatrix
** Purpose: sets Projection matrix from fov, near and far planes
** Notes:
** 1. fov is in radians.
** 2. See Blinn, "A Trip Down the Graphics Pipeline" pg 188 for details.
**-----------------------------------------------------------------------------
*/
MATRIX3D
ProjectionMatrix(const float near_plane,
const float far_plane,
const float fov) {
float c = (float) cos(fov * 0.5);
float s = (float) sin(fov * 0.5);
float Q = s / (1.0f - near_plane / far_plane);
MATRIX3D ret = ZeroMatrix();
ret(0, 0) = c;
ret(1, 1) = c;
ret(2, 2) = Q;
ret(3, 2) = -Q * near_plane;
ret(2, 3) = s;
return ret;
} // end ProjectionMatrix
/*
**-----------------------------------------------------------------------------
** Name: ViewMatrix
** Purpose: Controls where the camara is.
** Notes:
** 1. Note the roll parameter is in radians and rools the viewpoint
** around the viewing direction
**-----------------------------------------------------------------------------
*/
MATRIX3D ViewMatrixByDir(const Vect3f &from,
const Vect3f &view_dir,
const Vect3f &world_up,
const Vect3f &cam_up) {
MATRIX3D view = IdentityMatrix();
//view_dir - Это ось Z в системе координат камеры
Vect3f zAxis = view_dir;
//ось Х в правой системе координат
Vect3f xAxis = vector_helpers::CrossProduct(zAxis, world_up);
xAxis = vector_helpers::Normalize(xAxis);
Vect3f yAxis = vector_helpers::CrossProduct(zAxis, xAxis);
view(0, 0) = xAxis.x;
view(1, 0) = xAxis.y;
view(2, 0) = xAxis.z;
view(0, 1) = yAxis.x;
view(1, 1) = yAxis.y;
view(2, 1) = yAxis.z;
view(0, 2) = zAxis.x;
view(1, 2) = zAxis.y;
view(2, 2) = zAxis.z;
view(3, 0) = -vector_helpers::DotProduct(xAxis, from);
view(3, 1) = -vector_helpers::DotProduct(cam_up, from);
view(3, 2) = -vector_helpers::DotProduct(zAxis, from);
/*
* после всех вычислений верх камеры имеет с верхом мира угол небольше 90 градусов,
* что по сути своей не всегда правильно. Для того, чтобы вычислить правильно поворот
* вокруг оси Z используем up и cam_up. Где cam_up - это верх камеры, который должен
* быть у неё после поворота.
* Перед тем как вычислить угол переводим оба вектора в координаты камеры, тогда они
* будут лежать в плоскости ХОУ. вычисляем угол и поворачиваем.
*/
//переводим в координаты камеры, чтобы получить
//плоскую картинку и пользоваться atan2
float r = vector_helpers::VectorAngle(TransformVector(cam_up, view),
TransformVector(yAxis, view));
view = MatrixMult(RotateZMatrix(-r), view);
return view;
}
MATRIX3D
ViewMatrix(const Vect3f &from,
const Vect3f &at,
const Vect3f &world_up,
const Vect3f &cam_up) {
Vect3f view_dir = vector_helpers::Normalize(at - from);
return ViewMatrixByDir(from, view_dir, world_up, cam_up);
} // end ViewMatrix
/*
**-----------------------------------------------------------------------------
** Name: RotateXMatrix
** Purpose: Rotate matrix about X axis
**-----------------------------------------------------------------------------
*/
MATRIX3D
RotateXMatrix(const float rads) {
float cosine = (float) cos(rads);
float sine = (float) sin(rads);
MATRIX3D ret = IdentityMatrix();
ret(1, 1) = cosine;
ret(2, 2) = -cosine;
ret(1, 2) = sine;
ret(2, 1) = sine;
return ret;
} // end RotateXMatrix
/*
**-----------------------------------------------------------------------------
** Name: RotateYMatrix
** Purpose: Rotate matrix about Y axis
**-----------------------------------------------------------------------------
*/
MATRIX3D
RotateYMatrix(const float rads) {
float const cosine = (float) cos(rads);
float const sine = (float) sin(rads);
MATRIX3D ret = IdentityMatrix();
ret(0, 0) = cosine;
ret(2, 2) = -cosine;
ret(0, 2) = sine;
ret(2, 0) = sine;
return ret;
} // end RotateY
/*
**-----------------------------------------------------------------------------
** Name: RotateZMatrix
** Purpose: Rotate matrix about Z axis
**-----------------------------------------------------------------------------
*/
MATRIX3D
RotateZMatrix(const float rads) {
float const cosine = (float) cos(rads);
float const sine = (float) sin(rads);
MATRIX3D ret = IdentityMatrix();
ret(0, 0) = cosine;
ret(1, 1) = -cosine;
ret(0, 1) = sine;
ret(1, 0) = sine;
return ret;
} // end RotateZMatrix
/*
**-----------------------------------------------------------------------------
** Name: TranslateMatrix
** Purpose: Returns matrix to translate by (dx, dy, dz)
**-----------------------------------------------------------------------------
*/
MATRIX3D
TranslateMatrix(const float dx, const float dy, const float dz) {
MATRIX3D ret = IdentityMatrix();
ret(3, 0) = dx;
ret(3, 1) = dy;
ret(3, 2) = dz;
return ret;
} // end TranslateMatrix
/*
**-----------------------------------------------------------------------------
** Name: TranslateMatrix
** Purpose: Returns matrix to translate by v
**-----------------------------------------------------------------------------
*/
MATRIX3D
TranslateMatrix(const Vect3f &v) {
MATRIX3D ret = IdentityMatrix();
ret(3, 0) = v[0];
ret(3, 1) = v[1];
ret(3, 2) = v[2];
return ret;
} // end TranslateMatrix
/*
**-----------------------------------------------------------------------------
** Name: ScaleMatrix
** Purpose: scale matrix (uniform)
**-----------------------------------------------------------------------------
*/
MATRIX3D
ScaleMatrix(const float size) {
MATRIX3D ret = IdentityMatrix();
ret(0, 0) = size;
ret(1, 1) = size;
ret(2, 2) = size;
return ret;
} // end ScaleMatrix
/*
**-----------------------------------------------------------------------------
** Name: ScaleMatrix
** Purpose: scale matrix
**-----------------------------------------------------------------------------
*/
MATRIX3D
ScaleMatrix(const float a, const float b, const float c) {
MATRIX3D ret = IdentityMatrix();
ret(0, 0) = a;
ret(1, 1) = b;
ret(2, 2) = c;
return ret;
} // end ScaleMatrix
/*
**-----------------------------------------------------------------------------
** Name: ScaleMatrix
** Purpose: scale matrix
**-----------------------------------------------------------------------------
*/
MATRIX3D
ScaleMatrix(const Vect3f &v) {
MATRIX3D ret = IdentityMatrix();
ret(0, 0) = v.x;
ret(1, 1) = v.y;
ret(2, 2) = v.z;
return ret;
} // end ScaleMatrix
/*
**-----------------------------------------------------------------------------
** Name: MatrixMult
** Purpose: [C] = [A] * [B]
**-----------------------------------------------------------------------------
*/
MATRIX3D
MatrixMult(const MATRIX3D &a, const MATRIX3D &b) {
MATRIX3D ret = ZeroMatrix();
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
for (int k = 0; k < 4; k++) {
ret(i, j) += a(k, j) * b(i, k);
}
}
}
return ret;
} // end MatrixMult
/*
**-----------------------------------------------------------------------------
** Name: TransformVector
** Purpose: V' = V * [M]
**-----------------------------------------------------------------------------
*/
Vect3f
TransformVector(const Vect3f &v, const MATRIX3D &m) {
float hvec[4] = {0.f};
for (int i = 0; i < 4; i++) {
hvec[i] = 0.0f;
// for (int j=0; j<3; j++) {
// hvec[i] += v[j] * m(j, i);
// }
// hvec[i] += m(3, i);
hvec[i] = v[0] * m(0, i)
+ v[1] * m(1, i)
+ v[2] * m(2, i)
+ m(3, i);
}
return Vect3f(hvec[0] / hvec[3], hvec[1] / hvec[3], hvec[2] / hvec[3]);
} // end TransformVector
/*
**-----------------------------------------------------------------------------
** Name: TransformNormal
** Purpose: N' = N * [M]
**-----------------------------------------------------------------------------
*/
Vect3f
TransformNormal(const Vect3f &v, const MATRIX3D &mat) {
MATRIX3D m;
m = MatrixInverse(mat);
m = MatrixTranspose(m);
return TransformVector(v, m);
} // end TransformNormal
/*
**-----------------------------------------------------------------------------
** Name: MatrixInverse
** Purpose: Creates the inverse of a 4x4 matrix
**-----------------------------------------------------------------------------
*/
static void lubksb(MATRIX3D &a, int *indx, float *b);
static void ludcmp(MATRIX3D &a, int *indx, float *d);
MATRIX3D
MatrixInverse(const MATRIX3D &m) {
MATRIX3D n, y;
int i, j, indx[4];
float d, col[4];
n = m;
ludcmp(n, indx, &d);
for (j = 0; j < 4; j++) {
for (i = 0; i < 4; i++) {
col[i] = 0.0f;
}
col[j] = 1.0f;
lubksb(n, indx, col);
for (i = 0; i < 4; i++) {
y(i, j) = col[i];
}
}
return y;
} // end MatrixInverse
/*
**-----------------------------------------------------------------------------
** Name: lubksb
** Purpose: backward subsitution
**-----------------------------------------------------------------------------
*/
static void
lubksb(MATRIX3D &a, int *indx, float *b) {
int i, j, ii = -1, ip;
float sum;
for (i = 0; i < 4; i++) {
ip = indx[i];
sum = b[ip];
b[ip] = b[i];
if (ii >= 0) {
for (j = ii; j <= i - 1; j++) {
sum -= a(i, j) * b[j];
}
} else if (sum != 0.0) {
ii = i;
}
b[i] = sum;
}
for (i = 3; i >= 0; i--) {
sum = b[i];
for (j = i + 1; j < 4; j++) {
sum -= a(i, j) * b[j];
}
b[i] = sum / a(i, i);
}
} // end lubksb
/*
**-----------------------------------------------------------------------------
** Name: ludcmp
** Purpose: LU decomposition
**-----------------------------------------------------------------------------
*/
static void
ludcmp(MATRIX3D &a, int *indx, float *d) {
float vv[4]; /* implicit scale for each row */
float big, dum, sum, tmp;
int i, imax = 0, j, k;
*d = 1.0f;
for (i = 0; i < 4; i++) {
big = 0.0f;
for (j = 0; j < 4; j++) {
if ((tmp = (float) fabs(a(i, j))) > big) {
big = tmp;
}
}
/*
if (big == 0.0f) {
printf("ludcmp(): singular matrix found...\n");
exit(1);
}
*/
vv[i] = 1.0f / big;
}
for (j = 0; j < 4; j++) {
for (i = 0; i < j; i++) {
sum = a(i, j);
for (k = 0; k < i; k++) {
sum -= a(i, k) * a(k, j);
}
a(i, j) = sum;
}
big = 0.0f;
for (i = j; i < 4; i++) {
sum = a(i, j);
for (k = 0; k < j; k++) {
sum -= a(i, k) * a(k, j);
}
a(i, j) = sum;
if ((dum = vv[i] * (float)fabs(sum)) >= big) {
big = dum;
imax = i;
}
}
if (j != imax) {
for (k = 0; k < 4; k++) {
dum = a(imax, k);
a(imax, k) = a(j, k);
a(j, k) = dum;
}
*d = -(*d);
vv[imax] = vv[j];
}
indx[j] = imax;
if (a(j, j) == 0.0f) {
a(j, j) = 1.0e-20f; /* can be 0.0 also... */
}
if (j != 3) {
dum = 1.0f / a(j, j);
for (i = j + 1; i < 4; i++) {
a(i, j) *= dum;
}
}
}
} // end ludcmp
/*
**-----------------------------------------------------------------------------
** Name: Matrix Transpose
** Purpose: [M] = [M]'
**-----------------------------------------------------------------------------
*/
MATRIX3D
MatrixTranspose(const MATRIX3D &m) {
MATRIX3D ret;
int i, j;
for (i = 0; i < 4; i++) {
for (j = 0; j < 4; j++) {
ret(i, j) = m(j, i);
}
}
return ret;
} // end MatrixTranspose
/*
Class Methods
*/
/*
**-----------------------------------------------------------------------------
** end of File
**-----------------------------------------------------------------------------
*/
} // namespace QDEngine

View File

@@ -0,0 +1,108 @@
/* 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/>.
*
*/
#ifndef QDENGINE_QDCORE_QD_ENGINE_D3DUTILS_H
#define QDENGINE_QDCORE_QD_ENGINE_D3DUTILS_H
#include "qdengine/xmath.h"
namespace QDEngine {
typedef float VALUE3D;
typedef struct _MATRIX3D {
VALUE3D m[4][4];
_MATRIX3D() { }
_MATRIX3D(VALUE3D _m00, VALUE3D _m01, VALUE3D _m02, VALUE3D _m03,
VALUE3D _m10, VALUE3D _m11, VALUE3D _m12, VALUE3D _m13,
VALUE3D _m20, VALUE3D _m21, VALUE3D _m22, VALUE3D _m23,
VALUE3D _m30, VALUE3D _m31, VALUE3D _m32, VALUE3D _m33
) {
m[0][0] = _m00;
m[0][1] = _m01;
m[0][2] = _m02;
m[0][3] = _m03;
m[1][0] = _m10;
m[1][1] = _m11;
m[1][2] = _m12;
m[1][3] = _m13;
m[2][0] = _m20;
m[2][1] = _m21;
m[2][2] = _m22;
m[2][3] = _m23;
m[3][0] = _m30;
m[3][1] = _m31;
m[3][2] = _m32;
m[3][3] = _m33;
}
VALUE3D &operator()(int iRow, int iColumn) {
return m[iRow][iColumn];
}
const VALUE3D &operator()(int iRow, int iColumn) const {
return m[iRow][iColumn];
}
} MATRIX3D, *LPDMATRIX3D;
/*
**-----------------------------------------------------------------------------
** Function Prototypes
**-----------------------------------------------------------------------------
*/
// generic simple matrix routines
MATRIX3D ZeroMatrix();
MATRIX3D IdentityMatrix();
MATRIX3D ProjectionMatrix(const float near_plane, const float far_plane, const float fov);
MATRIX3D ViewMatrixByDir(const Vect3f &from,
const Vect3f &view_dir,
const Vect3f &world_up,
const Vect3f &cam_up);
MATRIX3D ViewMatrix(const Vect3f &from, const Vect3f &at,
const Vect3f &world_up,
const Vect3f &cam_up);
MATRIX3D RotateXMatrix(const float rads);
MATRIX3D RotateYMatrix(const float rads);
MATRIX3D RotateZMatrix(const float rads);
MATRIX3D TranslateMatrix(const float dx, const float dy, const float dz);
MATRIX3D TranslateMatrix(const Vect3f &v);
MATRIX3D ScaleMatrix(const float size);
MATRIX3D ScaleMatrix(const float a, const float b, const float c);
MATRIX3D ScaleMatrix(const Vect3f &v);
MATRIX3D MatrixMult(const MATRIX3D &a, const MATRIX3D &b);
MATRIX3D MatrixInverse(const MATRIX3D &m);
MATRIX3D MatrixTranspose(const MATRIX3D &m);
Vect3f TransformVector(const Vect3f &v, const MATRIX3D &m);
Vect3f TransformNormal(const Vect3f &v, const MATRIX3D &m);
/*
**-----------------------------------------------------------------------------
** End of File
**-----------------------------------------------------------------------------
*/
} // namespace QDEngine
#endif // QDENGINE_QDCORE_QD_ENGINE_D3DUTILS_H

View File

@@ -0,0 +1,86 @@
/* 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/>.
*
*/
#ifndef QDENGINE_QDCORE_QD_ENGINE_INTERFACE_H
#define QDENGINE_QDCORE_QD_ENGINE_INTERFACE_H
#include "qdengine/qdcore/qd_rnd.h"
namespace QDEngine {
class qdGameScene;
namespace qdmg {
/// Интерфейс к движку для миниигр, реализация.
class qdEngineInterfaceImpl : public qdEngineInterface {
public:
static const qdEngineInterfaceImpl &instance();
qdMinigameSceneInterface *current_scene_interface() const;
qdMinigameSceneInterface *scene_interface(qdGameScene *scene) const;
void release_scene_interface(qdMinigameSceneInterface *p) const;
qdMinigameCounterInterface *counter_interface(const char *counter_name) const;
void release_counter_interface(qdMinigameCounterInterface *p) const;
//! Возвращает размер экрана в пикселах.
mgVect2i screen_size() const;
//! Возвращает true, если на клавиатуре нажата кнопка vkey.
bool is_key_pressed(int vkey) const;
//! Возвращает true, если в данный момент произошло событие event_id.
bool is_mouse_event_active(qdMinigameMouseEvent event_id) const;
//! Возвращает текущие координаты мышиного курсора.
mgVect2i mouse_cursor_position() const;
/// Добавляет набранные очки в таблицу рекордов.
/// Если очков оказалось достаточно, возвращает true.
bool add_hall_of_fame_entry(int score) const;
bool set_interface_text(const char *screen_name, const char *control_name, const char *text) const;
/// Инициализация rnd
void rnd_init(int seed) const {
qd_rnd_init(seed);
}
/// Возвращает случайное значение в интервале [0, m-1].
uint32 rnd(uint32 m) const {
return qd_rnd(m);
}
/// Возвращает случайное значение в интервале [-x, x].
float frnd(float x) const {
return qd_frnd(x);
}
/// Возвращает случайное значение в интервале [0, x].
float fabs_rnd(float x) const {
return qd_fabs_rnd(x);
}
};
} // namespace qdmg
} // namespace QDEngine
#endif // QDENGINE_QDCORE_QD_ENGINE_INTERFACE_H

View File

@@ -0,0 +1,147 @@
/* 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 "common/compression/unzip.h"
#include "qdengine/qdengine.h"
#include "qdengine/qd_fwd.h"
#include "qdengine/qdcore/qd_file_manager.h"
namespace QDEngine {
qdFilePackage::qdFilePackage() : _container_index(0) {
}
qdFilePackage::~qdFilePackage() {
close();
}
bool qdFilePackage::open() {
Common::File zipFile;
if (zipFile.exists(file_name())) {
_container = Common::makeZipArchive(file_name());
return is_open();
}
return false;
}
void qdFilePackage::close() {
delete _container;
_container = nullptr;
}
bool qdFilePackage::check_container() {
Common::File zipFile;
return zipFile.exists(file_name());
}
const char *qdFilePackage::file_name() const {
static Common::String fname;
fname = Common::String::format("Resource/resource%d.pak", _container_index);
return fname.c_str();
}
void qdFilePackage::init() {
}
qdFileManager::qdFileManager() {
for (int i = 0; i < ARRAYSIZE(_packages); i++) {
_packages[i].init();
_packages[i].set_container_index(i);
if (_packages[i].check_container())
_packageCount = i + 1;
}
debug(0, "qdFileManager(): Package count: %d", _packageCount);
}
qdFileManager::~qdFileManager() {
}
bool qdFileManager::init(int cd_count) {
return true;
}
qdFileManager *mgr = nullptr;
qdFileManager &qdFileManager::instance() {
if (!mgr)
mgr = new qdFileManager();
return *mgr;
}
void qdFileManager::Finit() {
delete mgr;
}
bool qdFileManager::open_file(Common::SeekableReadStream **fh, const Common::Path &file_name, bool err_message) {
debugC(4, kDebugLoad, "qdFileManager::open_file(%s)", transCyrillic(file_name.toString()));
if (SearchMan.hasFile((char *)transCyrillic(file_name.toString()))) {
Common::File *f = new Common::File;
if (f->open((char *)transCyrillic(file_name.toString())))
*fh = f;
else
return false;
return true;
}
for (int i = 0; i < _packageCount; i++) {
if (!_packages[i].is_open())
_packages[i].open();
if (!_packages[i].is_open())
continue;
if (_packages[i]._container->hasFile(file_name)) {
debugC(5, kDebugLoad, "qdFileManager::open_file(%s): found in %s", transCyrillic(file_name.toString()), _packages[i].file_name());
*fh = _packages[i]._container->createReadStreamForMember(file_name);
if (*fh)
return true;
debugC(4, kDebugLoad, "qdFileManager::open_file(%s): Cannot read file", transCyrillic(file_name.toString()));
return false;
}
}
warning("qdFileManager::open_file(%s) (%s): NOT FOUND", transCyrillic(file_name.toString()), file_name.toString().c_str());
return false;
}
bool qdFileManager::is_package_available(const qdFileOwner &file_owner) {
return true;
}
} // namespace QDEngine

View File

@@ -0,0 +1,124 @@
/* 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/>.
*
*/
#ifndef QDENGINE_QDCORE_QD_FILE_MANAGER_H
#define QDENGINE_QDCORE_QD_FILE_MANAGER_H
#include "qdengine/qdcore/qd_file_owner.h"
namespace Common {
class Archive;
}
namespace QDEngine {
class qdFilePackage {
public:
qdFilePackage();
~qdFilePackage();
const char *file_name() const;
void set_drive_ID(int drive_id) {
}
void set_container_index(int idx) {
_container_index = idx;
}
bool is_open() {
return _container != nullptr;
}
void init();
bool open();
void close();
bool check_container();
Common::Archive *_container = nullptr;
private:
int _container_index;
};
//! Менеджер файлов.
class qdFileManager {
public:
~qdFileManager();
bool init(int cd_count);
void Finit();
void enable_packages() {}
bool open_file(Common::SeekableReadStream **fh, const Common::Path &file_name, bool err_message = true);
int last_CD_id() const {
return 1;
}
void set_last_CD_id(int cd_id) {
}
int CD_count() const {
return 1;
}
bool is_CD_available(int cd_id) const {
return true;
}
bool is_package_available(int idx) {
assert(idx >= 0 && idx < _packageCount);
return _packages[idx].is_open();
}
bool is_package_available(const qdFileOwner &file_owner);
int get_num_packages() { return _packageCount; }
Common::Archive *get_package(int idx) {
if (!_packages[idx].is_open())
_packages[idx].open();
return _packages[idx]._container;
}
bool scan_drives(const qdFileOwner *file_owner = NULL) { return true; }
bool scan_drives(int cd_id) { return true; }
void update_packages() {}
void toggle_silent_update_mode(bool mode) {
}
bool check_drives(const char *cdkey = NULL) { return true; }
bool check_drive(int drive_letter) { return true; }
static qdFileManager &instance();
private:
qdFileManager();
qdFilePackage _packages[3];
int _packageCount = 0;
};
} // namespace QDEngine
#endif // QDENGINE_QDCORE_QD_FILE_MANAGER_H

View File

@@ -0,0 +1,59 @@
/* 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/>.
*
*/
#ifndef QDENGINE_QDCORE_QD_FILE_OWNER
#define QDENGINE_QDCORE_QD_FILE_OWNER
namespace QDEngine {
class qdFileOwner {
public:
qdFileOwner() : _cd_info(0) { }
virtual ~qdFileOwner() {}
void add_to_CD(int cd_number) {
_cd_info |= 1 << cd_number;
}
void remove_from_CD(int cd_number) {
_cd_info &= ~(1 << cd_number);
}
bool is_on_CD(int cd_number = 1) const {
int inf = (_cd_info) ? _cd_info : 1;
return (inf & (1 << cd_number));
}
void clear_cd_info() {
_cd_info = 0;
}
uint32 CD_info() const {
return _cd_info;
}
void set_CD_info(uint32 inf) {
_cd_info = inf;
}
private:
uint32 _cd_info;
};
} // namespace QDEngine
#endif // QDENGINE_QDCORE_QD_FILE_OWNER

View File

@@ -0,0 +1,137 @@
/* 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/path.h"
#include "qdengine/qd_fwd.h"
#include "qdengine/qdcore/qd_font_info.h"
#include "qdengine/qdcore/qd_file_manager.h"
namespace QDEngine {
qdFontInfo::qdFontInfo(const qdFontInfo &fi) : qdNamedObject(fi),
_type(fi.type()), _font_file_name(fi.font_file_name()) {
if (NULL != fi.font()) {
_font = new grFont;
*_font = *fi.font();
} else
_font = NULL;
}
qdFontInfo::~qdFontInfo() {
delete _font;
}
qdFontInfo &qdFontInfo::operator = (const qdFontInfo &fi) {
if (this == &fi) return *this;
*static_cast<qdNamedObject *>(this) = fi;
_type = fi.type();
_font_file_name = fi.font_file_name();
delete _font;
if (NULL != fi.font()) {
_font = new grFont;
*_font = *fi.font();
} else
_font = NULL;
return *this;
}
bool qdFontInfo::load_script(const xml::tag *p) {
for (xml::tag::subtag_iterator it = p->subtags_begin(); it != p->subtags_end(); ++it) {
switch (it->ID()) {
case QDSCR_TYPE:
set_type(xml::tag_buffer(*it).get_int());
break;
case QDSCR_FILE:
set_font_file_name(Common::Path(it->data(), '\\'));
break;
case QDSCR_NAME:
set_name(it->data());
break;
}
}
return true;
}
bool qdFontInfo::save_script(Common::WriteStream &fh, int indent) const {
for (int i = 0; i < indent; i++) {
fh.writeString("\t");
}
fh.writeString(Common::String::format("<fontinfo type=\"%d\"", _type));
if (!_font_file_name.empty()) {
fh.writeString(Common::String::format(" file=\"%s\"", qdscr_XML_string(_font_file_name.toString('\\'))));
}
if (name()) {
fh.writeString(Common::String::format(" name=\"%s\"", qdscr_XML_string(name())));
}
fh.writeString("/>\r\n");
return true;
}
bool qdFontInfo::load_font() {
grFont *buf_font = new grFont;
bool load_fl = false; // По умолчанию загрузка прошла успешно
Common::SeekableReadStream *fh;
if (qdFileManager::instance().open_file(&fh, font_file_name(), false)) {
// Грузим альфу шрифта из .tga
if (buf_font->load_alpha(fh)) {
// Меняем расширение с .tga на .idx
Common::String fpath(font_file_name().toString());
Common::String tgaExt = ".tga";
Common::String idxExt = ".idx";
Common::replace(fpath, tgaExt, idxExt);
delete fh;
fh = nullptr;
// Открываем .idx и грузим индекс
if (qdFileManager::instance().open_file(&fh, Common::Path(fpath), false)) {
if (buf_font->load_index(fh))
load_fl = true;
}
}
delete fh;
}
if (!load_fl) {
delete buf_font;
return false;
}
_font = buf_font;
return true;
}
} // namespace QDEngine

View File

@@ -0,0 +1,96 @@
/* 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/>.
*
*/
#ifndef QDENGINE_QDCORE_QD_FONT_INFO_H
#define QDENGINE_QDCORE_QD_FONT_INFO_H
#include "qdengine/parser/xml_fwd.h"
#include "qdengine/parser/xml_tag.h"
#include "qdengine/parser/xml_tag_buffer.h"
#include "qdengine/parser/qdscr_parser.h"
#include "qdengine/system/graphics/gr_font.h"
#include "qdengine/qdcore/qd_named_object.h"
#include "qdengine/qdcore/qd_object_list_container.h"
namespace QDEngine {
//! Шрифт
class qdFontInfo : public qdNamedObject {
public:
qdFontInfo() : _type(0), _font(NULL) {}
explicit qdFontInfo(int tp) : _type(tp), _font(NULL) {}
// Копирующий конструктор
qdFontInfo(const qdFontInfo &fi);
~qdFontInfo();
int named_object_type() const {
return QD_NAMED_OBJECT_FONT_INFO;
}
bool operator == (const qdFontInfo &fi) const {
return (fi.type() == _type) && (fi._font_file_name == _font_file_name);
}
bool operator == (int t) const {
return (t == _type);
}
qdFontInfo &operator = (const qdFontInfo &fi);
int type() const {
return _type;
}
void set_type(int tp) {
_type = tp;
}
void set_font_file_name(const Common::Path &fname) {
_font_file_name = fname;
}
const Common::Path font_file_name() const {
return _font_file_name;
}
const grFont *font() const {
return _font;
}
bool load_script(const xml::tag *p);
bool save_script(Common::WriteStream &fh, int indent = 0) const;
//! Грузит шрифт из файлов: font_file_name_ (хранит путь к *.tga части)
//! и файла аналогичного font_file_name_, но с расш-ем. *.idx (индексная часть)
bool load_font();
private:
//! Тип шрифта.
int _type;
//! Файл шрифта (*.tga).
Common::Path _font_file_name;
//! Сам шрифт
grFont *_font;
};
typedef Std::list<qdFontInfo *> qdFontInfoList;
} // namespace QDEngine
#endif // QDENGINE_QDCORE_QD_FONT_INFO_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,681 @@
/* 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/>.
*
*/
#ifndef QDENGINE_QDCORE_QD_GAME_DISPATCHER_H
#define QDENGINE_QDCORE_QD_GAME_DISPATCHER_H
#include "qdengine/parser/xml_fwd.h"
#include "qdengine/system/input/mouse_input.h"
#include "qdengine/system/graphics/gr_screen_region.h"
#include "qdengine/qdcore/qd_camera_mode.h"
#include "qdengine/qdcore/qd_object_list_container.h"
#include "qdengine/qdcore/qd_game_dispatcher_base.h"
#include "qdengine/qdcore/qd_resource_dispatcher.h"
#include "qdengine/qdcore/qd_screen_text_dispatcher.h"
#include "qdengine/qdcore/qd_interface_dispatcher.h"
#include "qdengine/qdcore/qd_inventory.h"
#include "qdengine/qdcore/qd_font_info.h"
#include "qdengine/qdcore/qd_file_owner.h"
#include "qdengine/qdcore/util/WinVideo.h"
namespace QDEngine {
class grFont;
class qdGameDispatcher : public qdGameDispatcherBase, public qdResourceDispatcher<qdNamedObject>, public qdFileOwner {
public:
//! Идентификаторы наборов текстов.
enum {
//! Фразы диалогов, отображаемые в данный момент на экране
TEXT_SET_DIALOGS = 0
};
//! Флаги
enum {
EXIT_FLAG = 0x01,
RESTART_FLAG = 0x02,
OBJECT_CLICK_FLAG = 0x04,
DIALOG_CLICK_FLAG = 0x08,
FULLSCREEN_REDRAW_FLAG = 0x10,
SKIP_REDRAW_FLAG = 0x20,
MAIN_MENU_FLAG = 0x40,
INTRO_MODE_FLAG = 0x80,
CLICK_FAILED_FLAG = 0x100,
OBJECT_CLICK_FAILED_FLAG = 0x200,
CLICK_WAS_FAILED_FLAG = 0x400,
OBJECT_CLICK_WAS_FAILED_FLAG = 0x800,
LOAD_GAME_FLAG = 0x1000,
SAVE_GAME_FLAG = 0x2000,
FADE_IN_FLAG = 0x4000,
FADE_OUT_FLAG = 0x8000,
NEXT_FRAME_FLAG = 0x10000
};
qdGameDispatcher();
~qdGameDispatcher();
int named_object_type() const {
return QD_NAMED_OBJECT_DISPATCHER;
}
qdGameObjectMouse *mouse_object() {
return _mouse_obj;
}
qdGameObjectMouse const *mouse_object() const {
return _mouse_obj;
}
bool drop_mouse_object();
int CD_count() const;
int resource_compression() const {
return _resource_compression;
}
void set_resource_compression(int compression) {
_resource_compression = compression;
}
void request_file_package(const qdFileOwner &file_owner) const;
Common::Path find_file(const Common::Path &file_name, const qdFileOwner &file_owner) const;
void startup_check() const;
qdLoadingProgressFnc set_scene_loading_progress_callback(qdLoadingProgressFnc p, void *dp = 0) {
qdLoadingProgressFnc old_fnc = _scene_loading_progress_fnc;
_scene_loading_progress_fnc = p;
_scene_loading_progress_data = dp;
return old_fnc;
}
qdLoadingProgressFnc get_scene_loading_progress_callback() {
return _scene_loading_progress_fnc;
}
void *get_scene_loading_progress_data() {
return _scene_loading_progress_data;
}
void set_time(int tm) {
_timer = tm;
}
int get_time() const {
return _timer;
}
bool start_intro_videos();
void quant();
void quant(float dt);
void pre_redraw();
void redraw();
void post_redraw();
void update_time();
bool mouse_handler(int x, int y, mouseDispatcher::mouseEvent ev);
bool keyboard_handler(Common::KeyCode vkey, bool event);
bool add_game_end(qdGameEnd *p);
bool rename_game_end(qdGameEnd *p, const char *name);
bool remove_game_end(qdGameEnd *p);
qdGameEnd *get_game_end(const char *name);
bool is_game_end_in_list(const char *name);
bool is_game_end_in_list(qdGameEnd *p);
bool add_counter(qdCounter *p);
bool rename_counter(qdCounter *p, const char *name);
bool remove_counter(qdCounter *p);
qdCounter *get_counter(const char *name);
bool is_counter_in_list(const char *name);
bool is_counter_in_list(qdCounter *p);
bool add_minigame(qdMiniGame *p);
bool rename_minigame(qdMiniGame *p, const char *name);
bool remove_minigame(const char *name);
bool remove_minigame(qdMiniGame *p);
qdMiniGame *get_minigame(const char *name);
bool is_minigame_in_list(const char *name);
bool is_minigame_in_list(qdMiniGame *p);
void set_mouse_click_state(const qdGameObjectState *p) {
_mouse_click_state = p;
}
bool add_inventory(qdInventory *p) {
if (_inventories.add_object(p)) {
p->set_owner(this);
return 1;
}
return 0;
}
bool remove_inventory(const char *name) {
return _inventories.remove_object(name);
}
bool remove_inventory(qdInventory *p) {
return _inventories.remove_object(p);
}
qdInventory *get_inventory(const char *name) {
return _inventories.get_object(name);
}
bool is_inventory_in_list(const char *name) {
return _inventories.is_in_list(name);
}
bool is_inventory_in_list(qdInventory *p) {
return _inventories.is_in_list(p);
}
bool rename_inventory(qdInventory *p, const char *name);
const qdInventoryCellType *add_inventory_cell_type(const qdInventoryCellType &tp) {
//проверяем уникальность идентификатора, вставляемого типа
assert(find_inventory_cell_type(tp.type()) == _inventory_cell_types.end());
_inventory_cell_types.push_back(tp);
return &_inventory_cell_types.back();
}
bool remove_inventory_cell_type(int type) {
typedef qdInventoryCellTypeVector::iterator i_t;
i_t res = find_inventory_cell_type(type);
if (res != _inventory_cell_types.end()) {
_inventory_cell_types.erase(res);
return true;
}
return false;
}
const qdInventoryCellType *get_inventory_cell_type(int type) const {
typedef qdInventoryCellTypeVector::const_iterator ci_t;
ci_t res = find_inventory_cell_type(type);
if (res != _inventory_cell_types.end())
return &*res;
return NULL;
}
int get_unique_inventory_cell_type() const {
if (_inventory_cell_types.empty()) return 0;
return _inventory_cell_types.back().type() + 1;
}
bool set_inventory_cell_type(const qdInventoryCellType &tp) {
typedef qdInventoryCellTypeVector::iterator i_t;
i_t res = find_inventory_cell_type(tp.type());
if (res != _inventory_cell_types.end()) {
*res = tp;
return true;
}
return false;
}
const qdInventoryCellTypeVector &inventory_cell_types() const {
return _inventory_cell_types;
}
qdInventoryCellTypeVector &not_const_inventory_cell_types() {
return _inventory_cell_types;
}
//! Операции со шрифтами
/**
Для создания типов всех добавляемых шрифтов нужно
использовать get_unique_font_info_type().
Самостоятельное задание типов шрифтов крайне не рекомендуется -
возможны проблемы с уникальностью типов шрифтов и извлечением шрифта по типу.
*/
bool add_font_info(qdFontInfo *fi);
bool remove_font_info(qdFontInfo *fi);
bool rename_font_info(qdFontInfo *fi, char const *name);
const qdFontInfo *get_font_info(int type) const;
int get_unique_font_info_type() const;
bool set_font_info(const qdFontInfo &fi);
const qdFontInfoList &fonts_list() const {
return _fonts.get_list();
}
int default_font() const {
return _default_font;
}
void set_default_font(int font) {
_default_font = font;
}
bool toggle_inventory(bool state);
bool toggle_inventory() {
if (!_cur_inventory) return toggle_inventory(true);
else return toggle_inventory(false);
}
bool put_to_inventory(qdGameObjectAnimated *p);
bool remove_from_inventory(qdGameObjectAnimated *p);
bool is_in_inventory(const qdGameObjectAnimated *p) const;
bool is_on_mouse(const qdGameObjectAnimated *p) const;
const qdInventory *cur_inventory() const {
return _cur_inventory;
}
bool add_video(qdVideo *p, qdVideo const *before = NULL);
bool remove_video(const char *name);
bool remove_video(qdVideo *p);
qdVideo *get_video(const char *name);
bool is_video_in_list(const char *name);
bool is_video_in_list(qdVideo *p);
bool rename_video(qdVideo *p, const char *name);
bool add_trigger_chain(qdTriggerChain *p);
bool remove_trigger_chain(const char *name);
bool remove_trigger_chain(qdTriggerChain *p);
qdTriggerChain *get_trigger_chain(const char *name);
bool is_trigger_chain_in_list(const char *name);
bool is_trigger_chain_in_list(qdTriggerChain *p);
bool rename_trigger_chain(qdTriggerChain *p, const char *name);
bool add_scene(qdGameScene *p);
bool rename_scene(qdGameScene *p, const char *name);
bool remove_scene(qdGameScene *p);
qdGameScene *get_scene(const char *name);
bool is_scene_in_list(const char *name);
bool is_scene_in_list(const qdGameScene *p);
bool add_global_object(qdGameObject *p);
bool rename_global_object(qdGameObject *p, const char *name);
bool remove_global_object(const char *name);
bool remove_global_object(qdGameObject *p);
qdGameObject *get_global_object(const char *name);
bool is_global_object_in_list(const char *name);
bool is_global_object_in_list(qdGameObject *p);
bool merge_global_objects(qdGameObject *obj = NULL);
bool split_global_objects(qdGameObject *obj = NULL);
bool update_walk_state(const char *object_name, qdGameObjectState *p);
qdGameObjectState *get_walk_state(const char *object_name);
// Управление видеовставками
bool play_video(const char *vid_name);
bool play_video(qdVideo *p);
bool pause_video();
bool stop_video();
bool close_video();
bool is_video_finished();
bool is_video_playing() {
if (!_cur_video) return false;
return true;
}
void continueVideo();
bool is_video_playing(qdVideo *p) {
return (_cur_video == p);
}
bool play_music_track(const qdMusicTrack *p, bool interface_mode = false);
const qdMusicTrack *current_music() const {
return _cur_music_track;
}
bool stop_music();
bool select_scene(const char *s_name);
bool select_scene(qdGameScene *sp, bool resources_flag = true);
//! Делает активными все линки, идущие от объекта в триггерах.
bool activate_trigger_links(const qdNamedObject *p);
//! Делает неактивными все триггера сцены.
bool deactivate_scene_triggers(const qdGameScene *p);
void set_next_scene(qdGameScene *p) {
_next_scene = p;
}
int load_resources();
void free_resources();
int get_resources_size();
void load_script(const char *fname);
void load_script(const xml::tag *p);
bool save_script(Common::SeekableWriteStream &fh) const;
bool save_script(const char *fname) const;
//! Загрузка данных из сэйва.
bool load_save(Common::SeekableReadStream *fh);
//! Запись данных в сэйв.
bool save_save(Common::WriteStream *stream) const;
bool save_game(int slot_id) const;
//! Инициализация данных, вызывается один раз сразу после загрузки скрипта.
bool init();
bool init_triggers();
bool reset_triggers();
const qdVideoList &video_list() const {
return _videos.get_list();
}
const qdTriggerChainList &trigger_chain_list() const {
return _trigger_chains.get_list();
}
const qdGameObjectList &global_object_list() const {
return _global_objects.get_list();
}
const qdInventoryList &inventory_list() const {
return _inventories.get_list();
}
const qdMiniGameList &minigame_list() const {
return _minigames.get_list();
}
const qdGameEndList &game_end_list() const {
return _game_ends.get_list();
}
const qdGameSceneList &scene_list() const {
return _scenes.get_list();
}
const qdCounterList &counter_list() const {
return _counters.get_list();
}
qdGameScene *get_active_scene() const {
return _cur_scene;
}
qdSound *get_sound(const char *name);
qdAnimation *get_animation(const char *name);
qdAnimationSet *get_animation_set(const char *name);
qdGameObject *get_object(const char *name);
qdGameObjectMoving *get_active_personage();
qdNamedObject *get_named_object(const qdNamedObjectReference *ref);
qdScaleInfo *get_scale_info(const char *p);
bool check_condition(qdCondition *cnd);
void pause();
void resume();
bool is_paused() {
return _is_paused;
}
bool is_gameplay_paused() {
return _is_gameplay_paused;
}
void pause_gameplay(bool pause) { _is_gameplay_paused = pause; }
bool restart();
qdScreenTextDispatcher &screen_texts_dispatcher() {
return _screen_texts;
}
bool toggle_main_menu() {
return toggle_main_menu(!_interface_dispatcher.is_active());
}
bool toggle_main_menu(bool state, const char *screen_name = NULL);
bool has_main_menu() const {
return _interface_dispatcher.has_main_menu();
}
bool is_main_menu_exit_enabled() const;
void set_game_end(const qdGameEnd *p) {
_game_end = p;
}
bool end_game(const qdGameEnd *p);
bool add_redraw_region(const grScreenRegion &reg);
//! Ставит флаг, что надо выйти из игры.
void toggle_exit() {
set_flag(EXIT_FLAG);
}
//! Возвращает true, если надо выйти из игры.
bool need_exit() const {
return check_flag(EXIT_FLAG);
}
//! Ставит флаг, что надо перезапустить игру.
void toggle_restart() {
set_flag(RESTART_FLAG);
}
//! Возвращает true, если надо перезапустить игру.
bool need_restart() const {
return check_flag(RESTART_FLAG);
}
void toggle_full_redraw() {
set_flag(FULLSCREEN_REDRAW_FLAG);
}
bool need_full_redraw() const {
return check_flag(FULLSCREEN_REDRAW_FLAG);
}
static void set_dispatcher(qdGameDispatcher *p);
static qdGameDispatcher *get_dispatcher() {
return _dispatcher;
}
//! Возвращает имя стартовой сцены игры.
const char *startup_scene() const {
return _startup_scene.c_str();
}
//! Устанавливает имя стартовой сцены игры.
/**
Передавать NULL, чтобы скинуть установку.
*/
void set_startup_scene(const char *name) {
if (name)
_startup_scene = name;
else
_startup_scene.clear();
}
//! Возвращает true, если задана стартовая сцена игры.
bool has_startup_scene() const {
return !_startup_scene.empty();
}
bool game_screenshot(Graphics::Surface &thumb) const;
void set_game_title(const char *p) {
_game_title = p;
}
const char *game_title() const {
return _game_title.c_str();
}
void set_texts_database(const Common::Path &file_name) {
_texts_database = file_name;
}
const Common::Path texts_database() const {
return _texts_database;
}
void set_cd_key(const char *key) {
_cd_key = key;
}
const char *cd_key() const {
return _cd_key.c_str();
}
bool add_dialog_state(qdGameObjectState *p);
const Vect2f &mouse_cursor_pos() const {
return _mouse_cursor_pos;
}
bool is_autosave_slot(int save_slot) {
return save_slot == _autosave_slot;
}
void set_auto_save(int save_slot) {
set_flag(SAVE_GAME_FLAG);
_autosave_slot = save_slot;
}
void set_auto_load(int save_slot) {
set_flag(LOAD_GAME_FLAG);
_autosave_slot = save_slot;
}
static grFont *create_font(int font_idx);
static void free_font(grFont *fnt);
//! Поиск шрифтов
qdFontInfo *find_font_info(int type);
const qdFontInfo *find_font_info(int type) const;
const grFont *find_font(int type) const;
bool set_fade(bool fade_in, float duration);
bool write_resource_stats(const char *file_name) const;
int hall_of_fame_size() const {
return _hall_of_fame_size;
}
void set_hall_of_fame_size(int size) {
_hall_of_fame_size = size;
}
const char *hall_of_fame_player_name(int place) const;
void set_hall_of_fame_player_name(int place, const char *name);
int hall_of_fame_player_score(int place) const;
bool is_hall_of_fame_updated(int place) const;
bool has_hall_of_fame() const {
return _hall_of_fame_size != 0;
}
bool load_hall_of_fame();
bool save_hall_of_fame() const;
bool add_hall_of_fame_entry(int score);
private:
int _timer;
bool _is_paused;
bool _is_gameplay_paused = false;
bool _enable_file_packages;
/// компрессия ресурсов в архивах, 0 - отключена, 9 - максимальная
int _resource_compression;
/// таблица рекордов, если нету - размер нулевой
int _hall_of_fame_size;
struct HallOfFameEntry {
Common::String _player_name;
int _score;
bool _updated;
HallOfFameEntry() {
_score = 0;
_updated = false;
}
};
typedef Std::vector<HallOfFameEntry> HallOfFame;
HallOfFame _hall_of_fame;
qdInterfaceDispatcher _interface_dispatcher;
winVideo _video_player;
qdVideo *_cur_video;
qdObjectListContainer<qdVideo> _videos;
qdObjectListContainer<qdGameObject> _global_objects;
qdObjectListContainer<qdTriggerChain> _trigger_chains;
qdObjectListContainer<qdInventory> _inventories;
qdObjectListContainer<qdGameEnd> _game_ends;
qdObjectListContainer<qdGameScene> _scenes;
qdObjectListContainer<qdCounter> _counters;
qdGameScene *_cur_scene;
bool _scene_saved;
int _autosave_slot;
//! Если ненулевой - игра заканчивается с переходом на него.
const qdGameEnd *_game_end;
qdObjectListContainer<qdMiniGame> _minigames;
qdInventory *_cur_inventory;
qdInventoryCellTypeVector _inventory_cell_types;
//! Шрифты
qdObjectListContainer<qdFontInfo> _fonts;
//! Шрифт, используемый для отрисовки текстов, в формате который шрифт не задан.
int _default_font;
qdGameObjectMouse *_mouse_obj;
qdGameObjectAnimated *_mouse_click_obj;
const qdGameObjectState *_mouse_click_state;
qdAnimation *_mouse_animation;
Vect2f _mouse_cursor_pos;
typedef Std::vector<qdGameObjectState *> dialog_states_container_t;
dialog_states_container_t _dialog_states;
dialog_states_container_t _dialog_states_last;
qdScreenTextDispatcher _screen_texts;
Common::String _startup_scene;
//! Файл с субтитрами.
Common::Path _texts_database;
//! Название игры.
Common::String _game_title;
Common::String _cd_key;
void *_scene_loading_progress_data;
qdLoadingProgressFnc _scene_loading_progress_fnc;
qdGameScene *_next_scene;
bool _interface_music_mode;
const qdMusicTrack *_cur_music_track;
const qdMusicTrack *_cur_interface_music_track;
float _fade_timer;
float _fade_duration;
qdCameraMode _default_camera_mode;
static qdGameDispatcher *_dispatcher;
bool init_inventories();
qdInventoryCellTypeVector::iterator find_inventory_cell_type(int type) {
return Common::find(_inventory_cell_types.begin(),
_inventory_cell_types.end(), type);
}
qdInventoryCellTypeVector::const_iterator find_inventory_cell_type(int type) const {
return Common::find(_inventory_cell_types.begin(), _inventory_cell_types.end(), type);
}
void redraw(const grScreenRegion &reg);
void redraw_scene(bool draw_interface = true);
/// включает нужный экран внутриигрового интерфейса
bool update_ingame_interface();
/// вытаскивает из интерфейса имена игроков в таблице рекордов
bool update_hall_of_fame_names();
};
qdGameDispatcher *qd_get_game_dispatcher();
} // namespace QDEngine
#endif // QDENGINE_QDCORE_QD_GAME_DISPATCHER_H

View File

@@ -0,0 +1,251 @@
/* 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 "qdengine/qd_fwd.h"
#include "qdengine/parser/xml_tag.h"
#include "qdengine/parser/qdscr_parser.h"
#include "qdengine/qdcore/qd_sound.h"
#include "qdengine/qdcore/qd_animation.h"
#include "qdengine/qdcore/qd_animation_set.h"
#include "qdengine/qdcore/qd_game_dispatcher_base.h"
namespace QDEngine {
qdGameDispatcherBase::qdGameDispatcherBase() {
}
qdGameDispatcherBase::~qdGameDispatcherBase() {
}
void qdGameDispatcherBase::load_script_body(const xml::tag *p) {
qdAnimation *ap;
qdAnimationSet *asp;
qdSound *snd;
for (xml::tag::subtag_iterator it = p->subtags_begin(); it != p->subtags_end(); ++it) {
switch (it->ID()) {
case QDSCR_OBJ_SCALE_INFO: {
qdScaleInfo sp;
sp.load_script(&*it);
add_scale_info(&sp);
}
break;
case QDSCR_ANIMATION:
ap = new qdAnimation;
ap->load_script(&*it);
add_animation(ap);
break;
case QDSCR_ANIMATION_SET:
asp = new qdAnimationSet;
asp->load_script(&*it);
add_animation_set(asp);
break;
case QDSCR_SOUND:
snd = new qdSound;
snd->load_script(&*it);
add_sound(snd);
break;
}
}
}
bool qdGameDispatcherBase::save_script_body(Common::WriteStream &fh, int indent) const {
for (auto &it : _scale_infos) {
it.save_script(fh, indent + 1);
}
for (auto &it : sound_list()) {
it->save_script(fh, indent + 1);
}
for (auto &it : animation_list()) {
it->save_script(fh, indent + 1);
}
for (auto &it : animation_set_list()) {
it->save_script(fh, indent + 1);
}
return true;
}
bool qdGameDispatcherBase::get_object_scale(const char *p, float &sc) {
qdScaleInfo *si = get_scale_info(p);
if (si) {
sc = si->scale();
return true;
}
return false;
}
qdScaleInfo *qdGameDispatcherBase::get_scale_info(const char *p) {
for (scale_info_container_t::iterator it = _scale_infos.begin(); it != _scale_infos.end(); ++it) {
if (!strcmp(it->name(), p)) {
return &*it;
}
}
return NULL;
}
bool qdGameDispatcherBase::set_object_scale(const char *p, float sc) {
for (scale_info_container_t::iterator it = _scale_infos.begin(); it != _scale_infos.end(); ++it) {
if (!strcmp(it->name(), p)) {
it->set_scale(sc);
return true;
}
}
qdScaleInfo scl;
scl.set_name(p);
scl.set_scale(sc);
add_scale_info(&scl);
return true;
}
int qdGameDispatcherBase::load_resources() {
return 0;
}
void qdGameDispatcherBase::free_resources() {
for (auto &ia : animation_list()) {
ia->free_resources();
}
for (auto &is : sound_list()) {
is->free_resource();
}
}
int qdGameDispatcherBase::get_resources_size() {
return 0;
}
void qdGameDispatcherBase::show_loading_progress(int sz) {
_loading_progress.show_progress(sz);
}
bool qdGameDispatcherBase::add_sound(qdSound *p) {
if (_sounds.add_object(p)) {
p->set_owner(this);
return true;
}
return false;
}
bool qdGameDispatcherBase::remove_sound(const char *name) {
return _sounds.remove_object(name);
}
bool qdGameDispatcherBase::remove_sound(qdSound *p) {
return _sounds.remove_object(p);
}
qdSound *qdGameDispatcherBase::get_sound(const char *name) {
return _sounds.get_object(name);
}
bool qdGameDispatcherBase::is_sound_in_list(const char *name) {
return _sounds.is_in_list(name);
}
bool qdGameDispatcherBase::is_sound_in_list(qdSound *p) {
return _sounds.is_in_list(p);
}
bool qdGameDispatcherBase::add_animation(qdAnimation *p) {
if (_animations.add_object(p)) {
p->set_owner(this);
return true;
}
return false;
}
bool qdGameDispatcherBase::remove_animation(const char *name) {
return _animations.remove_object(name);
}
bool qdGameDispatcherBase::remove_animation(qdAnimation *p) {
return _animations.remove_object(p);
}
qdAnimation *qdGameDispatcherBase::get_animation(const char *name) {
return _animations.get_object(name);
}
bool qdGameDispatcherBase::is_animation_in_list(const char *name) {
return _animations.is_in_list(name);
}
bool qdGameDispatcherBase::is_animation_in_list(qdAnimation *p) {
return _animations.is_in_list(p);
}
bool qdGameDispatcherBase::add_animation_set(qdAnimationSet *p) {
if (_animation_sets.add_object(p)) {
p->set_owner(this);
return true;
}
return false;
}
bool qdGameDispatcherBase::remove_animation_set(const char *name) {
return _animation_sets.remove_object(name);
}
bool qdGameDispatcherBase::remove_animation_set(qdAnimationSet *p) {
return _animation_sets.remove_object(p);
}
qdAnimationSet *qdGameDispatcherBase::get_animation_set(const char *name) {
return _animation_sets.get_object(name);
}
bool qdGameDispatcherBase::is_animation_set_in_list(const char *name) {
return _animation_sets.is_in_list(name);
}
bool qdGameDispatcherBase::is_animation_set_in_list(qdAnimationSet *p) {
return _animation_sets.is_in_list(p);
}
#ifdef __QD_DEBUG_ENABLE__
bool qdGameDispatcherBase::get_resources_info(qdResourceInfoContainer &infos) const {
for (qdSoundList::const_iterator it = sound_list().begin(); it != sound_list().end(); ++it) {
if ((*it)->is_resource_loaded())
infos.push_back(qdResourceInfo(*it, *it));
}
for (qdAnimationList::const_iterator it = animation_list().begin(); it != animation_list().end(); ++it) {
if ((*it)->is_resource_loaded())
infos.push_back(qdResourceInfo(*it, *it));
}
return true;
}
#endif
} // namespace QDEngine

View File

@@ -0,0 +1,165 @@
/* 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/>.
*
*/
#ifndef QDENGINE_QDCORE_QD_GAME_DISPATCHER_BASE_H
#define QDENGINE_QDCORE_QD_GAME_DISPATCHER_BASE_H
#include "qdengine/qdcore/qd_conditional_object.h"
#include "qdengine/qdcore/qd_object_map_container.h"
#include "qdengine/qdcore/qd_scale_info.h"
#include "qdengine/qd_fwd.h"
#include "qdengine/parser/xml_fwd.h"
#ifdef __QD_DEBUG_ENABLE__
#include "qdengine/qdcore/qd_resource.h"
#endif
namespace QDEngine {
typedef void (*qdLoadingProgressFnc)(int percents_loaded, void *data);
class qdLoadingProgressInfo {
uint32 _total_size;
uint32 _loaded_size;
void *_data;
qdLoadingProgressFnc _progress_fnc;
public:
qdLoadingProgressFnc set_callback_fnc(qdLoadingProgressFnc p, void *dp) {
qdLoadingProgressFnc old_fnc = _progress_fnc;
_progress_fnc = p;
_data = dp;
return old_fnc;
}
void set_total_size(int sz) {
_total_size = sz;
_loaded_size = 0;
}
int total_size() const {
return _total_size;
}
void show_progress(int sz) {
if (!_total_size || !_progress_fnc) return;
_loaded_size += sz;
uint32 percents = round(float(_loaded_size) / float(_total_size) * 100.0f);
(*_progress_fnc)(percents, _data);
}
qdLoadingProgressInfo() {
_progress_fnc = 0;
_total_size = _loaded_size = 0;
_data = 0;
}
~qdLoadingProgressInfo() { };
};
class qdGameDispatcherBase : public qdConditionalObject {
public:
qdGameDispatcherBase();
~qdGameDispatcherBase();
void set_resources_size(int sz) {
_loading_progress.set_total_size(sz);
}
qdLoadingProgressFnc set_loading_progress_callback(qdLoadingProgressFnc p, void *dp = 0) {
return _loading_progress.set_callback_fnc(p, dp);
}
void show_loading_progress(int sz = 0);
bool add_sound(qdSound *p);
bool remove_sound(const char *name);
bool remove_sound(qdSound *p);
virtual qdSound *get_sound(const char *name);
bool is_sound_in_list(const char *name);
bool is_sound_in_list(qdSound *p);
bool add_animation(qdAnimation *p);
bool remove_animation(const char *name);
bool remove_animation(qdAnimation *p);
virtual qdAnimation *get_animation(const char *name);
bool is_animation_in_list(const char *name);
bool is_animation_in_list(qdAnimation *p);
bool add_animation_set(qdAnimationSet *p);
bool remove_animation_set(const char *name);
bool remove_animation_set(qdAnimationSet *p);
virtual qdAnimationSet *get_animation_set(const char *name);
bool is_animation_set_in_list(const char *name);
bool is_animation_set_in_list(qdAnimationSet *p);
const qdSoundList &sound_list() const {
return _sounds.get_list();
}
const qdAnimationList &animation_list() const {
return _animations.get_list();
}
const qdAnimationSetList &animation_set_list() const {
return _animation_sets.get_list();
}
virtual void load_script_body(const xml::tag *p);
virtual bool save_script_body(Common::WriteStream &fh, int indent = 0) const;
virtual int load_resources();
void free_resources();
virtual void quant(float dt) { };
void add_scale_info(qdScaleInfo *p) {
_scale_infos.push_back(*p);
}
bool get_object_scale(const char *p, float &sc);
bool set_object_scale(const char *p, float sc);
virtual qdScaleInfo *get_scale_info(const char *p);
virtual int get_resources_size();
qdConditionalObject::trigger_start_mode trigger_start() {
return qdConditionalObject::TRIGGER_START_FAILED;
}
#ifdef __QD_DEBUG_ENABLE__
virtual bool get_resources_info(qdResourceInfoContainer &infos) const;
#endif
private:
qdObjectMapContainer<qdAnimation> _animations;
qdObjectMapContainer<qdAnimationSet> _animation_sets;
qdObjectMapContainer<qdSound> _sounds;
typedef Std::vector<qdScaleInfo> scale_info_container_t;
scale_info_container_t _scale_infos;
qdLoadingProgressInfo _loading_progress;
};
} // namespace QDEngine
#endif // QDENGINE_QDCORE_QD_GAME_DISPATCHER_BASE_H

View File

@@ -0,0 +1,114 @@
/* 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 "qdengine/qd_fwd.h"
#include "qdengine/parser/xml_tag_buffer.h"
#include "qdengine/parser/qdscr_parser.h"
#include "qdengine/qdcore/qd_game_end.h"
#include "qdengine/qdcore/qd_game_dispatcher.h"
namespace QDEngine {
qdGameEnd::qdGameEnd() {
}
qdGameEnd::qdGameEnd(const qdGameEnd &end) : qdConditionalObject(end),
_interface_screen(end._interface_screen) {
}
qdGameEnd::~qdGameEnd() {
}
qdGameEnd &qdGameEnd::operator = (const qdGameEnd &end) {
if (this == &end) return *this;
*static_cast<qdConditionalObject *>(this) = end;
_interface_screen = end._interface_screen;
return *this;
}
qdConditionalObject::trigger_start_mode qdGameEnd::trigger_start() {
if (qdGameDispatcher *p = qdGameDispatcher::get_dispatcher()) {
p->set_game_end(this);
return qdConditionalObject::TRIGGER_START_ACTIVATE;
}
return qdConditionalObject::TRIGGER_START_FAILED;
}
bool qdGameEnd::load_script(const xml::tag *p) {
load_conditions_script(p);
for (xml::tag::subtag_iterator it = p->subtags_begin(); it != p->subtags_end(); ++it) {
switch (it->ID()) {
case QDSCR_NAME:
set_name(it->data());
break;
case QDSCR_FLAG:
set_flag(xml::tag_buffer(*it).get_int());
break;
case QDSCR_GAME_END_SCREEN:
set_interface_screen(it->data());
break;
}
}
return true;
}
bool qdGameEnd::save_script(Common::WriteStream &fh, int indent) const {
for (int i = 0; i < indent; i++) {
fh.writeString("\t");
}
fh.writeString("<game_end");
if (name()) {
fh.writeString(Common::String::format(" name=\"%s\"", qdscr_XML_string(name())));
}
if (flags()) {
fh.writeString(Common::String::format(" flags=\"%d\"", flags()));
}
if (!_interface_screen.empty()) {
fh.writeString(Common::String::format(" end_screen=\"%s\"", qdscr_XML_string(_interface_screen.c_str())));
}
if (has_conditions()) {
fh.writeString(">\r\n");
save_conditions_script(fh, indent);
for (int i = 0; i < indent; i++) {
fh.writeString("\t");
}
fh.writeString("</game_end>\r\n");
} else {
fh.writeString("/>\r\n");
}
return true;
}
} // namespace QDEngine

View File

@@ -0,0 +1,71 @@
/* 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/>.
*
*/
#ifndef QDENGINE_QDCORE_QD_GAME_END_H
#define QDENGINE_QDCORE_QD_GAME_END_H
#include "qdengine/parser/xml_fwd.h"
#include "qdengine/qdcore/qd_conditional_object.h"
namespace QDEngine {
//! Концовка игры.
class qdGameEnd : public qdConditionalObject {
public:
qdGameEnd();
qdGameEnd(const qdGameEnd &end);
qdGameEnd &operator = (const qdGameEnd &end);
~qdGameEnd();
const char *interface_screen() const {
return _interface_screen.c_str();
}
bool has_interface_screen() const {
return !_interface_screen.empty();
}
void set_interface_screen(const char *name) {
if (name) _interface_screen = name;
else _interface_screen.clear();
}
qdConditionalObject::trigger_start_mode trigger_start();
int named_object_type() const {
return QD_NAMED_OBJECT_GAME_END;
};
bool load_script(const xml::tag *p);
bool save_script(Common::WriteStream &fh, int indent = 0) const;
private:
//! Имя интерфейсного экрана, на который выходить из игры.
/**
Если строка пустая - будет выход в main menu.
*/
Common::String _interface_screen;
};
} // namespace QDEngine
#endif // QDENGINE_QDCORE_QD_GAME_END_H

View File

@@ -0,0 +1,235 @@
/* 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/stream.h"
#include "qdengine/qd_fwd.h"
#include "qdengine/xmath.h"
#include "qdengine/parser/xml_tag_buffer.h"
#include "qdengine/parser/qdscr_parser.h"
#include "qdengine/qdcore/qd_game_object.h"
#include "qdengine/qdcore/qd_camera.h"
namespace QDEngine {
qdGameObject::qdGameObject() : _r(0, 0, 0),
_parallax_offset(0.0f, 0.0f),
_screen_r(0, 0),
_screen_depth(0.0f), _tempPosInList(0) {
}
qdGameObject::qdGameObject(const qdGameObject &obj) : qdNamedObject(obj),
_r(obj._r),
_parallax_offset(obj._parallax_offset),
_screen_r(obj._screen_r),
_screen_depth(obj._screen_depth), _tempPosInList(obj._tempPosInList) {
}
qdGameObject::~qdGameObject() {
}
qdGameObject &qdGameObject::operator = (const qdGameObject &obj) {
if (this == &obj) return *this;
*static_cast<qdNamedObject *>(this) = obj;
_r = obj._r;
_parallax_offset = obj._parallax_offset;
_screen_r = obj._screen_r;
_screen_depth = obj._screen_depth;
return *this;
}
bool qdGameObject::load_script_body(const xml::tag *p) {
int fl;
for (xml::tag::subtag_iterator it = p->subtags_begin(); it != p->subtags_end(); ++it) {
switch (it->ID()) {
case QDSCR_NAME:
set_name(it->data());
break;
case QDSCR_POS2D:
xml::tag_buffer(*it) > _r.x > _r.y;
_r.z = 0.0f;
break;
case QDSCR_POS3D:
xml::tag_buffer(*it) > _r.x > _r.y > _r.z;
break;
case QDSCR_FLAG:
xml::tag_buffer(*it) > fl;
set_flag(fl);
break;
case QDSCR_PARALLAX_OFFSET:
xml::tag_buffer(*it) > _parallax_offset.x > _parallax_offset.y;
break;
}
}
return true;
}
bool qdGameObject::save_script_body(Common::WriteStream &fh, int indent) const {
for (int i = 0; i <= indent; i++) {
fh.writeString("\t");
}
fh.writeString(Common::String::format("<pos_3d>%f %f %f</pos_3d>\r\n", _r.x, _r.y, _r.z));
if (_parallax_offset.x || _parallax_offset.y) {
for (int i = 0; i <= indent; i++) {
fh.writeString("\t");
}
fh.writeString(Common::String::format("<parallax_offset>%d %d</parallax_offset>\r\n", _parallax_offset.x, _parallax_offset.y));
}
if (flags()) {
for (int i = 0; i <= indent; i++) {
fh.writeString("\t");
}
if (debugChannelSet(-1, kDebugLog))
fh.writeString(Common::String::format("<flag>%s</flag>\r\n", flag2str(flags()).c_str()));
else
fh.writeString(Common::String::format("<flag>%d</flag>\r\n", flags()));
}
return true;
}
void qdGameObject::quant(float dt) {
}
bool qdGameObject::update_screen_pos() {
if (!check_flag(QD_OBJ_SCREEN_COORDS_FLAG)) {
if (const qdCamera * cp = qdCamera::current_camera()) {
Vect3f v = cp->global2camera_coord(R());
if (check_flag(QD_OBJ_FIXED_SCREEN_COORDS_FLAG)) {
_screen_r = cp->camera_coord2rscr(v);
_screen_r.x += cp->get_scr_sx() / 2;
_screen_r.y = cp->get_scr_sy() / 2 - _screen_r.y;
_screen_depth = 0.0f;
} else {
_screen_r = cp->camera_coord2scr(v);
Vect3f rr = R();
rr.z = cp->get_grid_center().z;
_screen_depth = cp->global2camera_coord(rr).z;
}
if (_parallax_offset.x || _parallax_offset.y) {
_screen_r.x += round(float(_parallax_offset.x) * cp->scrolling_phase_x());
_screen_r.y += round(float(_parallax_offset.y) * cp->scrolling_phase_y());
}
} else
return false;
} else {
_screen_r = Vect2i(R().xi(), R().yi());
_screen_depth = 0.0f;
}
return true;
}
bool qdGameObject::load_data(Common::SeekableReadStream &fh, int saveVersion) {
debugC(3, kDebugSave, " qdGameObject::load_data(): before: %d", (int)fh.pos());
if (!qdNamedObject::load_data(fh, saveVersion)) {
return false;
}
_r.x = fh.readFloatLE();
_r.y = fh.readFloatLE();
_r.z = fh.readFloatLE();
debugC(3, kDebugSave, " qdGameObject::load_data(): after: %d", (int)fh.pos());
return true;
}
bool qdGameObject::save_data(Common::WriteStream &fh) const {
debugC(3, kDebugSave, " qdGameObject::save_data(): before: %d", (int)fh.pos());
if (!qdNamedObject::save_data(fh)) {
return false;
}
fh.writeFloatLE(_r.x);
fh.writeFloatLE(_r.y);
fh.writeFloatLE(_r.z);
debugC(3, kDebugSave, " qdGameObject::save_data(): after: %d", (int)fh.pos());
return true;
}
bool qdGameObject::init() {
drop_flag(QD_OBJ_SCREEN_COORDS_FLAG);
drop_flag(QD_OBJ_STATE_CHANGE_FLAG | QD_OBJ_IS_IN_TRIGGER_FLAG | QD_OBJ_IS_IN_INVENTORY_FLAG);
drop_flag(QD_OBJ_HIDDEN_FLAG);
return true;
}
#define defFlag(x) { x, #x }
struct FlagsList {
int f;
const char *s;
} static flagList[] = {
defFlag(QD_OBJ_MOVING_FLAG),
defFlag(QD_OBJ_HIDDEN_FLAG),
defFlag(QD_OBJ_INVERSE_PERSPECTIVE_FLAG),
defFlag(QD_OBJ_SHOW_NAME_FLAG),
defFlag(QD_OBJ_NO_SCALE_FLAG),
defFlag(QD_OBJ_SCREEN_COORDS_FLAG),
defFlag(QD_OBJ_NON_PLAYER_PERSONAGE_FLAG),
defFlag(QD_OBJ_HAS_BOUND_FLAG),
defFlag(QD_OBJ_DISABLE_MOVEMENT_FLAG),
defFlag(QD_OBJ_DISABLE_MOUSE_FLAG),
defFlag(QD_OBJ_IS_IN_TRIGGER_FLAG),
defFlag(QD_OBJ_STATE_CHANGE_FLAG),
defFlag(QD_OBJ_IS_IN_INVENTORY_FLAG),
defFlag(QD_OBJ_KEYBOARD_CONTROL_FLAG),
defFlag(QD_OBJ_FIXED_SCREEN_COORDS_FLAG),
};
Common::String qdGameObject::flag2str(int fl) const {
Common::String res;
for (int i = 0; i < ARRAYSIZE(flagList); i++) {
if (fl & flagList[i].f) {
if (!res.empty())
res += " | ";
res += flagList[i].s;
fl &= ~flagList[i].f;
}
}
if (fl)
res += Common::String::format(" | %x", fl);
return res;
}
} // namespace QDEngine

View File

@@ -0,0 +1,199 @@
/* 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/>.
*
*/
#ifndef QDENGINE_QDCORE_QD_GAME_OBJECT_H
#define QDENGINE_QDCORE_QD_GAME_OBJECT_H
#include "qdengine/parser/xml_fwd.h"
#include "qdengine/system/input/mouse_input.h"
#include "qdengine/system/graphics/gr_screen_region.h"
#include "qdengine/qdcore/qd_named_object.h"
namespace Common {
class WriteStream;
}
namespace QDEngine {
const int QD_OBJ_MOVING_FLAG = 0x01;
const int QD_OBJ_HIDDEN_FLAG = 0x02;
const int QD_OBJ_INVERSE_PERSPECTIVE_FLAG = 0x04;
const int QD_OBJ_SHOW_NAME_FLAG = 0x08;
const int QD_OBJ_NO_SCALE_FLAG = 0x10;
const int QD_OBJ_SCREEN_COORDS_FLAG = 0x20;
const int QD_OBJ_NON_PLAYER_PERSONAGE_FLAG = 0x40;
const int QD_OBJ_HAS_BOUND_FLAG = 0x80;
const int QD_OBJ_DISABLE_MOVEMENT_FLAG = 0x100;
const int QD_OBJ_DISABLE_MOUSE_FLAG = 0x200;
const int QD_OBJ_IS_IN_TRIGGER_FLAG = 0x400;
const int QD_OBJ_STATE_CHANGE_FLAG = 0x800;
const int QD_OBJ_IS_IN_INVENTORY_FLAG = 0x1000;
const int QD_OBJ_KEYBOARD_CONTROL_FLAG = 0x2000;
const int QD_OBJ_FIXED_SCREEN_COORDS_FLAG = 0x4000;
//! Базовый класс для игровых объектов.
class qdGameObject : public qdNamedObject {
public:
qdGameObject();
qdGameObject(const qdGameObject &obj);
~qdGameObject();
qdGameObject &operator = (const qdGameObject &obj);
void set_parallax_offset(int offs_x, int offs_y) {
_parallax_offset.x = offs_x;
_parallax_offset.y = offs_y;
}
const Vect2i &get_parallax_offset() const {
return _parallax_offset;
}
virtual void redraw(int offs_x = 0, int offs_y = 0) const = 0;
virtual void debug_redraw() const = 0;
virtual void draw_contour(uint32 color) const = 0;
virtual void draw_shadow(int offs_x, int offs_y, uint32 color, int alpha) const = 0;
virtual bool need_redraw() const {
return false;
}
virtual void post_redraw() { }
virtual const grScreenRegion last_screen_region() const {
return grScreenRegion_EMPTY;
}
virtual grScreenRegion screen_region() const {
return grScreenRegion(grScreenRegion_EMPTY);
}
virtual bool mouse_handler(int x, int y, mouseDispatcher::mouseEvent ev) = 0;
virtual bool hit(int x, int y) const = 0;
const Vect2i &screen_pos() const {
return _screen_r;
}
virtual bool update_screen_pos();
virtual Vect2s screen_size() const = 0;
float screen_depth() const {
return _screen_depth;
}
virtual bool is_visible() const {
return !check_flag(QD_OBJ_HIDDEN_FLAG);
}
const Vect3f &R() const {
return _r;
}
void set_pos(const Vect3f &rr) {
_r = rr;
}
virtual bool load_script(const xml::tag *p) = 0;
virtual bool save_script(Common::WriteStream &fh, int indent = 0) const = 0;
virtual bool load_resources() {
return true;
};
virtual void free_resources() { };
//! Инициализация объекта, вызывается при старте и перезапуске игры.
virtual bool init();
virtual void quant(float dt);
virtual bool init_grid_zone() {
return false;
}
virtual bool toggle_grid_zone(bool make_walkable = false) {
return false;
}
virtual bool save_grid_zone() {
return false;
}
virtual bool restore_grid_zone() {
return false;
}
virtual bool set_grid_zone_attributes(int attr) const {
return false;
}
virtual bool drop_grid_zone_attributes(int attr) const {
return false;
}
//! Копирует в буфер отладочную информацию для вывода на экран в отладочном режиме.
virtual bool get_debug_info(Common::String &buf) const {
return true;
}
//! Загрузка данных из сэйва.
bool load_data(Common::SeekableReadStream &fh, int saveVersion);
//! Запись данных в сэйв.
bool save_data(Common::WriteStream &fh) const;
const Vect2i &get_screen_R() const {
return _screen_r;
}
void setTempPosInList(uint pos) { _tempPosInList = pos; }
uint tempPosInList() const { return _tempPosInList; }
Common::String flag2str(int flags) const;
protected:
virtual bool load_script_body(const xml::tag *p);
virtual bool save_script_body(Common::WriteStream &fh, int indent = 0) const;
void set_screen_R(const Vect2i &v) {
_screen_r = v;
}
private:
Vect3f _r;
Vect2i _parallax_offset;
Vect2i _screen_r;
float _screen_depth;
uint _tempPosInList;
};
#ifdef __QD_DEBUG_ENABLE__
inline bool qdbg_is_object(const qdNamedObject *obj, const char *scene_name, const char *object_name) {
if (dynamic_cast<const qdGameObject * >(obj)) {
if (obj->name() && !strcmp(object_name, obj->name())) {
if (!scene_name || (obj->owner() && obj->owner()->name() && !strcmp(obj->owner()->name(), scene_name)))
return true;
}
}
return false;
}
#else
inline bool qdbg_is_object(const qdNamedObject *, const char *, const char *) {
return false;
}
#endif
} // namespace QDEngine
#endif // QDENGINE_QDCORE_QD_GAME_OBJECT_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,391 @@
/* 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/>.
*
*/
#ifndef QDENGINE_QDCORE_QD_GAME_OBJECT_ANIMATED_H
#define QDENGINE_QDCORE_QD_GAME_OBJECT_ANIMATED_H
#include "qdengine/parser/xml_fwd.h"
#include "qdengine/qdcore/qd_animation.h"
#include "qdengine/qdcore/qd_coords_animation.h"
#include "qdengine/qdcore/qd_game_object.h"
#include "qdengine/qdcore/qd_game_object_state.h"
namespace QDEngine {
//! Dynamic object
/**
Paticularly dynamic object, actors and the mouse.
*/
class qdGameObjectAnimated : public qdGameObject {
public:
enum StateStatus {
STATE_INACTIVE,
STATE_QUEUED,
STATE_ACTIVE,
STATE_DONE
};
qdGameObjectAnimated();
qdGameObjectAnimated(const qdGameObjectAnimated &obj);
~qdGameObjectAnimated();
qdGameObjectAnimated &operator = (const qdGameObjectAnimated &obj);
int named_object_type() const {
return QD_NAMED_OBJECT_ANIMATED_OBJ;
}
//! Возвращает баунд объекта.
virtual const Vect3f &bound(bool perspective_correction = true) const;
//! Устанавливает баунд объекта по текущему состоянию.
bool auto_bound();
//! Возвращает радиус объекта.
virtual float radius() const;
//! Устанавливает баунд объекта.
void set_bound(const Vect3f &b);
//! Возвращает true, если у объекта выставлен баунд.
bool has_bound() const {
if (check_flag(QD_OBJ_HAS_BOUND_FLAG)) return true;
if (_cur_state != -1 && _states[_cur_state]->has_bound()) return true;
return false;
}
//! Отрисовка баунда (для отладки).
void draw_bound() const;
void draw_bound(Vect3f r, Vect3f const &bound, int const color) const;
//! Пересекается ли баунд, расположенный в точке с заданным баундом с центром в cen
bool inters_with_bound(Vect3f bnd, Vect3f cen, bool perspective_correction = true) const;
//! Возвращает номер текущего состояния объекта.
int cur_state() const {
return _cur_state;
}
//! Устанавливает номер текущего состояния объекта.
void set_cur_state(int st) {
_cur_state = st;
}
//! Возвращает количество состояний объекта.
int max_state() const {
return _states.size();
}
//! Возвращает номер состояния или -1 если не может такое состояние найти.
int get_state_index(const qdGameObjectState *p) const;
//! Установка владельца состояний.
void set_states_owner();
//! Возвращает true, если состояние с именем state_name активно.
bool is_state_active(const char *state_name) const;
//! Возвращает true, если состояние с именем state_name было активировано.
bool was_state_active(const char *state_name) const;
//! Возвращает true, если состояние state активно.
bool is_state_active(const qdGameObjectState *p) const {
if (_cur_state != -1 && _states[_cur_state] == p)
return true;
return false;
}
//! Возвращает true, если состояние с именем state_name было активно перед активным в данный момент состоянием.
bool was_state_previous(const char *state_name) const;
//! Возвращает true, если состояние p было активно перед активным в данный момент состоянием.
bool was_state_previous(const qdGameObjectState *p) const {
return (_last_state == p);
}
//! Возвращает true, если состояние state было активировано.
bool was_state_active(const qdGameObjectState *p) const {
return p->check_flag(qdGameObjectState::QD_OBJ_STATE_FLAG_WAS_ACTIVATED);
}
bool is_state_waiting(const qdGameObjectState *p) const {
if (_queued_state == p) return true;
return false;
}
bool is_state_waiting(const char *state_name) const;
//! Возвращает состояния объекта.
const qdGameObjectStateVector &state_vector() const {
return _states;
}
//! Возвращает true, если объект в данный момент может менять состояние.
virtual bool can_change_state(const qdGameObjectState *state = NULL) const;
qdGameObjectState *get_state(const char *state_name);
qdGameObjectState *get_state(int state_index);
qdGameObjectState *get_cur_state() {
return get_state(cur_state());
}
const qdGameObjectState *get_state(const char *state_name) const;
const qdGameObjectState *get_state(int state_index) const;
const qdGameObjectState *get_cur_state() const {
return get_state(cur_state());
}
void set_queued_state(qdGameObjectState *st) {
_queued_state = st;
}
qdGameObjectState *queued_state() {
return _queued_state;
}
const qdGameObjectState *queued_state() const {
return _queued_state;
}
void merge_states(qdGameObjectAnimated *p);
void split_states(qdGameObjectAnimated *p);
StateStatus state_status(const qdGameObjectState *p) const;
bool add_state(qdGameObjectState *p);
bool insert_state(int iBefore, qdGameObjectState *p);
qdGameObjectState *remove_state(int state_num);
bool remove_state(qdGameObjectState *p);
virtual void set_state(int st);
virtual void set_state(qdGameObjectState *p);
void restore_state();
bool has_camera_mode() const;
const qdCameraMode &camera_mode() const;
//! Возвращает количество имеющихся у объекта направлений.
int num_directions() const;
const Vect3f &default_R() const {
return _default_r;
}
void set_default_pos(const Vect3f &r) {
_default_r = r;
}
void set_default_state();
virtual qdGameObjectState *get_default_state();
virtual const qdGameObjectState *get_default_state() const;
qdGameObjectState *get_inventory_state();
qdGameObjectState *get_mouse_state();
qdGameObjectState *get_mouse_hover_state();
bool update_screen_pos();
// Animation
qdAnimation *get_animation() {
return &_animation;
}
const qdAnimation *get_animation() const {
return &_animation;
}
void set_animation(qdAnimation *p, const qdAnimationInfo *inf = NULL);
void set_animation_info(qdAnimationInfo *inf);
Vect2s screen_size() const {
return Vect2s(_animation.size_x(), _animation.size_y());
}
void set_screen_rotation(float target_angle, float speed);
float screen_rotation() const;
void set_screen_scale(const Vect2f &scale, const Vect2f &speed);
const Vect2f &screen_scale() const;
bool has_screen_transform() const {
return _current_transform();
}
// Inventory
int inventory_type() const {
return _inventory_type;
}
void set_inventory_type(int tp) {
_inventory_type = tp;
}
// Logic
bool hit(int x, int y) const;
bool mouse_handler(int x, int y, mouseDispatcher::mouseEvent ev);
void quant(float dt);
//! Обработка окончания текущего состояния.
bool handle_state_end();
// Redraw
void redraw(int offs_x = 0, int offs_y = 0) const;
bool need_redraw() const;
void post_redraw();
void draw_shadow(int offs_x, int offs_y, uint32 color, int alpha) const;
bool get_debug_info(Common::String &buf) const;
void debug_redraw() const;
void draw_contour(uint32 color) const;
void draw_grid_zone(const Vect2s sz) const;
int mouse_cursor_ID() const;
bool load_script(const xml::tag *p);
bool save_script(Common::WriteStream &fh, int indent = 0) const;
//! Загрузка данных из сэйва.
bool load_data(Common::SeekableReadStream &fh, int save_version);
//! Запись данных в сэйв.
bool save_data(Common::WriteStream &fh) const;
bool load_resources();
void free_resources();
//! Инициализация объекта, вызывается при старте и перезапуске игры.
bool init();
bool is_visible() const;
const Vect2s &grid_size() const {
return _grid_size;
}
bool init_grid_zone();
virtual bool toggle_grid_zone(bool make_walkable = false);
bool save_grid_zone();
bool restore_grid_zone();
bool set_grid_zone_attributes(int attr) const;
bool check_grid_zone_attributes(int attr) const;
bool drop_grid_zone_attributes(int attr) const;
bool set_grid_zone_attributes(const Vect2f &r, int attr) const;
bool check_grid_zone_attributes(const Vect2f &r, int attr) const;
bool drop_grid_zone_attributes(const Vect2f &r, int attr) const;
const char *inventory_name() const {
return _inventory_name.c_str();
}
void set_inventory_name(const char *name) {
if (name)
_inventory_name = name;
else
_inventory_name.clear();
}
bool has_inventory_name() const {
return !_inventory_name.empty();
}
const grScreenRegion last_screen_region() const {
return _last_screen_region;
}
virtual grScreenRegion screen_region() const;
int inventory_cell_index() const {
return _inventory_cell_index;
}
void set_inventory_cell_index(int idx) {
_inventory_cell_index = idx;
}
int last_chg_time() const {
return _last_chg_time;
}
void set_last_chg_time(int time) {
_last_chg_time = time;
}
int idle_time() const;
int shadow_color() const;
int shadow_alpha() const;
void set_shadow(uint32 color, int alpha) {
_shadow_color = color;
_shadow_alpha = alpha;
}
void clear_shadow() {
set_shadow(0, QD_NO_SHADOW_ALPHA);
}
protected:
bool load_script_body(const xml::tag *p);
bool save_script_body(Common::WriteStream &fh, int indent = 0) const;
void set_last_state(qdGameObjectState *p) {
if (!p || !p->check_flag(qdGameObjectState::QD_OBJ_STATE_FLAG_MOUSE_STATE | qdGameObjectState::QD_OBJ_STATE_FLAG_MOUSE_HOVER_STATE))
_last_state = p;
}
void set_last_inventory_state(qdGameObjectState *p) {
if (!p || (p->check_flag(qdGameObjectState::QD_OBJ_STATE_FLAG_INVENTORY) && !p->check_flag(qdGameObjectState::QD_OBJ_STATE_FLAG_MOUSE_STATE | qdGameObjectState::QD_OBJ_STATE_FLAG_MOUSE_HOVER_STATE)))
_last_inventory_state = p;
}
private:
int _cur_state;
qdGameObjectStateVector _states;
qdGameObjectState *_queued_state;
qdGameObjectState *_last_inventory_state;
int _inventory_type;
qdAnimation _animation;
Vect3f _bound;
float _radius;
Vect3f _default_r;
Vect3f _grid_r;
Vect2s _grid_size;
Common::String _inventory_name;
qdScreenTransform _current_transform;
qdScreenTransform _target_transform;
qdScreenTransform _transform_speed;
qdScreenTransform _last_transform;
qdGameObjectState *_last_state;
//! Индекс ячейки инвентори, в которой лежал объект.
int _inventory_cell_index;
const qdAnimationFrame *_last_frame;
grScreenRegion _last_screen_region;
float _last_screen_depth;
uint32 _lastShadowColor;
int _lastShadowAlpha;
//! Последнее время изменения объекта
int _last_chg_time;
//! Цвет затенения.
uint32 _shadow_color;
//! Прозрачность затенения, значения - [0, 255], если равно QD_NO_SHADOW_ALPHA, то персонаж не затеняется.
int _shadow_alpha;
void clear_states();
};
} // namespace QDEngine
#endif // QDENGINE_QDCORE_QD_GAME_OBJECT_ANIMATED_H

View File

@@ -0,0 +1,293 @@
/* 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/stream.h"
#include "qdengine/qdengine.h"
#include "qdengine/qd_fwd.h"
#include "qdengine/qdcore/qd_rnd.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_game_object_mouse.h"
#include "qdengine/qdcore/qd_game_dispatcher.h"
namespace QDEngine {
qdGameObjectMouse::qdGameObjectMouse() : _object(NULL),
_object_screen_region(grScreenRegion_EMPTY) {
set_flag(QD_OBJ_SCREEN_COORDS_FLAG);
set_name("\xcc\xfb\xf8\xfc"); // "Мышь" -- mouse
_screen_pos_offset = _screen_pos_offset_delta = Vect2f(0, 0);
for (int i = 0; i < MAX_CURSOR_ID; i++)
_default_cursors[i] = 0;
}
qdGameObjectMouse::qdGameObjectMouse(const qdGameObjectMouse &obj) : qdGameObjectAnimated(obj),
_object(NULL),
_object_screen_region(grScreenRegion_EMPTY) {
set_flag(QD_OBJ_SCREEN_COORDS_FLAG);
set_name("\xcc\xfb\xf8\xfc"); // "Мышь" -- mouse
for (int i = 0; i < MAX_CURSOR_ID; i++)
_default_cursors[i] = 0;
}
qdGameObjectMouse::~qdGameObjectMouse() {
}
qdGameObjectMouse &qdGameObjectMouse::operator = (const qdGameObjectMouse &obj) {
if (this == &obj) return *this;
*static_cast<qdGameObjectAnimated *>(this) = obj;
for (int i = 0; i < MAX_CURSOR_ID; i++)
_default_cursors[i] = obj._default_cursors[i];
return *this;
}
bool qdGameObjectMouse::load_script_body(const xml::tag *p) {
qdGameObjectAnimated::load_script_body(p);
for (xml::tag::subtag_iterator it = p->subtags_begin(); it != p->subtags_end(); ++it) {
switch (it->ID()) {
case QDSCR_MOUSE_DEFAULT_CURSORS: {
xml::tag_buffer buf(*it);
for (int i = 0; i < MAX_CURSOR_ID; i++) buf > _default_cursors[i];
}
break;
}
}
for (int i = 0; i < max_state(); i++) {
if (get_state(i)->state_type() == qdGameObjectState::STATE_STATIC) {
static_cast<qdGameObjectStateStatic *>(get_state(i))->animation_info()->set_flag(QD_ANIMATION_FLAG_LOOP);
}
}
return true;
}
bool qdGameObjectMouse::save_script_body(Common::WriteStream &fh, int indent) const {
return qdGameObjectAnimated::save_script_body(fh, indent);
}
bool qdGameObjectMouse::load_script(const xml::tag *p) {
return load_script_body(p);
}
bool qdGameObjectMouse::save_script(Common::WriteStream &fh, int indent) const {
for (int i = 0; i < indent; i++) {
fh.writeString("\t");
}
fh.writeString(Common::String::format("<mouse_object name=\"%s\"", qdscr_XML_string(name())));
fh.writeString(" default_cursors=\"");
for (int i = 0; i < MAX_CURSOR_ID; i++) {
if (i) fh.writeString(" ");
fh.writeString(Common::String::format("%d", _default_cursors[i]));
}
fh.writeString("\">\r\n");
save_script_body(fh, indent);
for (int i = 0; i < indent; i++) {
fh.writeString("\t");
}
fh.writeString("</mouse_object>\r\n");
return true;
}
bool qdGameObjectMouse::load_data(Common::SeekableReadStream &fh, int save_version) {
debugC(3, kDebugSave, " qdGameObjectMouse::load_data before: %d", (int)fh.pos());
if (!qdGameObjectAnimated::load_data(fh, save_version))
return false;
int fl = fh.readSint32LE();
if (fl) {
qdNamedObjectReference ref;
if (!ref.load_data(fh, save_version))
return false;
if (qdGameDispatcher * p = qdGameDispatcher::get_dispatcher())
_object = static_cast<qdGameObjectAnimated * >(p->get_named_object(&ref));
if (!_object) return false;
}
debugC(3, kDebugSave, " qdGameObjectMouse::load_data after: %d", (int)fh.pos());
return true;
}
bool qdGameObjectMouse::save_data(Common::WriteStream &fh) const {
debugC(3, kDebugSave, " qdGameObjectMouse::save_data before: %d", (int)fh.pos());
if (!qdGameObjectAnimated::save_data(fh))
return false;
if (_object) {
fh.writeUint32LE(1);
qdNamedObjectReference ref(_object);
if (!ref.save_data(fh)) {
return false;
}
} else {
fh.writeUint32LE(0);
}
debugC(3, kDebugSave, " qdGameObjectMouse::save_data after: %d", (int)fh.pos());
return true;
}
void qdGameObjectMouse::redraw(int offs_x, int offs_y) const {
debugC(2, kDebugGraphics, "qdGameObjectMouse::redraw([%d, %d]), name: '%s'", offs_x, offs_y, transCyrillic(name()));
if (_object && !qdInterfaceDispatcher::get_dispatcher()->is_active()) {
update_object_position();
const qdGameObjectState *p = _object-> get_cur_state();
if (p->check_flag(qdGameObjectState::QD_OBJ_STATE_FLAG_STAY_IN_INVENTORY) || !p->has_mouse_cursor_ID())
_object->redraw(offs_x, offs_y);
if (p->check_flag(qdGameObjectState::QD_OBJ_STATE_FLAG_STAY_IN_INVENTORY) || p->has_mouse_cursor_ID())
qdGameObjectAnimated::redraw(offs_x, offs_y);
} else {
qdGameObjectAnimated::redraw(offs_x, offs_y);
}
}
void qdGameObjectMouse::set_cursor(cursor_ID_t id) {
debugC(2, kDebugGraphics, "qdGameObjectMouse::set_cursor(%d)", id);
if (cur_state() != _default_cursors[id])
set_state(_default_cursors[id]);
}
void qdGameObjectMouse::take_object(qdGameObjectAnimated *p) {
if (_object) {
if (_object->get_cur_state() && _object->get_cur_state()->check_flag(qdGameObjectState::QD_OBJ_STATE_FLAG_MOUSE_STATE)) {
if (qdGameObjectState * sp = _object->get_inventory_state())
_object->set_state(sp);
}
// object_->drop_flag(QD_OBJ_IS_IN_INVENTORY_FLAG);
}
_object = p;
if (_object) {
_object->set_flag(QD_OBJ_IS_IN_INVENTORY_FLAG);
if (qdGameObjectState * sp = _object->get_mouse_state()) {
if (_object->get_cur_state() != sp) {
qdGameObjectState *cur_st = _object->get_cur_state();
if (cur_st && cur_st->check_flag(qdGameObjectState::QD_OBJ_STATE_FLAG_MOUSE_HOVER_STATE))
cur_st = cur_st->prev_state();
sp->set_prev_state(cur_st);
_object->set_state(sp);
}
}
}
}
void qdGameObjectMouse::update_object_position() const {
if (_object) {
if (_object-> get_cur_state()->check_flag(qdGameObjectState::QD_OBJ_STATE_FLAG_STAY_IN_INVENTORY)) {
if (const qdGameDispatcher * dp = static_cast<const qdGameDispatcher * >(owner())) {
Vect2s pos = dp->cur_inventory()->cell_position(_object->inventory_cell_index());
_object->set_pos(Vect3f(pos.x, pos.y, 0));
}
} else
_object->set_pos(R());
_object->update_screen_pos();
}
}
void qdGameObjectMouse::pre_redraw() {
qdGameDispatcher *dp = static_cast<qdGameDispatcher *>(owner());
if (!dp) return;
update_object_position();
if (!dp->need_full_redraw()) {
if (_object && !qdInterfaceDispatcher::get_dispatcher()->is_active()) {
if (_object->need_redraw()) {
dp->add_redraw_region(_object->last_screen_region());
dp->add_redraw_region(_object->screen_region());
}
} else
dp->add_redraw_region(_object_screen_region);
dp->add_redraw_region(last_screen_region());
dp->add_redraw_region(screen_region());
}
}
void qdGameObjectMouse::post_redraw() {
if (_object && !qdInterfaceDispatcher::get_dispatcher()->is_active()) {
_object->post_redraw();
_object_screen_region = _object->last_screen_region();
} else
_object_screen_region = grScreenRegion_EMPTY;
qdGameObjectAnimated::post_redraw();
}
void qdGameObjectMouse::quant(float dt) {
qdGameObjectAnimated::quant(dt);
if (_object)
_object->quant(dt);
if (const qdGameObjectState * p = get_cur_state()) {
if (p->rnd_move_radius() > FLT_EPS) {
if (_screen_pos_offset.norm2() >= sqr(p->rnd_move_radius()) || (_screen_pos_offset_delta.x <= FLT_EPS && _screen_pos_offset_delta.y <= FLT_EPS)) {
float angle = qd_fabs_rnd(M_PI * 2.0f);
Vect2f r(p->rnd_move_radius() * cos(angle), p->rnd_move_radius() * sin(angle));
_screen_pos_offset_delta = r - _screen_pos_offset;
_screen_pos_offset_delta.normalize(p->rnd_move_speed());
}
_screen_pos_offset.x += _screen_pos_offset_delta.x * dt;
_screen_pos_offset.y += _screen_pos_offset_delta.y * dt;
} else
_screen_pos_offset = _screen_pos_offset_delta = Vect2f(0, 0);
}
}
bool qdGameObjectMouse::update_screen_pos() {
if (qdGameObjectAnimated::update_screen_pos()) {
set_screen_R(get_screen_R() + _screen_pos_offset);
return true;
}
return false;
}
} // namespace QDEngine

View File

@@ -0,0 +1,127 @@
/* 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/>.
*
*/
#ifndef QDENGINE_QDCORE_QD_GAME_OBJECT_MOUSE_H
#define QDENGINE_QDCORE_QD_GAME_OBJECT_MOUSE_H
#include "qdengine/qdcore/qd_game_object_animated.h"
namespace Common {
class WriteStream;
}
namespace QDEngine {
//! Mouse
class qdGameObjectMouse : public qdGameObjectAnimated {
public:
qdGameObjectMouse();
qdGameObjectMouse(const qdGameObjectMouse &obj);
~qdGameObjectMouse();
qdGameObjectMouse &operator = (const qdGameObjectMouse &obj);
int named_object_type() const {
return QD_NAMED_OBJECT_MOUSE_OBJ;
}
bool load_script(const xml::tag *p);
bool save_script(Common::WriteStream &fh, int indent = 0) const;
bool load_data(Common::SeekableReadStream &fh, int save_version);
bool save_data(Common::WriteStream &fh) const;
void take_object(qdGameObjectAnimated *p);
qdGameObjectAnimated *object() {
return _object;
}
//! Идентификаторы для курсоров по-умолчанию.
enum cursor_ID_t {
//! общий курсор по-умолчанию
DEFAULT_CURSOR,
//! курсор для анимированных объектов
OBJECT_CURSOR,
//! курсор для зон на сетке
ZONE_CURSOR,
//! курсор для main menu
MAIN_MENU_CURSOR,
//! курсор для внутриигрового интерфейса
INGAME_INTERFACE_CURSOR,
//! курсор для объектов в инвентори
INVENTORY_OBJECT_CURSOR,
MAX_CURSOR_ID
};
//! Установка курсора по-умолчанию.
/**
Номер состояния отсчитывается с нуля.
*/
void set_default_cursor(cursor_ID_t id, int state_index) {
_default_cursors[id] = state_index;
}
//! Возвращает курсор по-умолчанию.
int default_cursor(cursor_ID_t id) const {
return _default_cursors[id];
}
void set_cursor(cursor_ID_t id);
void set_cursor_state(int state_idx) {
if (cur_state() != state_idx) set_state(state_idx);
}
void pre_redraw();
void redraw(int offs_x = 0, int offs_y = 0) const;
void post_redraw();
void quant(float dt);
bool update_screen_pos();
const Vect2f &screen_pos_offset() const {
return _screen_pos_offset;
}
protected:
bool load_script_body(const xml::tag *p);
bool save_script_body(Common::WriteStream &fh, int indent = 0) const;
private:
//! Объект, который прицеплен к курсору.
qdGameObjectAnimated *_object;
//! Номера состояний, соответствующие курсорам по-умолчанию.
int _default_cursors[MAX_CURSOR_ID];
grScreenRegion _object_screen_region;
Vect2f _screen_pos_offset;
Vect2f _screen_pos_offset_delta;
void update_object_position() const;
};
} // namespace QDEngine
#endif // QDENGINE_QDCORE_QD_GAME_OBJECT_MOUSE_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,500 @@
/* 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/>.
*
*/
#ifndef QDENGINE_QDCORE_QD_GAME_OBJECT_MOVING_H
#define QDENGINE_QDCORE_QD_GAME_OBJECT_MOVING_H
#include "qdengine/parser/xml_fwd.h"
#include "qdengine/qdcore/qd_game_object_animated.h"
namespace QDEngine {
class qdInterfaceButton;
const int QD_MOVING_OBJ_PATH_LENGTH = 200;
//! Персонаж.
class qdGameObjectMoving : public qdGameObjectAnimated {
public:
qdGameObjectMoving();
qdGameObjectMoving(const qdGameObjectMoving &obj);
~qdGameObjectMoving();
qdGameObjectMoving &operator = (const qdGameObjectMoving &obj);
int named_object_type() const {
return QD_NAMED_OBJECT_MOVING_OBJ;
}
enum movement_mode_t {
MOVEMENT_MODE_STOP,
MOVEMENT_MODE_TURN,
MOVEMENT_MODE_START,
MOVEMENT_MODE_MOVE,
MOVEMENT_MODE_END,
MOVEMENT_MODE_NONE_EARLY
};
//! режимы управления персонажем
enum control_type_t {
//! можно указывать мышью точки куда идти
CONTROL_MOUSE = 0x01,
//! можно рулить с клавиатуры
CONTROL_KEYBOARD = 0x02,
//! можно толкать другим персонажем
CONTROL_COLLISION = 0x04,
//! автоматически избегать столкновений с другими персонажами
CONTROL_AVOID_COLLISION = 0x10,
//! автоматически двигаться
CONTROL_AUTO_MOVE = 0x20,
//! сгонять с пути других персонажей, если блокируют дорогу
CONTROL_CLEAR_PATH = 0x40,
//! Персонаж стремится не отходить от активного персонажа более чем на некоторый радиус
CONTROL_FOLLOW_ACTIVE_PERSONAGE = 0x80,
//! Персонаж пытается двигаться в ту же сторону, что и активный
CONTROL_REPEAT_ACTIVE_PERSONAGE_MOVEMENT = 0x100,
//! Жесткая привязка персонажа к заданному персонажу
CONTROL_ATTACHMENT_WITH_DIR_REL = 0x200,
/** Жесткая привязка персонажа к заданному персонажу _без учета направления движения_
персонажа к которому привязываемся */
CONTROL_ATTACHMENT_WITHOUT_DIR_REL = 0x400,
// Подходить к точке привязки относительно активного
CONTROL_ATTACHMENT_TO_ACTIVE_WITH_MOVING = 0x800,
//! Режим реагирования на клик активному персонажу (персонаж бежит туда же)
CONTROL_ACTIVE_CLICK_REACTING = 0x1000,
//! Режим с анимацией поворота
CONTROL_ANIMATED_ROTATION = 0x2000
};
//! флаги следования
enum follow_condition_t {
//! Для персонажа нужно просчитать путь следования
FOLLOW_UPDATE_PATH = 0x01,
//! Все ок
FOLLOW_DONE = 0x02,
//! Персонаж ждет, когда можно будет возобновить попытку следования
FOLLOW_WAIT = 0x03,
//! Персонаж ждет остановки всех следующих персонажей, чтобы продолжить следование
FOLLOW_FULL_STOP_WAIT = 0x04,
//! Персонаж следует
FOLLOW_MOVING = 0x05
};
bool has_control_type(control_type_t type) const {
if (_control_types & type) return true;
return false;
}
void add_control_type(control_type_t type) {
_control_types |= type;
}
void remove_control_type(control_type_t type) {
_control_types &= ~type;
}
int get_control_types() { return _control_types; }
int get_movement_mode() { return _movement_mode; }
qdGameObjectStateWalk::movement_type_t movement_type() const;
bool is_walkable(const Vect3f &pos) const;
bool is_walkable(const Vect2s &pos) const;
const Vect3f &bound(bool perspective_correction = true) const;
Vect3f calc_bound_in_pos(Vect3f pos, bool perspective_correction = true);
bool calc_walk_grid(Vect2s &center, Vect2s &size) const;
//! Возвращает текущее положение сетки движения, и положение сетки через dt
bool calc_cur_and_future_walk_grid(float dt, Vect2s &cen_cur, Vect2s &size_cur,
Vect2s &cen_next, Vect2s &size_next);
float radius() const;
bool adjust_z();
bool mouse_handler(int x, int y, mouseDispatcher::mouseEvent ev);
float direction_angle() const {
return _direction_angle;
}
float calc_direction_angle(const Vect3f &target) const;
float animate_rotation(float dt);
float rotation_angle() const {
return _rotation_angle;
}
float rotation_angle_per_quant() const {
return _rotation_angle_per_quant;
}
void set_rotation_angle_per_quant(float ang) {
_rotation_angle_per_quant = ang;
}
bool set_direction(float angle);
int get_direction(float angle) const;
float default_direction_angle() const {
return _default_direction_angle;
}
void set_default_direction(float ang) {
_default_direction_angle = ang;
}
void set_state(int st);
void set_state(qdGameObjectState *p);
void set_last_walk_state(qdGameObjectState *p) {
_last_walk_state = p;
}
qdGameObjectState *last_walk_state() {
return _last_walk_state;
}
qdGameObjectState *get_default_state();
const qdGameObjectState *get_default_state() const;
void merge(qdGameObjectMoving *p);
void split(qdGameObjectMoving *p);
void set_button(qdInterfaceButton *p) {
_button = p;
}
qdInterfaceButton *button() const {
return _button;
}
bool move(const Vect3f &target, bool lock_target = false);
bool move(const Vect3f &target, float angle, bool lock_target = false);
bool move2position(const Vect3f target);
bool skip_movement();
bool stop_movement();
bool is_moving() const {
return check_flag(QD_OBJ_MOVING_FLAG);
}
bool can_move() const;
bool is_in_position(const Vect3f pos) const;
bool is_in_position(const Vect3f pos, float angle) const;
bool is_moving2position(const Vect3f pos) const;
bool is_moving2position(const Vect3f pos, float angle) const;
//! Текущая точка, к которой движется персонаж.
Vect3f local_target_position() const {
if (is_moving())
return _target_r;
else
return R();
}
//! Точка, к которой движется персонаж.
Vect3f target_position() const {
if (is_moving())
return ((_path_length) ? _path[_path_length] : _target_r);
else
return R();
}
void set_scale(float sc) {
_scale = sc;
}
float scale() const {
return _scale;
}
bool load_script(const xml::tag *p);
bool save_script(Common::WriteStream &fh, int indent = 0) const;
//! Загрузка данных из сэйва.
bool load_data(Common::SeekableReadStream &fh, int save_version);
//! Запись данных в сэйв.
bool save_data(Common::WriteStream &fh) const;
bool load_resources();
//! Возвращает true, если объект в данный момент может менять состояние.
bool can_change_state(const qdGameObjectState *state = NULL) const;
//! Инициализация объекта, вызывается при старте и перезапуске игры.
bool init();
Vect3f get_future_r(float dt, bool &end_movement, bool real_moving = false);
void quant(float dt);
void redraw(int offs_x = 0, int offs_y = 0) const;
void drawDebugPath() const;
void debug_redraw() const;
void draw_contour(uint32 color) const;
void draw_shadow(int offs_x, int offs_y, uint32 color, int alpha) const;
bool get_debug_info(Common::String &buf) const;
grScreenRegion screen_region() const;
bool hit(int x, int y) const;
bool update_screen_pos();
Vect2s screen_size() const;
void disable_control() {
_disable_control = true;
}
void enable_control() {
_disable_control = false;
}
bool is_control_disabled() const {
return _disable_control;
}
bool keyboard_move();
bool set_movement_impulse(float dir_angle);
float collision_radius() const {
if (_collision_radius > FLT_EPS)
return _collision_radius;
else
return radius();
}
void set_collision_radius(float r) {
_collision_radius = r;
}
float collision_delay() const {
return _collision_delay;
}
void set_collision_delay(float r) {
_collision_delay = r;
}
float collision_path() const {
return _collision_path;
}
void set_collision_path(float path) {
_collision_path = path;
}
float follow_min_radius() const {
return _follow_min_radius;
}
void set_follow_min_radius(float fmr) {
_follow_min_radius = fmr;
}
float follow_max_radius() const {
return _follow_max_radius;
}
void set_follow_max_radius(float fmr) {
_follow_max_radius = fmr;
}
int follow_condition() const {
return _follow_condition;
};
void set_follow_condition(int cond) {
_follow_condition = cond;
};
const Std::vector<const qdGameObjectMoving *> &const_ref_circuit_objs() const {
return _circuit_objs;
};
Std::vector<const qdGameObjectMoving *> &ref_circuit_objs() {
return _circuit_objs;
};
// Для CONTROL_ATTACHMENT
const qdGameObjectMoving *attacher() const {
return _attacher;
}
void set_attacher(const qdGameObjectMoving *mov_obj);
const qdNamedObjectReference &attacher_ref() const {
return _attacher_ref;
}
Vect2s attach_shift() const {
return _attach_shift;
}
void set_attach_shift(Vect2s shift) {
_attach_shift = shift;
}
Vect3f last_move_order() const {
return _last_move_order;
};
void set_last_move_order(const Vect3f &pnt) {
_last_move_order = pnt;
};
bool avoid_collision(const qdGameObjectMoving *p);
bool move_from_personage_path();
bool toggle_grid_zone(bool make_walkable = false);
void toggle_selection(bool state) {
_is_selected = state;
}
void set_path_attributes(int attr) const;
void clear_path_attributes(int attr) const;
static Common::String control2str(int control, bool truncate = false);
static Common::String movement2str(int movement, bool truncate = false);
protected:
bool load_script_body(const xml::tag *p);
bool save_script_body(Common::WriteStream &fh, int indent = 0) const;
private:
//! Дистанция, на котрой персонаж взаимодействует с другими персонажами.
/**
Если не установлен, высчитывается по баунду персонажа.
*/
float _collision_radius;
//! Задержка от момента столкновения с другим персонажем до начала движения.
float _collision_delay;
//! Путь, который персонаж проходит после того, как другой персонаж его толкает.
float _collision_path;
//! Минимальный и максимальный радиусы следования
float _follow_min_radius;
float _follow_max_radius;
//! Состояние следования
int _follow_condition;
//! Объекты, который текущий объект пытается обойти
Std::vector<const qdGameObjectMoving *> _circuit_objs;
//! Для CONTROL_HARD_ATTACHMENT
const qdGameObjectMoving *_attacher; // Объект, который присоединяет к себе наш объект
qdNamedObjectReference _attacher_ref;
Vect2s _attach_shift; // Позиция нашего объекта - смещение от центра attacher'а
//! Режимы управления персонажем - комбинация значений control_type_t.
int _control_types;
bool _disable_control;
bool _impulse_movement_mode;
float _impulse_timer;
float _impulse_start_timer;
float _impulse_direction;
movement_mode_t _movement_mode;
float _movement_mode_time;
float _movement_mode_time_current;
float _scale;
float _direction_angle;
float _rotation_angle;
float _rotation_angle_per_quant;
float _default_direction_angle;
float _speed_delta;
Vect3f _last_move_order; //! Точка, заданная последним приказом на движение
Vect3f _target_r;
int _path_length;
int _cur_path_index;
float _target_angle;
Vect3f _path[QD_MOVING_OBJ_PATH_LENGTH];
Vect2s _walk_grid_size;
qdGameObjectState *_last_walk_state;
bool _ignore_personages;
bool _is_selected;
mutable qdInterfaceButton *_button;
Vect2s get_nearest_walkable_point(const Vect2s &target) const;
//! Возвращает доступную точку, предшествующую последней до target пустОте
Vect2s get_pre_last_walkable_point(const Vect2s &target) const;
bool is_path_walkable(int x1, int y1, int x2, int y2) const;
bool is_path_walkable(const Vect2i &src, const Vect2i &trg) const {
return is_path_walkable(src.x, src.y, trg.x, trg.y);
}
bool is_path_walkable(const Vect3f &src, const Vect3f &trg) const;
bool enough_far_target(const Vect3f &dest) const;
void toggle_ignore_personages(bool state) {
_ignore_personages = state;
}
bool find_path(const Vect3f target, bool lock_target = false);
void optimize_path(Std::vector<Vect2i> &path) const;
void optimize_path_four_dirs(Std::list<Vect2i> &path) const;
// Спрямление четырех точек для пути с восемью направлениями
bool four_pts_eight_dir_straight(Std::list<Vect2i> &path,
Std::list<Vect2i>::reverse_iterator cur) const;
bool four_pts_eight_dir_straight_old(Std::list<Vect2i> path, Std::list<Vect2i>::iterator cur) const;
// Удаляем точки, лежащие внутри прямых отрезков пути
bool del_coll_pts(Std::list<Vect2i> &path) const;
void optimize_path_eight_dirs(Std::list<Vect2i> &path) const;
void optimize_path_smooth(Std::list<Vect2i> &path) const;
void finalize_path(const Vect3f &from, const Vect3f &to, const Std::vector<Vect2i> &path, Std::vector<Vect3f> &out_path) const;
bool adjust_position(Vect3f &pos) const;
bool adjust_direction_angle(float &angle);
void change_direction_angle(float angle);
bool is_direction_allowed(float angle) const;
int allowed_directions_count() const;
float calc_scale() const {
return calc_scale(R());
}
float calc_scale(const Vect3f &r) const;
bool set_walk_animation();
bool movement_impulse();
float speed();
bool get_speed_parameters(float &speed, float &speed_max, float &acceleration);
const qdGameObjectStateWalk *current_walk_state() const;
bool adjust_position(Vect3f &pos, float dir_angle) const;
Vect2s walk_grid_size(const Vect3f &r) const;
Vect2s walk_grid_size(const Vect2s &r) const;
Vect2s walk_grid_size() const {
return walk_grid_size(R());
}
bool start_auto_move();
//! Проверяет, является ли корректной последующая позиция при движении персонажа
bool future_pos_correct(float dt);
/// Проверка, закончилось ли движение.
bool is_movement_finished() const;
};
} // namespace QDEngine
#endif // QDENGINE_QDCORE_QD_GAME_OBJECT_MOVING_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,936 @@
/* 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/>.
*
*/
#ifndef QDENGINE_QDCORE_QD_GAME_OBJECT_STATE_H
#define QDENGINE_QDCORE_QD_GAME_OBJECT_STATE_H
#include "qdengine/parser/xml_fwd.h"
#include "qdengine/qdcore/qd_contour.h"
#include "qdengine/qdcore/qd_sound_info.h"
#include "qdengine/qdcore/qd_animation_info.h"
#include "qdengine/qdcore/qd_animation_set_info.h"
#include "qdengine/qdcore/qd_conditional_object.h"
#include "qdengine/qdcore/qd_named_object.h"
#include "qdengine/qdcore/qd_coords_animation.h"
#include "qdengine/qdcore/qd_camera_mode.h"
#include "qdengine/qdcore/qd_sound_handle.h"
#include "qdengine/qdcore/qd_screen_text.h"
#include "qdengine/qdcore/qd_grid_zone.h"
#include "qdengine/system/sound/snd_sound.h"
namespace QDEngine {
class qdScreenTransform {
public:
qdScreenTransform(float angle = 0.f, const Vect2f &scale = Vect2f(1.f, 1.f)) : _angle(angle), _scale(scale) { }
bool operator == (const qdScreenTransform &trans) const;
bool operator != (const qdScreenTransform &trans) const {
return !(*this == trans);
}
bool operator()() const {
return _angle != 0.f || _scale.x != 1.f || _scale.y != 1.f;
}
qdScreenTransform operator *(float value) const {
return qdScreenTransform(_angle *value, _scale *value);
}
qdScreenTransform &operator += (const qdScreenTransform &trans) {
_angle += trans._angle;
_scale += trans._scale;
return *this;
}
bool change(float dt, const qdScreenTransform &target_trans, const qdScreenTransform &speed);
float angle() const {
return _angle;
}
void set_angle(float angle) {
_angle = angle;
}
const Vect2f &scale() const {
return _scale;
}
void set_scale(const Vect2f &scale) {
_scale = scale;
}
bool has_scale() const {
return fabs(_scale.x - 1.f) > FLT_EPS || fabs(_scale.y - 1.f) > FLT_EPS;
}
private:
float _angle;
Vect2f _scale;
};
//! Состояние динамического объекта - базовый класс.
class qdGameObjectState : public qdConditionalObject {
public:
//! Флаги состояния.
enum {
//! Объект спрятан.
QD_OBJ_STATE_FLAG_HIDDEN = 0x01,
//! The state is not in triggers. Deprecated, replaced with is_in_triggers()
QD_OBJ_STATE_FLAG_NOT_IN_TRIGGERS = 0x02,
//! Восстанавливать предыдущее состояние по окончании состояния.
QD_OBJ_STATE_FLAG_RESTORE_PREV_STATE = 0x04,
//! Прятать объект по окончании состояния.
QD_OBJ_STATE_FLAG_HIDE_OBJECT = 0x08,
//! Объект, которому принадлежит состояние - глобальный.
QD_OBJ_STATE_FLAG_GLOBAL_OWNER = 0x10,
//! Состояние для инвентори.
QD_OBJ_STATE_FLAG_INVENTORY = 0x20,
//! Положить объект в инвентори по окончании состояния.
QD_OBJ_STATE_FLAG_MOVE_TO_INVENTORY = 0x40,
//! Положить в инвентори не удалось.
QD_OBJ_STATE_FLAG_MOVE_TO_INVENTORY_FAILED = 0x80,
//! У состояния задан баунд.
QD_OBJ_STATE_FLAG_HAS_BOUND = 0x100,
//! Активирована задержка перед включением состояния.
QD_OBJ_STATE_FLAG_ACTIVATION_TIMER = 0x200,
//! Задержка перед включением состояния окончилась.
QD_OBJ_STATE_FLAG_ACTIVATION_TIMER_END = 0x400,
//! Заданный для состояния текст - вариант фразы для диалогов.
/**
Означает, что одно из условий активации состояния - клик мышью по
соответствующему тексту на экране.
*/
QD_OBJ_STATE_FLAG_DIALOG_PHRASE = 0x800,
//! Синхронизировать анимацию по звуку.
/**
Когда заканчивается звук, анимация останавливается.
Работает только для qdGameObjectStateStatic без координатной анимации.
*/
QD_OBJ_STATE_FLAG_SOUND_SYNC = 0x1000,
//! Разрешить прерывать состояние.
/**
Если флаг установлен, то состояние может быть прервано до
окончания его работы (например для персонажа - кликом мыши).
*/
QD_OBJ_STATE_FLAG_ENABLE_INTERRUPT = 0x2000,
//! Состояние уже было активировано.
/**
Показывает, что состояние уже было хотя бы один раз активировано.
*/
QD_OBJ_STATE_FLAG_WAS_ACTIVATED = 0x4000,
//! Запретить прерывать подход к стартовой точке состояния.
/**
Имеет смысл только если для состояний с подходом к точке запуска
(need_to_walk() == true).
*/
QD_OBJ_STATE_FLAG_DISABLE_WALK_INTERRUPT = 0x8000,
//! Включать состояние, когда объект взят на мышь.
QD_OBJ_STATE_FLAG_MOUSE_STATE = 0x10000,
//! Включать состояние, когда мышь над объектом в инвентори.
QD_OBJ_STATE_FLAG_MOUSE_HOVER_STATE = 0x20000,
//! Не вытаскивать объект из инвентори, когда он взят на мышь.
QD_OBJ_STATE_FLAG_STAY_IN_INVENTORY = 0x40000,
/**
Принудительно загружать ресурсы состояния в память при заходе на сцену,
не выгружать их до выхода со сцены.
*/
QD_OBJ_STATE_FLAG_FORCED_LOAD = 0x80000,
//! Состояние можно скиповать кликом мыши.
QD_OBJ_STATE_FLAG_ENABLE_SKIP = 0x100000,
//! При включении состояния класть объект на зону под мышью.
QD_OBJ_STATE_FLAG_MOVE_TO_ZONE = 0x200000,
//! При включении состояния класть объект в центр объекта под мышью.
QD_OBJ_STATE_FLAG_MOVE_ON_OBJECT = 0x400000,
//! По окончании работы состояния делать персонажа-владельца активным.
QD_OBJ_STATE_FLAG_ACTIVATE_PERSONAGE = 0x800000,
//! Автоматически загружать сэйв при активации состояния.
QD_OBJ_STATE_FLAG_AUTO_LOAD = 0x1000000,
//! Автоматически писать сэйв при активации состояния.
QD_OBJ_STATE_FLAG_AUTO_SAVE = 0x2000000,
QD_OBJ_STATE_FLAG_FADE_IN = 0x4000000,
QD_OBJ_STATE_FLAG_FADE_OUT = 0x8000000,
};
//! Тип состояния.
enum StateType {
//! Статическая анимация.
STATE_STATIC,
//! Походка.
STATE_WALK,
//! Маска на фоне.
STATE_MASK
};
enum {
CURSOR_UNASSIGNED = -1
};
qdGameObjectState(StateType tp);
qdGameObjectState(const qdGameObjectState &st);
virtual ~qdGameObjectState();
virtual qdGameObjectState &operator = (const qdGameObjectState &st);
int named_object_type() const {
return QD_NAMED_OBJECT_OBJ_STATE;
}
//! Возвращает указатель на траекторию движения объекта для состояния.
qdCoordsAnimation *coords_animation() {
return &_coords_animation;
}
//! Возвращает указатель на траекторию движения объекта для состояния.
const qdCoordsAnimation *coords_animation() const {
return &_coords_animation;
}
//! Загрузка данных из скрипта.
virtual bool load_script(const xml::tag *p) = 0;
//! Запись данных в скрипт.
virtual bool save_script(Common::WriteStream &fh, int indent = 0) const = 0;
//! Инициализация состояния, вызывается при старте и перезапуске игры.
bool init();
//! Загрузка данных из сэйва.
bool load_data(Common::SeekableReadStream &fh, int save_version);
//! Запись данных в сэйв.
bool save_data(Common::WriteStream &fh) const;
//! Регистрация ресурсов состояния в диспетчере ресурсов.
virtual bool register_resources();
//! Отмена регистрации ресурсов состояния в диспетчере ресурсов.
virtual bool unregister_resources();
//! Загрузка ресурсов.
virtual bool load_resources();
//! Выгрузка ресурсов.
virtual bool free_resources();
//! Возвращает true, если условия активации состояния выполняются.
bool check_conditions();
//! Возвращет смещение центра объекта для состояния (в экранных координатах).
const Vect2s &center_offset() const {
return _center_offset;
}
//! Устанавливает смещение центра объекта для состояния (в экранных координатах).
void set_center_offset(const Vect2s offs) {
_center_offset = offs;
}
//! Логический квант состояния.
void quant(float dt);
//! Возвращает указатель на предыдущее состояние.
qdGameObjectState *prev_state() {
return _prev_state;
}
//! Устанавливает предыдущее состояние.
void set_prev_state(qdGameObjectState *p) {
_prev_state = p;
}
//! Возвращает тип состояния.
StateType state_type() const {
return _state_type;
}
//! Возвращает идентификатор мышиного курсора для состояния.
int mouse_cursor_ID() const {
return _mouse_cursor_id;
}
//! Устанавливает идентификатор мышиного курсора для состояния.
/**
Если должен использоваться курсор по-умолчанию -
параметр должен быть равен CURSOR_UNASSIGNED.
*/
void set_mouse_cursor_ID(int id) {
_mouse_cursor_id = id;
}
//! Возвращает true, если состоянию назначен особый мышиный курсор.
bool has_mouse_cursor_ID() const {
return _mouse_cursor_id != CURSOR_UNASSIGNED;
}
//! Устанавливает имя звукового эффекта, привязанного к состоянию.
const char *sound_name() const {
return _sound_info.name();
}
//! Возвращает имя звукового эффекта, привязанного к состоянию.
void set_sound_name(const char *p) {
_sound_info.set_name(p);
}
//! Возвращает указатель на звуковой эффект, привязанный к состоянию.
qdSound *sound() const;
//! Возвращает true, если для состояния задан звук.
bool has_sound() const {
if (sound_name()) return true;
else return false;
}
//! Возвращает хэндл звука.
const qdSoundHandle *sound_handle() const {
return &_sound_handle;
}
//! Запускает звук, position - стартовяя позиция, от 0.0 до 1.0.
bool play_sound();
//! Останавливает звук.
bool stop_sound() const;
//! Установка частоты звука.
bool set_sound_frequency(float frequency_coeff) const;
bool is_sound_finished() const;
bool is_sound_playing() const;
//! Устанавливает флаг для звука.
void set_sound_flag(int fl) {
_sound_info.set_flag(fl);
}
//! Скидывает флаг для звука.
void drop_sound_flag(int fl) {
_sound_info.drop_flag(fl);
}
//! Возвращает true, если установлен флаг для звука.
bool check_sound_flag(int fl) const {
return _sound_info.check_flag(fl);
}
//! Возвращает true, если состояние пустое.
virtual bool is_state_empty() const;
//! Возвращает true, если в данный момент состояние включено.
bool is_active() const;
//! Возвращает true, если состояние стартовое.
bool is_default() const;
//! Возвращает задержку (в секундах) перед активацией состояния.
float activation_delay() const {
return _activation_delay;
}
//! Устанавливает задержку (в секундах) перед активацией состояния.
void set_activation_delay(float tm) {
_activation_delay = tm;
}
//! Устанавливает таймер перед активацией состояния.
void set_activation_timer() {
_activation_timer = _activation_delay;
}
//! Вызывается при активации состояния.
void start() {
_cur_time = 0.0f;
_is_sound_started = false;
}
//! Возвращает время в секундах, прошедшее с момента активации состояния.
float cur_time() const {
return _cur_time;
}
//! Возвращает длительность состояния в секундах.
float work_time() const;
//! Устанавливает длительность состояния в секундах.
void set_work_time(float tm) {
_work_time = tm;
}
//! Возвращает true, если персонажу требуется подойти к точке включения состояния.
bool need_to_walk() const {
if (!_coords_animation.is_empty()
&& _coords_animation.check_flag(QD_COORDS_ANM_OBJECT_START_FLAG))
return true;
else
return false;
}
virtual qdGameObjectState *clone() const = 0;
virtual float adjust_direction_angle(float angle) const {
return angle;
}
//! Возвращает координаты точки, в которой должно активироваться состояние.
const Vect3f start_pos() const {
if (!_coords_animation.is_empty()) {
return _coords_animation.get_point(0)->dest_pos();
} else
return Vect3f(0, 0, 0);
}
//! Возвращает направление объекта в точке, в которой должно активироваться состояние.
float start_direction_angle() const {
if (!_coords_animation.is_empty()) {
return _coords_animation.get_point(0)->direction_angle();
} else
return -1.0f;
}
//! Возвращает количество ссылок на состояние.
int reference_count() const {
return _reference_count;
}
//! Инкремент количества ссылок на состояние.
void inc_reference_count() {
_reference_count++;
}
//! Декремент количества ссылок на состояние.
void dec_reference_count() {
if (_reference_count) _reference_count--;
}
//! Возвращает true, если у состояния задан текст субтитров.
bool has_text() const {
return (!_text_ID.empty() || !_short_text_ID.empty());
}
//! Возвращает текст субтитров.
const char *text() const {
if (has_full_text()) return full_text();
else return short_text();
}
//! Возвращает полный текст субтитров.
const char *full_text() const;
//! Возвращает сокращенный текст субтитров.
const char *short_text() const;
//! Возвращает true, если у состояния задан текст субтитров.
bool has_full_text() const {
return !_text_ID.empty();
}
//! Возвращает true, если у состояния задан сокращенный текст субтитров.
bool has_short_text() const {
return !_short_text_ID.empty();
}
//! Возвращает полный текст субтитров.
const char *full_text_ID() const {
return _text_ID.c_str();
}
//! Устанавливает полный текст субтитров.
/**
Если параметр нулевой, то текст очищается.
*/
void set_full_text_ID(const char *p) {
if (p) _text_ID = p;
else _text_ID.clear();
}
//! Возвращает сокращенный текст субтитров.
const char *short_text_ID() const {
return _short_text_ID.c_str();
}
//! Устанавливает сокращенный текст субтитров.
/**
Если параметр нулевой, то текст очищается.
*/
void set_short_text_ID(const char *p) {
if (p) _short_text_ID = p;
else _short_text_ID.clear();
}
//! Возвращает true, если у состояния задан баунд.
bool has_bound() const {
return check_flag(QD_OBJ_STATE_FLAG_HAS_BOUND);
}
//! Возвращает баунд состояния.
const Vect3f &bound() const {
return _bound;
}
//! Возвращает радиус состояния.
float radius() const {
return _radius;
}
//! Устанавливает баунд состояния.
void set_bound(const Vect3f &b);
//! Устанавливает баунд состояния по анимации.
virtual bool auto_bound() {
return false;
}
//! Устанавливает режим работы камеры, включается при активации состояния.
void set_camera_mode(const qdCameraMode &mode) {
_camera_mode = mode;
}
//! Режим работы камеры, включается при активации состояния.
const qdCameraMode &camera_mode() const {
return _camera_mode;
}
//! Возвращает true, если у состояния задан режим работы камеры.
bool has_camera_mode() const {
return _camera_mode.camera_mode() != qdCameraMode::MODE_UNASSIGNED;
}
float rnd_move_radius() const {
return _rnd_move_radius;
}
void set_rnd_move_radius(float radius) {
_rnd_move_radius = radius;
}
float rnd_move_speed() const {
return _rnd_move_speed;
}
void set_rnd_move_speed(float speed) {
_rnd_move_speed = speed;
}
qdConditionalObject::trigger_start_mode trigger_start();
bool trigger_can_start() const;
bool forced_load() const {
return check_flag(QD_OBJ_STATE_FLAG_FORCED_LOAD);
}
float text_delay() const {
return _text_delay;
}
bool has_text_delay() const {
return _text_delay > FLT_EPS;
}
void set_text_delay(float delay) {
_text_delay = delay;
}
float sound_delay() const {
return _sound_delay;
}
bool has_sound_delay() const {
return _sound_delay > FLT_EPS;
}
void set_sound_delay(float delay) {
_sound_delay = delay;
}
int autosave_slot() const {
return _autosave_slot;
}
void set_autosave_slot(int slot) {
_autosave_slot = slot;
}
float fade_time() const {
return _fade_time;
}
void set_fade_time(float time) {
_fade_time = time;
}
uint32 shadow_color() const {
return _shadow_color;
}
int shadow_alpha() const {
return _shadow_alpha;
}
void set_shadow(uint32 color, int alpha) {
_shadow_color = color;
_shadow_alpha = alpha;
}
const qdScreenTextFormat &text_format(bool topic_mode = false) const {
if (_text_format.is_global_depend()) {
return (topic_mode && check_flag(QD_OBJ_STATE_FLAG_DIALOG_PHRASE)) ?
qdScreenTextFormat::global_topic_format() : qdScreenTextFormat::global_text_format();
}
return _text_format;
}
void set_text_format(const qdScreenTextFormat &text_format) {
_text_format = text_format;
}
bool has_transform() const {
return _transform() || _transform_speed();
}
const qdScreenTransform &transform() const {
return _transform;
}
void set_transform(const qdScreenTransform &tr) {
_transform = tr;
}
const qdScreenTransform &transform_speed() const {
return _transform_speed;
}
void set_transform_speed(const qdScreenTransform &tr_speed) {
_transform_speed = tr_speed;
}
static Common::String flag2str(int fl, bool truncate = false, bool icon = false);
protected:
//! Загрузка данных из скрипта.
bool load_script_body(const xml::tag *p);
//! Запись данных в скрипт.
bool save_script_body(Common::WriteStream &fh, int indent = 0) const;
//! Возвращает true, если надо перезапустить звук.
virtual bool need_sound_restart() const;
private:
//! Тип состояния.
StateType _state_type;
//! Смещение центра анимации относительно центра объекта в экранных координатах.
Vect2s _center_offset;
//! Баунд.
Vect3f _bound;
//! Радиус сферы, описывающей баунд.
float _radius;
//! Траектория движения объекта.
qdCoordsAnimation _coords_animation;
//! Задержка перед активацией состояния (в секундах)
float _activation_delay;
//! Время в секундах, оставшееся до активации состояния.
float _activation_timer;
//! Длительность состояния в секундах.
/**
Если нулевая, то состояние работает до конца анимации
или до конца траектории движения.
*/
float _work_time;
//! Время в секундах, прошедшее с момента активации состояния.
float _cur_time;
//! Информация о звуке, привязанном к состоянию.
qdSoundInfo _sound_info;
//! Хэндл для управления звуком, привязанным к состоянию.
qdSoundHandle _sound_handle;
//! Задержка запуска звука от старта состояния (в секундах)
float _sound_delay;
//! true, если звук состояния запущен
bool _is_sound_started;
//! Задержка перед появлением текста от старта состояния (в секундах)
float _text_delay;
//! true, если текст состояния появился
bool _is_text_shown;
//! Номер мышиного курсора, который включается, если мышь над объектом в этом состоянии.
int _mouse_cursor_id;
//! Текст, выводимый на экран при работе состояния (для диалогов и т.д.)
Common::String _text_ID;
//! Короткий вариант текста, выводимого на экран при работе состояния (для диалогов и т.д.)
Common::String _short_text_ID;
//! Режим работы камеры, включается при активации состояния.
qdCameraMode _camera_mode;
float _rnd_move_radius;
float _rnd_move_speed;
/// Номер слота автосэйва
int _autosave_slot;
/// Время фэйда экрана при включении состояния
float _fade_time;
//! Цвет затенения.
uint32 _shadow_color;
//! Прозрачность затенения, значения - [0, 255], если равно QD_NO_SHADOW_ALPHA, то объект не затеняется.
int _shadow_alpha;
/// Преобразование картинки объекта, включается при активации состояния
qdScreenTransform _transform;
qdScreenTransform _transform_speed;
//! Формат текста.
qdScreenTextFormat _text_format;
//! Количество ссылок на состояние.
/**
Если объект, которому принадлежит состояние - глобальный (т.е. принадлежит
игровому диспетрчеру, а не сцене), то состояние может одновременно находиться в более чем одном списке.
*/
int _reference_count;
//! Предыдущее состояние.
qdGameObjectState *_prev_state;
};
typedef Std::vector<qdGameObjectState *> qdGameObjectStateVector;
//! Состояние динамического объекта - статическая анимация.
class qdGameObjectStateStatic : public qdGameObjectState {
public:
qdGameObjectStateStatic();
qdGameObjectStateStatic(const qdGameObjectStateStatic &st);
~qdGameObjectStateStatic();
qdGameObjectState &operator = (const qdGameObjectState &st);
qdGameObjectStateStatic &operator = (const qdGameObjectStateStatic &st);
qdAnimation *animation() {
return _animation_info.animation();
}
const qdAnimation *animation() const {
return _animation_info.animation();
}
qdAnimationInfo *animation_info() {
return &_animation_info;
}
bool load_script(const xml::tag *p);
bool save_script(Common::WriteStream &fh, int indent = 0) const;
//! Регистрация ресурсов состояния в диспетчере ресурсов.
bool register_resources();
//! Отмена регистрации ресурсов состояния в диспетчере ресурсов.
bool unregister_resources();
bool load_resources();
bool free_resources();
bool is_state_empty() const;
qdGameObjectState *clone() const {
return new qdGameObjectStateStatic(*this);
}
//! Устанавливает баунд состояния по анимации.
bool auto_bound();
private:
qdAnimationInfo _animation_info;
};
//! Состояние динамического объекта - походка.
class qdGameObjectStateWalk : public qdGameObjectState {
public:
qdGameObjectStateWalk();
qdGameObjectStateWalk(const qdGameObjectStateWalk &st);
~qdGameObjectStateWalk();
qdGameObjectState &operator = (const qdGameObjectState &st);
qdGameObjectStateWalk &operator = (const qdGameObjectStateWalk &st);
//! режимы передвижения для персонажа
enum movement_type_t {
//! ходит только влево
MOVEMENT_LEFT,
//! ходит только вверх
MOVEMENT_UP,
//! ходит только вправо
MOVEMENT_RIGHT,
//! ходит только вниз
MOVEMENT_DOWN,
//! ходит только по горизонтали
MOVEMENT_HORIZONTAL,
//! ходит только по вертикали
MOVEMENT_VERTICAL,
//! ходит по четырем направлениям
MOVEMENT_FOUR_DIRS,
//! ходит по восьми направлениям
MOVEMENT_EIGHT_DIRS,
//! ходит по восьми и больше направлениям со сглаживанием
MOVEMENT_SMOOTH,
//! ходит только влево-вверх
MOVEMENT_UP_LEFT,
//! ходит только вправо-вверх
MOVEMENT_UP_RIGHT,
//! ходит только вправо-вниз
MOVEMENT_DOWN_RIGHT,
//! ходит только влево-вниз
MOVEMENT_DOWN_LEFT
};
enum OffsetType {
OFFSET_STATIC = 0,
OFFSET_WALK,
OFFSET_START,
OFFSET_END
};
qdAnimationSet *animation_set() const;
qdAnimation *animation(float direction_angle);
qdAnimation *static_animation(float direction_angle);
qdAnimationInfo *animation_info(float direction_angle);
qdAnimationInfo *static_animation_info(float direction_angle);
const Vect2i &center_offset(int direction_index, OffsetType offset_type = OFFSET_WALK) const;
const Vect2i &center_offset(float direction_angle, OffsetType offset_type = OFFSET_WALK) const;
void set_center_offset(int direction_index, const Vect2i &offs, OffsetType offset_type = OFFSET_WALK);
float walk_sound_frequency(int direction_index) const;
float walk_sound_frequency(float direction_angle) const;
void set_walk_sound_frequency(int direction_index, float freq);
//! Установка частоты звука.
bool update_sound_frequency(float direction_angle) const;
qdAnimationSetInfo *animation_set_info() {
return &_animation_set_info;
}
float adjust_direction_angle(float angle) const;
float direction_angle() const {
return _direction_angle;
}
void set_direction_angle(float ang) {
_direction_angle = ang;
}
bool load_script(const xml::tag *p);
bool save_script(Common::WriteStream &fh, int indent = 0) const;
//! Регистрация ресурсов состояния в диспетчере ресурсов.
bool register_resources();
//! Отмена регистрации ресурсов состояния в диспетчере ресурсов.
bool unregister_resources();
bool load_resources();
bool free_resources();
bool is_state_empty() const;
qdGameObjectState *clone() const {
return new qdGameObjectStateWalk(*this);
}
//! Устанавливает баунд состояния по анимации.
bool auto_bound();
float acceleration() const {
return _acceleration;
}
void set_acceleration(float acc) {
_acceleration = acc;
}
float max_speed() const {
return _max_speed;
}
void set_max_speed(float max_sp) {
_max_speed = max_sp;
}
void set_movement_type(movement_type_t type) {
_movement_type = type;
}
movement_type_t movement_type() const {
return _movement_type;
}
protected:
//! Возвращает true, если надо перезапустить звук.
bool need_sound_restart() const;
private:
float _direction_angle;
qdAnimationSetInfo _animation_set_info;
//! Ускорение - насколько увеличивается скорость передвижения за секунду.
float _acceleration;
//! Максимальная для походки скорость передвижения.
/**
Если нулевая - ограничения нет.
*/
float _max_speed;
//! Режим передвижения персонажа.
movement_type_t _movement_type;
//! Смещения центров анимаций походок.
Std::vector<Vect2i> _center_offsets;
//! Смещения центров статических анимаций.
Std::vector<Vect2i> _static_center_offsets;
//! Смещения центров анимаций стартов.
Std::vector<Vect2i> _start_center_offsets;
//! Смещения центров анимаций стопов.
Std::vector<Vect2i> _stop_center_offsets;
//! Коэффициенты для частоты звука походки.
Std::vector<float> _walk_sound_frequency;
};
//! Состояние динамического объекта - маска на статическом объекте.
class qdGameObjectStateMask : public qdGameObjectState, public qdContour {
public:
qdGameObjectStateMask();
qdGameObjectStateMask(const qdGameObjectStateMask &st);
~qdGameObjectStateMask();
qdGameObjectState &operator = (const qdGameObjectState &st);
qdGameObjectStateMask &operator = (const qdGameObjectStateMask &st);
const char *parent_name() const {
return _parent_name.c_str();
}
void set_parent_name(const char *p) {
_parent_name = p;
_parent = 0;
}
qdGameObject *parent();
const qdGameObject *parent() const;
bool hit(int x, int y) const;
bool draw_mask(uint32 color) const;
bool can_be_closed() const {
return (contour_size() > 2);
}
bool load_script(const xml::tag *p);
bool save_script(Common::WriteStream &fh, int indent = 0) const;
bool is_state_empty() const;
bool load_resources();
qdGameObjectState *clone() const {
return new qdGameObjectStateMask(*this);
}
private:
//! Имя объекта, к которому привязана маска.
Common::String _parent_name;
//! Указатель на объект, к которому привязана маска.
qdGameObject *_parent;
};
#ifdef __QD_DEBUG_ENABLE__
inline bool qdbg_is_object_state(const qdNamedObject *obj, const char *scene_name, const char *object_name, const char *state_name) {
if (dynamic_cast<const qdGameObjectState *>(obj)) {
if (obj->name() && !strcmp(state_name, obj->name())) {
if (!object_name || (obj->owner() && obj->owner()->name() && !strcmp(object_name, obj->owner()->name()))) {
if (!scene_name || (obj->owner()->owner() && obj->owner()->owner()->name() && !strcmp(obj->owner()->owner()->name(), scene_name)))
return true;
}
}
}
return false;
}
#else
inline bool qdbg_is_object_state(const qdNamedObject *obj, const char *scene_name, const char *object_name, const char *state_name) {
return false;
}
#endif
} // namespace QDEngine
#endif // QDENGINE_QDCORE_QD_GAME_OBJECT_STATE_H

View File

@@ -0,0 +1,111 @@
/* 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 "qdengine/qd_fwd.h"
#include "qdengine/parser/xml_tag.h"
#include "qdengine/parser/qdscr_parser.h"
#include "qdengine/qdcore/qd_game_object_static.h"
#include "qdengine/qdcore/qd_game_scene.h"
namespace QDEngine {
qdGameObjectStatic::qdGameObjectStatic() {
}
qdGameObjectStatic::~qdGameObjectStatic() {
}
void qdGameObjectStatic::redraw(int offs_x, int offs_y) const {
debugC(2, kDebugGraphics, "qdGameObjectStatic::redraw([%d, %d]), name: '%s'", offs_x, offs_y, transCyrillic(name()));
Vect2i scrCoord = screen_pos() + Vect2i(offs_x, offs_y);
_sprite.redraw(scrCoord.x, scrCoord.y, screen_depth(), 0);
}
bool qdGameObjectStatic::load_script(const xml::tag *p) {
return load_script_body(p);
}
bool qdGameObjectStatic::save_script(Common::WriteStream &fh, int indent) const {
for (int i = 0; i < indent; i++) {
fh.writeString("\t");
}
fh.writeString(Common::String::format("<static_object name=\"%s\">\r\n", qdscr_XML_string(name())));
save_script_body(fh, indent);
for (int i = 0; i < indent; i++) {
fh.writeString("\t");
}
fh.writeString("</static_object>\r\n");
return true;
}
bool qdGameObjectStatic::load_script_body(const xml::tag *p) {
qdGameObject::load_script_body(p);
for (xml::tag::subtag_iterator it = p->subtags_begin(); it != p->subtags_end(); ++it) {
switch (it->ID()) {
case QDSCR_FILE:
_sprite.set_file(Common::Path(it->data(), '\\'));
break;
}
}
return true;
}
bool qdGameObjectStatic::save_script_body(Common::WriteStream &fh, int indent) const {
qdGameObject::save_script_body(fh, indent);
for (int i = 0; i <= indent; i++) {
fh.writeString("\t");
}
if (!_sprite.file().empty()) {
fh.writeString(Common::String::format("<file>%s</file>\r\n", qdscr_XML_string(_sprite.file().toString('\\'))));
}
return true;
}
bool qdGameObjectStatic::load_resources() {
return _sprite.load();
}
void qdGameObjectStatic::free_resources() {
_sprite.free();
}
bool qdGameObjectStatic::hit(int x, int y) const {
return false;
}
void qdGameObjectStatic::draw_contour(uint32 color) const {
Vect2s pos = screen_pos();
_sprite.draw_contour(pos.x, pos.y, color);
}
} // namespace QDEngine

View File

@@ -0,0 +1,79 @@
/* 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/>.
*
*/
#ifndef QDENGINE_QDCORE_QD_GAME_OBJECT_STATIC_H
#define QDENGINE_QDCORE_QD_GAME_OBJECT_STATIC_H
#include "qdengine/qdcore/qd_sprite.h"
#include "qdengine/qdcore/qd_game_object.h"
namespace QDEngine {
class qdGameObjectStatic : public qdGameObject {
public:
qdGameObjectStatic();
~qdGameObjectStatic();
int named_object_type() const {
return QD_NAMED_OBJECT_STATIC_OBJ;
}
const qdSprite *get_sprite() const {
return &_sprite;
}
qdSprite *get_sprite() {
return &_sprite;
}
void redraw(int offs_x = 0, int offs_y = 0) const;
void debug_redraw() const { }
void draw_contour(uint32 color) const;
void draw_shadow(int offs_x, int offs_y, uint32 color, int alpha) const { }
bool load_script(const xml::tag *p);
bool save_script(Common::WriteStream &fh, int indent = 0) const;
bool mouse_handler(int x, int y, mouseDispatcher::mouseEvent ev) {
return false;
}
bool load_resources();
void free_resources();
Vect2s screen_size() const {
return Vect2s(_sprite.size_x(), _sprite.size_y());
}
bool hit(int x, int y) const;
protected:
bool load_script_body(const xml::tag *p);
bool save_script_body(Common::WriteStream &fh, int indent = 0) const;
private:
qdSprite _sprite;
};
} // namespace QDEngine
#endif // QDENGINE_QDCORE_QD_GAME_OBJECT_STATIC_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,321 @@
/* 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/>.
*
*/
#ifndef QDENGINE_QDCORE_QD_GAME_SCENE_H
#define QDENGINE_QDCORE_QD_GAME_SCENE_H
#include "qdengine/parser/xml_fwd.h"
#include "qdengine/qdcore/qd_camera.h"
#include "qdengine/qdcore/qd_conditional_object.h"
#include "qdengine/qdcore/qd_game_dispatcher_base.h"
#include "qdengine/qdcore/qd_object_map_container.h"
#include "qdengine/qdcore/qd_object_list_container.h"
#include "qdengine/qdcore/qd_file_owner.h"
#include "qdengine/qdcore/util/fps_counter.h"
#include "qdengine/system/input/mouse_input.h"
#include "qdengine/system/graphics/gr_screen_region.h"
namespace Common {
class WriteStream;
}
namespace QDEngine {
class qdMiniGame;
typedef Std::vector<qdGameObjectMoving *> personages_container_t;
//! Сцена.
class qdGameScene : public qdGameDispatcherBase, public qdFileOwner {
public:
qdGameScene();
~qdGameScene();
int named_object_type() const {
return QD_NAMED_OBJECT_SCENE;
}
//! флаги
enum {
/// зациклить сцену по горизонтали
CYCLE_X = 0x01,
/// зациклить сцену по вертикали
CYCLE_Y = 0x02,
/// перезапускать триггера при заходе на сцену
RESET_TRIGGERS_ON_LOAD = 0x04,
/// не переключать персонажей с клавиатуры
DISABLE_KEYBOARD_PERSONAGE_SWITCH = 0x08,
/// не давать выйти в главное меню по ESC
DISABLE_MAIN_MENU = 0x10
};
qdNamedObject *mouse_click_object() {
return _mouse_click_object;
}
void set_mouse_click_object(qdNamedObject *p) {
_mouse_click_object = p;
}
qdNamedObject *mouse_right_click_object() {
return _mouse_right_click_object;
}
void set_mouse_right_click_object(qdNamedObject *p) {
_mouse_right_click_object = p;
}
qdNamedObject *mouse_hover_object() {
return _mouse_hover_object;
}
void set_mouse_hover_object(qdNamedObject *p) {
_mouse_hover_object = p;
}
const Vect2f mouse_click_pos() const {
return _mouse_click_pos;
}
bool need_to_redraw_inventory(const char *inventory_name) const;
const char *minigame_name() const {
return _minigame_name.c_str();
}
void set_minigame_name(const char *name) {
_minigame_name = name;
}
bool has_minigame() const {
return !_minigame_name.empty();
}
const qdMiniGame *minigame() const {
return _minigame;
}
void start_minigame();
bool restart_minigame_on_load() const {
return _restart_minigame_on_load;
}
void toggle_restart_minigame_on_load(bool state) {
_restart_minigame_on_load = state;
}
bool add_object(qdGameObject *p);
bool rename_object(qdGameObject *p, const char *name);
bool remove_object(const char *name);
bool remove_object(qdGameObject *p);
qdGameObject *get_object(const char *name);
bool is_object_in_list(const char *name);
bool is_object_in_list(qdGameObject *p);
bool is_active() const;
void set_active_personage(qdGameObjectMoving *p);
void set_active_object(qdGameObjectAnimated *p);
qdGameObjectMoving *get_active_personage() {
return _selected_object;
}
bool change_active_personage(void);
bool set_personage_button(qdInterfaceButton *p);
bool add_grid_zone(qdGridZone *p);
bool rename_grid_zone(qdGridZone *p, const char *name);
bool remove_grid_zone(const char *name);
bool remove_grid_zone(qdGridZone *p);
qdGridZone *get_grid_zone(const char *name);
bool is_grid_zone_in_list(const char *name);
bool is_grid_zone_in_list(qdGridZone *p);
bool is_any_personage_in_zone(const qdGridZone *p) const;
bool add_music_track(qdMusicTrack *p);
bool rename_music_track(qdMusicTrack *p, const char *name);
bool remove_music_track(const char *name);
bool remove_music_track(qdMusicTrack *p);
qdMusicTrack *get_music_track(const char *name);
bool is_music_track_in_list(const char *name) const;
bool is_music_track_in_list(qdMusicTrack *p) const;
void init_objects_grid();
void quant(float dt);
//! Вызывается при заходе на сцену.
bool activate();
//! Вызывается при выходе со сцены.
bool deactivate();
//! Инициализация данных, вызывается при старте и перезапуске игры.
bool init();
void pre_redraw();
void redraw();
void post_redraw();
void debug_redraw();
void add_redraw_region(const grScreenRegion &reg) const;
bool mouse_handler(int x, int y, mouseDispatcher::mouseEvent ev);
void load_script(const xml::tag *p);
bool save_script(Common::WriteStream &fh, int indent = 0) const;
//! Загрузка данных из сэйва.
bool load_data(Common::SeekableReadStream &fh, int save_version);
//! Запись данных в сэйв.
bool save_data(Common::WriteStream &fh) const;
int load_resources();
void free_resources();
qdCamera *get_camera() {
return &_camera;
}
const qdCamera *get_camera() const {
return &_camera;
}
const qdGameObjectList &object_list() const {
return _objects.get_list();
}
const qdGridZoneList &grid_zone_list() const {
return _grid_zones.get_list();
}
const qdMusicTrackList &music_track_list() const {
return _music_tracks.get_list();
}
bool merge_global_objects(qdGameObject *obj = NULL);
bool split_global_objects(qdGameObject *obj = NULL);
int get_resources_size();
void inc_zone_update_count() {
_zone_update_count++;
}
uint32 zone_update_count() const {
return _zone_update_count;
}
qdConditionalObject::trigger_start_mode trigger_start();
static fpsCounter *fps_counter();
int autosave_slot() const {
return _autosave_slot;
}
void set_autosave_slot(int slot) {
_autosave_slot = slot;
}
bool has_interface_screen() const {
return !_interface_screen_name.empty();
}
const char *interface_screen_name() const {
return _interface_screen_name.c_str();
}
void set_interface_screen_name(const char *name) {
_interface_screen_name = name;
}
qdGameObject *get_hitted_obj(int x, int y);
bool set_camera_mode(const qdCameraMode &mode, qdGameObjectAnimated *object);
personages_container_t *getPersonages() { return &_personages; }
#ifdef __QD_DEBUG_ENABLE__
bool get_resources_info(qdResourceInfoContainer &infos) const;
#endif
private:
int _autosave_slot;
qdObjectMapContainer<qdGameObject> _objects;
qdObjectListContainer<qdGridZone> _grid_zones;
qdObjectListContainer<qdMusicTrack> _music_tracks;
qdCamera _camera;
//! текущий персонаж
qdGameObjectMoving *_selected_object;
/// миниигра, управляющая сценой
qdMiniGame *_minigame;
/// true если нужен перезапуск миниигры при загрузке из сэйва
bool _restart_minigame_on_load;
//! список персонажей сцены
personages_container_t _personages;
//! кликнутый мышью объект
qdNamedObject *_mouse_click_object;
//! кликнутый правой кнопкой мыши объект
qdNamedObject *_mouse_right_click_object;
//! объект, над кторым мышиный курсор
qdNamedObject *_mouse_hover_object;
//! кликнутая мышью точка на плоскости сцены
Vect2f _mouse_click_pos;
/// имя миниигры, управляющей сценой
Common::String _minigame_name;
/// используемый для сцены интерфейсный экран
Common::String _interface_screen_name;
uint32 _zone_update_count;
static char _fps_string[255];
bool init_visible_objects_list();
void update_mouse_cursor();
void personages_quant();
//! Инициализирует персонажей, участвующих в следовании
void follow_pers_init(int follow_cond);
//! Пытается найти путь к точке следования для pObj. Если идти нужно, но не удастся, то false.
bool follow_path_seek(qdGameObjectMoving *pObj, bool lock_target);
//! Реализует движение всех "следующих", кто достаточно далеко от точки следования
void follow_implement_update_path();
//! Снимает состояние FOLLOW_WAIT и заставляет пытаться следовать снова
void follow_wakening();
/** Обработка пересечений и попытка первого перса обойти второго, разрешив
таким образом пересечение */
void follow_circuit(float dt);
//! Останавливает дошедших следующих персонажей
void follow_end_moving();
//! Квант следования
void follow_quant(float dt);
void collision_quant();
void create_minigame_objects();
};
} // namespace QDEngine
#endif // QDENGINE_QDCORE_QD_GAME_SCENE_H

View File

@@ -0,0 +1,346 @@
/* 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/stream.h"
#include "qdengine/qdengine.h"
#include "qdengine/qd_fwd.h"
#include "qdengine/parser/xml_tag_buffer.h"
#include "qdengine/parser/qdscr_parser.h"
#include "qdengine/qdcore/qd_game_scene.h"
#include "qdengine/qdcore/qd_game_object.h"
#include "qdengine/qdcore/qd_grid_zone.h"
namespace QDEngine {
qdGridZone::qdGridZone() : qdContour(CONTOUR_POLYGON),
_height(0),
_state(false),
_initial_state(false),
_state_on(true),
_state_off(false),
_update_timer(0),
_shadow_alpha(QD_NO_SHADOW_ALPHA),
_shadow_color(0) {
_state_on.set_owner(this);
_state_off.set_owner(this);
}
qdGridZone::qdGridZone(const qdGridZone &gz) : qdNamedObject(gz), qdContour(gz),
_state(gz._state),
_initial_state(gz._initial_state),
_height(gz._height),
_state_on(gz._state_on),
_state_off(gz._state_off),
_update_timer(gz._update_timer),
_shadow_alpha(gz._shadow_alpha),
_shadow_color(gz._shadow_color) {
}
qdGridZone::~qdGridZone() {
}
qdGridZone &qdGridZone::operator = (const qdGridZone &gz) {
if (this == &gz) return *this;
*static_cast<qdNamedObject *>(this) = gz;
*static_cast<qdContour *>(this) = gz;
_state = gz._state;
_initial_state = gz._initial_state;
_height = gz._height;
_state_on = gz._state_on;
_state_off = gz._state_off;
_update_timer = gz._update_timer;
_shadow_alpha = gz._shadow_alpha;
_shadow_color = gz._shadow_color;
return *this;
}
bool qdGridZone::load_script(const xml::tag *p) {
for (xml::tag::subtag_iterator it = p->subtags_begin(); it != p->subtags_end(); ++it) {
switch (it->ID()) {
case QDSCR_NAME:
set_name(it->data());
break;
case QDSCR_FLAG:
set_flag(xml::tag_buffer(*it).get_int());
break;
case QDSCR_STATE:
_initial_state = _state = (xml::tag_buffer(*it).get_int()) ? true : false;
break;
case QDSCR_GRID_ZONE_STATE:
if (const xml::tag * tg = it->search_subtag(QDSCR_STATE)) {
if (xml::tag_buffer(*tg).get_int())
_state_on.load_script(&*it);
else
_state_off.load_script(&*it);
}
break;
case QDSCR_GRID_ZONE_HEIGHT:
xml::tag_buffer(*it) > _height;
break;
case QDSCR_GRID_ZONE_CONTOUR:
case QDSCR_CONTOUR_POLYGON:
qdContour::load_script(&*it);
break;
case QDSCR_GRID_ZONE_SHADOW_COLOR:
xml::tag_buffer(*it) > _shadow_color;
break;
case QDSCR_GRID_ZONE_SHADOW_ALPHA:
xml::tag_buffer(*it) > _shadow_alpha;
break;
}
}
return true;
}
bool qdGridZone::save_script(Common::WriteStream &fh, int indent) const {
for (int i = 0; i < indent; i++) {
fh.writeString("\t");
}
fh.writeString(Common::String::format("<grid_zone name=\"%s\" grid_zone_height=\"%d\"", qdscr_XML_string(name()), _height));
if (flags()) {
if (debugChannelSet(-1, kDebugLog))
fh.writeString(Common::String::format(" flags=\"%s\"", flags() == 1 ? "ZONE_EXIT_FLAG" : flags() == 0 ? "" : "<error>"));
else
fh.writeString(Common::String::format(" flags=\"%d\"", flags()));
}
if (_state) {
fh.writeString(" state=\"1\"");
} else {
fh.writeString(" state=\"0\"");
}
if (_shadow_color) {
fh.writeString(Common::String::format(" shadow_color=\"%d\"", _shadow_color));
}
if (_shadow_alpha != QD_NO_SHADOW_ALPHA) {
fh.writeString(Common::String::format(" shadow_alpha=\"%d\"", _shadow_alpha));
}
fh.writeString(">\r\n");
_state_on.save_script(fh, indent + 1);
_state_off.save_script(fh, indent + 1);
if (contour_size()) {
qdContour::save_script(fh, indent + 1);
}
for (int i = 0; i < indent; i++) {
fh.writeString("\t");
}
fh.writeString("</grid_zone>\r\n");
return true;
}
bool qdGridZone::set_height(int _h) {
_height = _h;
if (_state) {
if (apply_zone()) {
qdGameScene *sp = static_cast<qdGameScene *>(owner());
_update_timer = sp->zone_update_count();
sp->inc_zone_update_count();
return true;
} else
return false;
}
return true;
}
bool qdGridZone::apply_zone() const {
if (!owner() || owner()->named_object_type() != QD_NAMED_OBJECT_SCENE) return false;
if (is_mask_empty()) return false;
qdCamera *camera = static_cast<qdGameScene *>(owner())->get_camera();
if (!camera) return false;
Vect2s pos = mask_pos();
pos.x -= mask_size().x / 2;
pos.y -= mask_size().y / 2;
const byte* mask_ptr = maskData();
if (_state) {
for (int y = 0; y < mask_size().y; y++) {
for (int x = 0; x < mask_size().x; x++) {
if ((g_engine->_gameVersion <= 20050101 && *mask_ptr++) || is_inside(pos + Vect2s(x, y))) {
if (sGridCell *p = camera->get_cell(pos + Vect2s(x, y))) {
p->make_walkable();
p->set_height(_height);
}
}
}
}
} else {
for (int y = 0; y < mask_size().y; y++) {
for (int x = 0; x < mask_size().x; x++) {
if ((g_engine->_gameVersion <= 20050101 && *mask_ptr++) || is_inside(pos + Vect2s(x, y))) {
if (sGridCell * p = camera->get_cell(pos + Vect2s(x, y))) {
p->make_impassable();
p->set_height(0);
}
}
}
}
}
return true;
}
bool qdGridZone::set_state(bool st) {
_state = st;
if (apply_zone()) {
debugC(3, kDebugLog, "[%d] zone condition: %s %s", g_system->getMillis(), transCyrillic(name()), (st) ? "on" : "off");
qdGameScene *sp = static_cast<qdGameScene *>(owner());
_update_timer = sp->zone_update_count();
sp->inc_zone_update_count();
return true;
}
return false;
}
bool qdGridZone::select(qdCamera *camera, bool bSelect) const {
if (is_mask_empty())
return false;
Vect2s pos = mask_pos();
pos.x -= mask_size().x / 2;
pos.y -= mask_size().y / 2;
const byte* mask_ptr = maskData();
if (bSelect) {
for (int y = 0; y < mask_size().y; y++) {
for (int x = 0; x < mask_size().x; x++) {
if ((g_engine->_gameVersion <= 20050101 && *mask_ptr++) || is_inside(pos + Vect2s(x, y))) {
if (sGridCell *p = camera->get_cell(pos + Vect2s(x, y)))
p->select();
}
}
}
} else {
for (int y = 0; y < mask_size().y; y++) {
for (int x = 0; x < mask_size().x; x++) {
if ((g_engine->_gameVersion <= 20050101 && *mask_ptr++) || is_inside(pos + Vect2s(x, y))) {
if (sGridCell * p = camera->get_cell(pos + Vect2s(x, y)))
p->deselect();
}
}
}
}
return true;
}
bool qdGridZone::select(bool bSelect) const {
assert(owner() || owner()->named_object_type() == QD_NAMED_OBJECT_SCENE);
qdNamedObject *obj = owner();
qdCamera *camera = static_cast<qdGameScene *>(obj)->get_camera();
if (!camera) return false;
return select(camera, bSelect);
}
bool qdGridZone::is_object_in_zone(const qdGameObject *obj) const {
if (!owner() || owner()->named_object_type() != QD_NAMED_OBJECT_SCENE || owner() != obj->owner()) return false;
return is_point_in_zone(Vect2f(obj->R().x, obj->R().y));
}
bool qdGridZone::is_point_in_zone(const Vect2f &r) const {
assert(owner());
qdCamera *camera = static_cast<qdGameScene *>(owner())->get_camera();
assert(camera);
Vect2s v = camera->get_cell_index(r.x, r.y);
if (v.x == -1) return false;
return is_inside(v);
}
qdGridZoneState *qdGridZone::get_state(const char *state_name) {
if (!strcmp(state_name, _state_on.name())) return &_state_on;
return &_state_off;
}
bool qdGridZone::load_data(Common::SeekableReadStream &fh, int saveVersion) {
debugC(3, kDebugSave, " qdGridZone::load_data before: %d", (int)fh.pos());
if (!qdNamedObject::load_data(fh, saveVersion)) {
return false;
}
char st = fh.readByte();
_update_timer = fh.readSint32LE();
_state = (st) ? true : false;
debugC(3, kDebugSave, " qdGridZone::load_data after: %d", (int)fh.pos());
return true;
}
bool qdGridZone::save_data(Common::WriteStream &fh) const {
debugC(3, kDebugSave, " qdGridZone::save_data before: %d", (int)fh.pos());
if (!qdNamedObject::save_data(fh)) {
return false;
}
fh.writeByte(_state);
fh.writeSint32LE(_update_timer);
debugC(3, kDebugSave, " qdGridZone::save_data after: %d", (int)fh.pos());
return true;
}
bool qdGridZone::init() {
return set_state(_initial_state);
}
bool qdGridZone::is_any_personage_in_zone() const {
if (!owner() || owner()->named_object_type() != QD_NAMED_OBJECT_SCENE) return false;
const qdGameScene *p = static_cast<const qdGameScene *>(owner());
return p->is_any_personage_in_zone(this);
}
} // namespace QDEngine

View File

@@ -0,0 +1,160 @@
/* 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/>.
*
*/
#ifndef QDENGINE_QDCORE_QD_GRID_ZONE_H
#define QDENGINE_QDCORE_QD_GRID_ZONE_H
#include "qdengine/parser/xml_fwd.h"
#include "qdengine/qdcore/qd_contour.h"
#include "qdengine/qdcore/qd_grid_zone_state.h"
namespace Common {
class WriteStream;
}
namespace QDEngine {
class qdCamera;
const int QD_NO_SHADOW_ALPHA = -1;
const int QD_SHADOW_ALPHA_MIN = 0;
const int QD_SHADOW_ALPHA_MAX = 255;
//! Зона на сетке сцены.
class qdGridZone : public qdNamedObject, public qdContour {
public:
qdGridZone();
qdGridZone(const qdGridZone &gz);
qdGridZone &operator = (const qdGridZone &gz);
~qdGridZone();
//! Команды для изменения состояния зоны в триггерах.
enum status_change {
//! выключить зону
ZONE_OFF = 0,
//! включить зону
ZONE_ON,
//! переключить зону
ZONE_TOGGLE
};
//! Флаги
enum {
//! т.н. "зона выхода" - при наведении на нее мышью курсор меняется
ZONE_EXIT_FLAG = 0x01
};
int named_object_type() const {
return QD_NAMED_OBJECT_GRID_ZONE;
}
//! Загрузка данных из скрипта.
bool load_script(const xml::tag *p);
//! Запись данных в скрипт.
bool save_script(Common::WriteStream &fh, int indent = 0) const;
//! Загрузка данных из сэйва.
bool load_data(Common::SeekableReadStream &fh, int saveVersion);
//! Запись данных в сэйв.
bool save_data(Common::WriteStream &fh) const;
bool state() const {
return _state;
}
bool set_state(bool st);
bool toggle_state() {
return set_state(!_state);
}
uint32 height() const {
return _height;
}
bool set_height(int _h);
bool select(qdCamera *camera, bool bSelect) const;
bool select(bool bSelect) const;
bool is_object_in_zone(const qdGameObject *obj) const;
bool is_point_in_zone(const Vect2f &vPoint) const;
uint32 update_timer() const {
return _update_timer;
}
qdGridZoneState *get_state(const char *state_name);
//! Инициализация данных, вызывается при старте и перезапуске игры.
bool init();
bool is_any_personage_in_zone() const;
int shadow_alpha() const {
return _shadow_alpha;
}
void set_shadow_alpha(int alpha) {
_shadow_alpha = alpha;
}
uint32 shadow_color() const {
return _shadow_color;
}
void set_shadow_color(uint32 color) {
_shadow_color = color;
}
bool has_shadow() const {
return (_shadow_alpha != QD_NO_SHADOW_ALPHA);
}
private:
//! Текущее состояние зоны (вкл/выкл).
bool _state;
//! Начальное состояние зоны (вкл/выкл).
bool _initial_state;
//! Цвет затенения персонажа, когда он находится в зоне, RGB().
uint32 _shadow_color;
//! Прозрачность затенения персонажа, когда он находится в зоне.
/**
Значения - [0, 255].
Допускается также QD_NO_SHADOW_ALPHA, затенения при этом отключается.
*/
int _shadow_alpha;
//! Высота зоны.
uint32 _height;
//! Время изменения состояния.
uint32 _update_timer;
//! Состояние включающее зону.
qdGridZoneState _state_on;
//! Состояние выключающее зону.
qdGridZoneState _state_off;
bool apply_zone() const;
};
} // namespace QDEngine
#endif // QDENGINE_QDCORE_QD_GRID_ZONE_H

View File

@@ -0,0 +1,92 @@
/* 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 "qdengine/qd_fwd.h"
#include "qdengine/qdcore/qd_grid_zone.h"
#include "qdengine/qdcore/qd_grid_zone_state.h"
namespace QDEngine {
const char *const qdGridZoneState::ZONE_STATE_ON_NAME = "\xc2\xea\xeb"; // "Вкл" -- "On"
const char *const qdGridZoneState::ZONE_STATE_OFF_NAME = "\xc2\xfb\xea\xeb"; // "Выкл" -- "Off"
qdGridZoneState::qdGridZoneState(bool st) : _state(st) {
if (st)
set_name(ZONE_STATE_ON_NAME);
else
set_name(ZONE_STATE_OFF_NAME);
}
qdGridZoneState::qdGridZoneState(const qdGridZoneState &st) : qdConditionalObject(st),
_state(st._state) {
}
qdGridZoneState::~qdGridZoneState() {
}
qdGridZoneState &qdGridZoneState::operator = (const qdGridZoneState &st) {
if (this == &st) return *this;
_state = st._state;
return *this;
}
bool qdGridZoneState::load_script(const xml::tag *p) {
return load_conditions_script(p);
}
bool qdGridZoneState::save_script(Common::WriteStream &fh, int indent) const {
for (int i = 0; i < indent; i++) {
fh.writeString("\t");
}
fh.writeString("<grid_zone_state");
if (_state) {
fh.writeString(" state=\"1\"");
} else {
fh.writeString(" state=\"0\"");
};
if (has_conditions()) {
fh.writeString(">\r\n");
save_conditions_script(fh, indent);
for (int i = 0; i < indent; i++) {
fh.writeString("\t");
}
fh.writeString("</grid_zone_state>\r\n");
} else {
fh.writeString("/>\r\n");
}
return true;
}
qdConditionalObject::trigger_start_mode qdGridZoneState::trigger_start() {
if (!owner()) return qdConditionalObject::TRIGGER_START_FAILED;
static_cast<qdGridZone *>(owner())->set_state(state());
return qdConditionalObject::TRIGGER_START_ACTIVATE;
}
} // namespace QDEngine

View File

@@ -0,0 +1,66 @@
/* 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/>.
*
*/
#ifndef QDENGINE_QDCORE_QD_GRID_ZONE_STATE_H
#define QDENGINE_QDCORE_QD_GRID_ZONE_STATE_H
#include "qdengine/parser/xml_fwd.h"
#include "qdengine/qdcore/qd_conditional_object.h"
namespace QDEngine {
//! Состояние зоны на сетке сцены.
class qdGridZoneState : public qdConditionalObject {
public:
explicit qdGridZoneState(bool st);
qdGridZoneState(const qdGridZoneState &st);
~qdGridZoneState();
qdGridZoneState &operator = (const qdGridZoneState &st);
int named_object_type() const {
return QD_NAMED_OBJECT_GRID_ZONE_STATE;
}
//! Загрузка данных из скрипта.
bool load_script(const xml::tag *p);
//! Запись данных в скрипт.
bool save_script(Common::WriteStream &fh, int indent = 0) const;
//! Включить или выключить зону.
bool state() const {
return _state;
}
qdConditionalObject::trigger_start_mode trigger_start();
static const char *const ZONE_STATE_ON_NAME;
static const char *const ZONE_STATE_OFF_NAME;
private:
//! Включить или выключить зону.
bool _state;
};
} // namespace QDEngine
#endif // QDENGINE_QDCORE_QD_GRID_ZONE_STATE_H

View File

@@ -0,0 +1,85 @@
/* 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 "qdengine/qd_fwd.h"
#include "qdengine/parser/qdscr_parser.h"
#include "qdengine/parser/xml_tag.h"
#include "qdengine/qdcore/qd_interface_background.h"
namespace QDEngine {
qdInterfaceBackground::qdInterfaceBackground() {
_state.set_owner(this);
}
qdInterfaceBackground::qdInterfaceBackground(const qdInterfaceBackground &bk) : qdInterfaceElement(bk) {
_state.set_owner(this);
_state = bk._state;
}
qdInterfaceBackground::~qdInterfaceBackground() {
_state.unregister_resources();
}
qdInterfaceBackground &qdInterfaceBackground::operator = (const qdInterfaceBackground &bk) {
if (this == &bk) return *this;
*static_cast<qdInterfaceElement *>(this) = bk;
_state = bk._state;
return *this;
}
bool qdInterfaceBackground::save_script_body(Common::WriteStream &fh, int indent) const {
if (!_state.save_script(fh, indent)) {
return false;
}
return true;
}
bool qdInterfaceBackground::load_script_body(const xml::tag *p) {
for (xml::tag::subtag_iterator it = p->subtags_begin(); it != p->subtags_end(); ++it) {
switch (it->ID()) {
case QDSCR_INTERFACE_ELEMENT_STATE:
if (!_state.load_script(&*it)) return false;
break;
}
}
return true;
}
bool qdInterfaceBackground::mouse_handler(int x, int y, mouseDispatcher::mouseEvent ev) {
return true;
}
bool qdInterfaceBackground::keyboard_handler(Common::KeyCode vkey) {
return false;
}
bool qdInterfaceBackground::init(bool is_game_active) {
return set_state(&_state);
}
} // namespace QDEngine

View File

@@ -0,0 +1,106 @@
/* 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/>.
*
*/
#ifndef QDENGINE_QDCORE_QD_INTERFACE_BACKGROUND_H
#define QDENGINE_QDCORE_QD_INTERFACE_BACKGROUND_H
#include "qdengine/qdcore/qd_interface_element.h"
#include "qdengine/qdcore/qd_interface_element_state.h"
namespace QDEngine {
//! Интерфейсный элемент - фон.
class qdInterfaceBackground : public qdInterfaceElement {
public:
qdInterfaceBackground();
qdInterfaceBackground(const qdInterfaceBackground &bk);
~qdInterfaceBackground();
qdInterfaceBackground &operator = (const qdInterfaceBackground &bk);
//! Возвращает тип элемента.
qdInterfaceElement::element_type get_element_type() const {
return qdInterfaceElement::EL_BACKGROUND;
}
//! Обработчик событий мыши.
bool mouse_handler(int x, int y, mouseDispatcher::mouseEvent ev);
//! Обработчик ввода с клавиатуры.
bool keyboard_handler(Common::KeyCode vkey);
//! Инициализация элемента.
/**
Вызывается каждый раз при заходе на экран, которому принадлежит элемент.
*/
bool init(bool is_game_active = true);
//! Устанавливает имя файла для анимации.
/**
Если надо убрать анимацию - передать NULL в качестве имени файла.
*/
void set_animation_file(const Common::Path &name) {
_state.set_animation_file(name);
}
//! Возвращает имя файла для анимации.
const Common::Path animation_file() const {
return _state.animation_file();
}
//! Возвращает флаги анимации.
int animation_flags() const {
return _state.animation_flags();
}
//! Устанавливает флаг анимации.
void set_animation_flag(int fl) {
_state.set_animation_flag(fl);
}
//! Скидывает флаг анимации.
void drop_animation_flag(int fl) {
_state.drop_animation_flag(fl);
}
//! Возвращает true, если для анимации установлен флаг fl.
bool check_animation_flag(int fl) const {
return _state.check_animation_flag(fl);
}
//! Возвращает true, если к фону привязана анимация.
bool has_animation() const {
return _state.has_animation();
}
protected:
//! Запись данных в скрипт.
bool save_script_body(Common::WriteStream &fh, int indent = 0) const;
//! Загрузка данных из скрипта.
bool load_script_body(const xml::tag *p);
private:
//! Состояние, в котором хранятся все необходимые настройки.
qdInterfaceElementState _state;
};
} // namespace QDEngine
#endif // QDENGINE_QDCORE_QD_INTERFACE_BACKGROUND_H

View File

@@ -0,0 +1,298 @@
/* 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 "qdengine/qdengine.h"
#include "qdengine/qd_fwd.h"
#include "qdengine/parser/qdscr_parser.h"
#include "qdengine/parser/xml_tag.h"
#include "qdengine/qdcore/qd_game_dispatcher.h"
#include "qdengine/qdcore/qd_interface_button.h"
namespace QDEngine {
qdInterfaceButton::qdInterfaceButton() : _cur_state(-1) {
}
qdInterfaceButton::qdInterfaceButton(const qdInterfaceButton &bt) : qdInterfaceElement(bt),
#ifndef _QD_INTERFACE_BUTTON_PTR_CONTAINER
_states(bt._states),
#endif
_cur_state(-1) {
#ifdef _QD_INTERFACE_BUTTON_PTR_CONTAINER
for (int i = 0; i < bt.num_states(); i++)
_states.push_back(new qdInterfaceElementState(*bt.get_state(i)));
#endif
for (int i = 0; i < num_states(); i++) {
get_state(i)->set_owner(this);
get_state(i)->register_resources();
}
}
qdInterfaceButton::~qdInterfaceButton() {
_states.clear();
}
qdInterfaceButton &qdInterfaceButton::operator = (const qdInterfaceButton &bt) {
if (this == &bt) return *this;
*static_cast<qdInterfaceElement *>(this) = bt;
#ifdef _QD_INTERFACE_BUTTON_PTR_CONTAINER
for (state_container_t::iterator it = _states.begin(); it != _states.end(); ++it)
delete *it;
_states.clear();
for (int i = 0; i < bt.num_states(); i++)
_states.push_back(new qdInterfaceElementState(*bt.get_state(i)));
#else
_states.clear();
_states = bt._states;
#endif
for (int i = 0; i < num_states(); i++) {
get_state(i)->set_owner(this);
get_state(i)->register_resources();
}
_cur_state = -1;
return *this;
}
bool qdInterfaceButton::activate_state(int state_num) {
if (state_num >= (int)_states.size())
state_num = _states.size() - 1;
if (!_states.size() || _cur_state == state_num) return false;
if (state_num < 0)
state_num = 0;
_cur_state = state_num;
if (state_num < (int)_states.size())
return set_state(get_state(state_num));
return true;
}
bool qdInterfaceButton::activate_state(const char *state_name) {
for (int i = 0; i < num_states(); i++) {
qdInterfaceElementState *p = get_state(i);
if (!strcmp(p->name(), state_name))
return activate_state(i);
}
return false;
}
bool qdInterfaceButton::set_option_value(int value) {
value = CLIP<int>(value, 0, _states.size() - 1);
activate_state(value);
return true;
}
bool qdInterfaceButton::add_state(const qdInterfaceElementState &st) {
#ifdef _QD_INTERFACE_BUTTON_PTR_CONTAINER
_states.push_back(new qdInterfaceElementState(st));
#else
_states.push_back(st);
#endif
get_state(_states.size() - 1)->set_owner(this);
get_state(_states.size() - 1)->register_resources();
return true;
}
bool qdInterfaceButton::insert_state(int insert_before, const qdInterfaceElementState &st) {
assert(insert_before >= 0 && insert_before < (int)_states.size());
#ifdef _QD_INTERFACE_BUTTON_PTR_CONTAINER
_states.insert(_states.begin() + insert_before, new qdInterfaceElementState(st));
#else
_states.insert(_states.begin() + insert_before, st);
#endif
get_state(insert_before)->set_owner(this);
get_state(insert_before)->register_resources();
return true;
}
bool qdInterfaceButton::erase_state(int state_num) {
assert(state_num >= 0 && state_num < (int)_states.size());
#ifdef _QD_INTERFACE_BUTTON_PTR_CONTAINER
// delete *(_states.begin() + state_num);
#endif
_states.erase(_states.begin() + state_num);
if (_cur_state == state_num)
activate_state(--_cur_state);
return true;
}
bool qdInterfaceButton::mouse_handler(int x, int y, mouseDispatcher::mouseEvent ev) {
if (_cur_state == -1) return false;
return get_state(_cur_state)->mouse_handler(x, y, ev);
}
bool qdInterfaceButton::keyboard_handler(Common::KeyCode vkey) {
return false;
}
bool qdInterfaceButton::init(bool is_game_active) {
set_lock(false);
if (!is_game_active) {
for (int i = 0; i < num_states(); i++) {
if (get_state(i)->need_active_game()) {
set_lock(true);
break;
}
}
}
for (int i = 0; i < num_states(); i++) {
qdInterfaceElementState *p = get_state(i);
p->set_state_mode(qdInterfaceElementState::DEFAULT_MODE);
}
if (_cur_state != -1)
return set_state(get_state(_cur_state));
return true;
}
bool qdInterfaceButton::save_script_body(Common::WriteStream &fh, int indent) const {
for (int i = 0; i < num_states(); i++) {
if (!get_state(i)->save_script(fh, indent + 1))
return false;
}
return true;
}
bool qdInterfaceButton::load_script_body(const xml::tag *p) {
int num_states = 0;
for (xml::tag::subtag_iterator it = p->subtags_begin(); it != p->subtags_end(); ++it) {
switch (it->ID()) {
case QDSCR_INTERFACE_ELEMENT_STATE:
num_states++;
break;
}
}
if (num_states) {
_states.reserve(num_states);
_cur_state = 0;
}
for (xml::tag::subtag_iterator it = p->subtags_begin(); it != p->subtags_end(); ++it) {
switch (it->ID()) {
case QDSCR_INTERFACE_ELEMENT_STATE: {
qdInterfaceElementState st;
if (!st.load_script(&*it))
return false;
add_state(st);
}
break;
}
}
return true;
}
bool qdInterfaceButton::quant(float dt) {
debugC(9, kDebugQuant, "qdInterfaceButton::quant()");
qdInterfaceElement::quant(dt);
if (find_event(qdInterfaceEvent::EVENT_CLEAR_MOUSE)) {
if (qdGameDispatcher * dp = qdGameDispatcher::get_dispatcher()) {
debugC(3, kDebugQuant, "qdInterfaceButton::quant()");
if (dp->is_on_mouse(NULL))
activate_state(1);
else
activate_state(0);
}
}
if (_cur_state != -1) {
get_state(_cur_state)->quant(dt);
}
return true;
}
bool qdInterfaceButton::hit_test(int x, int y) const {
if (_cur_state != -1) {
if (get_state(_cur_state)->has_contour(get_state(_cur_state)->state_mode()))
return get_state(_cur_state)->hit_test(x - r().x, y - r().y, get_state(_cur_state)->state_mode());
}
return qdInterfaceElement::hit_test(x, y);
}
bool qdInterfaceButton::change_state(bool direction) {
if (num_states()) {
if (_cur_state != -1) {
_cur_state += (direction) ? 1 : -1;
if (_cur_state < 0) _cur_state = num_states() - 1;
if (_cur_state >= num_states()) _cur_state = 0;
} else
_cur_state = 0;
return activate_state(_cur_state);
}
return false;
}
const qdInterfaceEvent *qdInterfaceButton::find_event(qdInterfaceEvent::event_t type) const {
for (int i = 0; i < num_states(); i++) {
const qdInterfaceElementState *p = get_state(i);
if (const qdInterfaceEvent * ev = p->find_event(type))
return ev;
}
return NULL;
}
bool qdInterfaceButton::has_event(qdInterfaceEvent::event_t type, const char *ev_data) const {
for (int i = 0; i < num_states(); i++) {
const qdInterfaceElementState *p = get_state(i);
if (p->has_event(type, ev_data))
return true;
}
return false;
}
} // namespace QDEngine

View File

@@ -0,0 +1,163 @@
/* 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/>.
*
*/
#ifndef QDENGINE_QDCORE_QD_INTERFACE_BUTTON_H
#define QDENGINE_QDCORE_QD_INTERFACE_BUTTON_H
#include "qdengine/qdcore/qd_interface_element.h"
#include "qdengine/qdcore/qd_interface_element_state.h"
namespace QDEngine {
//! Интерфейсный элемент - кнопка.
class qdInterfaceButton : public qdInterfaceElement {
public:
qdInterfaceButton();
qdInterfaceButton(const qdInterfaceButton &bt);
~qdInterfaceButton();
qdInterfaceButton &operator = (const qdInterfaceButton &bt);
//! Возвращает тип элемента.
qdInterfaceElement::element_type get_element_type() const {
return qdInterfaceElement::EL_BUTTON;
}
//! Возвращает количество состояний кнопки.
int num_states() const {
return _states.size();
}
//! Включает состояние номер state_num.
bool activate_state(int state_num);
//! Включает состояние с именем state_name.
bool activate_state(const char *state_name);
int option_value() const {
return _cur_state;
}
bool set_option_value(int value);
//! Возвращает указатель на состояние кнопки.
const qdInterfaceElementState *get_state(int state_num) const {
assert(state_num >= 0 && state_num < (int)_states.size());
#ifndef _QD_INTERFACE_BUTTON_PTR_CONTAINER
return &_states[state_num];
#else
return &*_states[state_num];
#endif
}
//! Возвращает указатель на состояние кнопки.
qdInterfaceElementState *get_state(int state_num) {
assert(state_num >= 0 && state_num < (int)_states.size());
#ifndef _QD_INTERFACE_BUTTON_PTR_CONTAINER
return &_states[state_num];
#else
return &*_states[state_num];
#endif
}
//! Добавляет состояние кнопки.
/**
Параметры из состояния st копируются во внутренние данные.
*/
bool add_state(const qdInterfaceElementState &st);
//! Добавляет состояние кнопки.
/**
Новое состояние добавляется в список перед состоянием номер insert_before.
Параметры из состояния st копируются во внутренние данные.
*/
bool insert_state(int insert_before, const qdInterfaceElementState &st);
//! Удаляет состояние номер state_num.
bool erase_state(int state_num);
//! Модифицирует состояние кнопки.
/**
Соответствующему состоянию присваивается значение st.
*/
bool modify_state(int state_num, const qdInterfaceElementState &st) {
assert(state_num >= 0 && state_num < (int)_states.size());
#ifdef _QD_INTERFACE_BUTTON_PTR_CONTAINER
*_states[state_num] = st;
_states[state_num]->set_owner(this);
#else
_states[state_num] = st;
_states[state_num].set_owner(this);
#endif
return true;
}
//! Переключает состояние кнопки.
/**
Если direction == true включается следующее состояние, если false - предыдущее.
*/
bool change_state(bool direction = true);
//! Обработчик событий мыши.
bool mouse_handler(int x, int y, mouseDispatcher::mouseEvent ev);
//! Обработчик ввода с клавиатуры.
bool keyboard_handler(Common::KeyCode vkey);
//! Инициализация элемента.
/**
Вызывается каждый раз при заходе на экран, которому принадлежит элемент.
*/
bool init(bool is_game_active = true);
//! Обсчет логики, параметр - время в секундах.
bool quant(float dt);
//! Возвращает true, если точка с экранными координатами (x,у) попадает в элемент.
bool hit_test(int x, int y) const;
const qdInterfaceEvent *find_event(qdInterfaceEvent::event_t type) const;
bool has_event(qdInterfaceEvent::event_t type, const char *ev_data = NULL) const;
protected:
//! Запись данных в скрипт.
bool save_script_body(Common::WriteStream &fh, int indent = 0) const;
//! Загрузка данных из скрипта.
bool load_script_body(const xml::tag *p);
private:
#ifdef _QD_INTERFACE_BUTTON_PTR_CONTAINER
typedef Std::vector<qdInterfaceElementState *> state_container_t;
#else
typedef Std::vector<qdInterfaceElementState> state_container_t;
#endif
//! Состояния кнопки.
state_container_t _states;
//! Текущее состояние кнопки.
int _cur_state;
};
} // namespace QDEngine
#endif // QDENGINE_QDCORE_QD_INTERFACE_BUTTON_H

View File

@@ -0,0 +1,206 @@
/* 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 "qdengine/qd_fwd.h"
#include "qdengine/parser/xml_tag_buffer.h"
#include "qdengine/parser/qdscr_parser.h"
#include "qdengine/qdcore/qd_game_dispatcher.h"
#include "qdengine/qdcore/qd_counter.h"
#include "qdengine/qdcore/qd_interface_counter.h"
#include "qdengine/system/graphics/gr_dispatcher.h"
namespace QDEngine {
qdInterfaceCounter::qdInterfaceCounter() : _counter(0) {
_digits = 4;
_lastValue = -1;
_textFormat.toggle_global_depend(false);
}
qdInterfaceCounter::qdInterfaceCounter(const qdInterfaceCounter &counter) : qdInterfaceElement(counter),
_textFormat(counter._textFormat),
_digits(counter._digits),
_counterName(counter._counterName),
_counter(counter._counter) {
_lastValue = -1;
_textFormat.toggle_global_depend(false);
}
qdInterfaceCounter::~qdInterfaceCounter() {
}
qdInterfaceCounter &qdInterfaceCounter::operator = (const qdInterfaceCounter &counter) {
if (this == &counter) return *this;
*static_cast<qdInterfaceElement *>(this) = counter;
_textFormat = counter._textFormat;
_digits = counter._digits;
_counterName = counter._counterName;
_counter = counter._counter;
_lastValue = -1;
return *this;
}
bool qdInterfaceCounter::save_script_body(Common::WriteStream &fh, int indent) const {
for (int i = 0; i <= indent; i++) {
fh.writeString("\t");
}
fh.writeString(Common::String::format("<counter_name>%s</counter_name>\r\n", qdscr_XML_string(counterName())));
_textFormat.save_script(fh, indent + 1);
for (int i = 0; i <= indent; i++) {
fh.writeString("\t");
}
fh.writeString(Common::String::format("<digits>%d</digits>\r\n", _digits));
return true;
}
bool qdInterfaceCounter::load_script_body(const xml::tag *p) {
for (xml::tag::subtag_iterator it = p->subtags_begin(); it != p->subtags_end(); ++it) {
switch (it->ID()) {
case QDSCR_COUNTER_NAME:
setCounterName(it->data());
break;
case QDSCR_SCREEN_TEXT_FORMAT:
_textFormat.load_script(&*it);
break;
case QDSCR_COUNTER_DIGITS:
_digits = xml::tag_buffer(*it).get_int();
break;
}
}
return true;
}
Common::String qdInterfaceCounter::data() const {
Common::String str;
int val = 0;
if (_counter)
val = _counter->value();
if (val < 0)
str += "-";
int delta = 10;
for (int i = 1; i < _digits; i++) {
if (abs(val) < abs(delta))
str += "0";
delta *= 10;
}
str += Common::String::format("%d", abs(val));
debugC(3, kDebugLog, "qdInterfaceCounter::data() %s", str.c_str());
return str;
}
bool qdInterfaceCounter::mouse_handler(int x, int y, mouseDispatcher::mouseEvent ev) {
return true;
}
bool qdInterfaceCounter::keyboard_handler(Common::KeyCode vkey) {
return false;
}
grScreenRegion qdInterfaceCounter::screen_region() const {
Vect2i sz = Vect2i(size_x(), size_y());
Vect2i pos = r();
return grScreenRegion(pos.x, pos.y, sz.x, sz.y);
}
bool qdInterfaceCounter::init(bool is_game_active) {
_counter = qdGameDispatcher::get_dispatcher()->get_counter(counterName());
return true;
}
bool qdInterfaceCounter::redraw() const {
qdInterfaceElement::redraw();
if (_counter) {
const grFont *font = qdGameDispatcher::get_dispatcher()->
find_font(_textFormat.font_type());
Vect2i sz = Vect2i(size_x(), size_y());
Vect2i pos = r() - sz / 2;
grDispatcher::instance()->drawAlignedText(pos.x, pos.y, sz.x, sz.y,
_textFormat.color(), data().c_str(), GR_ALIGN_LEFT, 0, 0, font);
}
return true;
}
bool qdInterfaceCounter::need_redraw() const {
if (qdInterfaceElement::need_redraw())
return true;
if (_counter)
return _lastValue != _counter->value();
return false;
}
bool qdInterfaceCounter::post_redraw() {
qdInterfaceElement::post_redraw();
if (_counter)
_lastValue = _counter->value();
return true;
}
int qdInterfaceCounter::size_x() const {
const grFont *font = qdGameDispatcher::get_dispatcher()->find_font(_textFormat.font_type());
return grDispatcher::instance()->textWidth(data().c_str(), 0, font);
}
int qdInterfaceCounter::size_y() const {
const grFont *font = qdGameDispatcher::get_dispatcher()->find_font(_textFormat.font_type());
return grDispatcher::instance()->textHeight(data().c_str(), 0, font);
}
void qdInterfaceCounter::setCounter(const qdCounter *counter) {
_counter = counter;
_counterName = (counter) ? counter->name() : "";
}
const char *qdInterfaceCounter::counterName() const {
return _counterName.c_str();
}
void qdInterfaceCounter::setCounterName(const char *name) {
_counterName = name;
}
} // namespace QDEngine

View File

@@ -0,0 +1,115 @@
/* 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/>.
*
*/
#ifndef QDENGINE_QDCORE_QD_INTERFACE_COUNTER_H
#define QDENGINE_QDCORE_QD_INTERFACE_COUNTER_H
#include "qdengine/qdcore/qd_named_object_reference.h"
#include "qdengine/qdcore/qd_screen_text.h"
#include "qdengine/qdcore/qd_interface_element.h"
namespace QDEngine {
//! Interface widget for displaying a counter
class qdInterfaceCounter : public qdInterfaceElement {
public:
qdInterfaceCounter();
qdInterfaceCounter(const qdInterfaceCounter &counter);
~qdInterfaceCounter();
qdInterfaceCounter &operator = (const qdInterfaceCounter &counter);
//! Возвращает тип элемента.
qdInterfaceElement::element_type get_element_type() const {
return qdInterfaceElement::EL_COUNTER;
}
//! Обработчик событий мыши.
bool mouse_handler(int x, int y, mouseDispatcher::mouseEvent ev);
//! Обработчик ввода с клавиатуры.
bool keyboard_handler(Common::KeyCode vkey);
grScreenRegion screen_region() const;
//! Инициализация элемента.
/**
Вызывается каждый раз при заходе на экран, которому принадлежит элемент.
*/
bool init(bool is_game_active = true);
bool redraw() const;
bool need_redraw() const;
bool post_redraw();
//! Возвращает размер элемента по горизонтали в пикселах.
int size_x() const;
//! Возвращает размер элемента по вертикали в пикселах.
int size_y() const;
/// количество отображаемых разрядов
int digits() const {
return _digits;
}
void setDigits(int digits) {
_digits = digits;
}
const qdScreenTextFormat &textFormat() const {
return _textFormat;
}
void setTextFormat(const qdScreenTextFormat &text_format) {
_textFormat = text_format;
}
const qdCounter *counter() const {
return _counter;
}
void setCounter(const qdCounter *counter);
const char *counterName() const;
void setCounterName(const char *name);
protected:
//! Запись данных в скрипт.
bool save_script_body(Common::WriteStream &fh, int indent = 0) const;
//! Загрузка данных из скрипта.
bool load_script_body(const xml::tag *p);
Common::String data() const;
private:
qdScreenTextFormat _textFormat;
int _digits;
Common::String _counterName;
const qdCounter *_counter;
int _lastValue;
};
} // namespace QDEngine
#endif // QDENGINE_QDCORE_QD_INTERFACE_COUNTER_H

View File

@@ -0,0 +1,752 @@
/* 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/config-manager.h"
#include "common/debug.h"
#include "common/savefile.h"
#include "qdengine/qdengine.h"
#include "qdengine/qd_fwd.h"
#include "qdengine/parser/xml_tag_buffer.h"
#include "qdengine/parser/qdscr_parser.h"
#include "qdengine/system/input/mouse_input.h"
#include "qdengine/qdcore/qd_interface_dispatcher.h"
#include "qdengine/qdcore/qd_interface_screen.h"
#include "qdengine/qdcore/qd_interface_element_state.h"
#include "qdengine/qdcore/qd_interface_save.h"
#include "qdengine/qdcore/qd_interface_button.h"
#include "qdengine/qdcore/qd_game_dispatcher.h"
#include "qdengine/qdcore/qd_game_object_moving.h"
#include "qdengine/qdcore/qd_game_scene.h"
#include "qdengine/qdcore/qd_setup.h"
#include "qdengine/qdcore/qd_interface_text_window.h"
#include "qdengine/qdcore/qd_resource.h"
namespace QDEngine {
qdInterfaceDispatcher *qdInterfaceDispatcher::_dispatcher = NULL;
qdInterfaceDispatcher::qdInterfaceDispatcher() : _cur_screen(NULL),
_next_screen(NULL),
_is_active(false),
_is_mouse_hover(false),
_autohide_disable(false),
_need_full_redraw(false),
_need_scene_redraw(false),
_end_game_mode(false),
_need_save_screenshot(true),
_need_show_save_time(false),
_need_show_save_title(false),
_save_font_type(QD_FONT_TYPE_NONE),
_save_font_color(0x00FFFFFF),
_background_screen(NULL),
_background_screen_lock(false),
_modalScreenMode(MODAL_SCREEN_OTHER) {
}
qdInterfaceDispatcher::~qdInterfaceDispatcher() {
_screens.clear();
}
bool qdInterfaceDispatcher::add_screen(qdInterfaceScreen *scr) {
return _screens.add_object(scr);
}
bool qdInterfaceDispatcher::rename_screen(qdInterfaceScreen *scr, const char *name) {
return _screens.rename_object(scr, name);
}
bool qdInterfaceDispatcher::remove_screen(qdInterfaceScreen *scr) {
return _screens.remove_object(scr);
}
qdInterfaceScreen *qdInterfaceDispatcher::get_screen(const char *screen_name) {
if (!screen_name) return NULL;
return _screens.get_object(screen_name);
}
bool qdInterfaceDispatcher::is_screen_in_list(const qdInterfaceScreen *scr) {
return _screens.is_in_list(scr);
}
bool qdInterfaceDispatcher::select_screen(const char *screen_name, bool lock_resources) {
qdInterfaceScreen *p = get_screen(screen_name);
if (p) {
if (_cur_screen && _cur_screen->is_locked()) {
debugC(3, kDebugQuant, "qdInterfaceDispatcher::select_screen() Selecting screen: %s", transCyrillic(screen_name));
for (resource_container_t::resource_list_t::const_iterator it = _resources.resource_list().begin(); it != _resources.resource_list().end(); ++it) {
if (p->has_references(*it)) {
if (!(*it)->is_resource_loaded()) {
debugC(3, kDebugQuant, "qdInterfaceDispatcher::select_screen() Resource is used in both screens %s and %s", transCyrillic(_cur_screen->name()), transCyrillic(p->name()));
(*it)->load_resource();
}
} else {
if ((*it)->is_resource_loaded() && !_cur_screen->has_references(*it))
(*it)->free_resource();
}
}
} else {
for (resource_container_t::resource_list_t::const_iterator it = _resources.resource_list().begin(); it != _resources.resource_list().end(); ++it) {
if (p->has_references(*it)) {
if (!(*it)->is_resource_loaded())
(*it)->load_resource();
} else {
if ((*it)->is_resource_loaded())
(*it)->free_resource();
}
}
}
} else {
if (_cur_screen && _cur_screen->is_locked()) {
for (resource_container_t::resource_list_t::const_iterator it = _resources.resource_list().begin(); it != _resources.resource_list().end(); ++it) {
if ((*it)->is_resource_loaded() && !_cur_screen->has_references(*it))
(*it)->free_resource();
}
} else {
for (resource_container_t::resource_list_t::const_iterator it = _resources.resource_list().begin(); it != _resources.resource_list().end(); ++it) {
if ((*it)->is_resource_loaded())
(*it)->free_resource();
}
}
}
if (p && _cur_screen != p)
p->set_autohide_phase(1.0f);
_cur_screen = p;
if (_cur_screen) {
debugC(3, kDebugQuant, "qdInterfaceDispatcher::select_screen() if(cur_scene_): %s", transCyrillic(screen_name));
bool is_game_active = false;
if (qdGameDispatcher * dp = qdGameDispatcher::get_dispatcher()) {
if (dp->get_active_scene())
is_game_active = true;
if (_cur_screen->has_music_track())
dp->play_music_track(&_cur_screen->music_track(), true);
}
_cur_screen->init(is_game_active);
if (lock_resources)
_cur_screen->lock_resources();
}
_need_full_redraw = true;
return true;
}
bool qdInterfaceDispatcher::select_background_screen(qdInterfaceScreen *p) {
if (_background_screen && _background_screen != p) {
if (!_background_screen_lock)
_background_screen->unlock_resources();
_background_screen = 0;
_need_full_redraw = true;
}
if (p) {
_background_screen = p;
_background_screen_lock = p->is_locked();
p->lock_resources();
_need_full_redraw = true;
}
return true;
}
bool qdInterfaceDispatcher::select_ingame_screen(bool inventory_state) {
if (has_ingame_screen(inventory_state)) {
debugC(3, kDebugQuant, "qdInterfaceDispatcher::select_ingame_screen(): Selecting ingame screen: %s", transCyrillic(ingame_screen_name(inventory_state)));
return select_screen(ingame_screen_name(inventory_state), true);
}
return select_screen(NULL);
}
qdResource *qdInterfaceDispatcher::add_resource(const Common::Path &file_name, const qdInterfaceElementState *owner) {
return _resources.add_resource(file_name, owner);
}
bool qdInterfaceDispatcher::remove_resource(const Common::Path &file_name, const qdInterfaceElementState *owner) {
return _resources.remove_resource(file_name, owner);
}
bool qdInterfaceDispatcher::redraw(int dx, int dy) const {
if (_background_screen)
_background_screen->redraw(dx, dy);
if (_cur_screen)
return _cur_screen->redraw(dx, dy);
return false;
}
bool qdInterfaceDispatcher::pre_redraw() {
if (_cur_screen)
_cur_screen->pre_redraw(_need_full_redraw);
if (_background_screen)
_background_screen->pre_redraw(_need_full_redraw);
return false;
}
bool qdInterfaceDispatcher::post_redraw() {
_need_full_redraw = false;
if (_cur_screen)
return _cur_screen->post_redraw();
if (_background_screen)
_background_screen->post_redraw();
return false;
}
bool qdInterfaceDispatcher::quant(float dt) {
if (_next_screen) {
select_screen(_next_screen);
set_next_screen(NULL);
}
if (_cur_screen)
return _cur_screen->quant(dt);
return false;
}
bool qdInterfaceDispatcher::mouse_handler(int x, int y, mouseDispatcher::mouseEvent ev) {
debugC(9, kDebugInput, "qdInterfaceDispatcher::mouse_handler() x: %d, y: %d, ev: %d", x, y, ev);
_is_mouse_hover = false;
_autohide_disable = false;
if (_end_game_mode) {
debugC(3, kDebugInput, "qdInterfaceDispatcher::mouse_handler() _end_game_mode: %d", _end_game_mode);
if (ev == mouseDispatcher::EV_LEFT_DOWN || ev == mouseDispatcher::EV_RIGHT_DOWN) {
handle_event(qdInterfaceEvent::EVENT_CHANGE_INTERFACE_SCREEN, main_menu_screen_name());
_end_game_mode = false;
return true;
}
}
if (_cur_screen) {
debugC(9, kDebugInput, "qdInterfaceDispatcher::mouse_handler() _cur_screen");
return _cur_screen->mouse_handler(x, y, ev);
}
return false;
}
bool qdInterfaceDispatcher::keyboard_handler(Common::KeyCode vkey) {
if (_cur_screen) {
if (vkey == Common::KEYCODE_ESCAPE && has_main_menu()) {
if (_cur_screen->name() && !strcmp(_cur_screen->name(), main_menu_screen_name()))
handle_event(qdInterfaceEvent::EVENT_RESUME_GAME, NULL);
else {
if (qdGameDispatcher::get_dispatcher()->is_main_menu_exit_enabled())
select_screen(main_menu_screen_name());
}
return true;
}
return _cur_screen->keyboard_handler(vkey);
}
return false;
}
bool qdInterfaceDispatcher::char_input_handler(int vkey) {
if (_cur_screen)
return _cur_screen->char_input_handler(vkey);
return false;
}
bool qdInterfaceDispatcher::save_script(Common::WriteStream &fh, int indent) const {
for (int i = 0; i < indent; i++) {
fh.writeString("\t");
}
fh.writeString("<interface");
if (has_main_menu()) {
fh.writeString(Common::String::format(" main_menu=\"%s\"", qdscr_XML_string(main_menu_screen_name())));
}
if (need_scene_redraw()) {
fh.writeString(" draw_scene=\"1\"");
}
if (has_ingame_screen(false)) {
fh.writeString(Common::String::format(" ingame_screen0=\"%s\"", qdscr_XML_string(ingame_screen_name(false))));
}
if (has_ingame_screen(true)) {
fh.writeString(Common::String::format(" ingame_screen1=\"%s\"", qdscr_XML_string(ingame_screen_name(true))));
}
if (!_save_prompt_screen_name.empty()) {
fh.writeString(Common::String::format(" save_prompt_screen=\"%s\"", qdscr_XML_string(_save_prompt_screen_name.c_str())));
}
if (!_save_title_screen_name.empty()) {
fh.writeString(Common::String::format(" save_title_screen=\"%s\"", qdscr_XML_string(_save_title_screen_name.c_str())));
}
// Значение по умолчанию - true, значит сохраняем только false
if (!_need_save_screenshot) {
fh.writeString(" need_save_screenshot=\"0\"");
}
if (_need_show_save_time) {
fh.writeString(" need_show_save_time=\"1\"");
}
if (_need_show_save_title) {
fh.writeString(" need_show_save_name=\"1\"");
}
if (QD_FONT_TYPE_NONE != _save_font_type) {
fh.writeString(Common::String::format(" save_font_type=\"%d\"", _save_font_type));
}
if (0x00FFFFFF != _save_font_color) {
fh.writeString(Common::String::format(" save_font_color=\"%d\"", _save_font_color));
}
fh.writeString(">\r\n");
for (auto &it : screen_list()) {
it->save_script(fh, indent + 1);
}
for (int i = 0; i < indent; i++) {
fh.writeString("\t");
}
fh.writeString("</interface>\r\n");
return true;
}
bool qdInterfaceDispatcher::load_script(const xml::tag *p) {
for (xml::tag::subtag_iterator it = p->subtags_begin(); it != p->subtags_end(); ++it) {
switch (it->ID()) {
case QDSCR_INTERFACE_SCENE_REDRAW:
set_scene_redraw(xml::tag_buffer(*it).get_int());
break;
case QDSCR_INTERFACE_SCREEN: {
qdInterfaceScreen *scr = new qdInterfaceScreen;
scr->set_owner(this);
scr->load_script(&*it);
add_screen(scr);
}
break;
case QDSCR_INTERFACE_MAIN_MENU:
set_main_menu_screen(it->data());
break;
case QDSCR_INTERFACE_INGAME_SCREEN0:
_ingame_screen_names[0] = it->data();
break;
case QDSCR_INTERFACE_INGAME_SCREEN1:
_ingame_screen_names[1] = it->data();
break;
case QDSCR_INTERFACE_SAVE_PROMPT_SCREEN:
_save_prompt_screen_name = it->data();
break;
case QDSCR_INTERFACE_SAVE_NAME_SCREEN:
_save_title_screen_name = it->data();
break;
case QDSCR_INTERFACE_NEED_SAVE_SCREENSHOT:
_need_save_screenshot = (0 != xml::tag_buffer(*it).get_int());
break;
case QDSCR_INTERFACE_NEED_SHOW_SAVE_TIME:
_need_show_save_time = (0 != xml::tag_buffer(*it).get_int());
break;
case QDSCR_INTERFACE_NEED_SHOW_SAVE_NAME:
_need_show_save_title = (0 != xml::tag_buffer(*it).get_int());
break;
case QDSCR_INTERFACE_SAVE_FONT_TYPE:
_save_font_type = xml::tag_buffer(*it).get_int();
break;
case QDSCR_INTERFACE_SAVE_FONT_COLOR:
_save_font_color = xml::tag_buffer(*it).get_int();
break;
}
}
return true;
}
void qdInterfaceDispatcher::set_main_menu_screen(const char *name) {
if (name)
_main_menu_screen_name = name;
else
_main_menu_screen_name.clear();
}
void qdInterfaceDispatcher::set_ingame_screen(const char *name, bool inventory_state) {
if (name)
_ingame_screen_names[inventory_state] = name;
else
_ingame_screen_names[inventory_state].clear();
}
bool qdInterfaceDispatcher::handle_event(int event_code, const char *event_data, qdInterfaceObjectBase *sender) {
switch (event_code) {
case qdInterfaceEvent::EVENT_EXIT:
if (qdGameDispatcher * p = qd_get_game_dispatcher()) {
p->toggle_exit();
return true;
}
break;
case qdInterfaceEvent::EVENT_CHANGE_INTERFACE_SCREEN:
if (event_data) {
if (has_main_menu() && !strcmp(main_menu_screen_name(), event_data)) {
if (qdGameDispatcher * p = qdGameDispatcher::get_dispatcher()) {
p->set_flag(qdGameDispatcher::MAIN_MENU_FLAG);
return true;
}
} else {
set_next_screen(event_data);
return true;
}
}
return false;
case qdInterfaceEvent::EVENT_TMP_HIDE_ELEMENT:
if (_cur_screen)
return _cur_screen->hide_element(event_data, true);
break;
case qdInterfaceEvent::EVENT_HIDE_ELEMENT:
if (_cur_screen)
return _cur_screen->hide_element(event_data);
break;
case qdInterfaceEvent::EVENT_SHOW_ELEMENT:
if (_cur_screen)
return _cur_screen->show_element(event_data);
break;
case qdInterfaceEvent::EVENT_RESUME_GAME:
if (qdGameDispatcher * p = qd_get_game_dispatcher()) {
if (p->get_active_scene())
return p->toggle_main_menu(false);
}
break;
case qdInterfaceEvent::EVENT_SET_SAVE_MODE:
qdInterfaceSave::set_save_mode(true);
return true;
case qdInterfaceEvent::EVENT_SET_LOAD_MODE:
qdInterfaceSave::set_save_mode(false);
return true;
case qdInterfaceEvent::EVENT_NEW_GAME:
if (qdGameDispatcher * p = qd_get_game_dispatcher()) {
p->toggle_main_menu(false);
return p->restart();
}
break;
case qdInterfaceEvent::EVENT_ACTIVATE_PERSONAGE:
if (!event_data) return false;
if (qdGameDispatcher * p = qd_get_game_dispatcher()) {
if (qdGameScene * sp = p->get_active_scene()) {
qdGameObjectMoving *obj = dynamic_cast<qdGameObjectMoving *>(sp->get_object(event_data));
if (!obj || obj == sp->get_active_personage()) return false;
sp->set_active_personage(obj);
return true;
}
}
break;
case qdInterfaceEvent::EVENT_CHANGE_PERSONAGE:
if (qdGameDispatcher * p = qd_get_game_dispatcher()) {
if (qdGameScene * sp = p->get_active_scene()) {
sp->change_active_personage();
return true;
}
}
break;
case qdInterfaceEvent::EVENT_PREV_ELEMENT_STATE:
if (_cur_screen && event_data) {
if (qdInterfaceButton * p = dynamic_cast<qdInterfaceButton * >(_cur_screen->get_element(event_data)))
return p->change_state(false);
}
break;
case qdInterfaceEvent::EVENT_NEXT_ELEMENT_STATE:
if (_cur_screen && event_data) {
if (qdInterfaceButton * p = dynamic_cast<qdInterfaceButton * >(_cur_screen->get_element(event_data)))
return p->change_state(true);
}
break;
case qdInterfaceEvent::EVENT_MAIN_MENU:
if (qdGameDispatcher * p = qdGameDispatcher::get_dispatcher()) {
p->set_flag(qdGameDispatcher::MAIN_MENU_FLAG);
return true;
}
break;
case qdInterfaceEvent::EVENT_PLAY_VIDEO:
if (event_data) {
if (qdGameDispatcher * p = qdGameDispatcher::get_dispatcher()) {
if (p->play_video(event_data)) {
p->pause();
return true;
}
}
}
break;
case qdInterfaceEvent::EVENT_BUTTON_STATE:
if (_cur_screen && event_data) {
Common::String str = event_data;
size_t pos = str.find("::");
if (pos != Common::String::npos) {
if (qdInterfaceButton * p = dynamic_cast<qdInterfaceButton * >(_cur_screen->get_element(str.substr(0, pos).c_str()))) {
return p->activate_state(str.substr(pos + 2).c_str());
}
}
}
break;
case qdInterfaceEvent::EVENT_CLEAR_MOUSE:
if (qdGameDispatcher * p = qdGameDispatcher::get_dispatcher()) {
return p->drop_mouse_object();
}
break;
case qdInterfaceEvent::EVENT_LOAD_SCENE:
if (event_data) {
if (qdGameDispatcher * p = qdGameDispatcher::get_dispatcher()) {
if (qdGameScene * sp = p->get_scene(event_data)) {
p->set_next_scene(sp);
p->activate_trigger_links(sp);
return true;
}
}
}
break;
case qdInterfaceEvent::EVENT_SCROLL_LEFT:
case qdInterfaceEvent::EVENT_SCROLL_RIGHT:
case qdInterfaceEvent::EVENT_SCROLL_UP:
case qdInterfaceEvent::EVENT_SCROLL_DOWN:
if (qdGameDispatcher * p = qdGameDispatcher::get_dispatcher()) {
if (!p->get_active_personage())
break;
if (qdInventory * sp = p->get_inventory(p->get_active_personage()->inventory_name())) {
if (qdInterfaceEvent::EVENT_SCROLL_LEFT == event_code)
sp->scroll_left();
else if (qdInterfaceEvent::EVENT_SCROLL_RIGHT == event_code)
sp->scroll_right();
else if (qdInterfaceEvent::EVENT_SCROLL_UP == event_code)
sp->scroll_up();
else sp->scroll_down();
return true;
}
}
break;
case qdInterfaceEvent::EVENT_SHOW_INTERFACE_SCREEN_AS_MODAL:
if (event_data) {
if (qdInterfaceScreen * screen = get_screen(event_data))
screen->set_modal_caller(sender);
select_background_screen(_cur_screen);
select_screen(event_data);
if (_modalScreenMode == MODAL_SCREEN_SAVE_NAME_EDIT) {
if (qdInterfaceSave * save = dynamic_cast<qdInterfaceSave * >(sender))
set_save_title(save->title());
}
return true;
}
break;
case qdInterfaceEvent::EVENT_MODAL_OK:
case qdInterfaceEvent::EVENT_MODAL_CANCEL:
if (sender) {
select_background_screen(0);
// Определяем экран, от которого пришло событие
qdInterfaceScreen *screen_ptr = NULL;
if (sender->owner())
screen_ptr = dynamic_cast<qdInterfaceScreen * >(sender->owner());
if (!screen_ptr)
screen_ptr = dynamic_cast<qdInterfaceScreen * >(sender);
if (!screen_ptr) {
assert(0 && "Не найден экран, от которого пришло событие модального типа");
return true; // Сообщение все же обработано
}
// Обрабатываем положительную реакцию пользователя на запрос
qdInterfaceObjectBase *modal_caller_ptr = screen_ptr->modal_caller();
if (!modal_caller_ptr) return false;
if (_modalScreenMode == MODAL_SCREEN_SAVE_OVERWRITE) {
if (qdInterfaceEvent::EVENT_MODAL_OK == event_code) {
if (qdInterfaceSave * save = dynamic_cast<qdInterfaceSave * >(modal_caller_ptr)) {
save->set_title(_save_title.c_str());
save->perform_save();
handle_event(qdInterfaceEvent::EVENT_RESUME_GAME, NULL);
return true;
}
}
} else if (_modalScreenMode == MODAL_SCREEN_SAVE_NAME_EDIT) {
if (qdInterfaceEvent::EVENT_MODAL_OK == event_code) {
_save_title = get_save_title();
if (qdInterfaceSave *save = dynamic_cast<qdInterfaceSave * >(modal_caller_ptr)) {
if (has_save_prompt_screen() && g_engine->getSaveFileManager()->exists(g_engine->getSaveStateName(save->save_ID()))) {
setModalScreenMode(qdInterfaceDispatcher::MODAL_SCREEN_SAVE_OVERWRITE);
screen_ptr = dynamic_cast<qdInterfaceScreen *>(modal_caller_ptr->owner());
if (!screen_ptr)
screen_ptr = dynamic_cast<qdInterfaceScreen * >(modal_caller_ptr);
if (screen_ptr)
select_screen(screen_ptr->name());
handle_event(qdInterfaceEvent::EVENT_SHOW_INTERFACE_SCREEN_AS_MODAL, save_prompt_screen_name(), save);
return true;
} else {
save->set_title(_save_title.c_str());
save->perform_save();
handle_event(qdInterfaceEvent::EVENT_RESUME_GAME, NULL);
return true;
}
}
}
}
screen_ptr = dynamic_cast<qdInterfaceScreen *>(modal_caller_ptr->owner());
if (!screen_ptr)
screen_ptr = dynamic_cast<qdInterfaceScreen * >(modal_caller_ptr);
if (!screen_ptr) {
assert(0 && "Не найден экран, от которого пришло событие модального типа");
return true;
}
select_screen(screen_ptr->name());
return true;
}
break;
}
return false;
}
int qdInterfaceDispatcher::option_value(int option_id, const char *option_data) {
switch (option_id) {
case qdInterfaceElement::OPTION_SOUND:
return ConfMan.getBool("enable_sound");
case qdInterfaceElement::OPTION_SOUND_VOLUME:
return ConfMan.getInt("sound_volume");
case qdInterfaceElement::OPTION_MUSIC:
return ConfMan.getBool("enable_music");
case qdInterfaceElement::OPTION_MUSIC_VOLUME:
return ConfMan.getInt("music_volume");
case qdInterfaceElement::OPTION_ACTIVE_PERSONAGE:
if (option_data) {
if (qdGameObjectMoving * p = qdGameDispatcher::get_dispatcher()->get_active_personage()) {
if (!strcmp(p->name(), option_data)) return 1;
}
}
return 0;
}
return -1;
}
bool qdInterfaceDispatcher::set_option_value(int option_id, int value, const char *option_data) {
switch (option_id) {
case qdInterfaceElement::OPTION_SOUND:
ConfMan.setBool("enable_sound", value > 0);
ConfMan.flushToDisk();
g_engine->syncSoundSettings();
return true;
case qdInterfaceElement::OPTION_SOUND_VOLUME:
ConfMan.setInt("sound_volume", value);
ConfMan.flushToDisk();
g_engine->syncSoundSettings();
return true;
case qdInterfaceElement::OPTION_MUSIC:
ConfMan.setBool("enable_music", value > 0);
ConfMan.flushToDisk();
g_engine->syncSoundSettings();
return true;
case qdInterfaceElement::OPTION_MUSIC_VOLUME:
ConfMan.setInt("music_volume", value);
ConfMan.flushToDisk();
g_engine->syncSoundSettings();
return true;
case qdInterfaceElement::OPTION_ACTIVE_PERSONAGE:
if (option_data) {
if (qdGameScene * sp = qdGameDispatcher::get_dispatcher()->get_active_scene()) {
qdGameObjectMoving *obj = dynamic_cast<qdGameObjectMoving *>(sp->get_object(option_data));
if (!obj || obj == sp->get_active_personage()) return false;
sp->set_active_personage(obj);
return true;
}
}
return false;
}
return false;
}
void qdInterfaceDispatcher::update_personage_buttons() {
if (_cur_screen)
_cur_screen->update_personage_buttons();
}
#ifdef __QD_DEBUG_ENABLE__
bool qdInterfaceDispatcher::get_resources_info(qdResourceInfoContainer &infos) const {
for (resource_container_t::resource_list_t::const_iterator it = _resources.resource_list().begin(); it != _resources.resource_list().end(); ++it) {
if ((*it)->is_resource_loaded())
infos.push_back(qdResourceInfo(*it));
}
return true;
}
#endif
bool qdInterfaceDispatcher::set_save_title(const char *title) {
if (!_cur_screen)
return false;
const qdInterfaceScreen::element_list_t &lst = _cur_screen->element_list();
for (qdInterfaceScreen::element_list_t::const_iterator it = lst.begin(); it != lst.end(); ++it) {
if ((*it)->get_element_type() == qdInterfaceElement::EL_TEXT_WINDOW) {
qdInterfaceTextWindow *wnd = static_cast<qdInterfaceTextWindow *>(*it);
if (wnd->windowType() == qdInterfaceTextWindow::WINDOW_EDIT) {
wnd->set_input_string(title);
wnd->edit_start();
return true;
}
}
}
return false;
}
const char *qdInterfaceDispatcher::get_save_title() const {
if (!_cur_screen)
return nullptr;
const qdInterfaceScreen::element_list_t &lst = _cur_screen->element_list();
for (qdInterfaceScreen::element_list_t::const_iterator it = lst.begin(); it != lst.end(); ++it) {
if ((*it)->get_element_type() == qdInterfaceElement::EL_TEXT_WINDOW) {
qdInterfaceTextWindow *wnd = static_cast<qdInterfaceTextWindow *>(*it);
if (wnd->windowType() == qdInterfaceTextWindow::WINDOW_EDIT)
return wnd->input_string();
}
}
return nullptr;
}
} // namespace QDEngine

View File

@@ -0,0 +1,341 @@
/* 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/>.
*
*/
#ifndef QDENGINE_QDCORE_QD_INTERFACE_DISPATCHER_H
#define QDENGINE_QDCORE_QD_INTERFACE_DISPATCHER_H
#include "common/keyboard.h"
#include "qdengine/qdcore/qd_resource_container.h"
#include "qdengine/qdcore/qd_interface_object_base.h"
#include "qdengine/qdcore/qd_object_list_container.h"
namespace QDEngine {
class qdInterfaceScreen;
class qdInterfaceElementState;
class qdInterfaceDispatcher : public qdInterfaceObjectBase {
public:
qdInterfaceDispatcher();
~qdInterfaceDispatcher();
/// для чего вызван модальный экран
enum ModalScreenMode {
MODAL_SCREEN_SAVE_OVERWRITE,
MODAL_SCREEN_SAVE_NAME_EDIT,
MODAL_SCREEN_OTHER
};
//! Отрисовка интерфейса.
bool redraw(int dx = 0, int dy = 0) const;
bool pre_redraw();
bool post_redraw();
//! Обсчет логики, параметр - время в секундах.
bool quant(float dt);
//! Добавление экрана.
bool add_screen(qdInterfaceScreen *scr);
//! Изменение имени экрана.
bool rename_screen(qdInterfaceScreen *scr, const char *name);
//! Удаление экрана из списка.
bool remove_screen(qdInterfaceScreen *scr);
//! Поиск экрана по имени.
qdInterfaceScreen *get_screen(const char *screen_name);
//! Возвращает true, если экран есть в списке.
bool is_screen_in_list(const qdInterfaceScreen *scr);
typedef Std::list<qdInterfaceScreen *> screen_list_t;
//! Возвращает список экранов.
const screen_list_t &screen_list() const {
return _screens.get_list();
}
//! Установка активного экрана.
/**
Имя можно передавать нулевое, для сброса активного экрана.
*/
bool select_screen(const char *screen_name, bool lock_resources = false);
/// Установка фонового экрана.
bool select_background_screen(qdInterfaceScreen *p);
//! Включает внутриигровой интерфейс.
bool select_ingame_screen(bool inventory_state = false);
//! Включает кнопки, связанные с персонажем p, выключает кнопки остальных персонажей.
void update_personage_buttons();
qdInterfaceScreen *selected_screen() const {
return _cur_screen;
}
//! Возвращает true, если экран p в данный момент активен.
bool is_screen_active(const qdInterfaceScreen *p) const {
return (_cur_screen == p);
}
//! Добавляет ресурс file_name с владельцем owner.
qdResource *add_resource(const Common::Path &file_name, const qdInterfaceElementState *owner);
//! Удаляет ресурс file_name с владельцем owner.
bool remove_resource(const Common::Path &file_name, const qdInterfaceElementState *owner);
//! Возвращает указатель на ресурс file_name.
qdResource *get_resource(const Common::Path &file_name) const {
return _resources.get_resource(file_name);
}
//! Обработчик событий мыши.
bool mouse_handler(int x, int y, mouseDispatcher::mouseEvent ev);
//! Обработчик ввода с клавиатуры.
bool keyboard_handler(Common::KeyCode vkey);
bool char_input_handler(int vkey);
//! Запись данных в скрипт.
bool save_script(Common::WriteStream &fh, int indent = 0) const;
//! Загрузка данных из скрипта.
bool load_script(const xml::tag *p);
//! Установка имени экрана с главным меню игры.
void set_main_menu_screen(const char *name);
//! Возвращает имя экрана с главным меню игры.
const char *main_menu_screen_name() const {
return _main_menu_screen_name.c_str();
}
//! Возвращает true, если установлено имя экрана с главным меню игры.
bool has_main_menu() const {
return !_main_menu_screen_name.empty();
}
//! Установка имени внутриигрового экрана.
void set_ingame_screen(const char *name, bool inventory_state = false);
//! Возвращает имя внутриигрового экрана.
const char *ingame_screen_name(bool inventory_state = false) const {
return _ingame_screen_names[inventory_state].c_str();
}
//! Возвращает true, если установлено имя внутриигрового экрана.
bool has_ingame_screen(bool inventory_state = false) const {
return !_ingame_screen_names[inventory_state].empty();
}
//! Имя модального экрана подсказки: перезаписывать сэйв или нет.
const char *save_prompt_screen_name() const {
return _save_prompt_screen_name.c_str();
}
void set_save_prompt_screen_name(const char *str) {
if (str) _save_prompt_screen_name = str;
else _save_prompt_screen_name = "";
}
bool has_save_prompt_screen() const {
return !_save_prompt_screen_name.empty();
}
const char *save_title_screen_name() const {
return _save_title_screen_name.c_str();
}
void set_save_title_screen_name(const char *name) {
_save_title_screen_name = name;
}
bool has_save_title_screen() const {
return !_save_title_screen_name.empty();
}
//! Нужно ли выводить скриншот к сохраненке
bool need_save_screenshot() const {
return _need_save_screenshot;
}
void toggle_save_screenshot(bool state) {
_need_save_screenshot = state;
}
//! Нужно ли выводить дату и время при отображении сэйва
bool need_show_save_time() const {
return _need_show_save_time;
}
void toggle_show_save_time(bool state) {
_need_show_save_time = state;
}
bool need_show_save_title() const {
return _need_show_save_title;
}
void toggle_show_save_title(bool state) {
_need_show_save_title = state;
}
//! Тип шрифт, которым выводится текст сэйва (в частности дата и время)
int save_font_type() const {
return _save_font_type;
}
void set_save_font_type(int type) {
_save_font_type = type;
}
//! Цвет, которым выводится текст сэйва (в частности дата и время)
int save_font_color() const {
return _save_font_color;
}
void set_save_font_color(int clr) {
_save_font_color = clr;
}
//! Возвращает true, если интерфейс отрисовывается поверх сцены.
bool need_scene_redraw() const {
return _need_scene_redraw;
}
//! Устанавливает, надо ли если интерфейсу отрисовываться поверх сцены.
void set_scene_redraw(bool state) {
_need_scene_redraw = state;
}
static void set_dispatcher(qdInterfaceDispatcher *p) {
_dispatcher = p;
}
static qdInterfaceDispatcher *get_dispatcher() {
return _dispatcher;
}
void activate() {
_is_active = true;
}
void deactivate() {
_is_active = false;
}
bool is_active() const {
return _is_active;
}
bool is_mouse_hover() const {
return _is_mouse_hover;
}
void toggle_mouse_hover() {
_is_mouse_hover = true;
}
bool is_autohide_enabled() const {
return !_autohide_disable;
}
void disable_autohide() {
_autohide_disable = true;
}
ModalScreenMode modalScreenMode() const {
return _modalScreenMode;
}
void setModalScreenMode(ModalScreenMode mode) {
_modalScreenMode = mode;
}
bool set_save_title(const char *title);
const char *get_save_title() const;
/**
Обработка события.
sender - тот, кто послал событие на выполнение. NULL - sender не известен.
*/
bool handle_event(int event_code, const char *event_data, qdInterfaceObjectBase *sender = NULL);
void toggle_end_game_mode(bool state) {
_end_game_mode = state;
}
//! Устанавливает следующий экран.
void set_next_screen(const char *screen_name) {
_next_screen = screen_name;
}
static int option_value(int option_id, const char *option_data = NULL);
static bool set_option_value(int option_id, int value, const char *oprtion_data = NULL);
#ifdef __QD_DEBUG_ENABLE__
bool get_resources_info(qdResourceInfoContainer &infos) const;
#endif
private:
//! Активный интерфейсный экран.
qdInterfaceScreen *_cur_screen;
/// Фоновый экран, рисуется под активным
qdInterfaceScreen *_background_screen;
/// true если ресурсы фонового экрана не надо выгружать
bool _background_screen_lock;
ModalScreenMode _modalScreenMode;
//! Имя экрана с главным меню игры
Common::String _main_menu_screen_name;
//! Имена экраноы с внутриигровым интерфейсом.
/**
Первый экран показывается когда инвентори спрятано, вотрой - когда оно активно.
*/
Common::String _ingame_screen_names[2];
/// Экран ввода имени сэйва
Common::String _save_title_screen_name;
Common::String _save_title;
//! Экран, вызывающийся при подтверждении перезаписи файла сэйва
Common::String _save_prompt_screen_name;
//! Нужно ли выводить скриншот к сохраненке
bool _need_save_screenshot;
//! Нужно ли выводить дату и время при отображении сэйва
bool _need_show_save_time;
//! Нужно ли выводить имя при отображении сэйва
bool _need_show_save_title;
//! Тип шрифта, которым выводится текст сэйва (в частности дата и время)
int _save_font_type;
//! Цвет, которым выводится текст сэйва (в частности дата и время)
int _save_font_color;
//! Интерфейсные экраны.
qdObjectListContainer<qdInterfaceScreen> _screens;
typedef qdResourceContainer<qdInterfaceElementState> resource_container_t;
//! Интерфейсные ресурсы.
resource_container_t _resources;
//! Равно true, если интерфейс активен.
bool _is_active;
//! Равно true, если курсор мыши попадает в интерфейс.
bool _is_mouse_hover;
/// true если надо показывать
bool _autohide_disable;
//! Равно true, если требуется полная отрисовка интерфейса, после отрисовки скидывается.
bool _need_full_redraw;
//! Равно true, если отрисовка сцены под интерфейсом.
bool _need_scene_redraw;
//! Режим окончания игры - после любого клика возврат с текущего экрана в главное меню.
//! После загрузки меню скидывается в false.
bool _end_game_mode;
//! Имя экрана, на который надо перейти.
const char *_next_screen;
//! Текущий диспетчер интерфейса.
static qdInterfaceDispatcher *_dispatcher;
};
} // namespace QDEngine
#endif // QDENGINE_QDCORE_QD_INTERFACE_DISPATCHER_H

View File

@@ -0,0 +1,268 @@
/* 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 "qdengine/qd_fwd.h"
#include "qdengine/parser/qdscr_parser.h"
#include "qdengine/parser/xml_tag_buffer.h"
#include "qdengine/system/sound/snd_dispatcher.h"
#include "qdengine/qdcore/qd_sound.h"
#include "qdengine/qdcore/qd_interface_screen.h"
#include "qdengine/qdcore/qd_interface_element.h"
#include "qdengine/qdcore/qd_interface_element_state.h"
#include "qdengine/qdcore/qd_interface_background.h"
#include "qdengine/qdcore/qd_interface_counter.h"
#include "qdengine/qdcore/qd_interface_button.h"
#include "qdengine/qdcore/qd_interface_save.h"
#include "qdengine/qdcore/qd_interface_slider.h"
#include "qdengine/qdcore/qd_interface_text_window.h"
namespace QDEngine {
qdInterfaceElement::qdInterfaceElement() : _r(0, 0),
_option_ID(OPTION_NONE),
_screen_depth(0),
_is_visible(true),
_is_locked(false),
_last_animation_frame(NULL) {
}
qdInterfaceElement::qdInterfaceElement(const qdInterfaceElement &el) : qdInterfaceObjectBase(el),
_r(el._r),
_option_ID(el._option_ID),
_option_data(el._option_data),
_screen_depth(el._screen_depth),
_is_visible(el._is_visible),
_is_locked(el._is_locked),
_last_animation_frame(NULL) {
}
qdInterfaceElement::~qdInterfaceElement() {
}
qdInterfaceElement &qdInterfaceElement::operator = (const qdInterfaceElement &el) {
if (this == &el) return *this;
this->qdInterfaceObjectBase::operator = (el);
_r = el._r;
_option_ID = el._option_ID;
_option_data = el._option_data;
_screen_depth = el._screen_depth;
_is_visible = el._is_visible;
return *this;
}
bool qdInterfaceElement::set_animation(const qdAnimation *anm, int anm_flags) {
if (anm) {
anm->create_reference(&_animation);
if (anm_flags & QD_ANIMATION_FLAG_LOOP)
_animation.set_flag(QD_ANIMATION_FLAG_LOOP);
if (anm_flags & QD_ANIMATION_FLAG_FLIP_HORIZONTAL)
_animation.set_flag(QD_ANIMATION_FLAG_FLIP_HORIZONTAL);
if (anm_flags & QD_ANIMATION_FLAG_FLIP_VERTICAL)
_animation.set_flag(QD_ANIMATION_FLAG_FLIP_VERTICAL);
_animation.start();
} else
_animation.clear();
return true;
}
bool qdInterfaceElement::set_state(const qdInterfaceElementState *p) {
qdInterfaceElementState::state_mode_t mode = p->state_mode();
set_animation(p->animation(mode), p->animation_flags(mode));
if (p->sound(mode)) {
if (sndDispatcher * dp = sndDispatcher::get_dispatcher())
dp->stop_sound(&_sound_handle);
p->sound(mode)->play(&_sound_handle);
}
return true;
}
bool qdInterfaceElement::redraw() const {
_animation.redraw(r().x, r().y, 0);
return true;
}
bool qdInterfaceElement::need_redraw() const {
if (_last_animation_frame != _animation.get_cur_frame())
return true;
if (_last_screen_region != screen_region())
return true;
return false;
}
bool qdInterfaceElement::post_redraw() {
_last_screen_region = screen_region();
_last_animation_frame = _animation.get_cur_frame();
return true;
}
bool qdInterfaceElement::quant(float dt) {
_animation.quant(dt);
return true;
}
grScreenRegion qdInterfaceElement::screen_region() const {
if (!_animation.is_empty()) {
grScreenRegion reg = _animation.screen_region();
reg.move(r().x, r().y);
return reg;
} else
return grScreenRegion_EMPTY;
}
bool qdInterfaceElement::save_script(Common::WriteStream &fh, int indent) const {
for (int i = 0; i < indent; i++) {
fh.writeString("\t");
}
fh.writeString("<interface_element");
fh.writeString(Common::String::format(" type=\"%d\"", static_cast<int>(get_element_type())));
if (name()) {
fh.writeString(Common::String::format(" name=\"%s\"", qdscr_XML_string(name())));
}
fh.writeString(Common::String::format(" pos=\"%d %d %d\"", _r.x, _r.y, _screen_depth));
if (_option_ID != OPTION_NONE) {
fh.writeString(Common::String::format(" option_id=\"%d\"", (int)_option_ID));
}
fh.writeString(">\r\n");
if (!save_script_body(fh, indent)) {
return false;
}
for (int i = 0; i < indent; i++) {
fh.writeString("\t");
}
fh.writeString("</interface_element>\r\n");
return true;
}
bool qdInterfaceElement::load_script(const xml::tag *p) {
for (xml::tag::subtag_iterator it = p->subtags_begin(); it != p->subtags_end(); ++it) {
switch (it->ID()) {
case QDSCR_NAME:
set_name(it->data());
break;
case QDSCR_INTERFACE_ELEMENT_POS:
xml::tag_buffer(*it) > _r.x > _r.y > _screen_depth;
break;
case QDSCR_INTERFACE_OPTION_ID:
_option_ID = option_ID_t(xml::tag_buffer(*it).get_int());
break;
}
}
return load_script_body(p);
}
qdResource *qdInterfaceElement::add_resource(const Common::Path &file_name, const qdInterfaceElementState *res_owner) {
if (qdInterfaceScreen * p = dynamic_cast<qdInterfaceScreen * >(owner()))
return p->add_resource(file_name, res_owner);
return NULL;
}
bool qdInterfaceElement::remove_resource(const Common::Path &file_name, const qdInterfaceElementState *res_owner) {
if (qdInterfaceScreen * p = dynamic_cast<qdInterfaceScreen * >(owner()))
return p->remove_resource(file_name, res_owner);
return false;
}
qdInterfaceElement *qdInterfaceElement::create_element(element_type tp) {
switch (tp) {
case EL_BACKGROUND:
return new qdInterfaceBackground;
case EL_BUTTON:
return new qdInterfaceButton;
case EL_SLIDER:
return new qdInterfaceSlider;
case EL_SAVE:
return new qdInterfaceSave;
case EL_TEXT_WINDOW:
return new qdInterfaceTextWindow;
case EL_COUNTER:
return new qdInterfaceCounter;
}
return NULL;
}
void qdInterfaceElement::destroy_element(qdInterfaceElement *p) {
delete p;
}
bool qdInterfaceElement::hit_test(int x, int y) const {
if (!_animation.is_empty()) {
x -= r().x;
y -= r().y;
return _animation.hit(x, y);
}
return false;
}
qdInterfaceElement::state_status_t qdInterfaceElement::state_status(const qdInterfaceElementState *p) const {
qdInterfaceElementState::state_mode_t mode = p->state_mode();
if (p->animation(mode)) {
if (p->animation(mode)->is_reference(&_animation)) {
if (!_animation.is_finished())
return STATE_ACTIVE;
} else
return STATE_INACTIVE;
} else {
if (!_animation.is_empty())
return STATE_INACTIVE;
}
if (p->sound(mode)) {
if (!p->sound(mode)->is_stopped(&_sound_handle))
return STATE_ACTIVE;
}
return STATE_DONE;
}
} // namespace QDEngine

View File

@@ -0,0 +1,266 @@
/* 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/>.
*
*/
#ifndef QDENGINE_QDCORE_QD_INTERFACE_ELEMENT_H
#define QDENGINE_QDCORE_QD_INTERFACE_ELEMENT_H
#include "common/keyboard.h"
#include "qdengine/system/sound/snd_sound.h"
#include "qdengine/system/input/mouse_input.h"
#include "qdengine/system/graphics/gr_screen_region.h"
#include "qdengine/qdcore/qd_animation.h"
#include "qdengine/qdcore/qd_interface_object_base.h"
#include "qdengine/qdcore/qd_sound_handle.h"
namespace QDEngine {
//class qdInterfaceScreen;
class qdInterfaceElementState;
//! Базовый класс для элементов GUI.
class qdInterfaceElement : public qdInterfaceObjectBase {
public:
qdInterfaceElement();
qdInterfaceElement(const qdInterfaceElement &el);
virtual ~qdInterfaceElement() = 0;
qdInterfaceElement &operator = (const qdInterfaceElement &el);
//! Идентификаторы настроек игры.
enum option_ID_t {
OPTION_NONE = 0,
/// звук вкл/выкл, 1/0
OPTION_SOUND,
/// громкость звука, [0, 255]
OPTION_SOUND_VOLUME,
/// музыка вкл/выкл, 1/0
OPTION_MUSIC,
/// громкость музыки, [0, 255]
OPTION_MUSIC_VOLUME,
/// определенный персонаж персонаж активен/неактивен, 1/0
OPTION_ACTIVE_PERSONAGE
};
//! Типы элементов интерфейса.
enum element_type {
//! фон
EL_BACKGROUND,
//! кнопка
EL_BUTTON,
//! слайдер
EL_SLIDER,
//! сэйв
EL_SAVE,
//! окно с текстами
EL_TEXT_WINDOW,
//! счётчик
EL_COUNTER
};
//! Статус состояния.
enum state_status_t {
//! состояние неактивно
STATE_INACTIVE,
//! состояние активно
STATE_ACTIVE,
//! работа состояния закончилась
STATE_DONE
};
//! Создание элемента интерфейса заданного типа.
static qdInterfaceElement *create_element(element_type tp);
//! Уничтожение элемента интерфейса.
static void destroy_element(qdInterfaceElement *p);
//! Возвращает тип элемента.
virtual element_type get_element_type() const = 0;
//! Возвращает идентификатор настройки игры, связанной с элементом.
option_ID_t option_ID() const {
return _option_ID;
}
//! Устанавливает идентификатор настройки игры, связанной с элементом.
void set_option_ID(option_ID_t id) {
_option_ID = id;
}
//! Возвращает true, если с элементом связана настройка игры.
bool linked_to_option() const {
return (_option_ID != OPTION_NONE);
}
//! Возвращает значение настройки игры, связанной с элементом.
virtual int option_value() const {
return -1;
}
//! Устанавливает значение настройки игры, связанной с элементом.
virtual bool set_option_value(int value) {
return false;
}
//! Возвращает экранные координаты центра элемента.
virtual const Vect2i r() const {
return _r + g_engine->screen_offset();
}
//! Устанавливает экранные координаты центра элемента.
void set_r(const Vect2i &v) {
_r = v;
}
//! Возвращает размер элемента по горизонтали в пикселах.
virtual int size_x() const {
return _animation.size_x();
}
//! Возвращает размер элемента по вертикали в пикселах.
virtual int size_y() const {
return _animation.size_y();
}
virtual grScreenRegion screen_region() const;
const grScreenRegion &last_screen_region() const {
return _last_screen_region;
}
//! Устанавливает экранную глубину элемента.
void set_screen_depth(int v) {
_screen_depth = v;
}
//! Возвращает экранную глубину эелмента.
int screen_depth() const {
return _screen_depth;
}
//! Запись данных в скрипт.
bool save_script(Common::WriteStream &fh, int indent = 0) const;
//! Загрузка данных из скрипта.
bool load_script(const xml::tag *p);
//! Отрисовка элемента.
virtual bool redraw() const;
virtual bool need_redraw() const;
virtual bool post_redraw();
//! Обработчик событий мыши.
virtual bool mouse_handler(int x, int y, mouseDispatcher::mouseEvent ev) = 0;
//! Обработчик ввода с клавиатуры.
virtual bool keyboard_handler(Common::KeyCode vkey) = 0;
//! Обработчик ввода символов с клавиатуры.
virtual bool char_input_handler(int vkey) {
return false;
}
virtual void hover_clear() { }
//! Инициализация элемента.
/**
Вызывается каждый раз при заходе на экран, которому принадлежит элемент.
*/
virtual bool init(bool is_game_active = true) = 0;
//! Обсчет логики, параметр - время в секундах.
virtual bool quant(float dt);
const qdAnimation &animation() const {
return _animation;
}
//! Устанавливает анимацию для элемента.
bool set_animation(const qdAnimation *anm, int anm_flags = 0);
//! Включает состояние элемента.
bool set_state(const qdInterfaceElementState *p);
//! Добавляет ресурс file_name с владельцем owner.
qdResource *add_resource(const Common::Path &file_name, const qdInterfaceElementState *res_owner);
//! Удаляет ресурс file_name с владельцем owner.
bool remove_resource(const Common::Path &file_name, const qdInterfaceElementState *res_owner);
//! Возвращает true, если точка с экранными координатами (x,у) попадает в элемент.
virtual bool hit_test(int x, int y) const;
//! Возвращает статус состояния.
state_status_t state_status(const qdInterfaceElementState *p) const;
//! Прячет элемент.
void hide() {
_is_visible = false;
}
//! Показывает элемент.
void show() {
_is_visible = true;
}
//! Возвращает true, если элемент не спрятан.
bool is_visible() const {
return _is_visible;
}
//! Возвращает true, если элемент заблокирован.
bool is_locked() const {
return _is_locked;
}
//! Блокировка/разблокировка элемента.
void set_lock(bool state) {
_is_locked = state;
}
protected:
//! Запись данных в скрипт.
virtual bool save_script_body(Common::WriteStream &fh, int indent = 0) const = 0;
//! Загрузка данных из скрипта.
virtual bool load_script_body(const xml::tag *p) = 0;
void clear_screen_region() {
_last_screen_region = grScreenRegion_EMPTY;
}
private:
//! Идентификатор настройки игры, связанной с элементом.
option_ID_t _option_ID;
//! Данные для настройки игры, связанной с элементом.
Common::String _option_data;
//! Экранные координаты центра элемента.
Vect2i _r;
//! Экранная глубина. Чем больше значение - тем дальше элемент.
int _screen_depth;
//! Анимация элемента.
qdAnimation _animation;
//! Хэндл для управления звуками.
qdSoundHandle _sound_handle;
//! Равно false, если элемент спрятан.
bool _is_visible;
//! Равно true, если элемент заблокирован (не реагирует на клавиатуру/мышь).
bool _is_locked;
grScreenRegion _last_screen_region;
const qdAnimationFrame *_last_animation_frame;
};
} // namespace QDEngine
#endif // QDENGINE_QDCORE_QD_INTERFACE_ELEMENT_H

View File

@@ -0,0 +1,453 @@
/* 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 "qdengine/qdengine.h"
#include "qdengine/qd_fwd.h"
#include "qdengine/parser/xml_tag_buffer.h"
#include "qdengine/parser/qdscr_parser.h"
#include "qdengine/qdcore/qd_sound.h"
#include "qdengine/qdcore/qd_animation.h"
#include "qdengine/qdcore/qd_interface_element.h"
#include "qdengine/qdcore/qd_interface_dispatcher.h"
#include "qdengine/qdcore/qd_interface_element_state.h"
namespace QDEngine {
qdInterfaceElementState::qdInterfaceElementState() : _state_mode(DEFAULT_MODE), _prev_state_mode(DEFAULT_MODE) {
}
qdInterfaceElementState::qdInterfaceElementState(const qdInterfaceElementState &st) : qdInterfaceObjectBase(st),
_events(st._events),
_state_mode(st._state_mode),
_prev_state_mode(st._prev_state_mode) {
for (int i = 0; i < NUM_MODES; i++)
_modes[i] = st._modes[i];
register_resources();
}
qdInterfaceElementState::~qdInterfaceElementState() {
unregister_resources();
}
qdInterfaceElementState &qdInterfaceElementState::operator = (const qdInterfaceElementState &st) {
if (this == &st) return *this;
unregister_resources();
this->qdInterfaceObjectBase::operator = (st);
_events = st._events;
for (int i = 0; i < NUM_MODES; i++)
_modes[i] = st._modes[i];
register_resources();
_state_mode = st._state_mode;
_prev_state_mode = st._prev_state_mode;
return *this;
}
#define defEnum(x) #x
static const char *eventList[] = {
defEnum(EVENT_NONE),
defEnum(EVENT_EXIT),
defEnum(EVENT_LOAD_SCENE),
defEnum(EVENT_SAVE_GAME),
defEnum(EVENT_NEW_GAME),
defEnum(EVENT_CHANGE_INTERFACE_SCREEN),
defEnum(EVENT_CHANGE_PERSONAGE),
defEnum(EVENT_TMP_HIDE_ELEMENT),
defEnum(EVENT_HIDE_ELEMENT),
defEnum(EVENT_SHOW_ELEMENT),
defEnum(EVENT_RESUME_GAME),
defEnum(EVENT_SET_SAVE_MODE),
defEnum(EVENT_SET_LOAD_MODE),
defEnum(EVENT_ACTIVATE_PERSONAGE),
defEnum(EVENT_PREV_ELEMENT_STATE),
defEnum(EVENT_NEXT_ELEMENT_STATE),
defEnum(EVENT_MAIN_MENU),
defEnum(EVENT_PLAY_VIDEO),
defEnum(EVENT_BUTTON_STATE),
defEnum(EVENT_CLEAR_MOUSE),
defEnum(EVENT_SCROLL_LEFT),
defEnum(EVENT_SCROLL_RIGHT),
defEnum(EVENT_SCROLL_UP),
defEnum(EVENT_SCROLL_DOWN),
defEnum(EVENT_SHOW_INTERFACE_SCREEN_AS_MODAL),
defEnum(EVENT_MODAL_OK),
defEnum(EVENT_MODAL_CANCEL),
defEnum(EVENT_HALL_OF_FAME_PLAYER),
defEnum(EVENT_HALL_OF_FAME_SCORE),
defEnum(EVENT_HALL_OF_FAME_CUR_SCORE),
};
static const char *eventActivationList[] = {
defEnum(EVENT_ACTIVATION_CLICK),
defEnum(EVENT_ACTIVATION_HOVER),
};
Common::String qdInterfaceElementState::event2Str(int fl, bool truncate) const {
if (fl >= ARRAYSIZE(eventList) || fl < 0)
return Common::String::format("<%d>", fl);
return Common::String(&eventList[fl][truncate ? 6 : 0]);
}
Common::String qdInterfaceElementState::eventActivation2Str(int fl, bool truncate) const {
if (fl >= ARRAYSIZE(eventActivationList) || fl < 0)
return Common::String::format("<%d>", fl);
return Common::String(&eventActivationList[fl][truncate ? 17 : 0]);
}
bool qdInterfaceElementState::save_script(Common::WriteStream &fh, int indent) const {
for (int i = 0; i < indent; i++) {
fh.writeString("\t");
}
fh.writeString("<interface_element_state");
if (name()) {
fh.writeString(Common::String::format(" name=\"%s\"", qdscr_XML_string(name())));
}
fh.writeString(">\r\n");
for (uint j = 0; j < _events.size(); j++) {
for (int i = 0; i <= indent; i++) {
fh.writeString("\t");
}
if (debugChannelSet(-1, kDebugLog)) {
fh.writeString(Common::String::format("<event type=\"%s\"", event2Str(_events[j].event()).c_str()));
} else {
fh.writeString(Common::String::format("<event type=\"%d\"", int(_events[j].event())));
}
if (_events[j].has_data()) {
fh.writeString(Common::String::format(" event_data=\"%s\"", qdscr_XML_string(_events[j].event_data())));
}
if (_events[j].is_before_animation()) {
fh.writeString(" before_animation=\"1\"");
}
if (_events[j].activation() != qdInterfaceEvent::EVENT_ACTIVATION_CLICK) {
if (debugChannelSet(-1, kDebugLog)) {
fh.writeString(Common::String::format(" activation_type=\"%s\"", eventActivation2Str((int)_events[j].activation()).c_str()));
} else {
fh.writeString(Common::String::format(" activation_type=\"%d\"", (int)_events[j].activation()));
}
}
fh.writeString("/>\r\n");
}
for (int i = 0; i < NUM_MODES; i++) {
if (has_state_mode(state_mode_t(i))) {
_modes[i].save_script(fh, i, indent + 1);
}
}
for (int i = 0; i < indent; i++) {
fh.writeString("\t");
}
fh.writeString("</interface_element_state>\r\n");
return true;
}
bool qdInterfaceElementState::load_script(const xml::tag *p) {
int num_events = 0;
for (xml::tag::subtag_iterator it = p->subtags_begin(); it != p->subtags_end(); ++it) {
switch (it->ID()) {
case QDSCR_INTERFACE_EVENT:
num_events++;
break;
}
}
if (num_events)
_events.reserve(num_events);
for (xml::tag::subtag_iterator it = p->subtags_begin(); it != p->subtags_end(); ++it) {
switch (it->ID()) {
case QDSCR_NAME:
set_name(it->data());
break;
case QDSCR_INTERFACE_ELEMENT_STATE_MODE:
if (const xml::tag * tg = it->search_subtag(QDSCR_TYPE))
_modes[state_mode_t(xml::tag_buffer(*tg).get_int())].load_script(&*it);
break;
case QDSCR_INTERFACE_EVENT: {
qdInterfaceEvent::event_t ev = qdInterfaceEvent::EVENT_NONE;
qdInterfaceEvent::activation_t act = qdInterfaceEvent::EVENT_ACTIVATION_CLICK;
const char *ev_data = "";
bool anm_flag = false;
if (const xml::tag * tg = it->search_subtag(QDSCR_TYPE))
ev = qdInterfaceEvent::event_t(xml::tag_buffer(*tg).get_int());
if (const xml::tag * tg = it->search_subtag(QDSCR_INTERFACE_EVENT_DATA))
ev_data = tg->data();
if (const xml::tag * tg = it->search_subtag(QDSCR_INTERFACE_EVENT_BEFORE_ANIMATION)) {
if (xml::tag_buffer(*tg).get_int())
anm_flag = true;
}
if (const xml::tag * tg = it->search_subtag(QDSCR_INTERFACE_EVENT_ACTIVATION_TYPE))
act = qdInterfaceEvent::activation_t(xml::tag_buffer(*tg).get_int());
_events.push_back(qdInterfaceEvent(ev, ev_data, anm_flag, act));
}
break;
default:
break;
}
}
register_resources();
return true;
}
bool qdInterfaceElementState::quant(float dt) {
debugC(9, kDebugQuant, "qdInterfaceElementState::quant(%f)", dt);
if (qdInterfaceElement * ep = dynamic_cast<qdInterfaceElement * >(owner())) {
if (_prev_state_mode == MOUSE_HOVER_MODE && state_mode() == DEFAULT_MODE)
handle_events(qdInterfaceEvent::EVENT_ACTIVATION_HOVER, false);
_prev_state_mode = state_mode();
switch (ep->state_status(this)) {
case qdInterfaceElement::STATE_INACTIVE:
ep->set_state(this);
break;
case qdInterfaceElement::STATE_DONE:
if (state_mode() == EVENT_MODE) {
debugC(3, kDebugQuant, "qdInterfaceElementState::quant(%f) - EVENT_MODE", dt);
handle_events(qdInterfaceEvent::EVENT_ACTIVATION_CLICK, false);
}
set_state_mode(DEFAULT_MODE);
break;
default:
break;
}
return true;
}
return false;
}
void qdInterfaceElementState::set_sound_file(const Common::Path &str, state_mode_t snd_id) {
if (has_sound(snd_id)) {
if (qdInterfaceElement * p = dynamic_cast<qdInterfaceElement * >(owner()))
p->remove_resource(sound_file(snd_id), this);
_modes[snd_id].set_sound(NULL);
}
_modes[snd_id].set_sound_file(str);
if (has_sound(snd_id)) {
if (qdInterfaceElement * p = dynamic_cast<qdInterfaceElement * >(owner()))
_modes[snd_id].set_sound(dynamic_cast<const qdSound * >(p->add_resource(sound_file(snd_id), this)));
}
}
void qdInterfaceElementState::set_animation_file(const Common::Path &name, state_mode_t anm_id) {
if (has_animation(anm_id)) {
if (qdInterfaceElement * p = dynamic_cast<qdInterfaceElement * >(owner()))
p->remove_resource(animation_file(anm_id), this);
_modes[anm_id].set_animation(NULL);
}
_modes[anm_id].set_animation_file(name);
if (has_animation(anm_id)) {
if (qdInterfaceElement * p = dynamic_cast<qdInterfaceElement * >(owner()))
_modes[anm_id].set_animation(dynamic_cast<const qdAnimation * >(p->add_resource(animation_file(anm_id), this)));
}
}
bool qdInterfaceElementState::unregister_resources() {
bool res = true;
for (int i = 0; i < NUM_MODES; i++) {
if (has_animation(state_mode_t(i))) {
if (qdInterfaceElement * p = dynamic_cast<qdInterfaceElement * >(owner())) {
if (!p->remove_resource(animation_file(state_mode_t(i)), this))
res = false;
_modes[i].set_animation(NULL);
}
}
if (has_sound(state_mode_t(i))) {
if (qdInterfaceElement * p = dynamic_cast<qdInterfaceElement * >(owner())) {
if (!p->remove_resource(sound_file(state_mode_t(i)), this))
res = false;
_modes[i].set_sound(NULL);
}
}
}
return res;
}
bool qdInterfaceElementState::register_resources() {
bool res = true;
for (int i = 0; i < NUM_MODES; i++) {
if (has_animation(state_mode_t(i))) {
if (qdInterfaceElement * p = dynamic_cast<qdInterfaceElement * >(owner()))
_modes[i].set_animation(dynamic_cast<const qdAnimation * >(p->add_resource(animation_file(state_mode_t(i)), this)));
if (!_modes[i].animation()) res = false;
}
if (has_sound(state_mode_t(i))) {
if (qdInterfaceElement * p = dynamic_cast<qdInterfaceElement * >(owner()))
_modes[i].set_sound(dynamic_cast<const qdSound * >(p->add_resource(sound_file(state_mode_t(i)), this)));
if (!_modes[i].sound()) res = false;
}
}
return res;
}
bool qdInterfaceElementState::has_state_mode(state_mode_t mode) const {
switch (mode) {
case DEFAULT_MODE:
return true;
case MOUSE_HOVER_MODE:
if (has_animation(MOUSE_HOVER_MODE) || has_sound(MOUSE_HOVER_MODE) || has_contour(MOUSE_HOVER_MODE)) return true;
return false;
case EVENT_MODE:
if (_events.size()) return true;
if (has_animation(EVENT_MODE) || has_sound(EVENT_MODE) || has_contour(EVENT_MODE)) return true;
return false;
}
return false;
}
bool qdInterfaceElementState::mouse_handler(int x, int y, mouseDispatcher::mouseEvent ev) {
switch (ev) {
case mouseDispatcher::EV_MOUSE_MOVE:
if (state_mode() != EVENT_MODE) {
if (has_state_mode(MOUSE_HOVER_MODE)) {
set_state_mode(MOUSE_HOVER_MODE);
if (_prev_state_mode != MOUSE_HOVER_MODE)
handle_events(qdInterfaceEvent::EVENT_ACTIVATION_HOVER, true);
return true;
}
}
break;
case mouseDispatcher::EV_LEFT_DOWN:
case mouseDispatcher::EV_RIGHT_DOWN:
if (has_state_mode(EVENT_MODE)) {
set_state_mode(EVENT_MODE);
handle_events(qdInterfaceEvent::EVENT_ACTIVATION_CLICK, true);
return true;
}
break;
default:
break;
}
return false;
}
bool qdInterfaceElementState::keyboard_handler(Common::KeyCode vkey) {
return false;
}
bool qdInterfaceElementState::handle_events(qdInterfaceEvent::activation_t activation_type, bool before_animation) {
if (qdInterfaceDispatcher * dp = qdInterfaceDispatcher::get_dispatcher()) {
for (uint i = 0; i < _events.size(); i++) {
if (_events[i].activation() == activation_type && _events[i].is_before_animation() == before_animation) {
dp->handle_event(_events[i].event(), _events[i].event_data(), owner());
}
}
return true;
}
return false;
}
bool qdInterfaceElementState::get_contour(state_mode_t mode, qdContour &cnt) const {
_modes[mode].get_contour(cnt);
return true;
}
bool qdInterfaceElementState::set_contour(state_mode_t mode, const qdContour &cnt) {
_modes[mode].set_contour(cnt);
return true;
}
bool qdInterfaceElementState::need_active_game() const {
for (uint i = 0; i < _events.size(); i++) {
if (_events[i].event() == qdInterfaceEvent::EVENT_SAVE_GAME)
return true;
if (_events[i].event() == qdInterfaceEvent::EVENT_CHANGE_PERSONAGE)
return true;
if (_events[i].event() == qdInterfaceEvent::EVENT_RESUME_GAME)
return true;
if (_events[i].event() == qdInterfaceEvent::EVENT_ACTIVATE_PERSONAGE)
return true;
if (_events[i].event() == qdInterfaceEvent::EVENT_SET_SAVE_MODE)
return true;
}
return false;
}
const qdInterfaceEvent *qdInterfaceElementState::find_event(qdInterfaceEvent::event_t type) const {
events_container_t::const_iterator it = Common::find(_events.begin(), _events.end(), type);
if (it != _events.end())
return &*it;
return NULL;
}
bool qdInterfaceElementState::has_event(qdInterfaceEvent::event_t type, const char *ev_data) const {
for (events_container_t::const_iterator it = _events.begin(); it != _events.end(); ++it) {
if (it->event() == type) {
if ((!ev_data && !it->event_data()) || (it->event_data() && ev_data && !strcmp(ev_data, it->event_data())))
return true;
}
}
return false;
}
} // namespace QDEngine

View File

@@ -0,0 +1,332 @@
/* 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/>.
*
*/
#ifndef QDENGINE_QDCORE_QD_INTERFACE_ELEMENT_STATE_H
#define QDENGINE_QDCORE_QD_INTERFACE_ELEMENT_STATE_H
#include "qdengine/parser/xml_fwd.h"
#include "qdengine/qdcore/qd_animation_info.h"
#include "qdengine/qdcore/qd_interface_object_base.h"
#include "qdengine/qdcore/qd_interface_element_state_mode.h"
namespace QDEngine {
//! Интерфейсное событие.
class qdInterfaceEvent {
public:
//! События, производимые элементами интерфейса.
enum event_t {
EVENT_NONE = 0,
//! выйти из игры
EVENT_EXIT, // 1
//! перейти на сцену, параметр - имя сцены
EVENT_LOAD_SCENE, // 2
//! записать игру [не используется]
EVENT_SAVE_GAME, // 3
//! новая игра
EVENT_NEW_GAME, // 4
//! перейти на другой интерфейсный экран (парметер имя экрана)
EVENT_CHANGE_INTERFACE_SCREEN, // 5
//! сменить активного персонажа
EVENT_CHANGE_PERSONAGE, // 6
//! временно спрятать элемент активного экрана (до перехода на другой экран)
// (парметер имя элемента)
EVENT_TMP_HIDE_ELEMENT, // 7
//! спрятать элемент активного экрана (насовсем)
// (парметер имя элемента)
EVENT_HIDE_ELEMENT, // 8
//! показать элемент активного экрана
// (парметер имя элемента)
EVENT_SHOW_ELEMENT, // 9
//! продолжить игру
EVENT_RESUME_GAME, // 10
//! включить режим записи игры
/**
при заходе на экран с сэйвами будет происходить
запись игры при клике по сэйву
*/
EVENT_SET_SAVE_MODE, // 11
//! включить режим загрузки игры
/**
при заходе на экран с сэйвами будет происходить
загрузка сохраненной игры при клике по сэйву
*/
EVENT_SET_LOAD_MODE, // 12
//! сделать персонажа активным, имя персонажа - в данных события
EVENT_ACTIVATE_PERSONAGE, // 13
//! включить предыдущее состояние кнопки (парметер имя кнопки)
EVENT_PREV_ELEMENT_STATE, // 14
//! включить следующее состояние кнопки (парметер имя кнопки)
EVENT_NEXT_ELEMENT_STATE, // 15
//! перейти в main menu
EVENT_MAIN_MENU, // 16
//! играть видеоролик (параметр - имя видеоролика из списка в qdGameDispatcher)
EVENT_PLAY_VIDEO, // 17
//! включить определенное состояние кнопки, параметр - имя кнопки
EVENT_BUTTON_STATE, // 18
//! скинуть с мыши объект в инвентори
EVENT_CLEAR_MOUSE, // 19
//! скроллинг
EVENT_SCROLL_LEFT, // 20
EVENT_SCROLL_RIGHT, // 21
EVENT_SCROLL_UP, // 22
EVENT_SCROLL_DOWN, // 23
//! показать экран интерфейса, как модальный
EVENT_SHOW_INTERFACE_SCREEN_AS_MODAL, // 24
//! пользователь завершил работу с модальным окном и ответил Ок
EVENT_MODAL_OK, // 25
//! пользователь завершил работу с модальным окном и ответил Отмена
EVENT_MODAL_CANCEL, // 26
/// Имя игрока в таблице рекордов, параметр - номер места
EVENT_HALL_OF_FAME_PLAYER, // 27
/// Очки игрока в таблице рекордов, параметр - номер места
EVENT_HALL_OF_FAME_SCORE, // 28
/// Текущие очки игрока на экране таблицы рекордов
EVENT_HALL_OF_FAME_CUR_SCORE // 29
};
/// Способы запуска события
enum activation_t {
/// левый клик по кнопке
EVENT_ACTIVATION_CLICK = 0,
/// наведение мышиного курсора
EVENT_ACTIVATION_HOVER
};
qdInterfaceEvent(event_t id, const char *data, bool anm_flag, activation_t activation = EVENT_ACTIVATION_CLICK) : _event(id), _event_data(data), _is_before_animation(anm_flag), _activation(activation) { }
~qdInterfaceEvent() {}
bool operator == (event_t id) const {
return (_event == id);
}
//! Код события.
event_t event() const {
return _event;
}
//! Возвращает true, если у события есть данные.
bool has_data() const {
return !_event_data.empty();
}
//! Возвращает данные для события.
const char *event_data() const {
return _event_data.c_str();
}
//! Возвращает true, если событие должно происходить до включения анимации.
bool is_before_animation() const {
return _is_before_animation;
}
activation_t activation() const {
return _activation;
}
private:
//! Код события.
event_t _event;
//! Данные для события.
Common::String _event_data;
//! Равно true, если событие должно происходить до включения анимации.
bool _is_before_animation;
//! Как событие активируется
activation_t _activation;
};
//! Состояние элемента интерфейса.
class qdInterfaceElementState : public qdInterfaceObjectBase {
public:
//! Идентификаторы для режимов работы состояния.
enum state_mode_t {
//! режим по умолчанию
DEFAULT_MODE = 0,
//! режим, который включается когда мышиный курсор находится над элементом-владельцем
MOUSE_HOVER_MODE,
//! режим, который включается при клике по элементу-владельцу
EVENT_MODE
};
enum {
//! количество режимов работы состояния
NUM_MODES = 3
};
qdInterfaceElementState();
qdInterfaceElementState(const qdInterfaceElementState &st);
~qdInterfaceElementState();
qdInterfaceElementState &operator = (const qdInterfaceElementState &st);
//! Устанавливает имя файла звукового эффекта, привязанного к состоянию.
/**
Если надо убрать звук - передать NULL в качестве имени файла.
*/
void set_sound_file(const Common::Path &str, state_mode_t snd_id = DEFAULT_MODE);
//! Возвращает имя файла звукового эффекта, привязанного к состоянию.
const Common::Path sound_file(state_mode_t snd_id = DEFAULT_MODE) const {
return _modes[snd_id].sound_file();
}
//! Возвращает указатель на звуковой эффект, привязанный к состоянию.
const qdSound *sound(state_mode_t snd_id = DEFAULT_MODE) const {
return _modes[snd_id].sound();
}
//! Возвращает true, если к состоянию привязан звук.
bool has_sound(state_mode_t snd_id = DEFAULT_MODE) const {
return _modes[snd_id].has_sound();
}
//! Устанавливает имя файла для анимации.
/**
Если надо убрать анимацию - передать NULL в качестве имени файла.
*/
void set_animation_file(const Common::Path &name, state_mode_t anm_id = DEFAULT_MODE);
//! Возвращает имя файла для анимации.
const Common::Path animation_file(state_mode_t anm_id = DEFAULT_MODE) const {
return _modes[anm_id].animation_file();
}
//! Возвращает флаги анимации.
int animation_flags(state_mode_t anm_id = DEFAULT_MODE) const {
return _modes[anm_id].animation_flags();
}
//! Устанавливает флаг анимации.
void set_animation_flag(int fl, state_mode_t anm_id = DEFAULT_MODE) {
_modes[anm_id].set_animation_flag(fl);
}
//! Скидывает флаг анимации.
void drop_animation_flag(int fl, state_mode_t anm_id = DEFAULT_MODE) {
_modes[anm_id].drop_animation_flag(fl);
}
//! Возвращает true, если для анимации установлен флаг fl.
bool check_animation_flag(int fl, state_mode_t anm_id = DEFAULT_MODE) const {
return _modes[anm_id].check_animation_flag(fl);
}
//! Возвращает указатель на анимацию, привязанную к состоянию.
const qdAnimation *animation(state_mode_t anm_id = DEFAULT_MODE) const {
return _modes[anm_id].animation();
}
//! Возвращает true, если к состоянию привязана анимация.
bool has_animation(state_mode_t anm_id = DEFAULT_MODE) const {
return _modes[anm_id].has_animation();
}
//! Регистрирует ресурсы, связанные с состоянием (анимацию и звук).
bool register_resources();
//! Высвобождает ресурсы, связанные с состоянием (анимацию и звук).
bool unregister_resources();
Common::String event2Str(int fl, bool truncate = false) const;
Common::String eventActivation2Str(int fl, bool truncate = false) const;
//! Запись данных в скрипт.
bool save_script(Common::WriteStream &fh, int indent = 0) const;
//! Загрузка данных из скрипта.
bool load_script(const xml::tag *p);
//! Обсчет логики, параметр - время в секундах.
/**
Вызывается владельцем для активного состояния.
*/
bool quant(float dt);
//! Устанавливает режим работы состояния.
void set_state_mode(state_mode_t mode) {
_state_mode = mode;
}
//! Возвращает режим работы состояния.
state_mode_t state_mode() const {
return _state_mode;
}
//! Возвращает true, если у состояния может быть включен режим работы mode.
bool has_state_mode(state_mode_t mode) const;
//! Возвращает true, если у состояния задан контур для режима mode.
bool has_contour(state_mode_t mode) const {
return _modes[mode].has_contour();
}
//! Записывает контур режима mode в cnt.
bool get_contour(state_mode_t mode, qdContour &cnt) const;
//! Устанавливает контур для режима mode.
bool set_contour(state_mode_t mode, const qdContour &cnt);
//! Возвращает true, если точка с экранными координатами (x,у) попадает внутрь контура режима mode.
bool hit_test(int x, int y, state_mode_t mode) const {
return _modes[mode].hit_test(x, y);
}
//! Обработчик событий мыши.
bool mouse_handler(int x, int y, mouseDispatcher::mouseEvent ev);
//! Обработчик ввода с клавиатуры.
bool keyboard_handler(Common::KeyCode vkey);
typedef Std::vector<qdInterfaceEvent> events_container_t;
//! Список событий.
const events_container_t &events() const {
return _events;
}
//! Добавление события.
void add_event(const qdInterfaceEvent &ev) {
_events.push_back(ev);
}
//! Уделение события
void erase_event(int iNum) {
_events.erase(_events.begin() + iNum);
}
//! Удаление всех событий
void erase_all_events() {
_events.clear();
}
/**
Возвращает true, если хотя бы для одного из событий состояния необходимо
наличие загруженной сцены (т.е. оно происходит только во время игры).
*/
bool need_active_game() const;
const qdInterfaceEvent *find_event(qdInterfaceEvent::event_t type) const;
bool has_event(qdInterfaceEvent::event_t type, const char *ev_data = NULL) const;
private:
//! События, происходящие при активации состояния.
events_container_t _events;
//! События состояния.
qdInterfaceElementStateMode _modes[NUM_MODES];
//! Текущий режим работы состояния.
state_mode_t _state_mode;
//! Режим работы состояния в прошлый квант времени.
state_mode_t _prev_state_mode;
//! Обработка событий состояния.
/**
Параметром выбирается, какие события выполнять - которые должны
происхолить до анимации (параметр true) или после нее (параметр false).
*/
bool handle_events(qdInterfaceEvent::activation_t activation_type, bool before_animation);
};
} // namespace QDEngine
#endif // QDENGINE_QDCORE_QD_INTERFACE_ELEMENT_STATE_H

View File

@@ -0,0 +1,157 @@
/* 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 "qdengine/qd_fwd.h"
#include "qdengine/qdcore/qd_animation.h"
#include "qdengine/parser/xml_tag_buffer.h"
#include "qdengine/parser/qdscr_parser.h"
#include "qd_interface_element_state_mode.h"
namespace QDEngine {
qdInterfaceElementStateMode::qdInterfaceElementStateMode() : _sound(NULL),
_animation_flags(0),
_animation(NULL),
_contour(qdContour::CONTOUR_POLYGON) {
}
qdInterfaceElementStateMode::qdInterfaceElementStateMode(const qdInterfaceElementStateMode &ev) : _sound_file(ev._sound_file),
_sound(ev._sound),
_animation_file(ev._animation_file),
_animation_flags(ev._animation_flags),
_animation(ev._animation),
_contour(ev._contour) {
}
qdInterfaceElementStateMode::~qdInterfaceElementStateMode() {
}
qdInterfaceElementStateMode &qdInterfaceElementStateMode::operator = (const qdInterfaceElementStateMode &ev) {
if (this == &ev) return *this;
_sound_file = ev._sound_file;
_sound = ev._sound;
_animation_file = ev._animation_file;
_animation_flags = ev._animation_flags;
_animation = ev._animation;
_contour = ev._contour;
return *this;
}
void qdInterfaceElementStateMode::set_sound_file(const Common::Path &name) {
_sound_file = name;
}
void qdInterfaceElementStateMode::set_animation_file(const Common::Path &name) {
_animation_file = name;
}
#define defEnum(x) #x
static const char *stateModeList[] = {
defEnum(DEFAULT_MODE),
defEnum(MOUSE_HOVER_MODE),
defEnum(EVENT_MODE),
};
Common::String qdInterfaceElementStateMode::stateMode2Str(int fl, bool truncate) const {
if (fl >= ARRAYSIZE(stateModeList) || fl < 0)
return Common::String::format("<%d>", fl);
Common::String out(stateModeList[fl]);
return truncate ? out.substr(0, out.size() - 5) : out;
}
bool qdInterfaceElementStateMode::save_script(Common::WriteStream &fh, int type_id, int indent) const {
for (int i = 0; i < indent; i++) {
fh.writeString("\t");
}
if (debugChannelSet(-1, kDebugLog)) {
fh.writeString(Common::String::format("<state_mode type=\"%s\"", stateMode2Str(type_id).c_str()));
} else {
fh.writeString(Common::String::format("<state_mode type=\"%d\"", type_id));
}
if (has_animation()) {
fh.writeString(Common::String::format(" animation=\"%s\"", qdscr_XML_string(animation_file().toString('\\'))));
}
if (_animation_flags) {
if (debugChannelSet(-1, kDebugLog))
fh.writeString(Common::String::format(" animation_flags=\"%s\"", qdAnimation::flag2str(_animation_flags).c_str()));
else
fh.writeString(Common::String::format(" animation_flags=\"%d\"", _animation_flags));
}
if (has_sound()) {
fh.writeString(Common::String::format(" sound=\"%s\"", qdscr_XML_string(sound_file().toString('\\'))));
}
if (has_contour()) {
fh.writeString(">\r\n");
_contour.save_script(fh, indent + 1);
for (int i = 0; i < indent; i++) {
fh.writeString("\t");
}
fh.writeString("</state_mode>\r\n");
} else {
fh.writeString("/>\r\n");
}
return true;
}
bool qdInterfaceElementStateMode::load_script(const xml::tag *p) {
for (xml::tag::subtag_iterator it = p->subtags_begin(); it != p->subtags_end(); ++it) {
switch (it->ID()) {
case QDSCR_SOUND:
set_sound_file(Common::Path(it->data(), '\\'));
break;
case QDSCR_INTERFACE_ANIMATION_FLAGS:
xml::tag_buffer(*it) > _animation_flags;
break;
case QDSCR_ANIMATION:
set_animation_file(Common::Path(it->data(), '\\'));
break;
case QDSCR_CONTOUR_CIRCLE:
_contour.set_contour_type(qdContour::CONTOUR_CIRCLE);
_contour.load_script(&*it);
break;
case QDSCR_CONTOUR_RECTANGLE:
_contour.set_contour_type(qdContour::CONTOUR_RECTANGLE);
_contour.load_script(&*it);
break;
case QDSCR_CONTOUR_POLYGON:
_contour.set_contour_type(qdContour::CONTOUR_POLYGON);
_contour.load_script(&*it);
break;
}
}
return true;
}
} // namespace QDEngine

View File

@@ -0,0 +1,158 @@
/* 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/>.
*
*/
#ifndef QDENGINE_QDCORE_QD_INTERFACE_ELEMENT_STATE_MODE_H
#define QDENGINE_QDCORE_QD_INTERFACE_ELEMENT_STATE_MODE_H
#include "common/path.h"
#include "qdengine/qdcore/qd_contour.h"
namespace QDEngine {
//! Событие, привязанное к состоянию интерфейсного элемента.
/**
Включает анимацию и звук при выполнении некоторых условий (например при наведении
мыши на элемент интерфейса.
*/
class qdInterfaceElementStateMode {
public:
qdInterfaceElementStateMode();
qdInterfaceElementStateMode(const qdInterfaceElementStateMode &ev);
~qdInterfaceElementStateMode();
qdInterfaceElementStateMode &operator = (const qdInterfaceElementStateMode &ev);
//! Устанавливает имя файла звука.
/**
Если надо убрать звук - передать NULL в качестве имени файла.
*/
void set_sound_file(const Common::Path &name);
//! Возвращает имя файла звука.
const Common::Path sound_file() const {
return _sound_file;
}
//! Устанавливает звук события.
void set_sound(const qdSound *p) {
_sound = p;
}
//! Возвращает звук события.
const qdSound *sound() const {
return _sound;
}
//! Возвращает true, если к событию привязан звук.
bool has_sound() const {
return !_sound_file.empty();
}
//! Устанавливает имя файла для анимации.
/**
Если надо убрать анимацию - передать NULL в качестве имени файла.
*/
void set_animation_file(const Common::Path &name);
//! Возвращает имя файла для анимации.
const Common::Path animation_file() const {
return _animation_file;
}
//! Возвращает флаги анимации.
int animation_flags() const {
return _animation_flags;
}
//! Устанавливает флаг анимации.
void set_animation_flag(int fl) {
_animation_flags |= fl;
}
//! Скидывает флаг анимации.
void drop_animation_flag(int fl) {
_animation_flags &= ~fl;
}
//! Возвращает true, если для анимации установлен флаг fl.
bool check_animation_flag(int fl) const {
if (_animation_flags & fl) return true;
return false;
}
//! Устанавливает указатель на анимацию события.
void set_animation(const qdAnimation *p) {
_animation = p;
}
//! Возвращает указатель на анимацию события.
const qdAnimation *animation() const {
return _animation;
}
//! Возвращает true, если к состоянию привязана анимация.
bool has_animation() const {
return !_animation_file.empty();
}
//! Возвращает true, если у состояния задан контур.
bool has_contour() const {
return !_contour.is_contour_empty();
}
//! Возвращает true, если точка с экранными координатами (x,у) попадает внутрь контура.
bool hit_test(int x, int y) const {
return _contour.is_inside(Vect2s(x, y));
}
//! Записывает контур в cnt.
bool get_contour(qdContour &cnt) const {
cnt = _contour;
return true;
}
//! Устанавливает контур.
bool set_contour(const qdContour &cnt) {
_contour = cnt;
return true;
}
Common::String stateMode2Str(int fl, bool truncate = false) const;
//! Запись данных в скрипт.
/**
type_id - тип события
*/
bool save_script(Common::WriteStream &fh, int type_id = 0, int indent = 0) const;
//! Загрузка данных из скрипта.
bool load_script(const xml::tag *p);
private:
//! Имя файла звука.
Common::Path _sound_file;
//! Звук, включаемый событием.
const qdSound *_sound;
//! Имя файла для анимации
Common::Path _animation_file;
//! Флаги для анимации.
int _animation_flags;
//! Анимация, включаемая событием.
const qdAnimation *_animation;
//! Контур.
qdContour _contour;
};
} // namespace QDEngine
#endif // QDENGINE_QDCORE_QD_INTERFACE_ELEMENT_STATE_MODE_H

View File

@@ -0,0 +1,47 @@
/* 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 "qdengine/qd_fwd.h"
#include "qdengine/qdcore/qd_interface_object_base.h"
namespace QDEngine {
qdInterfaceObjectBase::qdInterfaceObjectBase() : _owner(NULL) {
}
qdInterfaceObjectBase::qdInterfaceObjectBase(const qdInterfaceObjectBase &obj) : qdNamedObjectBase(obj),
_owner(obj._owner) {
}
qdInterfaceObjectBase::~qdInterfaceObjectBase() {
}
qdInterfaceObjectBase &qdInterfaceObjectBase::operator = (const qdInterfaceObjectBase &obj) {
if (this == &obj) return *this;
*static_cast<qdNamedObjectBase *>(this) = obj;
_owner = obj._owner;
return *this;
}
} // namespace QDEngine

View File

@@ -0,0 +1,59 @@
/* 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/>.
*
*/
#ifndef QDENGINE_QDCORE_QD_INTERFACE_OBJECT_BASE_H
#define QDENGINE_QDCORE_QD_INTERFACE_OBJECT_BASE_H
#include "qdengine/qdcore/qd_named_object_base.h"
namespace QDEngine {
//! Базовый класс для всех интерфейсных объектов.
class qdInterfaceObjectBase : public qdNamedObjectBase {
public:
qdInterfaceObjectBase();
qdInterfaceObjectBase(const qdInterfaceObjectBase &obj);
~qdInterfaceObjectBase();
//! Устанавливает владельца объекта.
void set_owner(qdInterfaceObjectBase *p) {
_owner = p;
}
//! Возвращает указатель на владельца объекта.
qdInterfaceObjectBase *owner() const {
return _owner;
}
protected:
qdInterfaceObjectBase &operator = (const qdInterfaceObjectBase &obj);
private:
//! Владелец объекта.
mutable qdInterfaceObjectBase *_owner;
};
} // namespace QDEngine
#endif // QDENGINE_QDCORE_QD_INTERFACE_OBJECT_BASE_H

View File

@@ -0,0 +1,357 @@
/* 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 "common/savefile.h"
#include "common/stream.h"
#include "engines/metaengine.h"
#include "qdengine/qdengine.h"
#include "qdengine/qd_fwd.h"
#include "qdengine/parser/qdscr_parser.h"
#include "qdengine/parser/xml_tag_buffer.h"
#include "qdengine/qdcore/qd_game_dispatcher.h"
#include "qdengine/qdcore/qd_interface_save.h"
#include "qdengine/qdcore/qd_interface_screen.h"
#include "qdengine/qdcore/qd_interface_dispatcher.h"
#include "qdengine/system/graphics/gr_dispatcher.h"
namespace QDEngine {
bool qdInterfaceSave::_save_mode = false;
int qdInterfaceSave::_current_save_ID = 0;
qdInterfaceSave::qdInterfaceSave() : _save_ID(0),
_thumbnail_size_x(0),
_thumbnail_size_y(0),
_text_dx(0),
_text_dy(0) {
_thumbnail.set_owner(this);
_frame.set_owner(this);
_isAutosaveSlot = false;
_save_ID = _current_save_ID++;
}
qdInterfaceSave::qdInterfaceSave(const qdInterfaceSave &sv) : qdInterfaceElement(sv),
_save_ID(sv._save_ID),
_thumbnail_size_x(sv._thumbnail_size_x),
_thumbnail_size_y(sv._thumbnail_size_y),
_text_dx(sv.text_dx()),
_text_dy(sv.text_dy()),
_isAutosaveSlot(sv._isAutosaveSlot) {
_thumbnail.set_owner(this);
_thumbnail = sv._thumbnail;
_frame.set_owner(this);
_frame = sv._frame;
}
qdInterfaceSave::~qdInterfaceSave() {
_thumbnail.unregister_resources();
_frame.unregister_resources();
}
qdInterfaceSave &qdInterfaceSave::operator = (const qdInterfaceSave &sv) {
if (this == &sv) return *this;
*static_cast<qdInterfaceElement *>(this) = sv;
_save_ID = sv._save_ID;
_thumbnail_size_x = sv._thumbnail_size_x;
_thumbnail_size_y = sv._thumbnail_size_y;
_isAutosaveSlot = sv._isAutosaveSlot;
_text_dx = sv.text_dx();
_text_dy = sv.text_dy();
_thumbnail = sv._thumbnail;
_frame = sv._frame;
return *this;
}
bool qdInterfaceSave::mouse_handler(int x, int y, mouseDispatcher::mouseEvent ev) {
_frame.mouse_handler(x, y, ev);
debugC(9, kDebugInput, "qdInterfaceSave::mouse_handler(): ev = %d, x = %d, y = %d", ev, x, y);
switch (ev) {
case mouseDispatcher::EV_LEFT_DOWN:
case mouseDispatcher::EV_RIGHT_DOWN:
if (/*qdGameDispatcher *dp = */qdGameDispatcher::get_dispatcher()) {
debugC(1, kDebugSave, "qdInterfaceSave::mouse_handler(): _save_mode = %d", _save_mode);
clear_screen_region();
if (_save_mode) {
if (isAutosaveSlot())
return true;
qdInterfaceDispatcher *ip = qdInterfaceDispatcher::get_dispatcher();
if (ip) {
if (ip->has_save_title_screen()) {
ip->setModalScreenMode(qdInterfaceDispatcher::MODAL_SCREEN_SAVE_NAME_EDIT);
ip->handle_event(qdInterfaceEvent::EVENT_SHOW_INTERFACE_SCREEN_AS_MODAL, ip->save_title_screen_name(), this);
} else if (ip->has_save_prompt_screen() && g_engine->getSaveFileManager()->exists(g_engine->getSaveStateName(_save_ID))) {
ip->setModalScreenMode(qdInterfaceDispatcher::MODAL_SCREEN_SAVE_OVERWRITE);
ip->handle_event(qdInterfaceEvent::EVENT_SHOW_INTERFACE_SCREEN_AS_MODAL, ip->save_prompt_screen_name(), this);
} else {
perform_save();
ip->handle_event(qdInterfaceEvent::EVENT_RESUME_GAME, NULL);
}
}
return true;
} else {
debugC(1, kDebugSave, "qdInterfaceSave::mouse_handler(): load_game() _save_ID = %d", _save_ID);
g_engine->loadGameState(_save_ID);
if (qdInterfaceDispatcher *ip = qdInterfaceDispatcher::get_dispatcher())
ip->handle_event(qdInterfaceEvent::EVENT_RESUME_GAME, NULL);
return true;
}
}
break;
default:
break;
}
return false;
}
bool qdInterfaceSave::keyboard_handler(Common::KeyCode vkey) {
return false;
}
bool qdInterfaceSave::init(bool is_game_active) {
if (!is_game_active && _frame.need_active_game())
set_lock(true);
else
set_lock(false);
Common::String saveFileName(g_engine->getSaveStateName(_save_ID));
bool fileExists = false;
if (g_engine->getSaveFileManager()->exists(saveFileName)) {
Common::InSaveFile *saveFile = g_engine->getSaveFileManager()->openForLoading(saveFileName);
fileExists = true;
ExtendedSavegameHeader header;
if (MetaEngine::readSavegameHeader(saveFile, &header, true))
_save_title = header.description.c_str();
delete saveFile;
_thumbnail.set_animation_file(Common::Path(Common::String::format("scummvm/%s", saveFileName.c_str())));
} else {
_save_title = "";
}
set_state(&_frame);
if (!_save_mode && !fileExists) {
if (is_visible()) {
debugC(3, kDebugInput, "qdInterfaceSave::init(): Hide %s", saveFileName.c_str());
hide();
if (qdInterfaceScreen *sp = dynamic_cast<qdInterfaceScreen * >(owner()))
sp->build_visible_elements_list();
}
} else {
if (!is_visible()) {
show();
if (qdInterfaceScreen *sp = dynamic_cast<qdInterfaceScreen * >(owner()))
sp->build_visible_elements_list();
}
}
return true;
}
bool qdInterfaceSave::redraw() const {
//warning("STUB: qdInterfaceSave::redraw()");
if (qdInterfaceDispatcher *pid = qdInterfaceDispatcher::get_dispatcher()) {
if (pid->need_save_screenshot())
if (const qdAnimation *p = _thumbnail.animation())
p->redraw(r().x, r().y, 0);
Common::String text;
if (pid->need_show_save_title()) {
text = title();
if (pid->need_show_save_time())
text += " ";
}
grDispatcher *gr_disp = grDispatcher::instance();
if (!text.empty()) {
qdGameDispatcher *game_disp = qdGameDispatcher::get_dispatcher();
const grFont *font = NULL;
if ((QD_FONT_TYPE_NONE != pid->save_font_type()) && game_disp)
font = game_disp->find_font(pid->save_font_type());
else
font = gr_disp->get_default_font();
int tx = r().x - size_x() / 2 + text_dx();
int ty = r().y - size_y() / 2 + text_dy();
gr_disp->drawText(tx, ty, pid->save_font_color(), text.c_str(), 0, 0, font);
}
}
return qdInterfaceElement::redraw();
}
grScreenRegion qdInterfaceSave::screen_region() const {
grScreenRegion reg0 = qdInterfaceElement::screen_region();
grScreenRegion reg1(r().x, r().y, _thumbnail_size_x, _thumbnail_size_y);
reg0 += reg1;
return reg0;
}
int qdInterfaceSave::size_x() const {
int x = _thumbnail_size_x;
if (const qdAnimation *p = _frame.animation()) {
if (x < p->size_x())
x = p->size_x();
}
return x;
}
int qdInterfaceSave::size_y() const {
int y = _thumbnail_size_y;
if (const qdAnimation *p = _frame.animation()) {
if (y < p->size_y())
y = p->size_y();
}
return y;
}
bool qdInterfaceSave::quant(float dt) {
qdInterfaceElement::quant(dt);
_frame.quant(dt);
return true;
}
bool qdInterfaceSave::hit_test(int x, int y) const {
if (qdInterfaceElement::hit_test(x, y)) return true;
x -= r().x;
y -= r().y;
bool result = false;
if (x >= -_thumbnail_size_x / 2 && x < _thumbnail_size_x / 2 && y >= -_thumbnail_size_y / 2 && y < _thumbnail_size_y / 2)
result = true;
return result;
}
bool qdInterfaceSave::perform_save() {
bool is_ok = true;
if (qdGameDispatcher *dp = qdGameDispatcher::get_dispatcher()) {
debugC(1, kDebugSave, "qdInterfaceSave::perform_save(): _save_ID = %d", _save_ID);
is_ok &= (g_engine->saveGameState(_save_ID, _save_title.c_str(), dp->is_autosave_slot(_save_ID)).getCode() == Common::kNoError);
debugC(1, kDebugSave, "qdInterfaceSave::perform_save(): is_ok = %d", is_ok);
is_ok &= init(true);
return is_ok;
}
return false;
}
bool qdInterfaceSave::save_script_body(Common::WriteStream &fh, int indent) const {
if (!_frame.save_script(fh, indent)) {
return false;
}
for (int i = 0; i <= indent; i++) {
fh.writeString("\t");
}
fh.writeString(Common::String::format("<ID>%d</ID>\r\n", _save_ID));
if (_thumbnail_size_x || _thumbnail_size_y) {
for (int i = 0; i <= indent; i++) {
fh.writeString("\t");
}
fh.writeString(Common::String::format("<thumbnail_size>%d %d</thumbnail_size>\r\n", _thumbnail_size_x, _thumbnail_size_y));
}
if (_text_dx || _text_dy) {
for (int i = 0; i <= indent; i++) {
fh.writeString("\t");
}
fh.writeString(Common::String::format("<text_shift>%d %d</text_shift>\r\n", _text_dx, _text_dy));
}
if (isAutosaveSlot()) {
for (int i = 0; i <= indent; i++) {
fh.writeString("\t");
}
fh.writeString("<is_autosave>1</is_autosave>\r\n");
}
return true;
}
bool qdInterfaceSave::load_script_body(const xml::tag *p) {
for (xml::tag::subtag_iterator it = p->subtags_begin(); it != p->subtags_end(); ++it) {
switch (it->ID()) {
case QDSCR_INTERFACE_ELEMENT_STATE:
if (!_frame.load_script(&*it)) return false;
break;
case QDSCR_INTERFACE_THUMBNAIL_SIZE:
xml::tag_buffer(*it) > _thumbnail_size_x > _thumbnail_size_y;
g_engine->_thumbSizeX = _thumbnail_size_x;
g_engine->_thumbSizeY = _thumbnail_size_y;
break;
case QDSCR_INTERFACE_TEXT_SHIFT:
xml::tag_buffer(*it) > _text_dx > _text_dy;
break;
case QDSCR_ID:
xml::tag_buffer(*it) > _save_ID;
break;
case QDSCR_INTERFACE_SAVE_IS_AUTOSAVE:
setAutosaveSlot(xml::tag_buffer(*it).get_int() != 0);
break;
}
}
return true;
}
} // namespace QDEngine

View File

@@ -0,0 +1,220 @@
/* 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/>.
*
*/
#ifndef QDENGINE_QDCORE_QD_INTERFACE_SAVE_H
#define QDENGINE_QDCORE_QD_INTERFACE_SAVE_H
#include "qdengine/parser/xml_fwd.h"
#include "qdengine/qdcore/qd_interface_element.h"
#include "qdengine/qdcore/qd_interface_element_state.h"
namespace QDEngine {
//! Интерфейсный элемент - сэйв.
class qdInterfaceSave : public qdInterfaceElement {
public:
qdInterfaceSave();
qdInterfaceSave(const qdInterfaceSave &sv);
~qdInterfaceSave();
qdInterfaceSave &operator = (const qdInterfaceSave &sv);
//! Возвращает тип элемента.
qdInterfaceElement::element_type get_element_type() const {
return qdInterfaceElement::EL_SAVE;
}
//! Обработчик событий мыши.
bool mouse_handler(int x, int y, mouseDispatcher::mouseEvent ev);
//! Обработчик ввода с клавиатуры.
bool keyboard_handler(Common::KeyCode vkey);
//! Инициализация элемента.
/**
Вызывается каждый раз при заходе на экран, которому принадлежит элемент.
*/
bool init(bool is_game_active = true);
//! Отрисовка элемента.
bool redraw() const;
grScreenRegion screen_region() const;
//! Возвращает размер элемента по горизонтали в пикселах.
int size_x() const;
//! Возвращает размер элемента по вертикали в пикселах.
int size_y() const;
//! Возвращает размер картики-превью по горизонтали в пикселах.
int thumbnail_size_x() const {
return _thumbnail_size_x;
}
//! Возвращает размер картики-превью по вертикали в пикселах.
int thumbnail_size_y() const {
return _thumbnail_size_y;
}
//! Назначает размер картики-превью по горизонтали в пикселах.
void set_thumbnail_size_x(int sx) {
_thumbnail_size_x = sx;
}
//! Назначает размер картики-превью по вертикали в пикселах.
void set_thumbnail_size_y(int sy) {
_thumbnail_size_y = sy;
}
//! Смещение текста, выводимого в сэйве, относительно левого-верхнего угла
int text_dx() const {
return _text_dx;
}
int text_dy() const {
return _text_dy;
}
void set_test_dx(int val) {
_text_dx = val;
}
void set_test_dy(int val) {
_text_dy = val;
}
//! Обсчет логики, параметр - время в секундах.
bool quant(float dt);
//! Возвращает true, если точка с экранными координатами (x,у) попадает в элемент.
bool hit_test(int x, int y) const;
//! Возвращает номер сэйва.
int save_ID() const {
return _save_ID;
}
//! Устанавливает номер сэйва.
void set_save_ID(int id) {
_save_ID = id;
}
//! Устанавливает режим работы - записывать (state == true) или загружать (state == false) сэйвы.
static void set_save_mode(bool state) {
_save_mode = state;
}
//! Устанавливает имя файла для анимации.
/**
Если надо убрать анимацию - передать NULL в качестве имени файла.
*/
void set_frame_animation_file(const Common::Path &name, qdInterfaceElementState::state_mode_t mode = qdInterfaceElementState::MOUSE_HOVER_MODE) {
_frame.set_animation_file(name, mode);
}
//! Возвращает имя файла для анимации.
const Common::Path frame_animation_file(qdInterfaceElementState::state_mode_t mode = qdInterfaceElementState::MOUSE_HOVER_MODE) const {
return _frame.animation_file(mode);
}
//! Возвращает флаги анимации.
int frame_animation_flags(qdInterfaceElementState::state_mode_t mode = qdInterfaceElementState::MOUSE_HOVER_MODE) const {
return _frame.animation_flags(mode);
}
//! Устанавливает флаг анимации.
void set_frame_animation_flag(int fl, qdInterfaceElementState::state_mode_t mode = qdInterfaceElementState::MOUSE_HOVER_MODE) {
_frame.set_animation_flag(fl, mode);
}
//! Скидывает флаг анимации.
void drop_frame_animation_flag(int fl, qdInterfaceElementState::state_mode_t mode = qdInterfaceElementState::MOUSE_HOVER_MODE) {
_frame.drop_animation_flag(fl, mode);
}
//! Возвращает true, если для анимации установлен флаг fl.
bool check_frame_animation_flag(int fl, qdInterfaceElementState::state_mode_t mode = qdInterfaceElementState::MOUSE_HOVER_MODE) const {
return _frame.check_animation_flag(fl, mode);
}
//! Возвращает true, если к сейву привязана анимация рамки.
bool frame_has_animation(qdInterfaceElementState::state_mode_t mode = qdInterfaceElementState::MOUSE_HOVER_MODE) const {
return _frame.has_animation(mode);
}
void set_frame_sound_file(const char *name, qdInterfaceElementState::state_mode_t mode = qdInterfaceElementState::MOUSE_HOVER_MODE) {
_frame.set_sound_file(name, mode);
}
const Common::Path frame_sound_file(qdInterfaceElementState::state_mode_t mode = qdInterfaceElementState::MOUSE_HOVER_MODE) const {
return _frame.sound_file(mode);
}
bool isAutosaveSlot() const {
return _isAutosaveSlot;
}
void setAutosaveSlot(bool state) {
_isAutosaveSlot = state;
}
void set_title(const char *title) {
_save_title = title;
}
const char *title() const {
return _save_title.c_str();
}
//! Осуществить сохранение текущего состояния игры в ячейку сэйва.
bool perform_save();
protected:
//! Запись данных в скрипт.
bool save_script_body(Common::WriteStream &fh, int indent = 0) const;
//! Загрузка данных из скрипта.
bool load_script_body(const xml::tag *p);
private:
//! Горизонтальный размер картинки-превью сэйва.
int _thumbnail_size_x;
//! Вертикальный размер картинки-превью сэйва.
int _thumbnail_size_y;
//! Смещение текста, выводимого в сэйве, относительно левого-верхнего угла
int _text_dx;
int _text_dy;
//! Номер сэйва, имя файла имеет вид saveNNN.dat, где NNN - save_ID_.
int _save_ID;
/// если true, то игрок сам не может записать игру в этот слот
bool _isAutosaveSlot;
//! Превью сэйва.
qdInterfaceElementState _thumbnail;
Common::String _save_title;
//! Опциональная рамка вокруг картинки-превью сэйва.
qdInterfaceElementState _frame;
//! Режим работы сэйвов - чтение/запись (== false/true).
static bool _save_mode;
//! Текущий номер сэйва.
static int _current_save_ID;
};
} // namespace QDEngine
#endif // QDENGINE_QDCORE_QD_INTERFACE_SAVE_H

View File

@@ -0,0 +1,400 @@
/* 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 "qdengine/qdengine.h"
#include "qdengine/qd_fwd.h"
#include "qdengine/parser/xml_tag_buffer.h"
#include "qdengine/parser/qdscr_parser.h"
#include "qdengine/qdcore/qd_interface_element.h"
#include "qdengine/qdcore/qd_interface_button.h"
#include "qdengine/qdcore/qd_interface_screen.h"
#include "qdengine/qdcore/qd_interface_dispatcher.h"
#include "qdengine/qdcore/qd_game_dispatcher.h"
#include "qdengine/qdcore/qd_game_scene.h"
#include "qdengine/qdcore/qd_game_object_moving.h"
namespace QDEngine {
struct qdInterfaceElementsOrdering {
bool operator()(const qdInterfaceElement *p0, const qdInterfaceElement *p1) {
return p0->screen_depth() < p1->screen_depth();
}
};
qdInterfaceScreen::qdInterfaceScreen() : _is_locked(false),
_autohide_time(0.0f),
_autohide_phase(1.0f),
_autohide_offset(Vect2i(0, 0)),
_modal_caller(NULL) {
_sorted_elements.reserve(20);
}
qdInterfaceScreen::~qdInterfaceScreen() {
_elements.clear();
}
bool qdInterfaceScreen::redraw(int dx, int dy) const {
debugC(6, kDebugText, "qdInterfaceScreen::redraw(): %d elements", _sorted_elements.size());
for (sorted_element_list_t::const_reverse_iterator it = _sorted_elements.rbegin(); it != _sorted_elements.rend(); ++it)
(*it)->redraw();
return true;
}
bool qdInterfaceScreen::pre_redraw(bool force_full_redraw) {
qdGameDispatcher *dp = qdGameDispatcher::get_dispatcher();
if (!dp) return false;
if (force_full_redraw) {
for (sorted_element_list_t::iterator it = _sorted_elements.begin(); it != _sorted_elements.end(); ++it) {
if ((*it)->last_screen_region() != (*it)->screen_region())
dp->add_redraw_region((*it)->last_screen_region());
dp->add_redraw_region((*it)->screen_region());
}
} else {
for (sorted_element_list_t::iterator it = _sorted_elements.begin(); it != _sorted_elements.end(); ++it) {
if ((*it)->need_redraw()) {
if ((*it)->last_screen_region() != (*it)->screen_region())
dp->add_redraw_region((*it)->last_screen_region());
dp->add_redraw_region((*it)->screen_region());
}
}
}
return true;
}
bool qdInterfaceScreen::post_redraw() {
for (sorted_element_list_t::iterator it = _sorted_elements.begin(); it != _sorted_elements.end(); ++it)
(*it)->post_redraw();
return true;
}
bool qdInterfaceScreen::quant(float dt) {
debugC(9, kDebugQuant, "qdInterfaceScreen::quant(%f)", dt);
if (_autohide_time > FLT_EPS) {
float delta = dt / _autohide_time;
qdInterfaceDispatcher *dp = dynamic_cast<qdInterfaceDispatcher *>(owner());
if (dp && !dp->is_autohide_enabled())
_autohide_phase -= delta;
else
_autohide_phase += delta;
if (_autohide_phase < 0.0f)
_autohide_phase = 0.0f;
if (_autohide_phase > 1.0f)
_autohide_phase = 1.0f;
int x = round(float(_autohide_offset.x) * _autohide_phase);
int y = round(float(_autohide_offset.y) * _autohide_phase);
g_engine->set_screen_offset(Vect2i(x, y));
} else {
g_engine->set_screen_offset(Vect2i(0, 0));
}
for (element_list_t::const_iterator it = element_list().begin(); it != element_list().end(); ++it) {
if ((*it)->linked_to_option() && qdInterfaceDispatcher::option_value((*it)->option_ID()) != (*it)->option_value())
qdInterfaceDispatcher::set_option_value((*it)->option_ID(), (*it)->option_value());
(*it)->quant(dt);
}
return true;
}
bool qdInterfaceScreen::save_script(Common::WriteStream &fh, int indent) const {
for (int i = 0; i < indent; i++) {
fh.writeString("\t");
}
fh.writeString("<interface_screen");
if (name()) {
fh.writeString(Common::String::format(" name=\"%s\"", qdscr_XML_string(name())));
}
if (_autohide_time > FLT_EPS) {
fh.writeString(Common::String::format(" hide_time=\"%f\"", _autohide_time));
}
if (_autohide_offset.x || _autohide_offset.y) {
fh.writeString(Common::String::format(" hide_offset=\"%d %d\"", _autohide_offset.x, _autohide_offset.y));
}
fh.writeString(">\r\n");
if (has_music_track()) {
_music_track.save_script(fh, indent + 1);
}
for (auto &it : element_list()) {
it->save_script(fh, indent + 1);
}
for (int i = 0; i < indent; i++) {
fh.writeString("\t");
}
fh.writeString("</interface_screen>\r\n");
return true;
}
bool qdInterfaceScreen::load_script(const xml::tag *p) {
for (xml::tag::subtag_iterator it = p->subtags_begin(); it != p->subtags_end(); ++it) {
switch (it->ID()) {
case QDSCR_NAME:
set_name(it->data());
break;
case QDSCR_INTERFACE_ELEMENT:
if (const xml::tag *tg = it->search_subtag(QDSCR_TYPE)) {
if (qdInterfaceElement *el = qdInterfaceElement::create_element(static_cast<qdInterfaceElement::element_type>(xml::tag_buffer(*tg).get_int()))) {
el->set_owner(this);
el->load_script(&*it);
add_element(el);
}
}
break;
case QDSCR_INTERFACE_SCREEN_HIDE_TIME:
xml::tag_buffer(*it) > _autohide_time;
break;
case QDSCR_INTERFACE_SCREEN_HIDE_OFFSET:
xml::tag_buffer(*it) > _autohide_offset.x > _autohide_offset.y;
break;
case QDSCR_MUSIC_TRACK:
_music_track.load_script(&*it);
break;
}
}
return true;
}
bool qdInterfaceScreen::add_element(qdInterfaceElement *p) {
debugC(3, kDebugText, "qdInterfaceScreen::add_element('%s')", transCyrillic(p->name()));
if (_elements.add_object(p)) {
_sorted_elements.push_back(p);
sort_elements();
return true;
}
return false;
}
bool qdInterfaceScreen::rename_element(qdInterfaceElement *p, const char *name) {
return _elements.rename_object(p, name);
}
bool qdInterfaceScreen::remove_element(qdInterfaceElement *p) {
sorted_element_list_t::iterator it = Common::find(_sorted_elements.begin(), _sorted_elements.end(), p);
if (it != _sorted_elements.end())
_sorted_elements.erase(it);
return _elements.remove_object(p);
}
qdInterfaceElement *qdInterfaceScreen::get_element(const char *el_name) {
return _elements.get_object(el_name);
}
bool qdInterfaceScreen::is_element_in_list(const qdInterfaceElement *el) const {
return _elements.is_in_list(el);
}
bool qdInterfaceScreen::mouse_handler(int x, int y, mouseDispatcher::mouseEvent ev) {
debugC(9, kDebugInput, "qdInterfaceScreen::mouse_handler(%d, %d, %u)", x, y, _sorted_elements.size());
if (qdInterfaceDispatcher *dp = dynamic_cast<qdInterfaceDispatcher* >(owner())) {
for (auto &it : _sorted_elements) {
if (it->hit_test(x, y)) {
if (ev != mouseDispatcher::EV_MOUSE_MOVE)
debugC(2, kDebugInput, "qdInterfaceScreen::mouse_handler(): [%d, %d], ev: %d", x, y, ev);
dp->toggle_mouse_hover();
if (it->get_element_type() != qdInterfaceElement::EL_TEXT_WINDOW)
dp->disable_autohide();
if (!it->is_locked() && it->mouse_handler(x, y, ev))
return true;
} else
it->hover_clear();
}
}
return false;
}
bool qdInterfaceScreen::keyboard_handler(Common::KeyCode vkey) {
for (element_list_t::const_iterator it = element_list().begin(); it != element_list().end(); ++it) {
if (!(*it)->is_locked() && (*it)->keyboard_handler(vkey))
return true;
}
return false;
}
bool qdInterfaceScreen::char_input_handler(int vkey) {
for (element_list_t::const_iterator it = element_list().begin(); it != element_list().end(); ++it) {
if (!(*it)->is_locked() && (*it)->char_input_handler(vkey))
return true;
}
return false;
}
qdResource *qdInterfaceScreen::add_resource(const Common::Path &file_name, const qdInterfaceElementState *res_owner) {
if (qdInterfaceDispatcher *dp = dynamic_cast<qdInterfaceDispatcher * >(owner())) {
if (qdResource *p = dp->add_resource(file_name, res_owner)) {
_resources.register_resource(p, res_owner);
if (dp->is_screen_active(this) && !p->is_resource_loaded())
p->load_resource();
return p;
}
}
return NULL;
}
bool qdInterfaceScreen::remove_resource(const Common::Path &file_name, const qdInterfaceElementState *res_owner) {
if (qdInterfaceDispatcher *dp = dynamic_cast<qdInterfaceDispatcher * >(owner())) {
if (qdResource *p = dp->get_resource(file_name)) {
_resources.unregister_resource(p, res_owner);
return dp->remove_resource(file_name, res_owner);
}
}
return false;
}
bool qdInterfaceScreen::init(bool is_game_active) {
for (element_list_t::const_iterator it = element_list().begin(); it != element_list().end(); ++it) {
(*it)->init(is_game_active);
if ((*it)->linked_to_option())
(*it)->set_option_value(qdInterfaceDispatcher::option_value((*it)->option_ID()));
}
build_visible_elements_list();
return true;
}
bool qdInterfaceScreen::hide_element(const char *element_name, bool temporary_hide) {
if (qdInterfaceElement *p = get_element(element_name))
return hide_element(p, temporary_hide);
return false;
}
bool qdInterfaceScreen::hide_element(qdInterfaceElement *p, bool temporary_hide) {
if (!temporary_hide)
p->hide();
sorted_element_list_t::iterator it = Common::find(_sorted_elements.begin(), _sorted_elements.end(), p);
if (it != _sorted_elements.end())
_sorted_elements.erase(it);
return true;
}
bool qdInterfaceScreen::show_element(const char *element_name) {
if (qdInterfaceElement *p = get_element(element_name))
return show_element(p);
return false;
}
bool qdInterfaceScreen::show_element(qdInterfaceElement *p) {
p->show();
debugC(2, kDebugText, "qdInterfaceScreen::show_element('%s')", transCyrillic(p->name()));
sorted_element_list_t::iterator it = Common::find(_sorted_elements.begin(), _sorted_elements.end(), p);
if (it == _sorted_elements.end()) {
_sorted_elements.push_back(p);
sort_elements();
}
return true;
}
bool qdInterfaceScreen::sort_elements() {
Common::sort(_sorted_elements.begin(), _sorted_elements.end(), qdInterfaceElementsOrdering());
return true;
}
bool qdInterfaceScreen::build_visible_elements_list() {
_sorted_elements.clear();
for (element_list_t::const_iterator it = element_list().begin(); it != element_list().end(); ++it) {
if ((*it)->is_visible())
_sorted_elements.push_back(*it);
}
sort_elements();
return true;
}
void qdInterfaceScreen::activate_personage_buttons(const qdNamedObject *p) {
for (element_list_t::const_iterator it = element_list().begin(); it != element_list().end(); ++it) {
if (qdInterfaceButton *bt = dynamic_cast<qdInterfaceButton *>(*it)) {
if (const qdInterfaceEvent *ev = bt->find_event(qdInterfaceEvent::EVENT_ACTIVATE_PERSONAGE)) {
if (p) {
if (ev->has_data() && !strcmp(p->name(), ev->event_data()))
bt->activate_state(1);
else
bt->activate_state(0);
} else
bt->activate_state(0);
}
}
}
}
void qdInterfaceScreen::update_personage_buttons() {
qdGameDispatcher *p = qdGameDispatcher::get_dispatcher();
if (!p) return;
qdGameScene *sp = p->get_active_scene();
if (!sp) return;
for (element_list_t::const_iterator it = element_list().begin(); it != element_list().end(); ++it) {
if (qdInterfaceButton *bt = dynamic_cast<qdInterfaceButton * >(*it)) {
if (!sp->set_personage_button(bt)) {
if (bt->find_event(qdInterfaceEvent::EVENT_ACTIVATE_PERSONAGE))
hide_element(bt);
}
}
}
}
} // namespace QDEngine

View File

@@ -0,0 +1,202 @@
/* 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/>.
*
*/
#ifndef QDENGINE_QDCORE_QD_INTERFACE_SCREEN_H
#define QDENGINE_QDCORE_QD_INTERFACE_SCREEN_H
#include "common/keyboard.h"
#include "qdengine/parser/xml_fwd.h"
#include "qdengine/xmath.h"
#include "qdengine/qdcore/qd_interface_object_base.h"
#include "qdengine/qdcore/qd_object_list_container.h"
#include "qdengine/qdcore/qd_resource_dispatcher.h"
#include "qdengine/qdcore/qd_music_track.h"
#include "qdengine/system/graphics/gr_screen_region.h"
#include "qdengine/system/input/mouse_input.h"
namespace QDEngine {
class qdInterfaceElement;
class qdInterfaceElementState;
//! Интерфейсный экран.
class qdInterfaceScreen : public qdInterfaceObjectBase {
public:
qdInterfaceScreen();
~qdInterfaceScreen();
//! Запись данных в скрипт.
bool save_script(Common::WriteStream &fh, int indent = 0) const;
//! Загрузка данных из скрипта.
bool load_script(const xml::tag *p);
//! Отрисовка экрана.
bool redraw(int dx = 0, int dy = 0) const;
bool pre_redraw(bool force_full_redraw = false);
bool post_redraw();
//! Обсчет логики, параметр - время в секундах.
bool quant(float dt);
//! Добавление элемента.
bool add_element(qdInterfaceElement *p);
//! Изменение имени элемента.
bool rename_element(qdInterfaceElement *p, const char *name);
//! Удаление элемента из списка.
bool remove_element(qdInterfaceElement *p);
//! Поиск элемента по имени.
qdInterfaceElement *get_element(const char *el_name);
//! Возвращает true, если элемент есть в списке.
bool is_element_in_list(const qdInterfaceElement *el) const;
typedef Std::list<qdInterfaceElement *> element_list_t;
//! Возвращает список элементов экрана.
const element_list_t &element_list() const {
return _elements.get_list();
}
//! Обработчик событий мыши.
bool mouse_handler(int x, int y, mouseDispatcher::mouseEvent ev);
//! Обработчик ввода с клавиатуры.
bool keyboard_handler(Common::KeyCode vkey);
bool char_input_handler(int vkey);
//! Добавляет ресурс file_name с владельцем owner.
qdResource *add_resource(const Common::Path &file_name, const qdInterfaceElementState *res_owner);
//! Удаляет ресурс file_name с владельцем owner.
bool remove_resource(const Common::Path &file_name, const qdInterfaceElementState *res_owner);
//! Возвращает true, если на ресурс есть ссылки.
bool has_references(const qdResource *p) const {
return _resources.is_registered(p);
}
//! Прячет элемент.
bool hide_element(const char *element_name, bool temporary_hide = true);
//! Прячет элемент.
bool hide_element(qdInterfaceElement *p, bool temporary_hide = true);
//! Показывает элемент.
bool show_element(const char *element_name);
//! Показывает элемент.
bool show_element(qdInterfaceElement *p);
//! Инициализация экрана при заходе на него.
bool init(bool is_game_active = true);
//! Строит сортированный список видимых элементов.
bool build_visible_elements_list();
//! Устанавливает, что ресурсы экрана не надо выгружать при выходе с него.
void lock_resources() {
_is_locked = true;
}
//! Устанавливает, что ресурсы экрана надо выгружать при выходе с него.
void unlock_resources() {
_is_locked = false;
}
//! Возвращает true, если ресурсы экрана не надо выгружать при выходе с него.
bool is_locked() const {
return _is_locked;
}
//! Включает кнопки, связанные с персонажем p, выключает кнопки остальных персонажей.
void activate_personage_buttons(const qdNamedObject *p);
void update_personage_buttons();
float autohide_time() const {
return _autohide_time;
}
void set_autohide_time(float time) {
_autohide_time = time;
}
const Vect2i &autohide_offset() const {
return _autohide_offset;
}
void set_autohide_offset(const Vect2i &offset) {
_autohide_offset = offset;
}
//! Фоновая музыка.
const qdMusicTrack &music_track() const {
return _music_track;
}
//! Устанавливает фоновую музыку.
void set_music_track(const qdMusicTrack &track) {
_music_track = track;
}
//! Возвращает true, если экрану задана фоновая музыка.
bool has_music_track() const {
return _music_track.has_file_name();
}
void set_autohide_phase(float ph) {
_autohide_phase = ph;
}
// Указатель на объект, последним вызвавший данный как модальный экран
qdInterfaceObjectBase *modal_caller() {
return _modal_caller;
}
void set_modal_caller(qdInterfaceObjectBase *caller) {
_modal_caller = caller;
}
private:
//! Список интерфейсных элементов экрана.
qdObjectListContainer<qdInterfaceElement> _elements;
//! Ресурсы, на которые ссылается экран.
qdResourceDispatcher<qdInterfaceElementState> _resources;
typedef Std::vector<qdInterfaceElement *> sorted_element_list_t;
//! Отсортированный по глубине список элементов экрана.
sorted_element_list_t _sorted_elements;
//! Сортирует элементы по глубине.
bool sort_elements();
//! true, если ресурсы экрана не надо выгружать при выходе с него.
bool _is_locked;
//! Время всплывания экрана в секундах.
float _autohide_time;
//! Смещение экрана, когда он минимизирован.
Vect2i _autohide_offset;
//! Текущая фаза всплывания экрана.
float _autohide_phase;
//! Фоновая музыка.
qdMusicTrack _music_track;
//! Указатель на объект, который последним вызвал данный экран в качестве модального
qdInterfaceObjectBase *_modal_caller;
};
} // namespace QDEngine
#endif // QDENGINE_QDCORE_QD_INTERFACE_SCREEN_H

View File

@@ -0,0 +1,341 @@
/* 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 "qdengine/qd_fwd.h"
#include "qdengine/parser/qdscr_parser.h"
#include "qdengine/parser/xml_tag_buffer.h"
#include "qdengine/qdcore/qd_interface_slider.h"
namespace QDEngine {
qdInterfaceSlider::qdInterfaceSlider() : _active_rectangle(0, 0),
_phase(0.5f),
_orientation(SL_HORIZONTAL),
_track_mouse(false),
_background_offset(Vect2i(0, 0)) {
_inverse_direction = false;
_background.set_owner(this);
_slider.set_owner(this);
}
qdInterfaceSlider::qdInterfaceSlider(const qdInterfaceSlider &sl) : qdInterfaceElement(sl),
_active_rectangle(sl._active_rectangle),
_phase(sl._phase),
_orientation(sl._orientation),
_inverse_direction(sl._inverse_direction),
_track_mouse(false) {
_background.set_owner(this);
_slider.set_owner(this);
_background = sl._background;
_background_offset = sl._background_offset;
_slider = sl._slider;
}
qdInterfaceSlider::~qdInterfaceSlider() {
_background.unregister_resources();
_slider.unregister_resources();
}
qdInterfaceSlider &qdInterfaceSlider::operator = (const qdInterfaceSlider &sl) {
if (this == &sl) return *this;
*static_cast<qdInterfaceElement *>(this) = sl;
_background = sl._background;
_background_offset = sl._background_offset;
_slider = sl._slider;
_active_rectangle = sl._active_rectangle;
_phase = sl._phase;
_orientation = sl._orientation;
_inverse_direction = sl._inverse_direction;
_track_mouse = false;
return *this;
}
bool qdInterfaceSlider::mouse_handler(int x, int y, mouseDispatcher::mouseEvent ev) {
x -= r().x;
y -= r().y;
Vect2i rect(_active_rectangle);
if (!_slider_animation.is_empty()) {
if (rect.x < _slider_animation.size_x())
rect.x = _slider_animation.size_x();
if (rect.y < _slider_animation.size_y())
rect.y = _slider_animation.size_y();
}
switch (ev) {
case mouseDispatcher::EV_LEFT_DOWN:
if (x >= -rect.x / 2 && x < rect.x / 2 && y >= -rect.y / 2 && y < rect.y / 2) {
set_phase(offset2phase(Vect2i(x, y)));
_track_mouse = true;
return true;
}
break;
case mouseDispatcher::EV_MOUSE_MOVE:
if (_track_mouse) {
if (mouseDispatcher::instance()->is_pressed(mouseDispatcher::ID_BUTTON_LEFT)) {
set_phase(offset2phase(Vect2i(x, y)));
return true;
} else
_track_mouse = false;
}
break;
default:
break;
}
return false;
}
bool qdInterfaceSlider::keyboard_handler(Common::KeyCode vkey) {
return false;
}
int qdInterfaceSlider::option_value() const {
return round(_phase * 255.0f);
}
bool qdInterfaceSlider::set_option_value(int value) {
set_phase(float(value) / 255.0f);
return true;
}
bool qdInterfaceSlider::init(bool is_game_active) {
set_state(&_background);
set_slider_animation(_slider.animation());
_track_mouse = false;
return true;
}
bool qdInterfaceSlider::save_script_body(Common::WriteStream &fh, int indent) const {
if (!_background.save_script(fh, indent)) {
return false;
}
if (!_slider.save_script(fh, indent)) {
return false;
}
if (_active_rectangle.x || _active_rectangle.y) {
for (int i = 0; i <= indent; i++) {
fh.writeString("\t");
}
fh.writeString(Common::String::format("<slider_rect>%d %d</slider_rect>\r\n", _active_rectangle.x, _active_rectangle.y));
}
if (_background_offset.x || _background_offset.y) {
for (int i = 0; i <= indent; i++) {
fh.writeString("\t");
}
fh.writeString(Common::String::format("<background_offset>%d %d</background_offset>\r\n", _background_offset.x, _background_offset.y));
}
if (_orientation != SL_HORIZONTAL) {
for (int i = 0; i <= indent; i++) {
fh.writeString("\t");
}
fh.writeString(Common::String::format("<slider_orientation>%d</slider_orientation>\r\n", int(_orientation)));
}
if (_inverse_direction) {
for (int i = 0; i <= indent; i++) {
fh.writeString("\t");
}
fh.writeString("<inverse_direction>1</inverse_direction>\r\n");
}
return true;
}
bool qdInterfaceSlider::load_script_body(const xml::tag *p) {
bool background_flag = false;
for (xml::tag::subtag_iterator it = p->subtags_begin(); it != p->subtags_end(); ++it) {
switch (it->ID()) {
case QDSCR_INTERFACE_ELEMENT_STATE:
if (!background_flag) {
if (!_background.load_script(&*it)) return false;
background_flag = true;
} else {
if (!_slider.load_script(&*it)) return false;
}
break;
case QDSCR_INTERFACE_SLIDER_RECTANGLE:
xml::tag_buffer(*it) > _active_rectangle.x > _active_rectangle.y;
break;
case QDSCR_INTERFACE_SLIDER_ORIENTATION: {
int v;
xml::tag_buffer(*it) > v;
_orientation = orientation_t(v);
}
break;
case QDSCR_INVERSE_DIRECTION:
_inverse_direction = xml::tag_buffer(*it).get_int() != 0;
break;
case QDSCR_INTERFACE_BACKGROUND_OFFSET:
xml::tag_buffer(*it) > _background_offset.x > _background_offset.y;
break;
}
}
return true;
}
bool qdInterfaceSlider::redraw() const {
Vect2i rr = r() + _background_offset;
animation().redraw(rr.x, rr.y, 0);
if (!_slider_animation.is_empty()) {
rr = r() + phase2offset(_phase);
_slider_animation.redraw(rr.x, rr.y, 0);
}
return true;
}
int qdInterfaceSlider::size_x() const {
int x = _active_rectangle.x;
if (!_slider_animation.is_empty()) {
if (x < _slider_animation.size_x())
x = _slider_animation.size_x();
}
return x;
}
int qdInterfaceSlider::size_y() const {
int y = _active_rectangle.y;
if (!_slider_animation.is_empty()) {
if (y < _slider_animation.size_y())
y = _slider_animation.size_y();
}
return y;
}
grScreenRegion qdInterfaceSlider::screen_region() const {
if (!_slider_animation.is_empty()) {
grScreenRegion reg = qdInterfaceElement::screen_region();
reg.move(_background_offset.x, _background_offset.y);
Vect2i rr = r() + phase2offset(_phase);
grScreenRegion reg1 = _slider_animation.screen_region();
reg1.move(rr.x, rr.y);
reg += reg1;
return reg;
} else
return qdInterfaceElement::screen_region();
}
bool qdInterfaceSlider::set_slider_animation(const qdAnimation *anm, int anm_flags) {
if (anm) {
anm->create_reference(&_slider_animation);
if (anm_flags & QD_ANIMATION_FLAG_LOOP)
_slider_animation.set_flag(QD_ANIMATION_FLAG_LOOP);
if (anm_flags & QD_ANIMATION_FLAG_FLIP_HORIZONTAL)
_slider_animation.set_flag(QD_ANIMATION_FLAG_FLIP_HORIZONTAL);
if (anm_flags & QD_ANIMATION_FLAG_FLIP_VERTICAL)
_slider_animation.set_flag(QD_ANIMATION_FLAG_FLIP_VERTICAL);
_slider_animation.start();
} else
_slider_animation.clear();
return true;
}
Vect2i qdInterfaceSlider::phase2offset(float ph) const {
if (ph < 0.0f) ph = 0.0f;
if (ph > 1.0f) ph = 1.0f;
Vect2i offs(0, 0);
if (_inverse_direction)
ph = 1.0f - ph;
switch (_orientation) {
case SL_HORIZONTAL:
offs.x = int(ph * _active_rectangle.x) - _active_rectangle.x / 2;
break;
case SL_VERTICAL:
offs.y = _active_rectangle.y / 2 - int(ph * _active_rectangle.y);
break;
}
return offs;
}
float qdInterfaceSlider::offset2phase(const Vect2i &offs) const {
float ph = 0.0f;
switch (_orientation) {
case SL_HORIZONTAL:
if (!_active_rectangle.x) return 0.0f;
ph = float(offs.x + _active_rectangle.x / 2) / float(_active_rectangle.x);
break;
case SL_VERTICAL:
if (!_active_rectangle.y) return 0.0f;
ph = float(-offs.y + _active_rectangle.y / 2) / float(_active_rectangle.y);
break;
}
if (ph < 0.0f) ph = 0.0f;
if (ph > 1.0f) ph = 1.0f;
if (_inverse_direction)
ph = 1.0f - ph;
return ph;
}
bool qdInterfaceSlider::hit_test(int x, int y) const {
x -= r().x;
y -= r().y;
Vect2i rect(_active_rectangle);
if (!_slider_animation.is_empty()) {
if (rect.x < _slider_animation.size_x())
rect.x = _slider_animation.size_x();
if (rect.y < _slider_animation.size_y())
rect.y = _slider_animation.size_y();
}
if (x >= -rect.x / 2 && x < rect.x / 2 && y >= -rect.y / 2 && y < rect.y / 2)
return true;
return false;
}
} // namespace QDEngine

View File

@@ -0,0 +1,181 @@
/* 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/>.
*
*/
#ifndef QDENGINE_QDCORE_QD_INTERFACE_SLIDER_H
#define QDENGINE_QDCORE_QD_INTERFACE_SLIDER_H
#include "qdengine/parser/xml_fwd.h"
#include "qdengine/qdcore/qd_interface_element.h"
#include "qdengine/qdcore/qd_interface_element_state.h"
namespace QDEngine {
//! Интерфейсный элемент - слайдер.
/**
Состоит из двух частей - неподвижной подложки и "ползунка".
Подложка не обязательна, она может быть нарисована прямо на фоне.
*/
class qdInterfaceSlider : public qdInterfaceElement {
public:
//! Ориентация слайдера (по-умолчанию - горизонтально).
enum orientation_t {
SL_HORIZONTAL,
SL_VERTICAL
};
qdInterfaceSlider();
qdInterfaceSlider(const qdInterfaceSlider &sl);
~qdInterfaceSlider();
qdInterfaceSlider &operator = (const qdInterfaceSlider &sl);
//! Возвращает тип элемента.
qdInterfaceElement::element_type get_element_type() const {
return qdInterfaceElement::EL_SLIDER;
}
//! Обработчик событий мыши.
bool mouse_handler(int x, int y, mouseDispatcher::mouseEvent ev);
//! Обработчик ввода с клавиатуры.
bool keyboard_handler(Common::KeyCode vkey);
int option_value() const;
bool set_option_value(int value);
//! Инициализация элемента.
/**
Вызывается каждый раз при заходе на экран, которому принадлежит элемент.
*/
bool init(bool is_game_active = true);
//! Отрисовка элемента.
bool redraw() const;
grScreenRegion screen_region() const;
//! Возвращает размер элемента по горизонтали в пикселах.
int size_x() const;
//! Возвращает размер элемента по вертикали в пикселах.
int size_y() const;
bool inverse_direction() const {
return _inverse_direction;
}
void set_inverse_direction(bool state) {
_inverse_direction = state;
}
const Vect2i &active_rectangle() const {
return _active_rectangle;
}
void set_active_rectangle(const Vect2i &rect) {
_active_rectangle = rect;
}
const qdInterfaceElementState *background() const {
return &_background;
}
void update_background(const qdInterfaceElementState &st) {
_background = st;
}
const Vect2i &background_offset() const {
return _background_offset;
}
void set_background_offset(const Vect2i &offs) {
_background_offset = offs;
}
const qdInterfaceElementState *slider() const {
return &_slider;
}
void update_slider(const qdInterfaceElementState &st) {
_slider = st;
}
float phase() const {
return _phase;
}
void set_phase(float ph) {
_phase = ph;
}
orientation_t orientation() const {
return _orientation;
}
void set_orientation(orientation_t v) {
_orientation = v;
}
//! Возвращает true, если точка с экранными координатами (x,у) попадает в элемент.
bool hit_test(int x, int y) const;
protected:
//! Запись данных в скрипт.
bool save_script_body(Common::WriteStream &fh, int indent = 0) const;
//! Загрузка данных из скрипта.
bool load_script_body(const xml::tag *p);
private:
//! Подложка под ползунком.
qdInterfaceElementState _background;
/// Смещение подложки относительно центра ползунка.
Vect2i _background_offset;
//! Ползунок.
qdInterfaceElementState _slider;
//! Анимация ползунка.
qdAnimation _slider_animation;
//! Прямоугольник, внутри которого ездит ползунок.
/**
По нему же отслеживаются мышиные клики.
Отсчитывается от центра элемента, в экранных координатах.
*/
Vect2i _active_rectangle;
//! Текущая фаза, диапазон значений - [0.0, 1.0].
float _phase;
//! Ориентация - по горизонтали или по вертикали.
orientation_t _orientation;
bool _inverse_direction;
//! true, если слайдер следит за перемещениями мышиного курсора
bool _track_mouse;
bool set_slider_animation(const qdAnimation *anm, int anm_flags = 0);
//! Пересчет фазы в смещение от центра элемента в экранных координатах.
Vect2i phase2offset(float ph) const;
//! Пересчет смещения от центра элемента в экранных координатах в фазу.
float offset2phase(const Vect2i &offs) const;
};
} // namespace QDEngine
#endif // QDENGINE_QDCORE_QD_INTERFACE_SLIDER_H

View File

@@ -0,0 +1,715 @@
/* 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 "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_game_dispatcher.h"
#include "qdengine/qdcore/qd_setup.h"
#include "qdengine/qdcore/qd_interface_screen.h"
#include "qdengine/qdcore/qd_interface_text_window.h"
#include "qdengine/qdcore/qd_interface_slider.h"
namespace QDEngine {
const float qdInterfaceCaretPeriod = 0.3f;
bool qdInterfaceTextWindow::_caretVisible = false;
float qdInterfaceTextWindow::_caretTimer = 0.f;
qdInterfaceTextWindow::qdInterfaceTextWindow() : _text_size(0, 0),
_scrolling_speed(0),
_text_set(NULL),
_text_set_id(qdGameDispatcher::TEXT_SET_DIALOGS),
_slider(NULL),
_windowType(WINDOW_DIALOGS),
_infoType(INFO_NONE) {
_textFormat.toggle_global_depend(false);
_inputStringLimit = 0;
_scrolling_position = 0;
_text_set_position = 0;
_textVAlign = VALIGN_BOTTOM;
_background_color = 0;
_has_background_color = false;
_background_alpha = 0;
_isEditing = false;
_caretPose = -1;
_playerID = 0;
_border_background.set_owner(this);
}
qdInterfaceTextWindow::qdInterfaceTextWindow(const qdInterfaceTextWindow &wnd) : qdInterfaceElement(wnd),
_text_size(wnd._text_size),
_scrolling_speed(wnd._scrolling_speed),
_text_set(wnd._text_set),
_text_set_id(wnd._text_set_id),
_slider_name(wnd._slider_name),
_slider(wnd._slider),
_windowType(wnd._windowType),
_infoType(wnd._infoType),
_playerID(wnd._playerID),
_inputString(wnd._inputString),
_inputStringBackup(wnd._inputStringBackup),
_inputStringLimit(wnd._inputStringLimit),
_textFormat(wnd._textFormat),
_textVAlign(wnd._textVAlign) {
_scrolling_position = 0;
_text_set_position = 0;
_background_color = wnd._background_color;
_has_background_color = wnd._has_background_color;
_background_alpha = wnd._background_alpha;
_isEditing = wnd._isEditing;
_caretPose = wnd._caretPose;
_border_background.set_owner(this);
_border_background = wnd._border_background;
}
qdInterfaceTextWindow &qdInterfaceTextWindow::operator = (const qdInterfaceTextWindow &wnd) {
if (this == &wnd) return *this;
*static_cast<qdInterfaceElement *>(this) = wnd;
_text_size = wnd._text_size;
_scrolling_speed = wnd._scrolling_speed;
_text_set = wnd._text_set;
_text_set_id = wnd._text_set_id;
_slider_name = wnd._slider_name;
_slider = wnd._slider;
_windowType = wnd._windowType;
_infoType = wnd._infoType;
_playerID = wnd._playerID;
_inputString = wnd._inputString;
_inputStringBackup = wnd._inputStringBackup;
_inputStringLimit = wnd._inputStringLimit;
_textFormat = wnd._textFormat;
_textVAlign = wnd._textVAlign;
_isEditing = wnd._isEditing;
_caretPose = wnd._caretPose;
_border_background = wnd._border_background;
_background_color = wnd._background_color;
return *this;
}
qdInterfaceTextWindow::~qdInterfaceTextWindow() {
_border_background.unregister_resources();
}
bool qdInterfaceTextWindow::mouse_handler(int x, int y, mouseDispatcher::mouseEvent ev) {
if (!_text_set) return false;
if (_windowType == WINDOW_DIALOGS) {
if (ev == mouseDispatcher::EV_LEFT_DOWN) {
if (qdScreenText * p = _text_set->get_text(x, y)) {
if (qdGameDispatcher * dp = qdGameDispatcher::get_dispatcher()) {
dp->set_flag(qdGameDispatcher::DIALOG_CLICK_FLAG);
dp->set_mouse_click_state(p->owner());
debugC(2, kDebugText, "qdInterfaceTextWindow::mouse_handler(): click [%d, %d], ev: %d", x, y, ev);
}
return true;
}
} else if (ev == mouseDispatcher::EV_MOUSE_MOVE) {
_text_set->clear_hover_mode();
if (qdScreenText *p = _text_set->get_text(x, y))
p->set_hover_mode(true);
}
}
return false;
}
bool qdInterfaceTextWindow::keyboard_handler(Common::KeyCode vkey) {
if (_windowType == WINDOW_EDIT && _isEditing)
return edit_input(vkey);
return false;
}
bool qdInterfaceTextWindow::char_input_handler(int input) {
bool ret = qdInterfaceElement::char_input_handler(input);
if (_windowType == WINDOW_EDIT && _isEditing) {
if (!_inputStringLimit || (int)_inputString.size() < _inputStringLimit) {
if (Common::isPrint(input) || input == '_' || input == '-' || input == ' ') {
_inputString.insertChar(input, _caretPose++);
return true;
}
}
}
return ret;
}
void qdInterfaceTextWindow::hover_clear() {
if (_windowType == WINDOW_DIALOGS) {
if (_text_set)
_text_set->clear_hover_mode();
}
}
bool qdInterfaceTextWindow::init(bool is_game_active) {
set_state(&_border_background);
if (_windowType == WINDOW_DIALOGS) {
if (!_text_set)
_text_set = qdGameDispatcher::get_dispatcher()->screen_texts_dispatcher().get_text_set(_text_set_id);
if (!_slider) {
if (qdInterfaceScreen * p = static_cast<qdInterfaceScreen * >(owner()))
_slider = dynamic_cast<qdInterfaceSlider * >(p->get_element(slider_name()));
}
if (_text_set) {
_text_set->set_max_text_width(_text_size.x);
update_text_position();
}
} else {
qdGameDispatcher *dp = qdGameDispatcher::get_dispatcher();
switch (_infoType) {
case INFO_PLAYER_NAME:
_inputString = dp->hall_of_fame_player_name(_playerID);
if (dp->is_hall_of_fame_updated(_playerID) && !_isEditing)
edit_start();
break;
case INFO_PLAYER_SCORE:
if (dp->hall_of_fame_player_score(_playerID)) {
Common::String buf;
buf += Common::String::format("%d", dp->hall_of_fame_player_score(_playerID));
_inputString = buf.c_str();
} else
_inputString = "";
break;
default:
break;
}
if (_windowType == WINDOW_TEXT)
set_input_string(input_string());
}
return true;
}
bool qdInterfaceTextWindow::save_script_body(Common::WriteStream &fh, int indent) const {
if (_border_background.has_animation()) {
for (int i = 0; i <= indent; i++) {
fh.writeString("\t");
}
fh.writeString(Common::String::format("<border_back>%s</border_back>\r\n", qdscr_XML_string(_border_background.animation_file().toString('\\'))));
}
if (!_slider_name.empty()) {
for (int i = 0; i <= indent; i++) {
fh.writeString("\t");
}
fh.writeString(Common::String::format("<window_slider>%s</window_slider>\r\n", qdscr_XML_string(_slider_name.c_str())));
}
if (_text_size.x || _text_size.y) {
for (int i = 0; i <= indent; i++) {
fh.writeString("\t");
}
fh.writeString(Common::String::format("<screen_size>%d %d</screen_size>\r\n", _text_size.x, _text_size.y));
}
if (_background_color) {
for (int i = 0; i <= indent; i++) {
fh.writeString("\t");
}
fh.writeString(Common::String::format("<color>%u</color>\r\n", _background_color));
}
if (_has_background_color) {
for (int i = 0; i <= indent; i++) {
fh.writeString("\t");
}
fh.writeString("<enable_background>1</enable_background>\r\n");
}
if (_background_alpha) {
for (int i = 0; i <= indent; i++) {
fh.writeString("\t");
}
fh.writeString(Common::String::format("<alpha>%d</alpha>\r\n", _background_alpha));
}
if (_windowType != WINDOW_DIALOGS) {
for (int i = 0; i <= indent; i++) {
fh.writeString("\t");
}
fh.writeString(Common::String::format("<window_type>%d</window_type>\r\n", (int)_windowType));
}
if (_infoType != INFO_NONE) {
for (int i = 0; i <= indent; i++) {
fh.writeString("\t");
}
fh.writeString(Common::String::format("<info_type>%d</info_type>\r\n", (int)_infoType));
}
if (_playerID) {
for (int i = 0; i <= indent; i++) {
fh.writeString("\t");
}
fh.writeString(Common::String::format("<player_id>%d</player_id>\r\n", _playerID));
}
if (!_inputString.empty()) {
for (int i = 0; i <= indent; i++) {
fh.writeString("\t");
}
fh.writeString(Common::String::format("<text>%s</text>\r\n", qdscr_XML_string(_inputString.c_str())));
}
if (_inputStringLimit) {
for (int i = 0; i <= indent; i++) {
fh.writeString("\t");
}
fh.writeString(Common::String::format("<string_length>%d</string_length>\r\n", _inputStringLimit));
}
for (int i = 0; i <= indent; i++) {
fh.writeString("\t");
}
fh.writeString(Common::String::format("<valign>%d</valign>\r\n", _textVAlign));
if (_textFormat != qdScreenTextFormat::default_format()) {
_textFormat.save_script(fh, indent + 1);
}
return true;
}
bool qdInterfaceTextWindow::load_script_body(const xml::tag *p) {
for (xml::tag::subtag_iterator it = p->subtags_begin(); it != p->subtags_end(); ++it) {
switch (it->ID()) {
case QDSCR_TEXT_WINDOW_BORDER_BACK:
set_border_background_file(Common::Path(it->data(), '\\'));
break;
case QDSCR_TEXT_WINDOW_SLIDER:
set_slider_name(it->data());
break;
case QDSCR_SCREEN_SIZE:
xml::tag_buffer(*it) > _text_size.x > _text_size.y;
break;
case QDSCR_COLOR:
xml::tag_buffer(*it) > _background_color;
break;
case QDSCR_ALPHA:
xml::tag_buffer(*it) > _background_alpha;
break;
case QDSCR_ENABLE_BACKGROUND:
_has_background_color = xml::tag_buffer(*it).get_int() != 0;
break;
case QDSCR_TEXT_WINDOW_TYPE:
_windowType = WindowType(xml::tag_buffer(*it).get_int());
break;
case QDSCR_TEXT_WINDOW_INFO_TYPE:
_infoType = InfoType(xml::tag_buffer(*it).get_int());
break;
case QDSCR_TEXT_WINDOW_PLAYER_ID:
xml::tag_buffer(*it) > _playerID;
break;
case QDSCR_SCREEN_TEXT_FORMAT:
_textFormat.load_script(&*it);
_textFormat.toggle_global_depend(false);
break;
case QDSCR_TEXT_WINDOW_MAX_STRING_LENGTH:
xml::tag_buffer(*it) > _inputStringLimit;
break;
case QDSCR_TEXT:
_inputString = it->data();
break;
case QDSCR_VALIGN:
_textVAlign = TextVAlign(xml::tag_buffer(*it).get_int());
break;
}
}
return true;
}
bool qdInterfaceTextWindow::redraw() const {
qdInterfaceElement::redraw();
if (_windowType == WINDOW_DIALOGS) {
debugC(3, kDebugText, "qdInterfaceTextWindow::redraw(): DIALOGS");
if (_text_set) {
debugC(3, kDebugText, "qdInterfaceTextWindow::redraw(): text_set");
int l_clip, t_clip, r_clip, b_clip;
grDispatcher::instance()->getClip(l_clip, t_clip, r_clip, b_clip);
Vect2i ar = r();
grDispatcher::instance()->limitClip(ar.x - _text_size.x / 2, ar.y - _text_size.y / 2, ar.x + _text_size.x / 2, ar.y + _text_size.y / 2);
if (_has_background_color) {
Vect2i text_r = _text_set->screen_pos();
Vect2i text_sz = _text_set->screen_size();
if (!_background_alpha)
grDispatcher::instance()->rectangle(ar.x - _text_size.x / 2, text_r.y - text_sz.y / 2, _text_size.x, text_sz.y, _background_color, _background_color, GR_FILLED);
else
grDispatcher::instance()->rectangleAlpha(ar.x - _text_size.x / 2, text_r.y - text_sz.y / 2, _text_size.x, text_sz.y, _background_color, _background_alpha);
}
_text_set->redraw();
grDispatcher::instance()->setClip(l_clip, t_clip, r_clip, b_clip);
if (g_engine->_debugDraw)
grDispatcher::instance()->rectangle(ar.x - _text_size.x / 2, ar.y - _text_size.y / 2, _text_size.x, _text_size.y, 0xFFFFFF, 0, GR_OUTLINED, 3);
}
} else if (_windowType == WINDOW_EDIT || _windowType == WINDOW_TEXT) {
debugC(3, kDebugText, "qdInterfaceTextWindow::redraw(): text_edit (%d)", _windowType);
if (_has_background_color) {
Vect2i ar = r();
if (!_background_alpha)
grDispatcher::instance()->rectangle(ar.x - _text_size.x / 2, ar.y - _text_size.y / 2, _text_size.x, _text_size.y, _background_color, _background_color, GR_FILLED);
else
grDispatcher::instance()->rectangleAlpha(ar.x - _text_size.x / 2, ar.y - _text_size.y / 2, _text_size.x, _text_size.y, _background_color, _background_alpha);
}
text_redraw();
}
return true;
}
bool qdInterfaceTextWindow::need_redraw() const {
if (qdInterfaceElement::need_redraw())
return true;
if (_windowType == WINDOW_DIALOGS) {
if (_text_set && _text_set->need_redraw())
return true;
if (_slider && _slider->need_redraw())
return true;
} else if (_windowType == WINDOW_EDIT || _windowType == WINDOW_TEXT)
return true;
return false;
}
bool qdInterfaceTextWindow::quant(float dt) {
qdInterfaceElement::quant(dt);
debugC(1, kDebugText, "*********** qdInterfaceTextWindow::quant()");
debugC(1, kDebugQuant, "*********** qdInterfaceTextWindow::quant()");
if (_isEditing) {
_caretTimer -= dt;
if (_caretTimer < 0.f) {
_caretVisible = !_caretVisible;
_caretTimer = qdInterfaceCaretPeriod;
}
}
if (_windowType == WINDOW_DIALOGS) {
debugC(2, kDebugText, "** qdInterfaceTextWindow::quant(): DIALOGS");
if (is_visible() && _text_set && _text_set->was_changed())
update_text_position();
if (_text_set) {
debugC(2, kDebugText,"** qdInterfaceTextWindow::quant(): text_set, id: %d", _text_set->ID());
if (fabs(_scrolling_position) > FLT_EPS) {
float delta = _scrolling_speed * dt;
if (fabs(_scrolling_position) > delta)
_scrolling_position += (_scrolling_position > 0) ? -delta : delta;
else
_scrolling_position = 0;
Vect2i pos = _text_set->screen_pos();
pos.y = _text_set_position + round(_scrolling_position);
_text_set->set_screen_pos(pos);
}
if (_text_set->is_empty()) {
debugC(2, kDebugText,"** qdInterfaceTextWindow::quant(): text_set->empty, vis: %d", is_visible());
if (is_visible()) {
hide();
if (qdInterfaceScreen * sp = dynamic_cast<qdInterfaceScreen * >(owner()))
sp->build_visible_elements_list();
qdGameDispatcher::get_dispatcher()->toggle_full_redraw();
}
} else {
debugC(2, kDebugText, "** qdInterfaceTextWindow::quant(): text_set->empty NOT, vis: %d", is_visible());
if (!is_visible()) {
show();
if (qdInterfaceScreen * sp = dynamic_cast<qdInterfaceScreen * >(owner()))
sp->build_visible_elements_list();
}
}
if (_slider) {
if (!is_visible() || _text_size.y > _text_set->screen_size().y) {
if (_slider->is_visible()) {
_slider->hide();
if (qdInterfaceScreen * sp = dynamic_cast<qdInterfaceScreen * >(owner()))
sp->build_visible_elements_list();
qdGameDispatcher::get_dispatcher()->toggle_full_redraw();
}
} else {
if (!_slider->is_visible()) {
_slider->show();
if (qdInterfaceScreen * sp = dynamic_cast<qdInterfaceScreen * >(owner()))
sp->build_visible_elements_list();
}
}
}
}
}
return true;
}
void qdInterfaceTextWindow::update_text_position() {
if (_text_set) {
Vect2f rr = r() - text_size() / 2 + _text_set->screen_size() / 2;
switch (_textVAlign) {
case VALIGN_CENTER:
rr.y += (float)(text_size().y - _text_set->screen_size().y) / 2.0;
break;
case VALIGN_BOTTOM:
rr.y += text_size().y - _text_set->screen_size().y;
break;
default:
break;
}
_text_set->set_screen_pos(rr);
_text_set_position = rr.y;
set_scrolling(_text_set->new_texts_height());
_text_set->clear_new_texts_height();
_text_set->toggle_changed(false);
}
}
bool qdInterfaceTextWindow::hit_test(int x, int y) const {
x -= r().x;
y -= r().y;
if (x >= -size_x() / 2 && x < size_x() / 2 && y >= -size_y() / 2 && y < size_y() / 2)
return true;
return false;
}
grScreenRegion qdInterfaceTextWindow::screen_region() const {
return grScreenRegion(r().x, r().y, size_x(), size_y());
}
int qdInterfaceTextWindow::size_x() const {
return (qdInterfaceElement::size_x() > _text_size.x) ? qdInterfaceElement::size_x() : _text_size.x;
}
int qdInterfaceTextWindow::size_y() const {
return (qdInterfaceElement::size_y() > _text_size.y) ? qdInterfaceElement::size_y() : _text_size.y;
}
void qdInterfaceTextWindow::set_scrolling(int y_delta) {
if (_scrolling_speed > FLT_EPS)
_scrolling_position = y_delta;
else
_scrolling_position = 0;
}
void qdInterfaceTextWindow::text_redraw() const {
Vect2i ar = r() - _text_size / 2;
uint32 col = _textFormat.color();
const grFont *font = qdGameDispatcher::get_dispatcher()->
find_font(_textFormat.font_type());
if (_windowType == WINDOW_EDIT && _isEditing) {
int sz0 = grDispatcher::instance()->textWidth("|", 0, font);
int sz1 = grDispatcher::instance()->textWidth(_inputString.c_str(), 0, font);
int x0 = ar.x;
switch (_textFormat.alignment()) {
case qdScreenTextFormat::ALIGN_LEFT:
break;
case qdScreenTextFormat::ALIGN_CENTER:
x0 += (_text_size.x - sz0 - sz1) / 2;
break;
case qdScreenTextFormat::ALIGN_RIGHT:
x0 += _text_size.x - sz0 - sz1;
break;
}
Common::String str = _inputString.substr(0, _caretPose);
if (!str.empty()) {
grDispatcher::instance()->drawAlignedText(x0, ar.y, _text_size.x, _text_size.y, col, str.c_str(), GR_ALIGN_LEFT, 0, 0, font);
x0 += grDispatcher::instance()->textWidth(str.c_str(), 0, font);
}
if (_caretVisible)
grDispatcher::instance()->drawAlignedText(x0, ar.y, _text_size.x, _text_size.y, col, "|", GR_ALIGN_LEFT, 0, 0, font);
x0 += grDispatcher::instance()->textWidth("|", 0, font);
str = _inputString.substr(_caretPose, Common::String::npos);
if (!str.empty())
grDispatcher::instance()->drawAlignedText(x0, ar.y, _text_size.x, _text_size.y, col, str.c_str(), GR_ALIGN_LEFT, 0, 0, font);
} else {
if (_windowType == WINDOW_TEXT)
grDispatcher::instance()->drawParsedText(ar.x, ar.y, _text_size.x, _text_size.y, col, &_parser, grTextAlign(_textFormat.alignment()), font);
else
grDispatcher::instance()->drawAlignedText(ar.x, ar.y, _text_size.x, _text_size.y, col, _inputString.c_str(), grTextAlign(_textFormat.alignment()), 0, 0, font);
}
}
bool qdInterfaceTextWindow::edit_input(Common::KeyCode vkey) {
if (_isEditing) {
switch (vkey) {
case Common::KEYCODE_ESCAPE:
if (!edit_done(true))
return false;
return true;
case Common::KEYCODE_RETURN:
if (!edit_done(false))
return false;
return true;
case Common::KEYCODE_LEFT:
if (_caretPose > 0)
--_caretPose;
return true;
case Common::KEYCODE_HOME:
_caretPose = 0;
return true;
case Common::KEYCODE_END:
_caretPose = _inputString.size();
return true;
case Common::KEYCODE_RIGHT:
if (_caretPose < (int)_inputString.size())
++_caretPose;
return true;
case Common::KEYCODE_BACKSPACE:
if (_caretPose > 0 && _caretPose <= (int)_inputString.size())
_inputString.erase(--_caretPose, 1);
return true;
case Common::KEYCODE_DELETE:
if (_caretPose >= 0 && _caretPose < (int)_inputString.size())
_inputString.erase(_caretPose, 1);
return true;
default:
break;
}
return true;
}
return false;
}
void qdInterfaceTextWindow::set_input_string(const char *str) {
_inputString = str;
if (_windowType == WINDOW_TEXT) {
_parser.setFont(qdGameDispatcher::get_dispatcher()->find_font(_textFormat.font_type()));
_parser.parseString(_inputString.c_str(), _textFormat.color());
}
}
bool qdInterfaceTextWindow::edit_start() {
if (!_isEditing) {
_isEditing = true;
_inputStringBackup = _inputString;
_caretPose = _inputString.size();
return true;
}
return false;
}
bool qdInterfaceTextWindow::edit_done(bool cancel) {
if (_isEditing) {
bool end_edit = true;
switch (_infoType) {
case INFO_NONE:
if (qdInterfaceDispatcher * dp = qdInterfaceDispatcher::get_dispatcher())
dp->handle_event(!cancel ? qdInterfaceEvent::EVENT_MODAL_OK : qdInterfaceEvent::EVENT_MODAL_CANCEL, 0, this);
break;
case INFO_PLAYER_NAME:
if (cancel || _inputString.empty()) {
end_edit = false;
} else {
if (qdGameDispatcher * dp = qdGameDispatcher::get_dispatcher()) {
dp->set_hall_of_fame_player_name(_playerID, _inputString.c_str());
dp->save_hall_of_fame();
}
}
break;
default:
break;
}
if (end_edit) {
_isEditing = false;
if (cancel)
_inputString = _inputStringBackup;
}
return true;
}
return false;
}
} // namespace QDEngine

View File

@@ -0,0 +1,249 @@
/* 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/>.
*
*/
#ifndef QDENGINE_QDCORE_QD_INTERFACE_TEXT_WINDOW_H
#define QDENGINE_QDCORE_QD_INTERFACE_TEXT_WINDOW_H
#include "qdengine/qdcore/qd_interface_element.h"
#include "qdengine/qdcore/qd_interface_element_state.h"
#include "qdengine/system/graphics/UI_TextParser.h"
namespace QDEngine {
class qdScreenTextSet;
class qdInterfaceSlider;
//! Интерфейсный элемент - окно для текстов.
class qdInterfaceTextWindow : public qdInterfaceElement {
public:
qdInterfaceTextWindow();
qdInterfaceTextWindow(const qdInterfaceTextWindow &wnd);
~qdInterfaceTextWindow();
enum WindowType {
WINDOW_DIALOGS,
WINDOW_EDIT,
WINDOW_TEXT
};
enum TextVAlign {
VALIGN_TOP,
VALIGN_CENTER,
VALIGN_BOTTOM
};
enum InfoType {
INFO_NONE,
INFO_PLAYER_NAME,
INFO_PLAYER_SCORE
};
qdInterfaceTextWindow &operator = (const qdInterfaceTextWindow &wnd);
qdInterfaceElement::element_type get_element_type() const {
return qdInterfaceElement::EL_TEXT_WINDOW;
}
//! Обработчик событий мыши.
bool mouse_handler(int x, int y, mouseDispatcher::mouseEvent ev);
//! Обработчик ввода с клавиатуры.
bool keyboard_handler(Common::KeyCode vkey);
bool char_input_handler(int input);
void hover_clear();
//! Инициализация элемента.
/**
Вызывается каждый раз при заходе на экран, которому принадлежит элемент.
*/
bool init(bool is_game_active = true);
bool quant(float dt);
const Common::Path border_background_file() const {
return _border_background.animation_file();
}
void set_border_background_file(const Common::Path &file_name) {
_border_background.set_animation_file(file_name);
}
uint32 background_color() const {
return _background_color;
}
void set_background_color(uint32 color) {
_background_color = color;
}
int background_alpha() const {
return _background_alpha;
}
void set_background_alpha(int alpha) {
_background_alpha = alpha;
}
bool has_background_color() const {
return _has_background_color;
}
void toggle_background_color(bool state) {
_has_background_color = state;
}
//! Возвращает экранные координаты центра окна.
virtual const Vect2i r() const {
return qdInterfaceElement::r() - g_engine->screen_offset();
}
//! Отрисовка окна.
bool redraw() const;
bool need_redraw() const;
grScreenRegion screen_region() const;
//! Возвращает размер окна вместе с рамкой по горизонтали в пикселах.
int size_x() const;
//! Возвращает размер окна вместе с рамкой по вертикали в пикселах.
int size_y() const;
const Vect2i &text_size() const {
return _text_size;
}
void set_text_size(const Vect2i &sz) {
_text_size = sz;
}
const char *slider_name() const {
return _slider_name.c_str();
}
void set_slider_name(const char *name) {
_slider_name = name;
}
//! Возвращает true, если точка с экранными координатами (x,у) попадает в элемент.
bool hit_test(int x, int y) const;
WindowType windowType() const {
return _windowType;
}
void setWindowType(WindowType type) {
_windowType = type;
}
InfoType infoType() const {
return _infoType;
}
void setInfoType(InfoType type) {
_infoType = type;
}
int playerID() const {
return _playerID;
}
void setPlayerID(int id) {
_playerID = id;
}
const char *input_string() const {
return _inputString.c_str();
}
void set_input_string(const char *str);
bool edit_start();
bool edit_done(bool cancel = false);
TextVAlign text_valign() const {
return _textVAlign;
}
void set_text_valign(TextVAlign align) {
_textVAlign = align;
}
int inputStringLimit() const {
return _inputStringLimit;
}
void setInputStringLimit(int length) {
_inputStringLimit = length;
}
const qdScreenTextFormat &textFormat() const {
return _textFormat;
}
void setTextFormat(const qdScreenTextFormat &format) {
_textFormat = format;
}
protected:
//! Запись данных в скрипт.
bool save_script_body(Common::WriteStream &fh, int indent = 0) const;
//! Загрузка данных из скрипта.
bool load_script_body(const xml::tag *p);
private:
WindowType _windowType;
InfoType _infoType;
int _playerID;
//! Размер текстовой области окна.
Vect2i _text_size;
TextVAlign _textVAlign;
//! Скорость выезжания текста, если нулевая появляется мгновенно.
float _scrolling_speed;
float _scrolling_position;
int _text_set_position;
qdInterfaceElementState _border_background;
uint32 _background_color;
bool _has_background_color;
int _background_alpha;
int _text_set_id;
qdScreenTextSet *_text_set;
Common::String _slider_name;
qdInterfaceSlider *_slider;
Common::String _inputString;
Common::String _inputStringBackup;
qdScreenTextFormat _textFormat;
int _inputStringLimit;
bool _isEditing;
int _caretPose;
UI_TextParser _parser;
static bool _caretVisible;
static float _caretTimer;
void update_text_position();
void set_scrolling(int y_delta);
void text_redraw() const;
bool edit_input(Common::KeyCode vkey);
};
} // namespace QDEngine
#endif // QDENGINE_QDCORE_QD_INTERFACE_TEXT_WINDOW_H

View File

@@ -0,0 +1,424 @@
/* 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/stream.h"
#include "qdengine/qd_fwd.h"
#include "qdengine/qdengine.h"
#include "qdengine/parser/xml_tag_buffer.h"
#include "qdengine/parser/qdscr_parser.h"
#include "qdengine/qdcore/qd_inventory.h"
#include "qdengine/qdcore/qd_game_scene.h"
#include "qdengine/qdcore/qd_game_dispatcher.h"
#include "qdengine/qdcore/qd_game_object_mouse.h"
namespace QDEngine {
qdInventory::qdInventory() : _need_redraw(false),
_shadow_color(INV_DEFAULT_SHADOW_COLOR),
_shadow_alpha(INV_DEFAULT_SHADOW_ALPHA),
_additional_cells(0, 0) {
}
qdInventory::~qdInventory() {
_cell_sets.clear();
}
void qdInventory::redraw(int offs_x, int offs_y, bool inactive_mode) const {
qdInventoryCell::set_shadow(_shadow_color, _shadow_alpha);
for (qdInventoryCellSetVector::const_iterator it = _cell_sets.begin(); it != _cell_sets.end(); ++it)
it->redraw(offs_x, offs_y, inactive_mode);
}
void qdInventory::pre_redraw() const {
for (qdInventoryCellSetVector::const_iterator it = _cell_sets.begin(); it != _cell_sets.end(); ++it)
it->pre_redraw();
if (_need_redraw) {
if (qdGameDispatcher * dp = qdGameDispatcher::get_dispatcher()) {
for (qdInventoryCellSetVector::const_iterator it = _cell_sets.begin(); it != _cell_sets.end(); ++it) {
dp->add_redraw_region(it->screen_region());
dp->add_redraw_region(it->last_screen_region());
}
}
}
}
void qdInventory::post_redraw() {
toggle_redraw(false);
for (qdInventoryCellSetVector::iterator it = _cell_sets.begin(); it != _cell_sets.end(); ++it)
it->post_redraw();
}
bool qdInventory::put_object(qdGameObjectAnimated *p) {
if (p->inventory_cell_index() != -1) {
if (put_object(p, cell_position(p->inventory_cell_index())))
return true;
}
for (auto &it : _cell_sets) {
if (it.put_object(p)) {
p->set_inventory_cell_index(cell_index(p));
p->set_flag(QD_OBJ_IS_IN_INVENTORY_FLAG);
_need_redraw = true;
return true;
}
}
return false;
}
bool qdInventory::put_object(qdGameObjectAnimated *p, const Vect2s &pos) {
for (auto &it : _cell_sets) {
if (it.put_object(p, pos)) {
p->set_inventory_cell_index(cell_index(p));
p->set_flag(QD_OBJ_IS_IN_INVENTORY_FLAG);
_need_redraw = true;
return true;
}
}
return false;
}
qdGameObjectAnimated *qdInventory::get_object(const Vect2s &pos) const {
for (auto &it : _cell_sets) {
if (it.hit(pos)) {
qdGameObjectAnimated *p = it.get_object(pos);
if (p) return p;
}
}
return NULL;
}
bool qdInventory::remove_object(qdGameObjectAnimated *p) {
for (auto &it : _cell_sets) {
if (it.remove_object(p)) {
p->drop_flag(QD_OBJ_IS_IN_INVENTORY_FLAG);
_need_redraw = true;
return true;
}
}
return false;
}
bool qdInventory::is_object_in_list(const qdGameObjectAnimated *p) const {
for (auto &it : _cell_sets) {
if (it.is_object_in_list(p))
return true;
}
return false;
}
bool qdInventory::load_script(const xml::tag *p) {
qdInventoryCellSet set;
for (xml::tag::subtag_iterator it = p->subtags_begin(); it != p->subtags_end(); ++it) {
switch (it->ID()) {
case QDSCR_NAME:
set_name(it->data());
break;
case QDSCR_FLAG:
set_flag(xml::tag_buffer(*it).get_int());
break;
case QDSCR_INVENTORY_CELL_SET:
set.load_script(&*it);
add_cell_set(set);
break;
case QDSCR_GRID_ZONE_SHADOW_COLOR:
xml::tag_buffer(*it) > _shadow_color;
break;
case QDSCR_GRID_ZONE_SHADOW_ALPHA:
xml::tag_buffer(*it) > _shadow_alpha;
break;
case QDSCR_INVENTORY_CELL_SET_ADDITIONAL_CELLS:
xml::tag_buffer(*it) > _additional_cells.x > _additional_cells.y;
break;
}
}
return true;
}
bool qdInventory::save_script(Common::WriteStream &fh, int indent) const {
for (int i = 0; i < indent; i++) {
fh.writeString("\t");
}
fh.writeString("<inventory name=");
if (name()) {
fh.writeString(Common::String::format("\"%s\"", qdscr_XML_string(name())));
} else {
fh.writeString("\" \"");
}
if (flags()) {
fh.writeString(Common::String::format(" flags=\"%d\"", flags()));
}
if (_shadow_color != INV_DEFAULT_SHADOW_COLOR) {
fh.writeString(Common::String::format(" shadow_color=\"%u\"", _shadow_color));
}
if (_shadow_alpha != INV_DEFAULT_SHADOW_ALPHA) {
fh.writeString(Common::String::format(" shadow_alpha=\"%d\"", _shadow_alpha));
}
fh.writeString(">\r\n");
for (int i = 0; i <= indent; i++) {
fh.writeString("\t");
}
fh.writeString(Common::String::format("<inventory_cell_set_additional_cells>%d %d</inventory_cell_set_additional_cells>\r\n", _additional_cells.x, _additional_cells.y));
for (auto &it : _cell_sets) {
it.save_script(fh, indent + 1);
}
for (int i = 0; i < indent; i++) {
fh.writeString("\t");
}
fh.writeString("</inventory>\r\n");
return true;
}
bool qdInventory::init(const qdInventoryCellTypeVector &tp) {
bool result = true;
for (auto &it : _cell_sets) {
if (!it.init(tp)) {
result = false;
}
it.set_additional_cells(_additional_cells);
}
return result;
}
bool qdInventory::mouse_handler(int x, int y, mouseDispatcher::mouseEvent ev) {
if (ev == mouseDispatcher::EV_LEFT_DOWN) {
if (qdGameDispatcher * dp = qdGameDispatcher::get_dispatcher()) {
bool ret_val = false;
bool obj_flag = false;
qdGameObjectAnimated *obj = get_object(Vect2s(x, y));
qdGameObjectMouse *mp = dp->mouse_object();
if (mp->object()) {
if (!obj) {
if (put_object(mp->object(), mp->screen_pos())) {
mp->take_object(NULL);
ret_val = true;
obj_flag = true;
}
}
} else {
if (obj) {
remove_object(obj);
mp->take_object(obj);
ret_val = true;
obj_flag = true;
}
}
if (obj) {
if (qdGameScene * sp = dp->get_active_scene()) {
sp->set_mouse_click_object(obj);
ret_val = true;
}
}
if (!obj_flag && check_flag(INV_PUT_OBJECT_AFTER_CLICK) && mp->object()) {
if (put_object(mp->object()))
mp->take_object(NULL);
}
return ret_val;
}
} else if (ev == mouseDispatcher::EV_RIGHT_DOWN) {
if (check_flag(INV_ENABLE_RIGHT_CLICK)) {
if (qdGameDispatcher * dp = qd_get_game_dispatcher()) {
qdGameObjectMouse *mp = dp->mouse_object();
if (mp->object()) {
if (put_object(mp->object())) {
mp->take_object(NULL);
return true;
}
}
}
}
if (qdGameDispatcher * dp = qdGameDispatcher::get_dispatcher()) {
if (qdGameObjectAnimated * obj = get_object(Vect2s(x, y))) {
if (qdGameScene * sp = dp->get_active_scene()) {
sp->set_mouse_right_click_object(obj);
return true;
}
}
}
} else if (ev == mouseDispatcher::EV_MOUSE_MOVE) {
qdGameObjectAnimated *obj = get_object(Vect2s(x, y));
if (obj) {
if (qdGameDispatcher * dp = qdGameDispatcher::get_dispatcher()) {
if (qdGameScene * sp = dp->get_active_scene()) {
sp->set_mouse_hover_object(obj);
}
}
}
for (auto &it : _cell_sets) {
it.set_mouse_hover_object(obj);
}
}
return false;
}
void qdInventory::remove_cell_set(int idx) {
assert(-1 < idx && idx < static_cast<int>(_cell_sets.size()));
_cell_sets.erase(_cell_sets.begin() + idx);
}
bool qdInventory::load_resources() {
debugC(4, kDebugLoad, "qdInventory::load_resources(), %u cells", _cell_sets.size());
for (auto &it : _cell_sets)
it.load_resources();
return true;
}
bool qdInventory::free_resources() {
for (auto &it : _cell_sets) {
it.free_resources();
}
return true;
}
bool qdInventory::load_data(Common::SeekableReadStream &fh, int save_version) {
debugC(3, kDebugSave, " qdInventory::load_data before: %d", (int)fh.pos());
for (auto &it : _cell_sets) {
if (!it.load_data(fh, save_version))
return false;
}
debugC(3, kDebugSave, " qdInventory::load_data after: %d", (int)fh.pos());
debug_log();
return true;
}
bool qdInventory::save_data(Common::WriteStream &fh) const {
debugC(3, kDebugSave, " qdInventory::save_data before: %d", (int)fh.pos());
for (auto &it : _cell_sets) {
if (!it.save_data(fh)) {
return false;
}
}
debugC(3, kDebugSave, " qdInventory::save_data after: %d", (int)fh.pos());
return true;
}
int qdInventory::cell_index(const qdGameObjectAnimated *obj) const {
int index = 0;
for (auto &it : _cell_sets) {
int idx = it.cell_index(obj);
if (idx != -1) {
return index + idx;
}
else {
index += it.num_cells();
}
}
return -1;
}
Vect2s qdInventory::cell_position(int cell_idx) const {
for (auto &it : _cell_sets) {
if (cell_idx < it.num_cells())
return it.cell_position(cell_idx);
cell_idx -= it.num_cells();
}
return Vect2s(0, 0);
}
void qdInventory::objects_quant(float dt) {
for (auto &it : _cell_sets) {
it.objects_quant(dt);
}
}
void qdInventory::scroll_left() {
for (auto &it : _cell_sets) {
it.scroll_left();
}
toggle_redraw(true);
}
void qdInventory::scroll_right() {
for (auto &it : _cell_sets) {
it.scroll_right();
}
toggle_redraw(true);
}
void qdInventory::scroll_up() {
for (auto &it : _cell_sets) {
it.scroll_up();
}
toggle_redraw(true);
}
void qdInventory::scroll_down() {
for (auto &it : _cell_sets) {
it.scroll_down();
}
toggle_redraw(true);
}
void qdInventory::debug_log() const {
#ifdef _DEBUG
debugCN(3, kDebugLog, "Inventory ");
if (name())
debugCN(3, kDebugLog, "%s", transCyrillic(name()));
debugC(3, kDebugLog, "--------------");
for (auto &it : _cell_sets) {
it.debug_log();
}
debugC(3, kDebugLog, "--------------");
#endif
}
} // namespace QDEngine

View File

@@ -0,0 +1,163 @@
/* 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/>.
*
*/
#ifndef QDENGINE_QDCORE_QD_INVENTORY_H
#define QDENGINE_QDCORE_QD_INVENTORY_H
#include "qdengine/system/input/mouse_input.h"
#include "qdengine/qdcore/qd_named_object.h"
#include "qdengine/qdcore/qd_inventory_cell.h"
namespace Common {
class WriteStream;
}
namespace QDEngine {
//! Инвентори.
class qdInventory : public qdNamedObject {
public:
qdInventory();
~qdInventory();
//! Флаги.
enum {
//! если установлен, объект берется сначала на мышь
INV_TAKE_TO_MOUSE = 0x01,
//! если установлен, то после взятия объекта инвентори не открывается
INV_DONT_OPEN_AFTER_TAKE = 0x02,
//! если установлен, то после клика объектом он возвращается в инветори
INV_PUT_OBJECT_AFTER_CLICK = 0x04,
//! если установлен, объект возвращается в инветори по правому клику мыши
INV_ENABLE_RIGHT_CLICK = 0x08,
//! отрисовывается даже если неактивно в данный момент
INV_VISIBLE_WHEN_INACTIVE = 0x10
};
enum {
INV_DEFAULT_SHADOW_COLOR = 0,
INV_DEFAULT_SHADOW_ALPHA = 128
};
int named_object_type() const {
return QD_NAMED_OBJECT_INVENTORY;
}
void add_cell_set(const qdInventoryCellSet &set) {
_cell_sets.push_back(set);
}
void remove_cell_set(int idx);
const qdInventoryCellSet &get_cell_set(int id = 0) const {
return _cell_sets[id];
}
void set_cells_pos(int set_id, const Vect2s &pos) {
if (set_id >= 0 && set_id < (int)_cell_sets.size())
_cell_sets[set_id].set_screen_pos(pos);
}
void set_cells_type(int set_id, const qdInventoryCellType &tp) {
if (set_id >= 0 && set_id < (int)_cell_sets.size())
_cell_sets[set_id].set_cell_type(tp);
}
void set_cells_size(int set_id, const Vect2s &sz) {
if (set_id >= 0 && set_id < (int)_cell_sets.size())
_cell_sets[set_id].set_size(sz);
}
int cell_index(const qdGameObjectAnimated *obj) const;
Vect2s cell_position(int cell_idx) const;
void redraw(int offs_x = 0, int offs_y = 0, bool inactive_mode = false) const;
void toggle_redraw(bool state) {
_need_redraw = state;
}
void pre_redraw() const;
void post_redraw();
bool put_object(qdGameObjectAnimated *p);
bool put_object(qdGameObjectAnimated *p, const Vect2s &pos);
bool remove_object(qdGameObjectAnimated *p);
qdGameObjectAnimated *get_object(const Vect2s &pos) const;
bool is_object_in_list(const qdGameObjectAnimated *p) const;
uint32 shadow_color() const {
return _shadow_color;
}
void set_shadow_color(uint32 color) {
_shadow_color = color;
}
int shadow_alpha() const {
return _shadow_alpha;
}
void set_shadow_alpha(int alpha) {
_shadow_alpha = alpha;
}
bool load_script(const xml::tag *p);
bool save_script(Common::WriteStream &fh, int indent = 0) const;
bool load_resources();
bool free_resources();
//! Загрузка данных из сэйва.
bool load_data(Common::SeekableReadStream &fh, int save_version);
//! Запись данных в сэйв.
bool save_data(Common::WriteStream &fh) const;
bool init(const qdInventoryCellTypeVector &tp);
bool mouse_handler(int x, int y, mouseDispatcher::mouseEvent ev);
void objects_quant(float dt);
Vect2s additional_cells() const {
return _additional_cells;
}
void set_additional_cells(Vect2s val) {
_additional_cells = val;
}
//! Скроллинг
void scroll_left();
void scroll_right();
void scroll_up();
void scroll_down();
void debug_log() const;
private:
qdInventoryCellSetVector _cell_sets;
bool _need_redraw;
uint32 _shadow_color;
int _shadow_alpha;
//! Дополнительные ячейки для всех наборов инвентори
Vect2s _additional_cells;
};
} // namespace QDEngine
#endif // QDENGINE_QDCORE_QD_INVENTORY_H

View File

@@ -0,0 +1,578 @@
/* 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/stream.h"
#include "qdengine/qd_fwd.h"
#include "qdengine/parser/qdscr_parser.h"
#include "qdengine/parser/xml_tag_buffer.h"
#include "qdengine/qdcore/qd_game_dispatcher.h"
#include "qdengine/qdcore/qd_inventory_cell.h"
#include "qdengine/qdcore/qd_interface_element.h"
#include "qdengine/qdcore/qd_named_object_reference.h"
#include "qdengine/qdcore/qd_game_object_animated.h"
namespace QDEngine {
// class qdInventoryCellType
bool qdInventoryCellType::load_resources() const {
return _sprite.load();
}
void qdInventoryCellType::free_resources() const {
_sprite.free();
}
bool qdInventoryCellType::load_script(const xml::tag *p) {
for (xml::tag::subtag_iterator it = p->subtags_begin(); it != p->subtags_end(); ++it) {
switch (it->ID()) {
case QDSCR_TYPE:
set_type(xml::tag_buffer(*it).get_int());
break;
case QDSCR_FILE:
_sprite.set_file(Common::Path(it->data(), '\\'));
break;
}
}
return true;
}
bool qdInventoryCellType::save_script(Common::WriteStream &fh, int indent) const {
for (int i = 0; i < indent; i++) {
fh.writeString("\t");
}
fh.writeString(Common::String::format("<inventory_cell_type type=\"%d\"", _type));
if (!_sprite.file().empty()) {
fh.writeString(Common::String::format(" file=\"%s\"", qdscr_XML_string(_sprite.file().toString('\\'))));
}
fh.writeString("/>\r\n");
return true;
}
// class qdInventoryCell
uint32 qdInventoryCell::_shadow_color = 0;
int qdInventoryCell::_shadow_alpha = -1;
qdInventoryCell::qdInventoryCell() : _type(0),
_sprite(NULL),
_object(NULL) {
}
qdInventoryCell::qdInventoryCell(const qdInventoryCellType &tp) : _type(tp.type()),
_sprite(tp.sprite()),
_object(NULL) {
}
qdInventoryCell::qdInventoryCell(const qdInventoryCell &cl) : _type(cl._type),
_sprite(cl._sprite),
_object(cl._object) {
}
qdInventoryCell &qdInventoryCell::operator = (const qdInventoryCell &cl) {
if (this == &cl) return *this;
_type = cl._type;
_sprite = cl._sprite;
_object = cl._object;
return *this;
}
void qdInventoryCell::redraw(int x, int y, bool inactive_mode) const {
if (sprite())
sprite()->redraw(x, y, 0);
if (_object) {
_object->set_pos(Vect3f(x, y, 0));
_object->set_flag(QD_OBJ_SCREEN_COORDS_FLAG);
_object->update_screen_pos();
_object->redraw();
if (inactive_mode)
_object->draw_shadow(0, 0, _shadow_color, _shadow_alpha);
}
}
void qdInventoryCell::set_object(qdGameObjectAnimated *obj) {
_object = obj;
if (_object) _object->set_flag(QD_OBJ_SCREEN_COORDS_FLAG);
}
bool qdInventoryCell::load_resources() {
if (_object)
return _object->load_resources();
return true;
}
bool qdInventoryCell::free_resources() {
if (_object) _object->free_resources();
return true;
}
bool qdInventoryCell::load_data(Common::SeekableReadStream &fh, int saveVersion) {
debugC(5, kDebugSave, " qdInventoryCell::load_data before: %d", (int)fh.pos());
char flag = fh.readByte();
if (flag) {
qdNamedObjectReference ref;
if (!ref.load_data(fh, saveVersion)) {
return false;
}
if (qdGameDispatcher * p = qdGameDispatcher::get_dispatcher()) {
_object = static_cast<qdGameObjectAnimated *>(p->get_named_object(&ref));
}
} else
_object = NULL;
debugC(5, kDebugSave, " qdInventoryCell::load_data after: %d", (int)fh.pos());
return true;
}
bool qdInventoryCell::save_data(Common::WriteStream &fh) const {
debugC(5, kDebugSave, " qdInventoryCell::save_data before: %d", (int)fh.pos());
if (_object) {
fh.writeByte(1);
qdNamedObjectReference ref(_object);
if (!ref.save_data(fh)) {
return false;
}
} else
fh.writeByte(0);
debugC(5, kDebugSave, " qdInventoryCell::save_data after: %d", (int)fh.pos());
return true;
}
// class qdInventoryCellSet
qdInventoryCellSet::qdInventoryCellSet() : _size(0, 0),
_screen_pos(0, 0),
_additional_cells(0, 0),
_cells_shift(0, 0) {
}
qdInventoryCellSet::qdInventoryCellSet(int x, int y, int sx, int sy, int16 addit_sx, int16 addit_sy, const qdInventoryCellType &tp) : _size(sx, sy),
_screen_pos(x, y),
_additional_cells(addit_sx, addit_sy),
_cells_shift(0, 0),
_cells((sx + addit_sx) * (sy + addit_sy), tp) {
}
qdInventoryCellSet::qdInventoryCellSet(const qdInventoryCellSet &set) : _size(set._size),
_cells(set._cells),
_screen_pos(set._screen_pos),
_additional_cells(set._additional_cells),
_cells_shift(set._cells_shift) {
}
qdInventoryCellSet::~qdInventoryCellSet() {
_cells.clear();
}
qdInventoryCellSet &qdInventoryCellSet::operator = (const qdInventoryCellSet &set) {
if (this == &set) return *this;
_size = set._size;
_cells = set._cells;
_screen_pos = set._screen_pos;
_additional_cells = set._additional_cells;
_cells_shift = set._cells_shift;
return *this;
}
void qdInventoryCellSet::redraw(int offs_x, int offs_y, bool inactive_mode) const {
Vect2s pos = screen_pos();
int idx;
for (int i = _cells_shift.y; i < _size.y + _cells_shift.y; i++) {
idx = i * (_size.x + _additional_cells.x) + _cells_shift.x;
for (int j = _cells_shift.x; j < _size.x + _cells_shift.x; j++) {
assert(idx >= 0 && idx < (int)_cells.size());
_cells[idx].redraw(offs_x + pos.x, offs_y + pos.y, inactive_mode);
pos.x += _cells[idx].size_x();
idx++;
}
pos.x = screen_pos().x;
pos.y += _cells.front().size_y();
}
}
bool qdInventoryCellSet::put_object(qdGameObjectAnimated *p) {
for (auto &it : _cells) {
if (it.is_empty() && it.type() == p->inventory_type()) {
it.set_object(p);
return true;
}
}
return false;
}
bool qdInventoryCellSet::hit(const Vect2s &pos) const {
if (_cells.empty() || !_cells.front().size_x() || !_cells.front().size_y())
return false;
Vect2s v = pos - screen_pos() + _cells.front().size() / 2;
if (v.x >= 0 && v.x < _size.x * _cells.front().size_x() && v.y >= 0 && v.y < _size.y * _cells.front().size_y())
return true;
return false;
}
grScreenRegion qdInventoryCellSet::screen_region() const {
if (_cells.empty() || !_cells.front().size_x() || !_cells.front().size_y())
return grScreenRegion_EMPTY;
int sx = _size.x * _cells.front().size_x();
int sy = _size.y * _cells.front().size_y();
int x = screen_pos().x - _cells.front().size_x() / 2 + sx / 2;
int y = screen_pos().y - _cells.front().size_y() / 2 + sy / 2;
return grScreenRegion(x, y, sx, sy);
}
bool qdInventoryCellSet::put_object(qdGameObjectAnimated *p, const Vect2s &pos) {
if (!hit(pos)) return false;
if (_cells.front().size_x() == 0 || _cells.front().size_y() == 0)
return false;
Vect2s v = pos - screen_pos() + _cells.front().size() / 2;
int idx = v.x / _cells.front().size_x() + (v.y / _cells.front().size_y()) * _size.x;
// Двигаем индекс на текущее смещение ячеек
idx += _cells_shift.y * (_size.x + _additional_cells.x) + _cells_shift.x;
if (idx >= 0 && idx < (int)_cells.size() && _cells[idx].is_empty() && _cells[idx].type() == p->inventory_type()) {
_cells[idx].set_object(p);
return true;
}
return false;
}
qdGameObjectAnimated *qdInventoryCellSet::get_object(const Vect2s &pos) const {
if (!hit(pos)) return NULL;
if (_cells.front().size_x() == 0 || _cells.front().size_y() == 0)
return NULL;
Vect2s v = pos - screen_pos() + _cells.front().size() / 2;
int idx = v.x / _cells.front().size_x() + (v.y / _cells.front().size_y()) * _size.x;
// Двигаем индекс на текущее смещение ячеек
idx += _cells_shift.y * (_size.x + _additional_cells.x) + _cells_shift.x;
if (idx >= 0 && idx < (int)_cells.size())
return _cells[idx].object();
return NULL;
}
bool qdInventoryCellSet::remove_object(qdGameObjectAnimated *p) {
for (auto &it : _cells) {
if (it.object() == p) {
it.set_object(NULL);
return true;
}
}
return false;
}
bool qdInventoryCellSet::is_object_in_list(const qdGameObjectAnimated *p) const {
for (auto &it : _cells) {
if (it.object() == p)
return true;
}
return false;
}
bool qdInventoryCellSet::load_script(const xml::tag *p) {
Vect2s v;
for (xml::tag::subtag_iterator it = p->subtags_begin(); it != p->subtags_end(); ++it) {
switch (it->ID()) {
case QDSCR_INVENTORY_CELL_SET_POS:
xml::tag_buffer(*it) > v.x > v.y;
set_screen_pos(v);
break;
case QDSCR_INVENTORY_CELL_SET_SIZE:
xml::tag_buffer(*it) > v.x > v.y;
set_size(v);
break;
case QDSCR_INVENTORY_CELL_SET_ADDITIONAL_CELLS:
xml::tag_buffer(*it) > _additional_cells.x > _additional_cells.y;
break;
}
}
if (const xml::tag * tp = p->search_subtag(QDSCR_TYPE))
set_cell_type(qdInventoryCellType(xml::tag_buffer(*tp).get_int()));
return true;
}
bool qdInventoryCellSet::save_script(Common::WriteStream &fh, int indent) const {
int tp = 0;
if (!_cells.empty()) {
tp = _cells.front().type();
}
for (int i = 0; i < indent; i++) {
fh.writeString("\t");
}
fh.writeString(Common::String::format("<inventory_cell_set type=\"%d\">\r\n", tp));
for (int i = 0; i <= indent; i++) {
fh.writeString("\t");
}
fh.writeString(Common::String::format("<inventory_cell_set_size>%d %d</inventory_cell_set_size>\r\n", _size.x, _size.y));
for (int i = 0; i <= indent; i++) {
fh.writeString("\t");
}
fh.writeString(Common::String::format("<inventory_cell_set_pos>%d %d</inventory_cell_set_pos>\r\n", _screen_pos.x, _screen_pos.y));
for (int i = 0; i <= indent; i++) {
fh.writeString("\t");
}
fh.writeString(Common::String::format("<inventory_cell_set_additional_cells>%d %d</inventory_cell_set_additional_cells>\r\n", _additional_cells.x, _additional_cells.y));
for (int i = 0; i < indent; i++) {
fh.writeString("\t");
}
fh.writeString("</inventory_cell_set>\r\n");
return true;
}
bool qdInventoryCellSet::init(const qdInventoryCellTypeVector &tp) {
if (_cells.empty()) return false;
for (auto &it : tp) {
if (it.type() == _cells.front().type()) {
set_cell_type(it);
return true;
}
}
return false;
}
bool qdInventoryCellSet::load_resources() {
for (auto &it : _cells)
it.load_resources();
return true;
}
bool qdInventoryCellSet::free_resources() {
for (auto &it : _cells) {
it.free_resources();
}
return true;
}
bool qdInventoryCellSet::load_data(Common::SeekableReadStream &fh, int save_version) {
debugC(4, kDebugSave, " qdInventoryCellSet::load_data before: %d", (int)fh.pos());
if (save_version >= 102) {
_additional_cells.x = fh.readSint32LE();
_additional_cells.y = fh.readSint32LE();
}
for (auto &it : _cells) {
if (!it.load_data(fh, save_version))
return false;
}
debugC(4, kDebugSave, " qdInventoryCellSet::load_data after: %d", (int)fh.pos());
return true;
}
bool qdInventoryCellSet::save_data(Common::WriteStream &fh) const {
debugC(4, kDebugSave, " qdInventoryCellSet::save_data before: %d", (int)fh.pos());
fh.writeSint32LE(_additional_cells.x);
fh.writeSint32LE(_additional_cells.y);
for (auto &it : _cells) {
if (!it.save_data(fh))
return false;
}
debugC(4, kDebugSave, " qdInventoryCellSet::save_data after: %d", (int)fh.pos());
return true;
}
int qdInventoryCellSet::cell_index(const qdGameObjectAnimated *obj) const {
for (auto it = _cells.begin(); it != _cells.end(); it++) {
if (it->object() == obj) {
return (it - _cells.begin());
}
}
return -1;
}
Vect2s qdInventoryCellSet::cell_position(int cell_idx) const {
if (cell_idx >= 0 && cell_idx < (int)_cells.size()) {
int x = (cell_idx % _size.x) * _cells.front().size_x() + screen_pos().x;
int y = (cell_idx / _size.x) * _cells.front().size_y() + screen_pos().y;
// Делаем поправку на смещение ячеек
x -= _cells.front().size_x() * _cells_shift.x;
y -= _cells.front().size_y() * _cells_shift.y;
return Vect2s(x, y);
}
return Vect2s(0, 0);
}
void qdInventoryCellSet::set_mouse_hover_object(qdGameObjectAnimated *obj) {
for (auto &it : _cells) {
if (it.object() && it.object() != obj && it.object()->get_cur_state()) {
if (it.object()->get_cur_state()->check_flag(qdGameObjectState::QD_OBJ_STATE_FLAG_MOUSE_HOVER_STATE)) {
if (qdGameObjectState *sp = it.object()->get_inventory_state())
it.object()->set_state(sp);
}
}
}
if (obj) {
if (qdGameObjectState * p = obj->get_mouse_hover_state()) {
if (obj->get_cur_state() != p)
p->set_prev_state(obj->get_cur_state());
obj->set_state(p);
}
}
}
void qdInventoryCellSet::objects_quant(float dt) {
for (auto &it : _cells) {
if (!it.is_empty()) {
it.object()->quant(dt);
}
}
}
void qdInventoryCellSet::pre_redraw() const {
qdGameDispatcher *dp = qdGameDispatcher::get_dispatcher();
if (!dp) return;
int idx;
for (int i = _cells_shift.y; i < size().y + _cells_shift.y; i++) {
idx = i * (_size.x + _additional_cells.x) + _cells_shift.x;
for (int j = _cells_shift.x; j < size().x + _cells_shift.x; j++) {
assert(idx >= 0 && idx < (int)_cells.size());
if (!_cells[idx].is_empty() && _cells[idx].object()->need_redraw()) {
dp->add_redraw_region(_cells[idx].object()->last_screen_region());
dp->add_redraw_region(_cells[idx].object()->screen_region());
}
idx++;
}
}
grScreenRegion reg = screen_region();
if (reg != _last_screen_region) {
dp->add_redraw_region(_last_screen_region);
dp->add_redraw_region(reg);
}
}
void qdInventoryCellSet::post_redraw() {
int idx;
for (int i = _cells_shift.y; i < size().y + _cells_shift.y; i++) {
idx = i * (_size.x + _additional_cells.x) + _cells_shift.x;
for (int j = _cells_shift.x; j < size().x + _cells_shift.x; j++) {
assert(idx >= 0 && idx < (int)_cells.size());
if (!_cells[idx].is_empty())
_cells[idx].object()->post_redraw();
idx++;
}
}
_last_screen_region = screen_region();
}
bool qdInventoryCellSet::has_rect_objects(int left, int top, int right, int bottom) const {
int idx;
for (int i = top; i <= bottom; i++)
for (int j = left; j <= right; j++) {
idx = i * (_size.x + _additional_cells.x) + j;
assert(idx >= 0 && idx < (int)_cells.size());
// Нашли объект вне видимой области - значит скроллинг нужен
if (!_cells[idx].is_empty())
return true;
}
return false;
}
void qdInventoryCellSet::scroll_left() {
if (!has_rect_objects(0, 0, _cells_shift.x - 1, _size.y + _additional_cells.y - 1))
return;
_cells_shift.x--;
if (_cells_shift.x < 0) _cells_shift.x = _additional_cells.x;
}
void qdInventoryCellSet::scroll_right() {
if (!has_rect_objects(_cells_shift.x + _size.x, 0, _size.x + _additional_cells.x - 1, _size.y + _additional_cells.y - 1))
return;
_cells_shift.x++;
if (_cells_shift.x > _additional_cells.x) _cells_shift.x = 0;
}
void qdInventoryCellSet::scroll_up() {
if (!has_rect_objects(0, 0, _size.x + _additional_cells.x - 1, _cells_shift.y - 1))
return;
_cells_shift.y--;
if (_cells_shift.y < 0) _cells_shift.y = _additional_cells.y;
}
void qdInventoryCellSet::scroll_down() {
if (!has_rect_objects(0, _cells_shift.y + _size.y, _size.x + _additional_cells.x - 1, _size.y + _additional_cells.y - 1))
return;
_cells_shift.y++;
if (_cells_shift.y > _additional_cells.y) _cells_shift.y = 0;
}
void qdInventoryCellSet::debug_log() const {
#ifdef _DEBUG
for (int i = _cells_shift.y; i < size().y + _cells_shift.y; i++) {
int idx = i * (_size.x + _additional_cells.x) + _cells_shift.x;
for (int j = _cells_shift.x; j < size().x + _cells_shift.x; j++) {
if (!_cells[idx].is_empty()) {
debugC(3, kDebugLog, "Inventory cell: %d %d %s", i, j, transCyrillic(_cells[idx].object()->name()));
}
}
}
#endif
}
} // namespace QDEngine

View File

@@ -0,0 +1,294 @@
/* 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/>.
*
*/
#ifndef QDENGINE_QDCORE_QD_INVENTORY_CELL_H
#define QDENGINE_QDCORE_QD_INVENTORY_CELL_H
#include "qdengine/parser/xml_fwd.h"
#include "qdengine/qdcore/qd_sprite.h"
namespace Common {
class WriteStream;
}
namespace QDEngine {
class qdGameObjectAnimated;
//! Описание ячейки инвентори.
class qdInventoryCellType {
public:
qdInventoryCellType() : _type(0) { }
explicit qdInventoryCellType(int tp) : _type(tp) { }
~qdInventoryCellType() {
_sprite.free();
}
int type() const {
return _type;
}
void set_type(int tp) {
_type = tp;
}
void set_sprite_file(const Common::Path &fname) {
_sprite.set_file(fname);
}
const Common::Path sprite_file() const {
return _sprite.file();
}
const qdSprite *sprite() const {
return &_sprite;
}
bool load_resources() const;
void free_resources() const;
bool load_script(const xml::tag *p);
bool save_script(Common::WriteStream &fh, int indent = 0) const;
const Vect2i &size() const {
return _sprite.size();
}
private:
//! Тип ячейки.
int _type;
//! Внешний вид ячейки.
mutable qdSprite _sprite;
};
inline bool operator == (const qdInventoryCellType &f, const qdInventoryCellType &s) {
return ((f.type() == s.type()) && (f.sprite() == s.sprite()));
}
inline bool operator == (const qdInventoryCellType &f, int type) {
return f.type() == type;
}
typedef Std::vector<qdInventoryCellType> qdInventoryCellTypeVector;
//! Ячейка инвентори.
class qdInventoryCell {
public:
qdInventoryCell();
qdInventoryCell(const qdInventoryCellType &tp);
qdInventoryCell(const qdInventoryCell &cl);
~qdInventoryCell() { }
qdInventoryCell &operator = (const qdInventoryCell &cl);
int type() const {
return _type;
}
void set_type(int tp) {
_type = tp;
}
const qdSprite *sprite() const {
return _sprite;
}
void set_sprite(const qdSprite *spr) {
_sprite = spr;
}
qdGameObjectAnimated *object() const {
return _object;
}
void set_object(qdGameObjectAnimated *obj);
bool is_empty() const {
if (!_object) return true;
else return false;
}
const Vect2i &size() const {
if (sprite())
return sprite()->size();
static Vect2i z = Vect2i(0, 0);
return z;
}
int size_x() const {
if (sprite()) return sprite()->size_x();
else return 0;
}
int size_y() const {
if (sprite()) return sprite()->size_y();
else return 0;
}
void redraw(int x, int y, bool inactive_mode = false) const;
bool load_resources();
bool free_resources();
//! Загрузка данных из сэйва.
bool load_data(Common::SeekableReadStream &fh, int save_version);
//! Запись данных в сэйв.
bool save_data(Common::WriteStream &fh) const;
static void set_shadow(uint32 color, int alpha) {
_shadow_color = color;
_shadow_alpha = alpha;
}
private:
//! Тип ячейки.
int _type;
//! Внешний вид ячейки.
/**
Указывает на _sprite из qdInventoryCellType соответствующего типа.
*/
const qdSprite *_sprite;
//! Объект, который лежит в ячейке.
mutable qdGameObjectAnimated *_object;
static uint32 _shadow_color;
static int _shadow_alpha;
};
typedef Std::vector<qdInventoryCell> qdInventoryCellVector;
//! Группа ячеек инвентори.
class qdInventoryCellSet {
public:
qdInventoryCellSet();
qdInventoryCellSet(int x, int y, int sx, int sy, int16 addit_sx, int16 addit_sy, const qdInventoryCellType &tp);
qdInventoryCellSet(const qdInventoryCellSet &set);
~qdInventoryCellSet();
qdInventoryCellSet &operator = (const qdInventoryCellSet &set);
const Vect2s screen_pos() const {
return _screen_pos + g_engine->screen_offset();
}
void set_screen_pos(const Vect2s &pos) {
_screen_pos = pos;
}
grScreenRegion screen_region() const;
const grScreenRegion &last_screen_region() const {
return _last_screen_region;
}
int cell_index(const qdGameObjectAnimated *obj) const;
Vect2s cell_position(int cell_idx) const;
const Vect2s &size() const {
return _size;
}
void set_size(const Vect2s &sz) {
assert(sz.x && sz.y);
qdInventoryCell t;
if (_size.x != 0)//предполагаю, что либо оба равны либо оба неравны 0
t = _cells.front();
_size = sz;
_cells.resize((sz.x + _additional_cells.x) * (sz.y + _additional_cells.y));
Common::fill(_cells.begin(), _cells.end(), t);
}
void set_cell_type(const qdInventoryCellType &tp) {
Common::fill(_cells.begin(), _cells.end(), tp);
}
bool hit(const Vect2s &pos) const;
void set_mouse_hover_object(qdGameObjectAnimated *obj);
void pre_redraw() const;
void redraw(int offs_x = 0, int offs_y = 0, bool inactive_mode = false) const;
void post_redraw();
bool put_object(qdGameObjectAnimated *p);
bool put_object(qdGameObjectAnimated *p, const Vect2s &pos);
bool remove_object(qdGameObjectAnimated *p);
qdGameObjectAnimated *get_object(const Vect2s &pos) const;
bool is_object_in_list(const qdGameObjectAnimated *p) const;
bool load_script(const xml::tag *p);
bool save_script(Common::WriteStream &fh, int indent = 0) const;
//! Загрузка данных из сэйва.
bool load_data(Common::SeekableReadStream &fh, int save_version);
//! Запись данных в сэйв.
bool save_data(Common::WriteStream &fh) const;
bool init(const qdInventoryCellTypeVector &tp);
const qdInventoryCellVector &cells() const {
return _cells;
}
int num_cells() const {
return _cells.size();
}
bool load_resources();
bool free_resources();
void objects_quant(float dt);
Vect2s additional_cells() const {
return _additional_cells;
}
void set_additional_cells(Vect2s val) {
_additional_cells = val;
// Изменили кол-во доп. ячеек - изменяем и всех кол-во массива ячеек
set_size(size());
}
//! Скроллинг
void scroll_left();
void scroll_right();
void scroll_up();
void scroll_down();
void debug_log() const;
private:
// Имеет ли область полного инвентори сета объекты
bool has_rect_objects(int left, int top, int right, int bottom) const;
//! Размер группы.
/**
В группе _size.x * _size.y ячеек.
*/
Vect2s _size;
//! Дополнительне ячейки по x и y
Vect2s _additional_cells;
//! Смещение по x и y (с него выводятся ячекйки в количестве _size)
Vect2s _cells_shift;
//! Ячейки.
qdInventoryCellVector _cells;
//! Экранные координаты центра первой ячейки группы.
Vect2s _screen_pos;
grScreenRegion _last_screen_region;
};
typedef Std::vector<qdInventoryCellSet> qdInventoryCellSetVector;
} // namespace QDEngine
#endif // QDENGINE_QDCORE_QD_INVENTORY_CELL_H

View File

@@ -0,0 +1,202 @@
/* 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/>.
*
*/
#ifndef QDENGINE_QDCORE_QD_LIST_H
#define QDENGINE_QDCORE_QD_LIST_H
namespace QDEngine {
template <class Type> class qdList {
int numElements;
Type *firstElement;
void test(int code);
public:
qdList();
~qdList();
int size();
Type *first();
Type *last();
void clear();
void delete_all();
void insert(Type *p);
void append(Type *p);
void insert(Type *pointer, Type *p);
void append(Type *pointer, Type *p);
void remove(Type *p);
Type *search(int ID);
};
template <class Type>
inline void qdList<Type>::test(int code) {
#ifdef _XT_TEST_LIST_
Type *p = first();
int cnt = 0;
while (p) {
cnt++;
p = p->next;
}
if (cnt != numElements)
ErrH.Abort("List", XERR_USER, code);
#endif
}
template <class Type>
inline qdList<Type>::qdList() {
numElements = 0;
firstElement = 0;
}
template <class Type>
inline qdList<Type>::~qdList() {
clear();
}
template <class Type>
inline void qdList<Type>::clear() {
while (first())
remove(first());
}
template <class Type>
inline void qdList<Type>::delete_all() {
Type *p;
while ((p = first()) != 0) {
remove(p);
delete p;
}
}
template <class Type>
inline int qdList<Type>::size() {
return numElements;
}
template <class Type>
inline Type *qdList<Type>::first() {
return firstElement;
}
template <class Type>
inline Type *qdList<Type>::last() {
return firstElement ? firstElement->prev : 0;
}
template <class Type>
inline void qdList<Type>::insert(Type *p) {
if (p->list)
ErrH.Abort("Element is already in list");
numElements++;
if (firstElement) {
p->next = firstElement;
p->prev = firstElement->prev;
firstElement->prev = p;
} else {
p->prev = p;
p->next = 0;
}
firstElement = p;
p->list = this;
test(0);
}
template <class Type>
inline void qdList<Type>::insert(Type *pointer, Type *p) {
if (!firstElement || firstElement == pointer) {
insert(p);
return;
}
if (!pointer) {
append(p);
return;
}
if (p->list)
ErrH.Abort("Element is already in list");
numElements++;
p->next = pointer;
p->prev = pointer->prev;
pointer->prev->next = p;
pointer->prev = p;
p->list = this;
test(5);
}
template <class Type>
inline void qdList<Type>::append(Type *p) {
// if(p->list)
// ErrH.Abort("Element is already in list");
numElements++;
if (firstElement) {
p->next = 0;
p->prev = firstElement->prev;
firstElement->prev->next = p;
firstElement->prev = p;
} else {
p->next = 0;
p->prev = firstElement = p;
}
p->list = this;
test(1);
}
template <class Type>
inline void qdList<Type>::remove(Type *p) {
// if(p->list != this)
// ErrH.Abort("Removed element isn't in list");
numElements--;
if (p->next)
p->next->prev = p->prev;
else
firstElement->prev = p->prev;
if (p != firstElement)
p->prev->next = p->next;
else {
firstElement = p->next;
if (firstElement)
firstElement->prev = p->prev;
}
p->next = p->prev = 0;
p->list = 0;
test(2);
}
template <class Type>
inline Type *qdList<Type>::search(int ID) {
Type *p = first();
while (p) {
if (p->ID == ID)
return p;
p = p->next;
}
return 0;
}
} // namespace QDEngine
#endif // QDENGINE_QDCORE_QD_LIST_H

View File

@@ -0,0 +1,421 @@
/* 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 "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/system/input/mouse_input.h"
#include "qdengine/system/input/keyboard_input.h"
#include "qdengine/qdcore/qd_game_dispatcher.h"
#include "qdengine/qdcore/qd_minigame.h"
#include "qdengine/qdcore/qd_setup.h"
#include "qdengine/qdcore/qd_minigame_interface.h"
#include "qdengine/qdcore/qd_engine_interface.h"
#include "qdengine/minigames/adv/RunTime.h"
#include "qdengine/minigames/qd_empty_minigame_interface.h"
// maski
#include "qdengine/minigames/arkada_avtomat.h"
#include "qdengine/minigames/kartiny.h"
#include "qdengine/minigames/maski_21.h"
#include "qdengine/minigames/masky_order.h"
#include "qdengine/minigames/orchestra.h"
#include "qdengine/minigames/scroll.h"
#include "qdengine/minigames/tetris.h"
// 2mice1
#include "qdengine/minigames/book_all.h"
#include "qdengine/minigames/puzzle_all.h"
// shveik
#include "qdengine/minigames/inv_popup.h"
#include "qdengine/minigames/shveik_shkatulka.h"
#include "qdengine/minigames/shveik_portret.h"
// 3mice2
#include "qdengine/minigames/3mice2_sbor_karty.h"
#include "qdengine/minigames/3mice2_kovrik.h"
#include "qdengine/minigames/3mice2_sudoku.h"
#include "qdengine/minigames/3mice2_states.h"
#include "qdengine/minigames/3mice2_testo.h"
#include "qdengine/minigames/3mice2_plate.h"
#include "qdengine/minigames/3mice2_raskr_all.h"
#include "qdengine/minigames/3mice2_babochka.h"
// dogncat
#include "qdengine/minigames/adv/m_scores.h"
#include "qdengine/minigames/adv/m_triangles.h"
// dogncat2
#include "qdengine/minigames/adv/m_swap.h"
// klepa
#include "qdengine/minigames/adv/m_puzzle.h"
#include "qdengine/minigames/adv/m_karaoke.h"
namespace QDEngine {
qdMiniGame::qdMiniGame() : _dll_handle(NULL),
_interface(NULL) {
}
qdMiniGame::qdMiniGame(const qdMiniGame &mg) : qdNamedObject(mg),
_dll_name(mg._dll_name),
_dll_handle(mg._dll_handle),
_interface(mg._interface),
_config(mg._config) {
}
qdMiniGame::~qdMiniGame() {
if (_interface)
_interface->finit();
release_interface();
}
bool qdMiniGame::start() {
if (load_interface()) {
return _interface->init(&qdmg::qdEngineInterfaceImpl::instance());
}
return false;
}
bool qdMiniGame::is_started() const {
return _interface != 0;
}
bool qdMiniGame::quant(float dt) {
if (_interface)
_interface->quant(dt);
return true;
}
bool qdMiniGame::end() {
if (_interface)
_interface->finit();
return release_interface();
}
int qdMiniGame::save_game(char *buffer, int buffer_size, qdGameScene *scene) {
bool need_release = false;
if (!is_started()) {
if (!load_interface()) return 0;
need_release = true;
}
qdMinigameSceneInterface *scene_int = qdmg::qdEngineInterfaceImpl::instance().scene_interface(scene);
int size = _interface->save_game(&qdmg::qdEngineInterfaceImpl::instance(), scene_int, buffer, buffer_size);
qdmg::qdEngineInterfaceImpl::instance().release_scene_interface(scene_int);
if (need_release)
release_interface();
return size;
}
int qdMiniGame::load_game(const char *buffer, int buffer_size, qdGameScene *scene) {
bool need_release = false;
if (!is_started()) {
if (!load_interface()) return 0;
need_release = true;
}
qdMinigameSceneInterface *scene_int = qdmg::qdEngineInterfaceImpl::instance().scene_interface(scene);
int size = _interface->load_game(&qdmg::qdEngineInterfaceImpl::instance(), scene_int, buffer, buffer_size);
qdmg::qdEngineInterfaceImpl::instance().release_scene_interface(scene_int);
if (need_release)
release_interface();
return size;
}
bool qdMiniGame::load_script(const xml::tag *p) {
int config_size = 0;
for (xml::tag::subtag_iterator it1 = p->subtags_begin(); it1 != p->subtags_end(); ++it1) {
if (it1->ID() == QDSCR_MINIGAME_CONFIG_PARAMETER)
config_size++;
}
for (xml::tag::subtag_iterator it = p->subtags_begin(); it != p->subtags_end(); ++it) {
switch (it->ID()) {
case QDSCR_FLAG:
set_flag(xml::tag_buffer(*it).get_int());
break;
case QDSCR_NAME:
set_name(it->data());
break;
case QDSCR_MINIGAME_DLL_NAME:
set_dll_name(it->data());
break;
case QDSCR_MINIGAME_GAME_NAME:
set_game_name(it->data());
break;
case QDSCR_MINIGAME_CONFIG_FILE:
set_config_file_name(Common::Path(it->data(), '\\'));
load_config();
_config.reserve(_config.size() + config_size);
break;
case QDSCR_MINIGAME_CONFIG_PARAMETER: {
/* if (!qdGameConfig::get_config().minigame_read_only_ini())
* has been removed since none of the games require this setting
*/
qdMinigameConfigParameter prm;
prm.load_script(&*it);
config_container_t::iterator cfg_it = Common::find(_config.begin(), _config.end(), prm);
if (cfg_it != _config.end()) {
cfg_it->set_data_string(prm.data_string());
cfg_it->set_data_count(prm.data_count());
}
}
break;
}
}
return true;
}
bool qdMiniGame::save_script(Common::WriteStream &fh, int indent) const {
for (int i = 0; i < indent; i++) {
fh.writeString("\t");
}
fh.writeString("<minigame");
if (name()) {
fh.writeString(Common::String::format(" name=\"%s\"", qdscr_XML_string(name())));
}
if (flags()) {
fh.writeString(Common::String::format(" flags=\"%d\"", flags()));
}
if (!_config_file_name.empty()) {
fh.writeString(Common::String::format(" config_file=\"%s\"", qdscr_XML_string(config_file_name().toString('\\'))));
}
if (!_dll_name.empty()) {
fh.writeString(Common::String::format(" dll_name=\"%s\"", qdscr_XML_string(dll_name())));
}
if (!_game_name.empty()) {
fh.writeString(Common::String::format(" game_name=\"%s\"", qdscr_XML_string(game_name())));
}
if (!_config.empty()) {
fh.writeString(">\r\n");
for (auto &it: _config) {
it.save_script(fh, indent + 1);
}
for (int i = 0; i < indent; i++) {
fh.writeString("\t");
}
fh.writeString("</minigame>\r\n");
} else {
fh.writeString("/>\r\n");
}
return true;
}
qdMiniGame &qdMiniGame::operator = (const qdMiniGame &mg) {
if (this == &mg) return *this;
*static_cast<qdNamedObject *>(this) = mg;
_dll_name = mg._dll_name;
_dll_handle = mg._dll_handle;
_interface = mg._interface;
_config_file_name = mg._config_file_name;
_config = mg._config;
return *this;
}
bool qdMiniGame::init() {
if (load_interface()) {
_interface->new_game(&qdmg::qdEngineInterfaceImpl::instance());
release_interface();
return true;
}
return false;
}
bool qdMiniGame::load_config() {
if (_config_file_name.empty())
return false;
_config.clear();
Common::INIFile ini;
Common::INIFile::SectionList section_list;
enumerateIniSections(ini, config_file_name(), section_list);
_config.reserve(section_list.size());
for (auto &it : section_list) {
qdMinigameConfigParameter prm;
prm.load_ini(ini, config_file_name(), it.name.c_str());
_config.push_back(prm);
}
return true;
}
const char *qdMiniGame::config_parameter_value(const char *cfg_param_name) const {
config_container_t::const_iterator it = Common::find(_config.begin(), _config.end(), cfg_param_name);
if (it != _config.end())
return it->data_string();
return NULL;
}
bool qdMiniGame::load_interface() {
if (!_dll_name.empty()) {
// maski
if (_dll_name == "Resource\\DLL\\tetris.dll") {
_interface = new qdTetrisMiniGame();
return true;
} else if (_dll_name == "Resource\\DLL\\scroll.dll") {
_interface = new qdScrollMiniGame();
return true;
} else if (_dll_name == "Resource\\DLL\\MaskyOrder.dll") {
_interface = new qdMaskyOrderMiniGame();
return true;
} else if (_dll_name == "Resource\\DLL\\maski_21.dll") {
_interface = new qdMaski21MiniGame(false);
return true;
} else if (_dll_name == "Resource\\DLL\\maski_21_random.dll") {
_interface = new qdMaski21MiniGame(true);
return true;
} else if (_dll_name == "Resource\\DLL\\kartiny.dll") {
_interface = new qdKartinyMiniGame();
return true;
} else if (_dll_name == "Resource\\DLL\\orchestra.dll") {
_interface = new qdOrchestraMiniGame();
return true;
} else if (_dll_name == "Resource\\DLL\\Arkada_avtomat.dll") {
_interface = new qdArkadaAvtomatMiniGame();
return true;
// 3mice1
} else if (_dll_name == "DLL\\Book_gusenica.dll" || _dll_name == "DLL\\Book_les.dll"
|| _dll_name == "DLL\\Book_buhta.dll") {
_interface = new qdBookAllMiniGame(_dll_name, g_engine->getLanguage());
return true;
} else if (_dll_name == "DLL\\Puzzle_ep01.dll" || _dll_name == "DLL\\Puzzle_ep02.dll"
|| _dll_name == "DLL\\Puzzle_ep04.dll" || _dll_name == "DLL\\Puzzle_ep05.dll"
|| _dll_name == "DLL\\Puzzle_ep07.dll" || _dll_name == "DLL\\Puzzle_ep08.dll"
|| _dll_name == "DLL\\Puzzle_ep13.dll") {
_interface = new qdPuzzleAllMiniGame(_dll_name, g_engine->getLanguage());
return true;
// shveik
} else if (_dll_name == "DLL\\inv_popup.dll") {
_interface = new qdInvPopupMiniGame();
return true;
} else if (_dll_name == "DLL\\ShveikShkatulka.dll") {
_interface = new qdShveikShkatulkaMiniGame();
return true;
} else if (_dll_name == "DLL\\ShveikPortret.dll") {
_interface = new qdShveikPortretMiniGame();
return true;
// klepa
} else if (_dll_name == "DLL\\puzzle.dll") {
_interface = create_adv_minigame(_dll_name.c_str(), createMinigamePuzzle);
return true;
} else if (_dll_name == "DLL\\Karaoke.dll") {
_interface = create_adv_minigame(_dll_name.c_str(), createMinigameKaraoke);
return true;
// 3mice2
} else if (_dll_name == "DLL\\3Mice2_sbor_karty.dll") {
_interface = new qd3mice2SborKartyMiniGame();
return true;
} else if (_dll_name == "DLL\\3Mice2_kovrik.dll") {
_interface = new qd3mice2KovrikMiniGame();
return true;
} else if (_dll_name == "DLL\\3Mice2_sudoku.dll") {
_interface = new qd3mice2SudokuMiniGame();
return true;
} else if (_dll_name == "DLL\\3Mice2_states.dll") {
_interface = new qd3mice2StatesMiniGame();
return true;
} else if (_dll_name == "DLL\\3Mice2_testo.dll") {
_interface = new qd3mice2TestoMiniGame();
return true;
} else if (_dll_name == "DLL\\3Mice2_plate.dll") {
_interface = new qd3mice2PlateMiniGame();
return true;
} else if (_dll_name == "DLL\\3Mice2_raskr1.dll") {
_interface = new qd3mice2RaskrAllMiniGame(1);
return true;
} else if (_dll_name == "DLL\\3Mice2_raskr2.dll") {
_interface = new qd3mice2RaskrAllMiniGame(2);
return true;
} else if (_dll_name == "DLL\\3Mice2_raskr3.dll") {
_interface = new qd3mice2RaskrAllMiniGame(3);
return true;
} else if (_dll_name == "DLL\\3Mice2_raskr4.dll") {
_interface = new qd3mice2RaskrAllMiniGame(4);
return true;
} else if (_dll_name == "DLL\\3Mice2_babochka.dll") {
_interface = new qd3mice2BabochkaMiniGame();
return true;
// dogncat
} else if (_dll_name == "DLL\\triangles.dll") {
_interface = create_adv_minigame(_dll_name.c_str(), createMinigameTriangle);
return true;
} else if (_dll_name == "DLL\\scores.dll") {
_interface = create_adv_minigame(_dll_name.c_str(), createMinigameScores);
return true;
// dogncat2
} else if (_dll_name == "DLL\\swap.dll") {
_interface = create_adv_minigame(_dll_name.c_str(), createMinigameSwap);
return true;
} else {
warning("qdMiniGame::load_interface(): trying to load dll: %s", _dll_name.c_str());
// call here dll->open_game_interface(game_name())
_interface = new qdEmptyMiniGameInterface;
return true;
}
}
return false;
}
bool qdMiniGame::release_interface() {
if (_interface)
close_adv_minigame(_interface);
_interface = nullptr;
return true;
}
} // namespace QDEngine

View File

@@ -0,0 +1,135 @@
/* 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/>.
*
*/
#ifndef QDENGINE_QDCORE_QD_MINIGAME_H
#define QDENGINE_QDCORE_QD_MINIGAME_H
#include "qdengine/qdcore/qd_named_object.h"
#include "qdengine/qdcore/qd_minigame_config.h"
namespace QDEngine {
class qdMiniGameInterface;
class qdEmptyMiniGameInterface;
//! Мини-игра.
class qdMiniGame : public qdNamedObject {
public:
qdMiniGame();
qdMiniGame(const qdMiniGame &mg);
~qdMiniGame();
qdMiniGame &operator = (const qdMiniGame &mg);
int named_object_type() const {
return QD_NAMED_OBJECT_MINIGAME;
}
//! Инициализация данных, вызывается при старте и перезапуске основной игры.
bool init();
const Common::Path config_file_name() const {
return _config_file_name;
}
void set_config_file_name(const Common::Path &file_name) {
_config_file_name = file_name;
}
bool has_config_file() const {
return !_config_file_name.empty();
}
//! Старт игры, вызывается при заходе на сцену, которой управляет игра.
bool start();
bool is_started() const;
//! Логический квант игры, параметр - время, которое должно пройти в игре (в секундах).
bool quant(float dt);
//! Окончание игры, вызывается при уходе со сцены, которая управляется игрой.
bool end();
/// Сохранение, вызывается при сохранении сцены \a scene
int save_game(char *buffer, int buffer_size, qdGameScene *scene);
/// Загрузка, вызывается при загрузке сцены \a scene
int load_game(const char *buffer, int buffer_size, qdGameScene *scene);
//! Возвращает имя подгружаемой для игры dll.
const char *dll_name() const {
return _dll_name.c_str();
}
//! Устанавливает имя подгружаемой для игры dll.
void set_dll_name(const char *p) {
_dll_name = p;
}
bool has_dll() const {
return !_dll_name.empty();
}
const char *game_name() const {
return _game_name.c_str();
}
void set_game_name(const char *p) {
_game_name = p;
}
//! Загрузка данных из скрипта.
bool load_script(const xml::tag *p);
//! Запись данных в скрипт.
bool save_script(Common::WriteStream &fh, int indent = 0) const;
typedef Std::vector<qdMinigameConfigParameter> config_container_t;
const config_container_t &config() const {
return _config;
}
void set_config(const config_container_t &cfg) {
_config = cfg;
}
bool load_config();
/// Возвращает значение параметра с именем cfg_param_name.
/**
Если параметр с таким именем не найден, то возвращает 0.
*/
const char *config_parameter_value(const char *cfg_param_name) const;
private:
//! Имя подгружаемой для игры dll.
Common::String _dll_name;
//! .ini файл с настройками игры.
Common::Path _config_file_name;
//! Имя игры, по которому она ищется в dll.
Common::String _game_name;
//! Хэндл подгруженной dll.
void *_dll_handle;
//! Интерфейс к игре из dll.
qdMiniGameInterface *_interface;
//! Настройки игры.
config_container_t _config;
bool load_interface();
bool release_interface();
};
} // namespace QDEngine
#endif // QDENGINE_QDCORE_QD_MINIGAME_H

View File

@@ -0,0 +1,122 @@
/* 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/str.h"
#include "qdengine/qd_fwd.h"
#include "qdengine/parser/qdscr_parser.h"
#include "qdengine/parser/xml_tag_buffer.h"
#include "qdengine/qdcore/qd_setup.h"
#include "qdengine/qdcore/qd_minigame_config.h"
namespace QDEngine {
qdMinigameConfigParameter::qdMinigameConfigParameter() : _data_type(PRM_DATA_STRING) {
_data_count = 1;
_is_data_valid = true;
}
qdMinigameConfigParameter::~qdMinigameConfigParameter() {
}
bool qdMinigameConfigParameter::load_script(const xml::tag *p) {
set_data_string(p->data());
for (xml::tag::subtag_iterator it = p->subtags_begin(); it != p->subtags_end(); ++it) {
switch (it->ID()) {
case QDSCR_NAME:
set_name(it->data());
break;
// case QDSCR_COMMENT:
// set_comment(it->data());
// break;
// case QDSCR_TYPE:
// set_data_type(data_type_t(xml::tag_buffer(*it).get_int()));
// break;
case QDSCR_SIZE:
set_data_count(xml::tag_buffer(*it).get_int());
break;
}
}
return true;
}
bool qdMinigameConfigParameter::save_script(Common::WriteStream &fh, int indent) const {
for (int i = 0; i < indent; i++) {
fh.writeString("\t");
}
fh.writeString("<minigame_config_prm");
fh.writeString(Common::String::format(" name=\"%s\"", qdscr_XML_string(name())));
// fh < " type=\"" <= int(_data_type) < "\"";
// if(!comment_.empty())
// fh < " comment=\"" < qdscr_XML_string(comment()) < "\"";
if (_data_count > 1) {
fh.writeString(Common::String::format(" size=\"%d\"", _data_count));
}
fh.writeString(">");
fh.writeString(qdscr_XML_string(data_string()));
fh.writeString("</minigame_config_prm>\r\n");
return true;
}
bool qdMinigameConfigParameter::validate_data() {
_is_data_valid = true;
return true;
}
bool qdMinigameConfigParameter::load_ini(Common::INIFile& ini, const Common::Path &ini_file, const char *ini_section) {
set_name(ini_section);
Common::String str = getIniKey(ini, ini_file, ini_section, "type");
if (!str.empty()) {
if (str.equalsIgnoreCase("string"))
set_data_type(PRM_DATA_STRING);
else if (str.equalsIgnoreCase("file"))
set_data_type(PRM_DATA_FILE);
else if (str.equalsIgnoreCase("object"))
set_data_type(PRM_DATA_OBJECT);
}
str = getIniKey(ini, ini_file, ini_section, "count");
if (!str.empty())
set_data_count(atoi(str.c_str()));
str = getIniKey(ini, ini_file, ini_section, "value");
if (!str.empty()) {
set_data_string(str.c_str());
}
str = getIniKey(ini, ini_file, ini_section, "comment");
if (!str.empty())
set_comment(str.c_str());
return true;
}
} // namespace QDEngine

View File

@@ -0,0 +1,160 @@
/* 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/>.
*
*/
#ifndef QDENGINE_QDCORE_QD_MINIGAME_CONFIG_H
#define QDENGINE_QDCORE_QD_MINIGAME_CONFIG_H
#include "common/formats/ini-file.h"
#include "common/path.h"
namespace QDEngine {
/// Конфигурационные данные для миниигры.
/**
В .ini файле:
имя секции - название параметра
ключи
type = "string" / "file" / "object" - тип данных data_type_
count - количество данных, data_count_
value - строка данных, data_string_
comment - комментарий, comment_
Если тип данных не указан, то считается равным "string".
Пример:
[ParameterExample]
type = "object"
count = 2
value = "Object0"
comment = "пример параметра"
*/
class qdMinigameConfigParameter {
public:
qdMinigameConfigParameter();
~qdMinigameConfigParameter();
bool operator == (const qdMinigameConfigParameter &prm) const {
return (_name == prm._name);
}
bool operator == (const char *str) const {
return !strcmp(str, name());
}
//! Тип данных.
enum data_type_t {
/// данные - строка
PRM_DATA_STRING,
/// данные - имя файла
PRM_DATA_FILE,
/// данные - имя объекта из игровой сцены
PRM_DATA_OBJECT
};
const char *name() const {
return _name.c_str();
}
void set_name(const char *name) {
_name = name;
}
data_type_t data_type() const {
return _data_type;
}
void set_data_type(data_type_t tp) {
_data_type = tp;
}
const char *data_string() const {
return _data_string.c_str();
}
void set_data_string(const char *str) {
_data_string = str;
}
const char *comment() const {
return _comment.c_str();
}
void set_comment(const char *str) {
_comment = str;
}
int data_count() const {
return _data_count;
}
void set_data_count(int cnt) {
_data_count = cnt;
}
/// Проверяет валидность данных.
/**
Если данные не того формата, то возвращает false и устанавливает
is_data_valid_ в false.
*/
bool validate_data();
bool is_data_valid() const {
return _is_data_valid;
}
//! Загрузка данных из скрипта.
bool load_script(const xml::tag *p);
//! Запись данных в скрипт.
bool save_script(Common::WriteStream &fh, int indent = 0) const;
//! Загрузка данных из .ini файла.
bool load_ini(Common::INIFile& ini, const Common::Path &ini_file, const char *ini_section);
private:
/// Имя параметра, данные из миниигры запрашиваются по нему.
Common::String _name;
/// Тип данных.
data_type_t _data_type;
/// Количество данных.
/**
Используется для числовых данных (указывает, сколько чисел записано в data_string_)
и объектов (в сцене создаётся соответствующее количество копий объекта по имени data_string_,
к их именам добавляется четырёхзначный порядковый номер).
По умолчанию = 1.
*/
int _data_count;
/// Строка данных.
/**
Формат зависит от типа данных.
Для числовых данных - числа в текстовом виде через пробел, для
остальных типов - просто строка.
*/
Common::String _data_string;
/// Комментарий.
Common::String _comment;
/// false если строка данных не того формата.
bool _is_data_valid;
};
} // namespace QDEngine
#endif // QDENGINE_QDCORE_QD_MINIGAME_CONFIG_H

View File

@@ -0,0 +1,614 @@
/* 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 "qdengine/qd_fwd.h"
#include "qdengine/parser/xml_fwd.h"
#include "qdengine/qdcore/qd_minigame.h"
#include "qdengine/qdcore/qd_counter.h"
#include "qdengine/qdcore/qd_minigame_interface.h"
#include "qdengine/qdcore/qd_engine_interface.h"
#include "qdengine/qdcore/qd_rnd.h"
#include "qdengine/system/graphics/gr_dispatcher.h"
#include "qdengine/system/input/keyboard_input.h"
#include "qdengine/qdcore/qd_game_dispatcher.h"
#include "qdengine/qdcore/qd_game_scene.h"
#include "qdengine/qdcore/qd_game_object_mouse.h"
#include "qdengine/qdcore/qd_game_object_animated.h"
#include "qdengine/qdcore/qd_game_object_moving.h"
#include "qdengine/qdcore/qd_interface_dispatcher.h"
#include "qdengine/qdcore/qd_interface_screen.h"
#include "qdengine/qdcore/qd_interface_text_window.h"
namespace QDEngine {
template<>
void mgVect3<float>::write(Common::WriteStream &out) const {
out.writeFloatLE(x);
out.writeFloatLE(y);
out.writeFloatLE(z);
}
template<>
void mgVect3<float>::read(Common::ReadStream &in) {
x = in.readFloatLE();
y = in.readFloatLE();
z = in.readFloatLE();
}
namespace qdmg {
class qdMinigameObjectInterfaceImplBase : public qdMinigameObjectInterface {
public:
qdMinigameObjectInterfaceImplBase(qdGameObjectAnimated *object) : _object(object) {
assert(_object);
}
/// Имя объекта.
const char *name() const;
//! Возвращает true, если у объекта есть состояние с именем state_name.
bool has_state(const char *state_name) const;
//! Возвращает имя активного в данный момент состояния.
//! Вернёт 0, если активного состояния нету.
const char *current_state_name() const;
//! Возвращает true, если состояние с именем state_name включено в данный момент.
bool is_state_active(const char *state_name) const;
//! Возвращает true, если состояние с именем state_name в данный момент ожидает активации.
bool is_state_waiting_activation(const char *state_name) const;
//! Возвращает номер включенного в данный момент состояния.
int current_state_index() const;
//! Включает состояние с именем state_name.
bool set_state(const char *state_name);
//! Включает состояние номер state_index (отсчитывается от нуля).
bool set_state(int state_index);
//! Возвращает номер состояния с именем state_name.
/**
Отсчитывается от нуля, если такого состояния нет, то
возвращает -1.
*/
int state_index(const char *state_name) const;
//! Возвращает координаты объекта в мировой системе координат.
mgVect3f R() const;
//! Устанавливает координаты объекта в мировой системе координат.
void set_R(const mgVect3f &r);
/// Проверка, попадает ли точка с экранными координатами pos в объект
bool hit_test(const mgVect2i &pos) const;
//! Возвращает координаты объекта в экранной системе координат.
mgVect2i screen_R() const;
//! Обновляет координаты объекта в экранной системе координат.
bool update_screen_R();
//! Возвращает текущие экранные размеры объекта в пикселах.
mgVect2i screen_size() const;
//! Устанавливает поворот картинки объекта.
//! angle - угол, на который должна быть повёрнута картинка, в радианах
//! speed - скорость поворота, в радианах в секунду
void set_screen_rotation(float angle, float speed);
//! Возвращает поворот картинки объекта в радианах.
float screen_rotation() const;
//! Устанавливает масштабирование картинки объекта.
void set_screen_scale(const mgVect2f &scale, const mgVect2f &speed);
//! Возвращает масштаб картинки объекта.
mgVect2f screen_scale() const;
//! Возвращает цвет затенения.
int shadow_color() const;
//! Возвращает прозрачность затенения, значения - [0, 255], если равно -1, то затенения нет.
int shadow_alpha() const;
//! Устанавливает затенение.
bool set_shadow(int shadow_color, int shadow_alpha);
//! Возвращает true, если объект не спрятан.
virtual bool is_visible() const;
//! Возвращает размеры объекта в мировой системе координат.
mgVect3f bound() const;
private:
qdGameObjectAnimated *_object;
};
class qdMinigameObjectInterfaceImpl : public qdMinigameObjectInterfaceImplBase {
public:
qdMinigameObjectInterfaceImpl(qdGameObjectAnimated *object) : qdMinigameObjectInterfaceImplBase(object) { }
//! Команда персонажу идти к точке target_position.
/**
Если второй параметр равен false, то если target_position непроходима
персонаж идёт к ближайшей от target_position проходимой точке.
*/
bool move(const mgVect3f &target_position, bool disable_target_change = false) {
return false;
}
float direction_angle() const {
return 0.f;
}
bool set_direction_angle(float direction) {
return false;
}
};
//! Интерфейс к персонажу.
class qdMinigamePersonageInterfaceImpl : public qdMinigameObjectInterfaceImplBase {
public:
qdMinigamePersonageInterfaceImpl(qdGameObjectMoving *p);
//! Команда персонажу идти к точке target_position.
/**
Если второй параметр равен false, то если target_position непроходима
персонаж идёт к ближайшей от target_position проходимой точке.
*/
bool move(const mgVect3f &target_position, bool disable_target_change = false);
float direction_angle() const;
bool set_direction_angle(float direction);
private:
qdGameObjectMoving *_personage_object;
};
//! Интерфейс к сцене.
class qdMinigameSceneInterfaceImpl : public qdMinigameSceneInterface {
public:
qdMinigameSceneInterfaceImpl(qdGameScene *scene);
/// Имя сцены.
const char *name() const;
//! Создаёт интерфейс к объекту с именем object_name.
qdMinigameObjectInterface *object_interface(const char *object_name, bool test = false);
//! Создаёт интерфейс к персонажу с именем personage_name.
qdMinigameObjectInterface *personage_interface(const char *personage_name);
//! Активация персонажа с именем personage_name.
bool activate_personage(const char *personage_name);
//! Преобразование из экранных координат в мировые.
mgVect3f screen2world_coords(const mgVect2i &screen_pos, float screen_depth = 0) const;
//! Преобразование из мировых координат в экранные.
mgVect2i world2screen_coords(const mgVect3f &world_pos) const;
//! Возвращает "глубину" точки с координатами pos в мировой системе координат.
float screen_depth(const mgVect3f &pos) const;
//! Возвращает мировые координаты точки на сетке по её экранным координатам.
mgVect3f screen2grid_coords(const mgVect2i &screen_pos) const;
//! Создаёт интерфейс к объекту, который взят мышью в данный момент.
qdMinigameObjectInterface *mouse_object_interface() const;
//! Создаёт интерфейс к объекту, по которому кликнули мышью.
qdMinigameObjectInterface *mouse_click_object_interface() const;
//! Создаёт интерфейс к объекту, по которому кликнули правой кнопкой мыши.
qdMinigameObjectInterface *mouse_right_click_object_interface() const;
//! Создаёт интерфейс к объекту, над которым находится мышиный курсор.
qdMinigameObjectInterface *mouse_hover_object_interface() const;
const char *minigame_parameter(const char *parameter_name) const;
void release_object_interface(qdMinigameObjectInterface *p) const;
private:
qdGameScene *_scene;
};
/// Интерфейс к счётчику.
class qdMinigameCounterInterfaceImpl : public qdMinigameCounterInterface {
public:
qdMinigameCounterInterfaceImpl(qdCounter *counter) : _counter(counter) {
assert(_counter);
}
/// возвращает текущее значение счётчика
int value() const;
/// устанавливает текущее значение счётчика
void set_value(int value);
/// добавляет к текущему значению счётчика value_delta
void add_value(int value_delta);
private:
qdCounter *_counter;
};
} // namespace qdmg
/* ----------------------------- EXTERN SECTION ----------------------------- */
/* --------------------------- PROTOTYPE SECTION ---------------------------- */
/* --------------------------- DEFINITION SECTION --------------------------- */
namespace qdmg {
qdMinigameSceneInterfaceImpl::qdMinigameSceneInterfaceImpl(qdGameScene *scene) : _scene(scene) {
assert(_scene);
}
const char *qdMinigameSceneInterfaceImpl::minigame_parameter(const char *parameter_name) const {
if (const qdMiniGame * p = _scene->minigame())
return p->config_parameter_value(parameter_name);
return NULL;
}
qdMinigameObjectInterface *qdMinigameSceneInterfaceImpl::mouse_object_interface() const {
if (qdGameDispatcher * dp = qdGameDispatcher::get_dispatcher()) {
if (dp->mouse_object()->object())
return new qdMinigameObjectInterfaceImpl(dp->mouse_object()->object());
}
return NULL;
}
qdMinigameObjectInterface *qdMinigameSceneInterfaceImpl::mouse_click_object_interface() const {
if (qdNamedObject * p = _scene->mouse_click_object()) {
if (qdGameObjectAnimated * obj = dynamic_cast<qdGameObjectAnimated * >(p))
return new qdMinigameObjectInterfaceImpl(obj);
}
return 0;
}
qdMinigameObjectInterface *qdMinigameSceneInterfaceImpl::mouse_right_click_object_interface() const {
if (qdNamedObject * p = _scene->mouse_right_click_object()) {
if (qdGameObjectAnimated * obj = dynamic_cast<qdGameObjectAnimated * >(p))
return new qdMinigameObjectInterfaceImpl(obj);
}
return 0;
}
qdMinigameObjectInterface *qdMinigameSceneInterfaceImpl::mouse_hover_object_interface() const {
if (qdNamedObject * p = _scene->mouse_hover_object()) {
if (qdGameObjectAnimated * obj = dynamic_cast<qdGameObjectAnimated * >(p))
return new qdMinigameObjectInterfaceImpl(obj);
}
return 0;
}
mgVect3f qdMinigameSceneInterfaceImpl::screen2world_coords(const mgVect2i &screen_pos, float screen_depth) const {
const qdCamera *cp = _scene->get_camera();
Vect3f pos = cp->rscr2global(cp->scr2rscr(Vect2s(screen_pos.x, screen_pos.y)), screen_depth);
return mgVect3f(pos.x, pos.y, pos.z);
}
mgVect2i qdMinigameSceneInterfaceImpl::world2screen_coords(const mgVect3f &world_pos) const {
const qdCamera *cp = _scene->get_camera();
Vect3f v = cp->global2camera_coord(Vect3f(world_pos.x, world_pos.y, world_pos.z));
Vect2i screen_pos = cp->camera_coord2scr(v);
return mgVect2i(screen_pos.x, screen_pos.y);
}
float qdMinigameSceneInterfaceImpl::screen_depth(const mgVect3f &pos) const {
const qdCamera *cp = _scene->get_camera();
Vect3f v = cp->global2camera_coord(Vect3f(pos.x, pos.y, pos.z));
return v.z;
}
mgVect3f qdMinigameSceneInterfaceImpl::screen2grid_coords(const mgVect2i &screen_pos) const {
const qdCamera *cp = _scene->get_camera();
Vect3f pos = cp->scr2plane(Vect2s(screen_pos.x, screen_pos.y));
return mgVect3f(pos.x, pos.y, pos.z);
}
void qdMinigameSceneInterfaceImpl::release_object_interface(qdMinigameObjectInterface *p) const {
delete p;
}
const char *qdMinigameSceneInterfaceImpl::name() const {
return _scene->name();
}
qdMinigameObjectInterface *qdMinigameSceneInterfaceImpl::object_interface(const char *object_name, bool test) {
if (qdGameObjectAnimated * p = dynamic_cast<qdGameObjectAnimated * >(_scene->get_object(object_name)))
return new qdMinigameObjectInterfaceImpl(p);
if (!test)
warning("object_interface(): Unknown object '%s'", transCyrillic(object_name));
return NULL;
}
qdMinigameObjectInterface *qdMinigameSceneInterfaceImpl::personage_interface(const char *personage_name) {
if (qdGameObjectMoving * p = dynamic_cast<qdGameObjectMoving * >(_scene->get_object(personage_name)))
return new qdMinigamePersonageInterfaceImpl(p);
warning("personage_interface(): Unknown personage '%s'", transCyrillic(personage_name));
return NULL;
}
bool qdMinigameSceneInterfaceImpl::activate_personage(const char *personage_name) {
if (qdGameObjectMoving * p = dynamic_cast<qdGameObjectMoving * >(_scene->get_object(personage_name))) {
_scene->set_active_personage(p);
return true;
}
return false;
}
qdMinigamePersonageInterfaceImpl::qdMinigamePersonageInterfaceImpl(qdGameObjectMoving *object) : qdMinigameObjectInterfaceImplBase(object), _personage_object(object) {
assert(_personage_object);
}
bool qdMinigamePersonageInterfaceImpl::move(const mgVect3f &target_position, bool disable_target_change) {
if (_personage_object) {
Vect3f target(target_position.x, target_position.y, target_position.z);
return _personage_object->move(target, disable_target_change);
}
return false;
}
float qdMinigamePersonageInterfaceImpl::direction_angle() const {
if (_personage_object)
return _personage_object->direction_angle();
return 0.f;
}
bool qdMinigamePersonageInterfaceImpl::set_direction_angle(float direction) {
if (_personage_object)
return _personage_object->set_direction(direction);
return false;
}
mgVect3f qdMinigameObjectInterfaceImplBase::bound() const {
if (_object) {
Vect3f b = _object->bound();
return mgVect3f(b.x, b.y, b.z);
}
return mgVect3f(0, 0, 0);
}
const char *qdMinigameObjectInterfaceImplBase::name() const {
if (_object)
return _object->name();
return 0;
}
bool qdMinigameObjectInterfaceImplBase::has_state(const char *state_name) const {
if (_object && _object->get_state(state_name))
return true;
return false;
}
const char *qdMinigameObjectInterfaceImplBase::current_state_name() const {
if (_object) {
if (const qdGameObjectState * p = _object->get_cur_state())
return p->name();
}
return 0;
}
bool qdMinigameObjectInterfaceImplBase::is_state_active(const char *state_name) const {
if (!has_state(state_name))
warning("is_state_active(): Unknown state '%s'", transCyrillic(state_name));
return _object->is_state_active(state_name);
}
bool qdMinigameObjectInterfaceImplBase::is_state_waiting_activation(const char *state_name) const {
return _object->is_state_waiting(state_name);
}
int qdMinigameObjectInterfaceImplBase::current_state_index() const {
return _object->cur_state();
}
bool qdMinigameObjectInterfaceImplBase::set_state(const char *state_name) {
int idx = state_index(state_name);
if (idx != -1) {
_object->set_state(idx);
return true;
}
warning("set_state(): Unknown state '%s'", transCyrillic(state_name));
return false;
}
bool qdMinigameObjectInterfaceImplBase::set_state(int state_index) {
_object->set_state(state_index);
return true;
}
int qdMinigameObjectInterfaceImplBase::state_index(const char *state_name) const {
if (const qdGameObjectState * p = _object->get_state(state_name))
return _object->get_state_index(p);
warning("state_index(): Unknown state '%s'", transCyrillic(state_name));
return -1;
}
mgVect3f qdMinigameObjectInterfaceImplBase::R() const {
Vect3f r = _object->R();
return mgVect3f(r.x, r.y, r.z);
}
void qdMinigameObjectInterfaceImplBase::set_R(const mgVect3f &r) {
Vect3f rr(r.x, r.y, r.z);
_object->set_pos(rr);
}
bool qdMinigameObjectInterfaceImplBase::hit_test(const mgVect2i &pos) const {
return _object->hit(pos.x, pos.y);
}
mgVect2i qdMinigameObjectInterfaceImplBase::screen_R() const {
return mgVect2i(_object->screen_pos().x, _object->screen_pos().y);
}
bool qdMinigameObjectInterfaceImplBase::update_screen_R() {
_object->update_screen_pos();
return true;
}
mgVect2i qdMinigameObjectInterfaceImplBase::screen_size() const {
Vect2s sz = _object->screen_size();
return mgVect2i(sz.x, sz.y);
}
void qdMinigameObjectInterfaceImplBase::set_screen_rotation(float angle, float speed) {
_object->set_screen_rotation(angle, speed);
}
float qdMinigameObjectInterfaceImplBase::screen_rotation() const {
return _object->screen_rotation();
}
void qdMinigameObjectInterfaceImplBase::set_screen_scale(const mgVect2f &scale, const mgVect2f &speed) {
_object->set_screen_scale(Vect2f(scale.x, scale.y), Vect2f(speed.x, speed.y));
}
mgVect2f qdMinigameObjectInterfaceImplBase::screen_scale() const {
return mgVect2f(_object->screen_scale().x, _object->screen_scale().y);
}
int qdMinigameObjectInterfaceImplBase::shadow_color() const {
return _object->shadow_color();
}
int qdMinigameObjectInterfaceImplBase::shadow_alpha() const {
return _object->shadow_alpha();
}
bool qdMinigameObjectInterfaceImplBase::set_shadow(int shadow_color, int shadow_alpha) {
_object->set_shadow(shadow_color, shadow_alpha);
return true;
}
bool qdMinigameObjectInterfaceImplBase::is_visible() const {
return _object->is_visible();
}
qdEngineInterfaceImpl *g_qdi = nullptr;
const qdEngineInterfaceImpl &qdEngineInterfaceImpl::instance() {
if (!g_qdi)
g_qdi = new qdEngineInterfaceImpl;
return *g_qdi;
}
qdMinigameSceneInterface *qdEngineInterfaceImpl::current_scene_interface() const {
if (qdGameDispatcher * dp = qdGameDispatcher::get_dispatcher())
return new qdMinigameSceneInterfaceImpl(dp->get_active_scene());
return NULL;
}
qdMinigameSceneInterface *qdEngineInterfaceImpl::scene_interface(qdGameScene *scene) const {
return new qdMinigameSceneInterfaceImpl(scene);
}
mgVect2i qdEngineInterfaceImpl::screen_size() const {
if (grDispatcher * dp = grDispatcher::instance())
return mgVect2i(dp->get_SizeX(), dp->get_SizeY());
return mgVect2i(0, 0);
}
void qdEngineInterfaceImpl::release_scene_interface(qdMinigameSceneInterface *p) const {
delete p;
}
qdMinigameCounterInterface *qdEngineInterfaceImpl::counter_interface(const char *counter_name) const {
if (qdGameDispatcher * dp = qdGameDispatcher::get_dispatcher())
return new qdMinigameCounterInterfaceImpl(dp->get_counter(counter_name));
return 0;
}
void qdEngineInterfaceImpl::release_counter_interface(qdMinigameCounterInterface *p) const {
delete p;
}
bool qdEngineInterfaceImpl::is_key_pressed(int vkey) const {
return keyboardDispatcher::instance()->is_pressed(vkey);
}
bool qdEngineInterfaceImpl::is_mouse_event_active(qdMinigameMouseEvent event_id) const {
return mouseDispatcher::instance()->is_event_active(mouseDispatcher::mouseEvent(event_id));
}
mgVect2i qdEngineInterfaceImpl::mouse_cursor_position() const {
if (qdGameDispatcher * dp = qdGameDispatcher::get_dispatcher()) {
Vect2f pos = dp->mouse_cursor_pos();
return mgVect2i(pos.x, pos.y);
}
return mgVect2i(0, 0);
}
bool qdEngineInterfaceImpl::add_hall_of_fame_entry(int score) const {
if (qdGameDispatcher * dp = qdGameDispatcher::get_dispatcher())
return dp->add_hall_of_fame_entry(score);
return false;
}
bool qdEngineInterfaceImpl::set_interface_text(const char *screen_name, const char *control_name, const char *text) const {
if (qdInterfaceDispatcher * dp = qdInterfaceDispatcher::get_dispatcher()) {
qdInterfaceScreen *scr = screen_name ? dp->get_screen(screen_name) : dp->selected_screen();
if (scr) {
qdInterfaceElement *el = scr->get_element(control_name);
if (el && el->get_element_type() == qdInterfaceElement::EL_TEXT_WINDOW) {
static_cast<qdInterfaceTextWindow *>(el)->set_input_string(text);
return true;
}
}
}
return false;
}
int qdMinigameCounterInterfaceImpl::value() const {
if (_counter)
return _counter->value();
return 0;
}
void qdMinigameCounterInterfaceImpl::set_value(int value) {
if (_counter)
_counter->set_value(value);
}
void qdMinigameCounterInterfaceImpl::add_value(int value_delta) {
if (_counter)
_counter->add_value(value_delta);
}
} // namespace qdmg
} // namespace QDEngine

Some files were not shown because too many files have changed in this diff Show More