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

View File

@@ -0,0 +1,428 @@
/* 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 "petka/petka.h"
#include "petka/q_manager.h"
#include "petka/q_system.h"
#include "petka/flc.h"
#include "petka/video.h"
#include "petka/sound.h"
#include "petka/objects/heroes.h"
#include "petka/interfaces/panel.h"
namespace Petka {
QObjectPetka::QObjectPetka()
: _walk(nullptr) {
_field7C = 1;
_reaction = nullptr;
_heroReaction = nullptr;
_sender = nullptr;
_isWalking = false;
_x = 574;
_y = 444;
_z = 200;
// _surfId = -5;
_surfH = 0;
_surfW = 0;
_k = 1.0;
_x_ = _y_ = 0;
_destX = _destY = 0;
_imageId = 0;
_fieldB4 = 0;
}
void QObjectPetka::processMessage(const QMessage &arg) {
QMessage msg = arg;
if (msg.opcode == kImage) {
msg.opcode = kSet;
_imageId = msg.arg1;
_walk.reset(new Walk(_imageId + 10));
QObjectBG *room = g_vm->getQSystem()->_room;
if (room)
_walk->setBackground(g_vm->resMgr()->findResourceName(room->_resourceId));
}
if (msg.opcode == kSaid || msg.opcode == kStand) {
msg.opcode = kSet;
msg.arg1 = _imageId;
msg.arg2 = 1;
}
if (msg.opcode == kSay) {
msg.opcode = kSet;
msg.arg1 = _imageId + 1;
msg.arg2 = 1;
}
if (msg.opcode == kSet || msg.opcode == kPlay) {
_field7C = msg.arg1 == _imageId || msg.opcode == kPlay;
}
if (msg.opcode != kWalk) {
if (msg.opcode == kWalked && _heroReaction) {
QReaction *reaction = _heroReaction;
_heroReaction = nullptr;
_sender->processReaction(reaction);
}
QMessageObject::processMessage(msg);
if (msg.opcode == kSet || msg.opcode == kPlay) {
initSurface();
if (!g_vm->getQSystem()->_totalInit) {
setPos(Common::Point(_x_, _y_), false);
}
}
}
}
void QObjectPetka::initSurface() {
QManager *resMgr = g_vm->resMgr();
FlicDecoder *flc = resMgr->getFlic(_resourceId);
_surfW = flc->getWidth() * _k;
_surfH = flc->getHeight() * _k;
}
void QObjectPetka::walk(int x, int y) {
Common::Point walkPos(x, y);
if (!_isShown) {
setPos(walkPos, false);
return;
}
Common::Point currPos;
if (_isWalking) {
currPos = _walk->currPos();
} else {
currPos.x = _x_;
currPos.y = _y_;
}
if (currPos.sqrDist(walkPos) >= 25) {
_walk->init(currPos, walkPos);
_destX = x;
_destY = y;
_resourceId = _imageId + _walk->getSpriteId() + 10;
_isWalking = true;
_animate = true;
initSurface();
FlicDecoder *flc = g_vm->resMgr()->getFlic(_resourceId);
flc->setFrame(1);
sub_408940();
g_vm->videoSystem()->makeAllDirty();
_field7C = 0;
_time = 0;
_holdMessages = true;
}
}
void QObjectPetka::draw() {
if (!_isShown || _resourceId == -1) {
return;
}
if (_animate && _startSound) {
if (_sound) {
_sound->play(_loopedSound);
if (_loopedSound) {
_sound = nullptr;
}
}
_startSound = false;
}
FlicDecoder *flc = g_vm->resMgr()->getFlic(_resourceId);
if (!flc) {
return;
}
Graphics::Surface *conv = flc->getCurrentFrame()->convertTo(g_system->getScreenFormat(), flc->getPalette());
Common::Rect srcRect(0, 0, conv->w, conv->h);
Common::Rect dstRect(0, 0, _surfW, _surfH);
dstRect.translate(_x - g_vm->getQSystem()->_xOffset, _y);
g_vm->videoSystem()->transBlitFrom(*conv, srcRect, dstRect, flc->getTransColor(conv->format));
conv->free();
delete conv;
}
void QObjectPetka::setPos(Common::Point p, bool) {
QSystem *sys = g_vm->getQSystem();
int xOff = sys->_xOffset;
Common::Rect dirty(_x - xOff, _y, _surfW + _x - xOff, _surfH + _y);
g_vm->videoSystem()->addDirtyRect(dirty);
p.y = MIN<int16>(p.y, 480);
FlicDecoder *flc = g_vm->resMgr()->getFlic(_resourceId);
_k = calcPerspective(p.y);
_surfH = flc->getHeight() * _k;
_surfW = flc->getWidth() * _k;
_x_ = p.x;
_y_ = p.y;
_x = p.x - _surfW / 2;
_y = p.y - _surfH;
recalcOffset();
g_vm->videoSystem()->addDirtyRect(Common::Rect(_x - xOff, _y, _surfW + _x - xOff, _surfH + _y));
}
double QObjectPetka::calcPerspective(int y) {
QSystem *qsys = g_vm->getQSystem();
y = MIN(y, 480);
const Perspective &pers = qsys->_room->_persp;
double res = (y - pers.y0) * pers.k / (pers.y1 - pers.y0);
if (res < 0.0)
res = 0.0;
if (res + pers.f0 > pers.f1)
return pers.f1;
return res + pers.f0;
}
void QObjectPetka::updateWalk() {
if (!_isWalking)
return;
int v = _walk->sub_423350();
switch (v) {
case 0: {
_isWalking = false;
setPos(Common::Point(_walk->destX, _walk->destY), false);
QMessage msg(_id, kSet, (uint16) _imageId, 1, 0, nullptr, 0);
if (_heroReaction) {
uint i;
for (i = 0; i < _heroReaction->messages.size(); ++i) {
if (_heroReaction->messages[i].opcode == kGoTo || _heroReaction->messages[i].opcode == kSetSeq) {
_resourceId = _imageId + _walk->getSpriteId() + 10;
FlicDecoder *flc = g_vm->resMgr()->getFlic(_resourceId);
flc->setFrame(1);
initSurface();
processMessage(QMessage(_id, kAnimate, 0, 0, 0, nullptr, 0));
_heroReaction->messages.push_back(msg);
_heroReaction->messages.push_back(QMessage(_id, kAnimate, 1, 0, 0, nullptr, 0));
break;
}
}
if (i == _heroReaction->messages.size())
processMessage(msg);
} else {
processMessage(msg);
}
_holdMessages = false;
g_vm->videoSystem()->makeAllDirty();
break;
}
case 1:
sub_408940();
break;
case 2: {
_resourceId = _walk->getSpriteId() + _imageId + 10;
FlicDecoder *flc = g_vm->resMgr()->getFlic(_resourceId);
flc->setFrame(1);
_time = flc->getDelay();
initSurface();
g_vm->videoSystem()->makeAllDirty();
break;
}
default:
break;
}
}
void QObjectPetka::setReactionAfterWalk(uint index, QReaction *reaction, QMessageObject *sender, bool deleteReaction) {
_heroReaction = nullptr;
stopWalk();
QMessage msg(_id, kWalked, 0, 0, 0, sender, 0);
g_vm->getQSystem()->addMessage(msg);
_heroReaction = new QReaction();
_sender = sender;
for (uint i = index + 1; i < reaction->messages.size(); ++i) {
_heroReaction->messages.push_back(reaction->messages[i]);
}
if (deleteReaction) {
delete reaction;
}
}
void QObjectPetka::stopWalk() {
_isWalking = false;
_holdMessages = false;
Common::List<QMessage> &list = g_vm->getQSystem()->_messages;
for (Common::List<QMessage>::iterator it = list.begin(); it != list.end(); ++it) {
if (it->opcode == kWalked && it->objId == _id) {
it->objId = (uint16)-1;
}
}
delete _heroReaction;
_heroReaction = nullptr;
if (!_field7C) {
Common::Point p = _walk->sub_4234B0();
_x = p.x;
_y = p.y;
QMessage msg(_id, kSet, (uint16)_imageId, 1, 0, nullptr, 0);
processMessage(msg);
}
}
void QObjectPetka::update(int time) {
if (!_animate || !_isShown)
return;
if (_isWalking)
_time += time * (g_vm->getQSystem()->_panelInterface->getHeroSpeed() + 50) / 50;
else
_time += time;
FlicDecoder *flc = g_vm->resMgr()->getFlic(_resourceId);
if (flc && flc->getFrameCount() != 1) {
if (_sound) {
Common::Rect bounds = flc->getBounds();
_sound->setBalance(bounds.left + bounds.width() / 2 - g_vm->getQSystem()->_xOffset, 640);
}
while (_time >= (int)flc->getDelay()) {
if (_sound && flc->getCurFrame() == 0) {
_startSound = true;
}
flc->setFrame(-1);
if (flc->getCurFrame() == (int32)flc->getFrameCount() - 1) {
g_vm->getQSystem()->addMessage(_id, kEnd, _resourceId, 0, 0, 0, nullptr);
}
if (flc->getCurFrame() + 1 == (int32)flc->getFrameCount() / 2) {
g_vm->getQSystem()->addMessage(_id, kHalf, _resourceId, 0, 0, 0, nullptr);
}
if (_field7C && flc->getCurFrame() == 0)
_time = -10000;
updateWalk();
flc = g_vm->resMgr()->getFlic(_resourceId);
_surfH = flc->getHeight() * _k;
_surfW = flc->getWidth() * _k;
_time -= flc->getDelay();
g_vm->videoSystem()->addDirtyRect(Common::Rect(_x, _y, _surfW + _x, _surfH + _y));
}
}
}
bool QObjectPetka::isInPoint(Common::Point p) {
if (!_isActive)
return false;
FlicDecoder *flc = g_vm->resMgr()->getFlic(_resourceId);
const Graphics::Surface *flcSurface = flc->getCurrentFrame();
Common::Rect bounds(_surfW, _surfH);
Graphics::ManagedSurface s(_surfW, _surfH, flcSurface->format);
s.transBlitFrom(*flcSurface, Common::Rect(0, 0, flcSurface->w, flcSurface->h), bounds);
p.x -= _x;
p.y -= _y;
if (!bounds.contains(p.x, p.y))
return false;
return *(uint16 *)s.getBasePtr(p.x, p.y) != 0;
}
void QObjectPetka::updateZ() {
if (_animate && _isShown && _updateZ) {
FlicDecoder *flc = g_vm->resMgr()->getFlic(_resourceId);
if (_isWalking) {
_z = _walk->currPos().y;
} else {
_z = _y + flc->getHeight() * _k;
}
}
}
void QObjectPetka::sub_408940() {
FlicDecoder *flc = g_vm->resMgr()->getFlic(_resourceId);
QSystem *sys = g_vm->getQSystem();
int xOff = sys->_xOffset;
Common::Rect dirty(_x - xOff, _y, _surfW + _x - xOff, _surfH + _y);
g_vm->videoSystem()->addDirtyRect(dirty);
Common::Point currPos = _walk->currPos();
_k = calcPerspective(currPos.y);
_surfW = flc->getWidth() * _k;
_surfH = flc->getHeight() * _k;
Common::Point p = _walk->sub_4234B0();
_x = p.x;
_y = p.y;
_x_ = currPos.x;
_y_ = currPos.y;
recalcOffset();
g_vm->videoSystem()->addDirtyRect(Common::Rect(_x - xOff, _y, _surfW + _x - xOff, _surfH + _y));
}
void QObjectPetka::recalcOffset() {
QSystem *sys = g_vm->getQSystem();
int xOff = sys->_xOffset;
if (_x_ < xOff + 160 || _x_ > xOff + 480) {
sys->_reqOffset = _x_ - 320;
}
sys->_reqOffset = CLIP<int>(sys->_reqOffset, 0, sys->_sceneWidth - 640);
}
QObjectChapayev::QObjectChapayev() {
_x = 477;
_y = 350;
// _surfId = -6;
}
}

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 PETKA_HEROES_H
#define PETKA_HEROES_H
#include "common/ptr.h"
#include "petka/objects/object.h"
#include "petka/walk.h"
namespace Petka {
class QObjectPetka : public QObject {
public:
QObjectPetka();
void processMessage(const QMessage &msg) override;
void initSurface();
void walk(int x, int y);
void stopWalk();
void updateWalk();
void setReactionAfterWalk(uint index, QReaction *reaction, QMessageObject *sender, bool deleteReaction);
void draw() override;
bool isInPoint(Common::Point p) override;
void update(int time) override;
void setPos(Common::Point p, bool ) override;
double calcPerspective(int y);
void updateZ() override;
void sub_408940();
private:
virtual void recalcOffset();
public:
int _field7C;
int _surfW;
int _surfH;
int _x_;
int _y_;
// int _surfId;
int _imageId;
double _k;
Common::ScopedPtr<Walk> _walk;
int _destX;
int _destY;
bool _isWalking;
QReaction *_heroReaction;
QMessageObject *_sender;
int _fieldB4;
};
class QObjectChapayev : public QObjectPetka {
public:
QObjectChapayev();
void recalcOffset() override {}
};
} // End of namespace Petka
#endif

View File

@@ -0,0 +1,603 @@
/* 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/formats/ini-file.h"
#include "common/stream.h"
#include "common/system.h"
#include "common/events.h"
#include "graphics/surface.h"
#include "petka/big_dialogue.h"
#include "petka/flc.h"
#include "petka/sound.h"
#include "petka/petka.h"
#include "petka/video.h"
#include "petka/q_system.h"
#include "petka/q_manager.h"
#include "petka/objects/object_star.h"
#include "petka/objects/object_cursor.h"
#include "petka/interfaces/main.h"
#include "petka/objects/heroes.h"
#include "petka/objects/object_case.h"
namespace Petka {
QReaction *createReaction(QMessage *messages, QMessage *end) {
QReaction *reaction = new QReaction();
while (messages != end) {
reaction->messages.push_back(*messages++);
}
return reaction;
}
QVisibleObject::QVisibleObject()
: _resourceId(-1), _z(240) {}
QMessageObject::QMessageObject() {
_id = (uint16)-1;
_status = 0;
_time = 0;
_dialogColor = -1;
_animate = true;
_isShown = true;
_isActive = true;
_updateZ = false;
_holdMessages = false;
_loopedSound = false;
_startSound = false;
_reaction = nullptr;
_x = _y = _walkX = _walkY = 0;
_frame = 0;
_sound = nullptr;
_reactionId = 0;
}
void QMessageObject::processMessage(const QMessage &msg) {
bool reacted = false;
int opcode = (msg.opcode == kObjectUse) ? (msg.sender->_id << 16) | kObjectUse : msg.opcode;
for (uint i = 0; i < _reactions.size(); ++i) {
QReaction *r = &_reactions[i];
if (r->opcode != msg.opcode ||
(r->status != -1 && r->status != _status) ||
(r->senderId != -1 && r->senderId != msg.sender->_id)) {
continue;
}
bool fallback;
if (g_vm->getBigDialogue()->findHandler(_id, opcode, &fallback) && !fallback) {
g_vm->getBigDialogue()->setHandler(_id, opcode);
g_vm->getQSystem()->_mainInterface->_dialog.setSender(this);
}
processReaction(r, &msg);
reacted = true;
}
if (reacted || !g_vm->getBigDialogue()->findHandler(_id, opcode, nullptr)) {
switch (msg.opcode) {
case kAddInv:
g_vm->getQSystem()->getCase()->addItem(msg.objId);
// original bug fix
g_vm->pushMouseMoveEvent();
break;
case kDelInv:
g_vm->getQSystem()->getCase()->removeItem(msg.objId);
break;
case kSetInv:
g_vm->getQSystem()->getCase()->transformItem(msg.sender->_id, msg.objId);
break;
case kAvi: {
Common::String videoName = g_vm->resMgr()->findResourceName((uint16) msg.arg1);
g_vm->playVideo(g_vm->openFile(videoName, false));
break;
}
case kContinue:
g_vm->getQSystem()->_mainInterface->_dialog.endUserMsg();
break;
case kCursor:
g_vm->getQSystem()->getCursor()->setInvItem(this, msg.arg1);
g_vm->videoSystem()->makeAllDirty();
break;
case kDialog:
g_vm->getQSystem()->_mainInterface->_dialog.start(msg.arg1, this);
break;
case kSetPos:
setPos(Common::Point(msg.arg1, msg.arg2), false);
break;
case kSet:
case kPlay:
play(msg.arg1, msg.arg2);
break;
case kAnimate:
_animate = msg.arg1;
break;
case kEnd:
if (_reaction && _reactionId == msg.arg1) {
QReaction *reaction = _reaction;
_reaction = nullptr;
processReaction(reaction);
}
break;
case kStatus:
_status = (int8) msg.arg1;
break;
case kOn:
_isActive = true;
show(true);
break;
case kOff:
_isActive = false;
show(false);
break;
case kStop:
g_vm->getQSystem()->getCursor()->show(msg.arg1);
g_vm->getQSystem()->getStar()->_isActive = msg.arg1;
break;
case kShow:
show(msg.arg1);
break;
case kShake:
g_vm->videoSystem()->setShake(msg.arg1);
break;
case kSystem:
switch (msg.arg1){
case 0:
g_vm->getQSystem()->getStar()->_isActive = false;
break;
case 1:
g_vm->getQSystem()->getStar()->_isActive = true;
break;
case 242:
Engine::quitGame();
break;
default:
break;
}
break;
case kHide:
show(false);
break;
case kZBuffer:
_updateZ = msg.arg1;
_z = (msg.arg2 != -1) ? msg.arg2 : _z;
break;
case kActive:
_isActive = msg.arg1;
break;
case kPassive:
_isActive = false;
break;
case kJump: {
Common::Point p;
p.x = (msg.arg1 == 0xffff ? _walkX : msg.arg1);
p.y = (msg.arg2 == -1 ? _walkY : msg.arg2);
g_vm->getQSystem()->getPetka()->setPos(p, false);
break;
}
case kJumpVich: {
Common::Point p;
p.x = (msg.arg1 == 0xffff ? _walkX : msg.arg1);
p.y = (msg.arg2 == -1 ? _walkY : msg.arg2);
g_vm->getQSystem()->getChapay()->setPos(p, false);
break;
}
case kWalk:
if (!reacted) {
if (_walkX == -1) {
g_vm->getQSystem()->getPetka()->walk(msg.arg1, msg.arg2);
} else {
g_vm->getQSystem()->getPetka()->walk(_walkX, _walkY);
}
}
break;
case kWalkTo: {
int destX = msg.arg1;
int destY = msg.arg2;
if (destX == -1 || destY == -1) {
destX = _walkX;
destY = _walkY;
}
if (destX != -1) {
g_vm->getQSystem()->getPetka()->walk(destX, destY);
QReaction *r = g_vm->getQSystem()->getPetka()->_heroReaction;
if (r) {
for (uint i = 0; i < r->messages.size(); ++i) {
if (r->messages[i].opcode == kGoTo) {
g_vm->getQSystem()->getChapay()->walk(destX, destY);
break;
}
}
}
}
break;
}
case kWalkVich: {
int destX = msg.arg1;
int destY = msg.arg2;
if (destX == -1 || destY == -1) {
destX = _walkX;
destY = _walkY;
}
if (destX != -1)
g_vm->getQSystem()->getChapay()->walk(destX, destY);
break;
}
case kDescription: {
Common::ScopedPtr<Common::SeekableReadStream> invStream(g_vm->openFile("invntr.txt", true));
if (invStream) {
Common::String desc;
Common::INIFile invIni;
invIni.allowNonEnglishCharacters();
invIni.loadFromStream(*invStream);
invIni.getKey(_name, "ALL", desc);
g_vm->getQSystem()->_mainInterface->setTextDescription(Common::convertToU32String(desc.c_str(), Common::kWindows1251), msg.arg1);
}
break;
}
case kPart:
g_vm->loadPartAtNextFrame(msg.arg1);
break;
case kChapter:
g_vm->loadChapter(msg.arg1);
break;
case kToMap:
g_vm->getQSystem()->toggleMapInterface();
break;
default:
break;
}
} else {
for (uint i = 0; i < _reactions.size(); ++i) {
QReaction &r = _reactions[i];
if (r.opcode != msg.opcode ||
(r.status != -1 && r.status != _status) ||
(r.senderId != -1 && r.senderId != msg.sender->_id)) {
continue;
}
g_vm->getQSystem()->_mainInterface->_dialog.setReaction(createReaction(r.messages.data(), r.messages.end()));
}
g_vm->getBigDialogue()->setHandler(_id, opcode);
g_vm->getQSystem()->_mainInterface->_dialog.start(msg.arg1, this);
}
}
void QMessageObject::show(bool v) {
_isShown = v;
}
void QMessageObject::setReaction(int16 id, QReaction *reaction) {
delete _reaction;
_reaction = reaction;
_reactionId = id;
}
void QMessageObject::processReaction(QReaction *r, const QMessage *msg) {
bool deleteReaction = (msg == nullptr);
for (uint j = 0; j < r->messages.size(); ++j) {
QMessage &rMsg = r->messages[j];
if (rMsg.opcode == kCheck && g_vm->getQSystem()->findObject(rMsg.objId)->_status != rMsg.arg1) {
break;
}
if (msg && rMsg.opcode == kIf &&
((rMsg.arg1 != 0xffff && rMsg.arg1 != msg->arg1) ||
(rMsg.arg2 != -1 && rMsg.arg2 != msg->arg2) ||
(rMsg.arg3 != -1 && rMsg.arg3 != msg->arg3))) {
break;
}
if (msg && rMsg.opcode == kRandom && rMsg.arg2 != -1) {
rMsg.arg1 = (int16) g_vm->getRnd().getRandomNumber((uint) (rMsg.arg2 - 1));
}
g_vm->getQSystem()->addMessage(rMsg.objId, rMsg.opcode, rMsg.arg1, rMsg.arg2, rMsg.arg3, rMsg.unk, this);
bool processed = true;
switch (rMsg.opcode) {
case kDialog: {
g_vm->getQSystem()->_mainInterface->_dialog.setReaction(createReaction(r->messages.data() + j + 1, r->messages.end()));
break;
}
case kPlay: {
QMessageObject *obj = g_vm->getQSystem()->findObject(rMsg.objId);
obj->setReaction(rMsg.arg1, createReaction(r->messages.data() + j + 1, r->messages.end()));
break;
}
case kWalk:
case kWalkTo:
g_vm->getQSystem()->getPetka()->setReactionAfterWalk(j, r, this, deleteReaction);
return;
case kWalkVich:
g_vm->getQSystem()->getChapay()->setReactionAfterWalk(j, r, this, deleteReaction);
return;
default:
processed = false;
break;
}
if (processed)
break;
}
if (deleteReaction)
delete r;
}
void QMessageObject::play(int id, int type) {
if (g_vm->getQSystem()->_totalInit) {
_resourceId = id;
_loopedSound = (type == 5);
return;
}
if (_loopedSound || g_vm->isDemo()) {
removeSound();
}
FlicDecoder *flc = g_vm->resMgr()->getFlic(_resourceId);
if (flc) {
g_vm->videoSystem()->addDirtyRect(Common::Point(_x, _y), *flc);
}
_resourceId = id;
loadSound();
flc = g_vm->resMgr()->getFlic(id);
flc->setFrame(1);
_time = 0;
_loopedSound = (type == 5);
}
void QMessageObject::loadSound() {
Common::String name = g_vm->resMgr()->findSoundName(_resourceId);
_sound = g_vm->soundMgr()->addSound(name, Audio::Mixer::kSFXSoundType);
_startSound = false;
}
void QMessageObject::removeSound() {
Common::String name = g_vm->resMgr()->findSoundName(_resourceId);
g_vm->soundMgr()->removeSound(name);
_sound = nullptr;
}
static Common::String readString(Common::ReadStream &readStream) {
uint32 stringSize = readStream.readUint32LE();
byte *data = (byte *)malloc(stringSize + 1);
readStream.read(data, stringSize);
data[stringSize] = '\0';
Common::String str((char *)data);
free(data);
return str;
}
void QMessageObject::readScriptData(Common::SeekableReadStream &stream) {
_id = stream.readUint16LE();
_name = readString(stream);
_reactions.resize(stream.readUint32LE());
for (uint i = 0; i < _reactions.size(); ++i) {
QReaction *reaction = &_reactions[i];
reaction->opcode = stream.readUint16LE();
reaction->status = stream.readByte();
reaction->senderId = stream.readUint16LE();
reaction->messages.resize(stream.readUint32LE());
for (uint j = 0; j < reaction->messages.size(); ++j) {
QMessage *msg = &reaction->messages[j];
msg->objId = stream.readUint16LE();
msg->opcode = stream.readUint16LE();
msg->arg1 = stream.readUint16LE();
msg->arg2 = stream.readUint16LE();
msg->arg3 = stream.readUint16LE();
}
}
}
void QMessageObject::readInisData(Common::INIFile &names, Common::INIFile &cast, Common::INIFile *bgs) {
names.getKey(_name, "all", _nameOnScreen);
Common::String rgbString;
if (cast.getKey(_name, "all", rgbString)) {
int r, g, b;
sscanf(rgbString.c_str(), "%d %d %d", &r, &g, &b);
_dialogColor = g_vm->_system->getScreenFormat().RGBToColor((byte)r, (byte)g, (byte)b);
}
}
QObject::QObject() {
_animate = true;
_updateZ = true;
_frame = 1;
_sound = nullptr;
_x = 0;
_y = 0;
_walkX = -1;
_walkY = -1;
}
bool QObject::isInPoint(Common::Point p) {
if (!_isActive)
return false;
FlicDecoder *flc = g_vm->resMgr()->getFlic(_resourceId);
if (!flc || !flc->getBounds().contains(p.x - _x, p.y - _y))
return false;
const Graphics::Surface *s = flc->getCurrentFrame();
auto format = g_system->getScreenFormat();
byte index = *(const byte *)s->getBasePtr(p.x - _x, p.y - _y);
const byte *pal = flc->getPalette();
return format.RGBToColor(pal[0], pal[1], pal[2]) != format.RGBToColor(pal[index * 3], pal[index * 3 + 1], pal[index * 3 + 2]);
}
void QObject::draw() {
if (!_isShown || _resourceId == -1) {
return;
}
FlicDecoder *flc = g_vm->resMgr()->getFlic(_resourceId);
if (!flc) {
return;
}
if (_animate && _startSound) {
if (_sound) {
_sound->play(_loopedSound);
if (_loopedSound) {
_sound = nullptr;
}
}
_startSound = false;
}
int xOff = g_vm->getQSystem()->_xOffset;
VideoSystem *videoSys = g_vm->videoSystem();
Common::Rect screen(640 + xOff, 480);
Common::Rect flcBounds(flc->getBounds());
Common::Rect objBounds(flcBounds);
objBounds.translate(_x, _y);
Common::Rect intersect(screen.findIntersectingRect(objBounds));
if (intersect.isEmpty())
return;
Graphics::Surface *surface = flc->getCurrentFrame()->getSubArea(flcBounds).convertTo(g_system->getScreenFormat(), flc->getPalette());
for (Common::Rect dirty : videoSys->rects()) {
dirty.translate(xOff, 0);
Common::Rect destRect(intersect.findIntersectingRect(dirty));
if (destRect.isEmpty())
continue;
Common::Rect srcRect(destRect);
srcRect.translate(-_x, -_y);
srcRect.translate(-flcBounds.left, -flcBounds.top);
destRect.translate(-xOff, 0);
videoSys->transBlitFrom(*surface, srcRect, destRect, flc->getTransColor(surface->format));
}
surface->free();
delete surface;
}
void QObject::updateZ() {
if (!_animate || !_isShown || !_updateZ)
return;
FlicDecoder *flc = g_vm->resMgr()->getFlic(_resourceId);
if (flc) {
_z = 1;
const Common::Array<Common::Rect> &rects = flc->getMskRects();
for (uint i = 0; i < rects.size(); ++i) {
if (_y + rects[i].bottom > _z)
_z = _y + rects[i].bottom;
}
}
}
void QObject::show(bool v) {
FlicDecoder *flc = g_vm->resMgr()->getFlic(_resourceId);
if (flc) {
g_vm->videoSystem()->addDirtyRect(Common::Point(_x, _y), *flc);
}
QMessageObject::show(v);
}
void QObject::update(int time) {
if (!_animate || !_isShown)
return;
_time += time;
FlicDecoder *flc = g_vm->resMgr()->getFlic(_resourceId);
if (flc && flc->getFrameCount() != 1) {
if (_sound) {
Common::Rect bounds = flc->getBounds();
_sound->setBalance(bounds.left + bounds.width() / 2 - g_vm->getQSystem()->_xOffset, 640);
}
while (_time >= (int32)flc->getDelay()) {
if (_sound && flc->getCurFrame() == 0) {
_startSound = true;
}
g_vm->videoSystem()->addDirtyRect(Common::Point(_x, _y), *flc);
flc->setFrame(-1);
if (flc->getCurFrame() == (int32)flc->getFrameCount() - 1) {
g_vm->getQSystem()->addMessage(_id, kEnd, _resourceId, 0, 0, 0, nullptr);
}
if (flc->getCurFrame() + 1 == (int32)flc->getFrameCount() / 2) {
g_vm->getQSystem()->addMessage(_id, kHalf, _resourceId, 0, 0, 0, nullptr);
}
g_vm->videoSystem()->addDirtyRect(Common::Point(_x, _y), *flc);
_time -= flc->getDelay();
}
}
}
void QObject::setPos(Common::Point p, bool) {
FlicDecoder *flc = g_vm->resMgr()->getFlic(_resourceId);
if (flc) {
g_vm->videoSystem()->addDirtyMskRects(Common::Point(_x, _y), *flc);
g_vm->videoSystem()->addDirtyMskRects(p, *flc);
_x = p.x;
_y = p.y;
}
}
void QObject::onClick(Common::Point p) {
QSystem *sys = g_vm->getQSystem();
QObjectCursor *cursor = g_vm->getQSystem()->getCursor();
sys->getPetka()->stopWalk();
sys->getChapay()->stopWalk();
switch (cursor->_actionType) {
case kActionLook:
g_vm->getQSystem()->addMessage(_id, kLook, 0, 0, 0, 0, this);
break;
case kActionWalk:
g_vm->getQSystem()->addMessage(_id, kWalk, p.x, p.y, 0, 0, this);
break;
case kActionUse:
g_vm->getQSystem()->addMessage(_id, kUse, 0, 0, 0, 0, this);
break;
case kActionTake:
g_vm->getQSystem()->addMessage(_id, kTake, 0, 0, 0, 0, this);
break;
case kActionTalk:
g_vm->getQSystem()->addMessage(_id, kTalk, 0, 0, 0, 0, this);
break;
case kActionObjUseChapayev:
g_vm->getQSystem()->addMessage(_id, kObjectUse, p.x, p.y, 0, 0, g_vm->getQSystem()->getChapay());
break;
case kActionObjUse:
g_vm->getQSystem()->addMessage(_id, kObjectUse, 0, 0, 0, 0, cursor->_invObj);
break;
default:
break;
}
}
void QObject::onMouseMove(Common::Point p) {
g_vm->getQSystem()->_mainInterface->_objUnderCursor = this;
}
}

View File

@@ -0,0 +1,117 @@
/* 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 PETKA_OBJECT_H
#define PETKA_OBJECT_H
#include "common/rect.h"
#include "common/str.h"
#include "petka/base.h"
namespace Common {
class INIFile;
class SeekableReadStream;
}
namespace Petka {
class QVisibleObject {
public:
QVisibleObject();
virtual ~QVisibleObject() {};
virtual void draw() {};
virtual void update(int time) {};
virtual void updateZ() {};
virtual void show(bool v) {};
virtual void setPos(Common::Point p, bool center) {};
virtual bool isInPoint(Common::Point p) { return false; }
virtual void onMouseMove(Common::Point p) {}
virtual void onClick(Common::Point p) {}
public:
int32 _resourceId;
int32 _z;
};
class Sound;
class QMessageObject : public QVisibleObject {
public:
QMessageObject();
void show(bool v) override;
void setReaction(int16 id, QReaction *reaction);
virtual void processMessage(const QMessage &msg);
void processReaction(QReaction *reaction, const QMessage *msg = nullptr);
virtual void play(int id, int type);
void loadSound();
void removeSound();
void readScriptData(Common::SeekableReadStream &stream);
virtual void readInisData(Common::INIFile &names, Common::INIFile &cast, Common::INIFile *bgs);
public:
int32 _x;
int32 _y;
int32 _walkX;
int32 _walkY;
int32 _time;
byte _frame;
bool _isShown;
bool _animate;
bool _updateZ;
bool _holdMessages;
bool _isActive;
bool _startSound;
bool _loopedSound;
Sound *_sound;
int8 _status;
uint16 _id;
Common::String _name;
Common::String _nameOnScreen;
int32 _dialogColor;
Common::Array<QReaction> _reactions;
QReaction *_reaction;
int16 _reactionId;
};
class QObject : public QMessageObject {
public:
QObject();
void draw() override;
void update(int time) override;
void updateZ() override;
bool isInPoint(Common::Point p) override;
void setPos(Common::Point p, bool center) override;
void show(bool v) override;
void onClick(Common::Point p) override;
void onMouseMove(Common::Point p) override;
};
} // End of namespace Petka
#endif

View File

@@ -0,0 +1,175 @@
/* 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/formats/ini-file.h"
#include "common/stream.h"
#include "common/system.h"
#include "common/events.h"
#include "graphics/surface.h"
#include "petka/flc.h"
#include "petka/walk.h"
#include "petka/petka.h"
#include "petka/video.h"
#include "petka/q_system.h"
#include "petka/q_manager.h"
#include "petka/interfaces/main.h"
#include "petka/interfaces/sequence.h"
#include "petka/objects/object_bg.h"
#include "petka/objects/heroes.h"
namespace Petka {
QObjectBG::QObjectBG() {
_x = 0;
_y = 0;
_z = 0;
_showMap = true;
_fxId = 0;
_musicId = 0;
}
void QObjectBG::processMessage(const QMessage &msg) {
QMessageObject::processMessage(msg);
switch (msg.opcode) {
case kSet: {
_resourceId = msg.arg1;
QSystem *sys = g_vm->getQSystem();
if (g_vm->isPetka2() && !sys->_totalInit && sys->_mainInterface->_roomId == _id) {
auto petka = sys->getPetka();
auto chapay = sys->getChapay();
auto bkgName = g_vm->resMgr()->findResourceName(_resourceId);
petka->_walk->setBackground(bkgName);
chapay->_walk->setBackground(bkgName);
petka->setPos(Common::Point(petka->_x_, petka->_y_), false);
chapay->setPos(Common::Point(chapay->_x_, chapay->_y_), false);
}
break;
}
case kMusic:
_musicId = msg.arg1;
break;
case kBGsFX:
_fxId = msg.arg1;
break;
case kMap:
_showMap = (msg.arg1 != 0);
break;
case kNoMap:
_showMap = false;
break;
case kGoTo:
goTo();
break;
case kSetSeq:
g_vm->getQSystem()->_sequenceInterface->start(_id);
break;
case kEndSeq:
g_vm->getQSystem()->_sequenceInterface->stop();
break;
default:
break;
}
}
void QObjectBG::draw() {
Graphics::Surface *s = g_vm->resMgr()->getSurface(_resourceId);
if (!s)
return;
int xOffset = g_vm->getQSystem()->_xOffset;
for (auto rect : g_vm->videoSystem()->rects()) {
rect.translate(xOffset, 0);
g_vm->videoSystem()->blitFrom(*s, rect, Common::Point(rect.left - xOffset, rect.top));
}
}
void QObjectBG::goTo() {
QSystem *sys = g_vm->getQSystem();
sys->getPetka()->stopWalk();
sys->getChapay()->stopWalk();
int oldRoomId = sys->_mainInterface->_roomId;
sys->_mainInterface->loadRoom(_id, false);
QMessageObject *oldRoom = sys->findObject(oldRoomId);
Common::ScopedPtr<Common::SeekableReadStream> bgsStream(g_vm->openFile("BGs.ini", true));
Common::INIFile bgsIni;
bgsIni.allowNonEnglishCharacters();
bgsIni.loadFromStream(*bgsStream);
Common::String entranceName;
if (bgsIni.getKey(oldRoom->_name, _name, entranceName)) {
setEntrance(entranceName);
return;
}
for (auto o : sys->_allObjects) {
QObjectBG *bg = dynamic_cast<QObjectBG *>(o);
if (bg && bgsIni.getKey(bg->_name, _name, entranceName)) {
setEntrance(entranceName);
}
}
}
void QObjectBG::setEntrance(const Common::String &name) {
QSystem *sys = g_vm->getQSystem();
QMessageObject *entrance = sys->findObject(name);
if (entrance) {
sys->getPetka()->_z = 0;
sys->getChapay()->_z = 0;
sys->getPetka()->setPos(Common::Point(entrance->_walkX, entrance->_walkY), false);
sys->getChapay()->setPos(Common::Point(entrance->_walkX, entrance->_walkY - 2), false);
sys->_xOffset = CLIP<int32>(entrance->_walkX - 320, 0, sys->_sceneWidth - 640);
sys->_reqOffset = sys->_xOffset;
}
g_vm->videoSystem()->makeAllDirty();
}
void QObjectBG::readInisData(Common::INIFile &names, Common::INIFile &cast, Common::INIFile *bgs) {
if (bgs) {
Common::String perspective;
bgs->getKey(_name, "Settings", perspective);
if (!perspective.empty()) {
sscanf(perspective.c_str(), "%lf %lf %d %d %lf", &_persp.f0, &_persp.k, &_persp.y0, &_persp.y1, &_persp.f1);
} else {
_persp.f0 = 1.0;
_persp.f1 = 1.0;
_persp.k = 0.0;
_persp.y0 = 0;
_persp.y1 = 480;
}
}
QMessageObject::readInisData(names, cast, bgs);
}
} // End of namespace Petka

View File

@@ -0,0 +1,60 @@
/* 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 PETKA_OBJECT_BG_H
#define PETKA_OBJECT_BG_H
#include "petka/objects/object.h"
namespace Petka {
// Linear Interpolation
struct Perspective {
double f0;
double k; // seems to be (f1 - f0) always in files
int y0;
int y1;
double f1;
Perspective() { f0 = f1 = 1.0; k = 0.0; y0 = 0; y1 = 480; }
};
class QObjectBG : public QMessageObject {
public:
QObjectBG();
void processMessage(const QMessage &msg) override;
void draw() override;
void goTo();
void setEntrance(const Common::String &name);
void play(int id, int type) override {}
void readInisData(Common::INIFile &names, Common::INIFile &cast, Common::INIFile *bgs) override;
public:
bool _showMap;
int _fxId;
int _musicId;
Perspective _persp;
};
} // End of namespace Petka
#endif

View File

@@ -0,0 +1,292 @@
/* 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 "petka/interfaces/main.h"
#include "petka/objects/object_case.h"
#include "petka/objects/object_cursor.h"
#include "petka/flc.h"
#include "petka/petka.h"
#include "petka/q_manager.h"
#include "petka/video.h"
#include "petka/objects/heroes.h"
#include "petka/q_system.h"
namespace Petka {
// Полоска в чемодане
const char *const kPoloska = "\xCF\xEE\xEB\xEE\xF1\xEA\xE0\x20\xE2\x20\xF7\xE5\xEC\xEE\xE4\xE0\xED\xE5";
const uint kCaseZ = 980;
const uint kItemZ = kCaseZ + 1;
const uint kPoloskaZ = kCaseZ + 2;
const uint kButtonsCount = 6;
const uint kItemsOnPage = 6;
const uint kObjectCaseId = 4099;
const uint kCaseResourceId = 6000;
const uint kFirstButtonResourceId = 6001;
const uint kExitCaseResourceId = 6009;
enum {
kChapayevButton = 0,
kPanelButton,
kMapButton,
kCloseButton,
kNextPageButton,
kPrevPageButton,
kInvalidButton
};
QObjectCase::QObjectCase() {
_id = kObjectCaseId;
_resourceId = kCaseResourceId;
_z = kCaseZ;
_itemIndex = 0;
_clickedObjIndex = kInvalidButton;
_isShown = false;
_updateZ = false;
_itemsLocation[0] = Common::Point(120, 145);
_itemsLocation[1] = Common::Point(240, 145);
_itemsLocation[2] = Common::Point(360, 145);
_itemsLocation[3] = Common::Point(100, 220);
_itemsLocation[4] = Common::Point(240, 220);
_itemsLocation[5] = Common::Point(380, 220);
}
void QObjectCase::update(int time) {
if (!_isShown || _clickedObjIndex == kInvalidButton)
return;
_time += time;
FlicDecoder *flc = g_vm->resMgr()->getFlic(kFirstButtonResourceId + _clickedObjIndex);
if (flc) {
while (_time >= (int32)flc->getDelay()) {
flc->setFrame(-1);
_time -= flc->getDelay();
g_vm->videoSystem()->addDirtyMskRects(*flc);
}
}
}
void QObjectCase::draw() {
if (!_isShown)
return;
QObject::draw();
if (_clickedObjIndex != kInvalidButton) {
FlicDecoder *flc = g_vm->resMgr()->getFlic(kFirstButtonResourceId + _clickedObjIndex);
Graphics::Surface *s = flc->getCurrentFrame()->convertTo(g_system->getScreenFormat(), flc->getPalette());
QSystem *sys = g_vm->getQSystem();
const Common::List<Common::Rect> &dirty = g_vm->videoSystem()->rects();
const Common::Array<Common::Rect> &mskRects = flc->getMskRects();
for (Common::List<Common::Rect>::const_iterator it = dirty.begin(); it != dirty.end(); ++it) {
for (uint i = 0; i < mskRects.size(); ++i) {
Common::Rect destRect = mskRects[i].findIntersectingRect(*it);
Common::Rect srcRect = destRect;
srcRect.translate(-_x + sys->_xOffset, -_y);
g_vm->videoSystem()->transBlitFrom(*s, srcRect, destRect, flc->getTransColor(s->format));
}
}
s->free();
delete s;
}
}
void QObjectCase::show(bool v) {
QSystem *sys = g_vm->getQSystem();
_x = sys->_xOffset;
QObject::show(v);
if (v) {
addItemObjects();
QMessageObject *obj = sys->findObject(kPoloska);
obj->_z = kPoloskaZ;
obj->_x = sys->_xOffset;
sys->_mainInterface->_objs.push_back(obj);
} else {
removeObjects(true);
sys->_currInterface->_startIndex = 0;
}
}
bool QObjectCase::isInPoint(Common::Point p) {
return _isShown;
}
void QObjectCase::onMouseMove(Common::Point p) {
p.x -= _x;
FlicDecoder *flc = g_vm->resMgr()->getFlic(kExitCaseResourceId);
if (*(const byte *)flc->getCurrentFrame()->getBasePtr(p.x, p.y) != 0) {
if (_clickedObjIndex != kCloseButton && _clickedObjIndex != kInvalidButton) {
flc = g_vm->resMgr()->getFlic(kFirstButtonResourceId + _clickedObjIndex);
flc->setFrame(1);
g_vm->videoSystem()->addDirtyMskRects(*flc);
}
_clickedObjIndex = kCloseButton;
} else {
uint i;
for (i = 0; i < kButtonsCount; ++i) {
flc = g_vm->resMgr()->getFlic(kFirstButtonResourceId + i);
if (flc->getMskRects()[0].contains(p)) {
break;
}
}
if (_clickedObjIndex != i && _clickedObjIndex != kInvalidButton) {
flc = g_vm->resMgr()->getFlic(kFirstButtonResourceId + _clickedObjIndex);
flc->setFrame(1);
g_vm->videoSystem()->addDirtyMskRects(*flc);
}
if (i == kButtonsCount && _clickedObjIndex != kInvalidButton) {
_clickedObjIndex = kInvalidButton;
} else if (i != _clickedObjIndex) {
if ((i != kChapayevButton || g_vm->getQSystem()->getChapay()->_isShown) && (i != kMapButton || g_vm->getQSystem()->_room->_showMap)) {
flc = g_vm->resMgr()->getFlic(kFirstButtonResourceId + i);
g_vm->videoSystem()->addDirtyMskRects(*flc);
_clickedObjIndex = i;
} else {
_clickedObjIndex = kInvalidButton;
}
}
}
}
void QObjectCase::onClick(Common::Point p) {
switch (_clickedObjIndex) {
case kChapayevButton:
g_vm->getQSystem()->setCursorAction(kActionObjUseChapayev);
break;
case kPanelButton:
g_vm->getQSystem()->togglePanelInterface();
break;
case kMapButton:
g_vm->getQSystem()->toggleMapInterface();
break;
case kCloseButton:
show(false);
break;
case kNextPageButton:
nextPage();
break;
case kPrevPageButton:
prevPage();
break;
default:
break;
}
}
void QObjectCase::addItemObjects() {
QSystem *sys = g_vm->getQSystem();
Common::Array<QVisibleObject *> &objs = sys->_mainInterface->_objs;
removeObjects(false);
for (uint i = 0; i < objs.size(); ++i) {
if (objs[i]->_resourceId == kCaseResourceId) {
sys->_currInterface->_startIndex = i;
}
}
const uint size = (_itemIndex + kItemsOnPage >= _items.size()) ? _items.size() : (_itemIndex + kItemsOnPage);
for (uint i = _itemIndex; i < size; ++i) {
QMessageObject *obj = sys->findObject(_items[i]);
obj->_x = sys->_xOffset + _itemsLocation[i - _itemIndex].x;
obj->_y = _itemsLocation[i - _itemIndex].y;
obj->_z = kItemZ;
g_vm->resMgr()->getFlic(obj->_resourceId);
objs.push_back(obj);
}
}
void QObjectCase::addItem(uint16 id) {
_items.push_back(id);
reshow();
}
void QObjectCase::removeItem(uint16 id) {
for (uint i = 0; i < _items.size(); ++i) {
if (_items[i] == id) {
_items.remove_at(i);
}
}
_itemIndex = (_items.size() < kItemsOnPage) ? 0 : (_items.size() - kItemsOnPage);
reshow();
}
void QObjectCase::transformItem(uint16 oldItem, uint16 newItem) {
for (uint i = 0; i < _items.size(); ++i) {
if (_items[i] == oldItem) {
_items[i] = newItem;
}
}
reshow();
}
void QObjectCase::nextPage() {
if (_items.size() > _itemIndex + kItemsOnPage) {
_itemIndex += kItemsOnPage;
addItemObjects();
g_vm->videoSystem()->makeAllDirty();
}
}
void QObjectCase::prevPage() {
if (_itemIndex > 0) {
_itemIndex = (_itemIndex <= kItemsOnPage) ? 0 : _itemIndex - kItemsOnPage;
addItemObjects();
g_vm->videoSystem()->makeAllDirty();
}
}
void QObjectCase::removeObjects(bool removePoloska) {
Common::Array<QVisibleObject *> &objs = g_vm->getQSystem()->_mainInterface->_objs;
for (uint i = 0; i < objs.size();) {
if (objs[i]->_z == kItemZ || (removePoloska && objs[i]->_z == kPoloskaZ)) {
objs.remove_at(i);
} else {
++i;
}
}
}
void QObjectCase::reshow() {
if (_isShown) {
show(false);
show(true);
}
}
} // End of namespace Petka

View File

@@ -0,0 +1,61 @@
/* 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 PETKA_OBJECT_CASE_H
#define PETKA_OBJECT_CASE_H
#include "petka/objects/object.h"
namespace Petka {
class QObjectCase : public QObject {
public:
QObjectCase();
void draw() override;
void update(int time) override;
void show(bool v) override;
bool isInPoint(Common::Point p) override;
void onMouseMove(Common::Point p) override;
void onClick(Common::Point p) override;
void addItem(uint16 id);
void removeItem(uint16 id);
void transformItem(uint16 oldItem, uint16 newItem);
private:
void reshow();
void nextPage();
void prevPage();
void addItemObjects();
void removeObjects(bool removePoloska);
public:
Common::Array<uint16> _items;
Common::Point _itemsLocation[6];
uint _clickedObjIndex;
uint _itemIndex;
};
} // End of namespace Petka
#endif

View File

@@ -0,0 +1,145 @@
/* 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/formats/ini-file.h"
#include "common/stream.h"
#include "common/system.h"
#include "common/events.h"
#include "graphics/surface.h"
#include "petka/flc.h"
#include "petka/petka.h"
#include "petka/video.h"
#include "petka/q_system.h"
#include "petka/q_manager.h"
#include "petka/objects/object_cursor.h"
namespace Petka {
const int kCursorLook = 5002;
QObjectCursor::QObjectCursor() {
_id = 4097;
_z = 1000;
_resourceId = kCursorLook;
Common::Point pos = g_vm->getEventManager()->getMousePos();
_x = pos.x;
_y = pos.y;
g_vm->resMgr()->getFlic(kCursorLook);
_actionType = kActionLook;
_invObj = nullptr;
_name = "Cursor";
}
void QObjectCursor::draw() {
if (!_isShown) {
return;
}
FlicDecoder *flc = g_vm->resMgr()->getFlic(_resourceId);
const Graphics::Surface *frame = flc->getCurrentFrame();
if (frame) {
Graphics::Surface *s = frame->convertTo(g_system->getScreenFormat(), flc->getPalette());
Common::Rect destRect(flc->getBounds());
destRect.translate(_x, _y);
destRect.clip(640, 480);
Common::Rect srcRect(destRect);
srcRect.translate(-_x, -_y);
g_vm->videoSystem()->transBlitFrom(*s, srcRect, destRect, flc->getTransColor(s->format));
s->free();
delete s;
}
}
void QObjectCursor::update(int time) {
if (!_isShown || !_animate)
return;
FlicDecoder *flc = g_vm->resMgr()->getFlic(_resourceId);
_time += time;
while (flc && _time >= (int32)flc->getDelay()) {
flc->setFrame(-1);
g_vm->videoSystem()->addDirtyRect(Common::Point(_x, _y), flc->getBounds());
_time -= flc->getDelay();
}
}
void QObjectCursor::setPos(Common::Point p, bool center) {
FlicDecoder *flc = g_vm->resMgr()->getFlic(_resourceId);
if (!_animate) {
flc->setFrame(1);
}
p.x = p.x - g_vm->getQSystem()->_xOffset;
g_vm->videoSystem()->addDirtyRect(Common::Point(_x, _y), flc->getBounds());
if (center) {
Common::Rect bounds = flc->getBounds();
p.x = p.x - bounds.left - bounds.width() / 2;
p.y = p.y - bounds.top - bounds.height() / 2;
}
_x = p.x;
_y = p.y;
g_vm->videoSystem()->addDirtyRect(Common::Point(_x, _y), flc->getBounds());
}
void QObjectCursor::show(bool v) {
FlicDecoder *flc = g_vm->resMgr()->getFlic(_resourceId);
g_vm->videoSystem()->addDirtyRect(Common::Point(_x, _y), flc->getBounds());
QMessageObject::show(v);
}
void QObjectCursor::returnInvItem() {
if (_actionType == kActionObjUse) {
_invObj->show(true);
_invObj->_isActive = true;
}
}
void QObjectCursor::setInvItem(QMessageObject *item, uint16 resourceId) {
returnInvItem();
if (resourceId != 0xffff) {
_resourceId = resourceId;
_actionType = kActionObjUse;
_invObj = item;
item->_isShown = false;
item->_isActive = false;
} else {
_resourceId = kCursorLook;
_actionType = kActionLook;
_invObj = nullptr;
}
}
void QObjectCursor::setAction(int actionType) {
show(false);
returnInvItem();
_resourceId = kCursorLook + actionType;
_actionType = actionType;
_invObj = nullptr;
show(true);
}
}

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 PETKA_OBJECT_CURSOR_H
#define PETKA_OBJECT_CURSOR_H
#include "petka/objects/object.h"
namespace Petka {
enum ActionType {
kActionLook,
kActionWalk,
kActionUse,
kActionTake,
kActionTalk,
kActionObjUseChapayev,
kActionObjUse
};
class QObjectCursor : public QMessageObject {
public:
QObjectCursor();
void setPos(Common::Point p, bool center) override;
void update(int time) override;
void draw() override;
void show(bool v) override;
bool isInPoint(Common::Point p) override { return false; }
void setAction(int actionType);
void setInvItem(QMessageObject *item, uint16 resourceId);
void returnInvItem();
public:
int _actionType;
QMessageObject *_invObj;
};
} // End of namespace Petka
#endif

View File

@@ -0,0 +1,100 @@
/* 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/rect.h"
#include "petka/objects/object_case.h"
#include "petka/objects/object_cursor.h"
#include "petka/objects/object_star.h"
#include "petka/q_system.h"
#include "petka/flc.h"
#include "petka/q_manager.h"
#include "petka/video.h"
#include "petka/petka.h"
namespace Petka {
//const uint kFirstCursorId = 5001;
const uint kCaseButtonIndex = 0;
QObjectStar::QObjectStar() {
_isShown = false;
_id = 4098;
_resourceId = 5000;
_z = 999;
_updateZ = false;
_isActive = true;
_buttonRects[0] = Common::Rect(70, 74, 112, 112);
_buttonRects[1] = Common::Rect(68, 0, 114, 41);
_buttonRects[2] = Common::Rect(151, 51, 180, 97);
_buttonRects[3] = Common::Rect(138, 125, 179, 166);
_buttonRects[4] = Common::Rect(55, 145, 96, 175);
_buttonRects[5] = Common::Rect(11, 79, 40, 118);
}
bool QObjectStar::isInPoint(Common::Point p) {
return _isShown;
}
void QObjectStar::onMouseMove(Common::Point p) {
uint frame = (findButtonIndex(p.x - _x, p.y - _y) + 1) % 7 + 1;
FlicDecoder *flc = g_vm->resMgr()->getFlic(_resourceId);
if (flc && flc->getCurFrame() + 1 != (int32)frame) {
g_vm->videoSystem()->addDirtyRect(Common::Point(_x, _y), *flc);
flc->setFrame(frame);
}
}
void QObjectStar::onClick(Common::Point p) {
uint button = findButtonIndex(p.x - _x, p.y - _y);
if (button == kCaseButtonIndex) {
g_vm->getQSystem()->getCase()->show(true);
} else if (button < ARRAYSIZE(_buttonRects)) {
QObjectCursor *cursor = g_vm->getQSystem()->getCursor();
cursor->setAction(button - 1);
}
show(false);
}
uint QObjectStar::findButtonIndex(int16 x, int16 y) const {
uint i = 0;
for (i = 0; i < ARRAYSIZE(_buttonRects); ++i) {
if (_buttonRects[i].contains(x, y))
return i;
}
return i;
}
void QObjectStar::setPos(Common::Point p, bool) {
if (!_isShown) {
QSystem *sys = g_vm->getQSystem();
FlicDecoder *flc = g_vm->resMgr()->getFlic(_resourceId);
p.x = MAX<int16>(p.x - sys->_xOffset - flc->getWidth() / 2, 0);
p.y = MAX<int16>(p.y - flc->getHeight() / 2, 0);
_x = MIN<int16>(p.x, 639 - flc->getWidth()) + sys->_xOffset;
_y = MIN<int16>(p.y, 479 - flc->getHeight());
}
}
}

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/>.
*
*/
#ifndef PETKA_OBJECT_STAR_H
#define PETKA_OBJECT_STAR_H
#include "petka/objects/object.h"
namespace Petka {
class QObjectStar : public QObject {
public:
QObjectStar();
void update(int time) override {}
bool isInPoint(Common::Point p) override;
void onMouseMove(Common::Point p) override;
void onClick(Common::Point p) override;
void setPos(Common::Point p, bool center) override;
private:
uint findButtonIndex(int16 x, int16 y) const;
private:
Common::Rect _buttonRects[6];
};
} // End of namespace Petka
#endif

