Files
scummvm-cursorfix/engines/qdengine/qdcore/qd_condition.cpp
2026-02-02 04:50:13 +01:00

542 lines
14 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/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