View File

@@ -0,0 +1,270 @@
/* 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 "graphics/fonts/ttf.h"
#include "graphics/fontman.h"
#include "petka/q_manager.h"
#include "petka/petka.h"
#include "petka/video.h"
#include "petka/objects/text.h"
#include "petka/q_system.h"
#include "petka/interfaces/panel.h"
#include "petka/sound.h"
#include "petka/interfaces/main.h"
#include "petka/flc.h"
namespace Petka {
QText::QText(const Common::U32String &text, uint16 textColor, uint16 outlineColor) {
_resourceId = -2;
_z = 3000;
auto *font = g_vm->getTextFont();
Common::Rect rect = calculateBoundingBoxForText(text, *font);
rect.right += 10;
rect.bottom += 4;
_rect = Common::Rect((640 - rect.width()) / 2, 479 - rect.height(), 639 - (640 - rect.width()) / 2, 479);
Graphics::Surface *s = g_vm->resMgr()->getSurface((uint32)-2, rect.width(), rect.height());
drawText(*s, 0, 630, text, textColor, *font, Graphics::kTextAlignCenter);
drawOutline(s, outlineColor);
}
void QText::draw() {
const Graphics::Surface *s = g_vm->resMgr()->getSurface((uint32)-2);
if (s) {
g_vm->videoSystem()->transBlitFrom(*s, Common::Point((640 - s->w) / 2, 479 - s->h));
}
}
const Common::Rect &QText::getRect() {
return _rect;
}
void QText::drawOutline(Graphics::Surface *s, uint16 color) {
for (int y = 0; y < s->h; ++y) {
for (int x = 1; x < s->w - 1; ++x) {
uint16 *pixel = (uint16 *)s->getBasePtr(x, y);
if (*pixel && *pixel != color) {
if (!pixel[-1])
pixel[-1] = (uint16)color;
if (!pixel[1])
pixel[1] = (uint16)color;
}
}
}
for (int x = 0; x < s->w; ++x) {
for (int y = 0; y < s->h - 1; ++y) {
uint16 *pixel = (uint16 *)s->getBasePtr(x, y);
if (*pixel && *pixel != color) {
pixel = (uint16 *)s->getBasePtr(x, y - 1);
if (*pixel == 0)
*pixel = color;
pixel = (uint16 *)s->getBasePtr(x, y + 1);
if (*pixel == 0)
*pixel = color;
}
}
}
}
QText::QText() {
_resourceId = -2;
_z = 3000;
}
void QText::update(int) {
g_vm->videoSystem()->addDirtyRect(_rect);
}
QTextPhrase::QTextPhrase(const Common::U32String &text, uint16 textColor, uint16 outlineColor)
: QText(text, textColor, outlineColor), _phrase(text), _time(0) {}
void QTextPhrase::draw() {
if (g_vm->getQSystem()->_panelInterface->showSubtitles()) {
QText::draw();
}
}
void QTextPhrase::update(int time) {
DialogInterface &dialog = g_vm->getQSystem()->_mainInterface->_dialog;
_time += time;
QText::update(time);
Sound *sound = dialog.findSound();
if (sound) {
if (!sound->isPlaying()) {
_time = 0;
dialog.next(-1);
}
} else if (_time > _phrase.size() * 30 + 1000 || !g_vm->getQSystem()->_panelInterface->showSubtitles()) {
_time = 0;
dialog.next(-1);
}
}
void QTextPhrase::onClick(Common::Point p) {
DialogInterface &dialog = g_vm->getQSystem()->_mainInterface->_dialog;
dialog.next(-1);
}
QTextDescription::QTextDescription(const Common::U32String &desc, uint32 frame) {
_z = 999;
_resourceId = -2;
_rect = Common::Rect(0, 0, 640, 480);
FlicDecoder *flc = g_vm->resMgr()->getFlic(6008);
flc->setFrame(frame);
const Graphics::Surface *frameS = flc->getCurrentFrame();
Graphics::Surface *s = g_vm->resMgr()->getSurface((uint32)-2, 640, 480);
Graphics::Surface *convS = frameS->convertTo(s->format, flc->getPalette());
s->copyRectToSurface(*convS, 0, 0, _rect);
convS->free();
delete convS;
Common::Rect textArea(160, 275, 598, 376);
auto *font = g_vm->getDescriptionFont();
auto textSurface = s->getSubArea(textArea);
drawText(textSurface, 0, textArea.width(), desc, 0, *font, Graphics::kTextAlignLeft);
g_vm->videoSystem()->addDirtyRect(_rect);
}
void QTextDescription::onClick(Common::Point p) {
g_vm->getQSystem()->_mainInterface->removeTextDescription();
}
void QTextDescription::draw() {
QManager *resMgr = g_vm->resMgr();
VideoSystem *videoSys = g_vm->videoSystem();
Graphics::Surface *s = resMgr->getSurface((uint32)-2);
FlicDecoder *flc = resMgr->getFlic(6008);
for (auto &dirty : videoSys->rects()) {
videoSys->transBlitFrom(*s, dirty, dirty, flc->getTransColor(s->format));
}
}
QTextChoice::QTextChoice(const Common::Array<Common::U32String> &choices, uint16 color, uint16 outlineColor, uint16 selectedColor) {
_activeChoice = 0;
_choiceColor = color;
_outlineColor = outlineColor;
_selectedColor = selectedColor;
_choices = choices;
int w = 0;
int h = 0;
auto *font = g_vm->getTextFont();
_rects.resize(choices.size());
for (uint i = 0; i < _choices.size(); ++i) {
_rects[i] = calculateBoundingBoxForText(_choices[i], *font);
w = MAX<int>(w, _rects[i].width());
_rects[i].setWidth(w);
_rects[i].setHeight(font->getFontHeight());
h += font->getFontHeight();
}
w += 10;
h += 4;
_rect = Common::Rect((640 - w) / 2, 479 - h, 639 - (640 - w) / 2, 479);
Graphics::Surface *s = g_vm->resMgr()->getSurface((uint32)-2, w, h);
int y = 0;
for (uint i = 0; i < _choices.size(); ++i) {
drawText(*s, y, 630, _choices[i], _choiceColor, *font, Graphics::TextAlign::kTextAlignLeft);
_rects[i].moveTo(0, y);
y += font->getFontHeight();
}
drawOutline(s, outlineColor);
}
void QTextChoice::onMouseMove(Common::Point p) {
p.x = p.x - _rect.left - g_vm->getQSystem()->_xOffset;
p.y = p.y - _rect.top;
uint newChoice;
for (newChoice = 0; newChoice < _rects.size(); ++newChoice) {
if (_rects[newChoice].contains(p)) {
break;
}
}
if (newChoice != _activeChoice) {
Graphics::Surface *s = g_vm->resMgr()->getSurface((uint32)-2);
auto *font = g_vm->getTextFont();
s->fillRect(Common::Rect(s->w, s->h), 0);
for (uint i = 0; i < _choices.size(); ++i) {
uint color = (i == newChoice) ? _selectedColor : _choiceColor;
drawText(*s, _rects[i].top, 630, _choices[i], color, *font, Graphics::kTextAlignLeft);
}
drawOutline(s, _outlineColor);
_activeChoice = newChoice;
}
}
void QTextChoice::onClick(Common::Point p) {
if (_activeChoice < _choices.size()) {
g_vm->getQSystem()->_mainInterface->_dialog.next(_activeChoice);
}
}
Common::Rect QText::calculateBoundingBoxForText(const Common::U32String &text, Graphics::Font &font) {
if (text.empty())
return {};
Common::Array<Common::U32String> lines;
font.wordWrapText(text, 630, lines);
Common::Rect rect = font.getBoundingBox(lines[0]);
rect.setHeight(font.getFontHeight());
for (uint j = 1; j < lines.size(); ++j) {
auto box = font.getBoundingBox(lines[j]);
rect.setHeight(rect.height() + font.getFontHeight());
if (box.width() > rect.width())
rect.setWidth(box.width());
}
return rect;
}
void QText::drawText(Graphics::Surface &s, int y, int maxWidth, const Common::U32String &text, uint color, Graphics::Font &font, Graphics::TextAlign alignment) {
Common::Array<Common::U32String> lines;
font.wordWrapText(text, maxWidth, lines);
int h = 0;
for (uint i = 0; i < lines.size(); ++i) {
font.drawString(&s, lines[i], 0, y + h, s.w, color, alignment);
h += font.getFontHeight();
}
}
} // End of namespace Petka

View File

@@ -0,0 +1,97 @@
/* 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 PETKA_TEXT_H
#define PETKA_TEXT_H
#include "common/rect.h"
#include "common/ustr.h"
#include "common/str-array.h"
#include "graphics/font.h"
#include "petka/objects/object.h"
namespace Petka {
class QText : public QVisibleObject {
public:
QText(const Common::U32String &text, uint16 textColor, uint16 outlineColor);
void draw();
void update(int time);
const Common::Rect &getRect();
protected:
QText();
static void drawOutline(Graphics::Surface *surface, uint16 color);
static Common::Rect calculateBoundingBoxForText(const Common::U32String &text, Graphics::Font &font);
static void drawText(Graphics::Surface &s, int y, int maxWidth, const Common::U32String &text, uint color, Graphics::Font &font, Graphics::TextAlign alignment);
protected:
Common::Rect _rect;
};
class QTextPhrase : public QText {
public:
QTextPhrase(const Common::U32String &phrase, uint16 textColor, uint16 outlineColor);
void draw() override;
void update(int time) override;
void onClick(Common::Point p) override;
bool isInPoint(Common::Point p) override { return true; }
private:
Common::U32String _phrase;
uint _time;
};
class QTextDescription : public QText {
public:
QTextDescription(const Common::U32String &desc, uint32 frame);
void draw() override;
void onClick(Common::Point p) override;
bool isInPoint(Common::Point p) override { return true; }
void update(int t) override {}
};
class QTextChoice : public QText {
public:
QTextChoice(const Common::Array<Common::U32String> &choices, uint16 color, uint16 outlineColor, uint16 selectedColor);
void onMouseMove(Common::Point p) override;
void onClick(Common::Point p) override;
bool isInPoint(Common::Point p) override { return true; }
private:
Common::Array<Common::Rect> _rects;
Common::Array<Common::U32String> _choices;
uint _activeChoice;
uint16 _outlineColor;
uint16 _choiceColor;
uint16 _selectedColor;
};
} // End of namespace Petka
#endif