Initial commit
This commit is contained in:
2
engines/petka/POTFILES
Normal file
2
engines/petka/POTFILES
Normal file
@@ -0,0 +1,2 @@
|
||||
engines/petka/metaengine.cpp
|
||||
engines/petka/saveload.cpp
|
||||
132
engines/petka/base.h
Normal file
132
engines/petka/base.h
Normal file
@@ -0,0 +1,132 @@
|
||||
/* 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_BASE_H
|
||||
#define PETKA_BASE_H
|
||||
|
||||
#include "common/array.h"
|
||||
|
||||
namespace Petka {
|
||||
|
||||
enum Opcode {
|
||||
kZero = 0,
|
||||
kUse = 1,
|
||||
kSetPos = 2,
|
||||
kGoTo = 3,
|
||||
kLook = 4,
|
||||
kSay = 5,
|
||||
kTake = 6,
|
||||
kQMUse = 7,
|
||||
kVasiliyIvanovich = 8,
|
||||
kWalk = 9,
|
||||
kTalk = 10,
|
||||
kEnd = 11,
|
||||
kSetAnimation = 12,
|
||||
kForceMove = 13,
|
||||
kSet = 14,
|
||||
kShow = 15,
|
||||
kHide = 16,
|
||||
kDialog = 17,
|
||||
kZBuffer = 18,
|
||||
kTotalInit = 19,
|
||||
kAnimate = 20,
|
||||
kStatus = 21,
|
||||
kAddInv = 22,
|
||||
kDelInv = 23,
|
||||
kStop = 24,
|
||||
kCursor = 25,
|
||||
kObjectUse = 26,
|
||||
kActive = 27,
|
||||
kSaid = 28,
|
||||
kSetSeq = 29,
|
||||
kEndSeq = 30,
|
||||
kCheck = 31,
|
||||
kIf = 32,
|
||||
kDescription = 33,
|
||||
kHalf = 34,
|
||||
kWalked = 35,
|
||||
kWalkTo = 36,
|
||||
kWalkVich = 37,
|
||||
kInitBG = 38,
|
||||
kUserMsg = 39,
|
||||
kSystem = 40,
|
||||
kSetZBuffer = 41, // ??? Reserved1
|
||||
kContinue = 42,
|
||||
kMap = 43,
|
||||
kPassive = 44,
|
||||
kNoMap = 45,
|
||||
kSetInv = 46,
|
||||
kBGsFX = 47,
|
||||
kMusic = 48,
|
||||
kImage = 49,
|
||||
kStand = 50,
|
||||
kOn = 51,
|
||||
kOff = 52,
|
||||
kPlay = 53,
|
||||
kLeaveBG = 54,
|
||||
kShake = 55,
|
||||
kSP = 56,
|
||||
kRandom = 57,
|
||||
kJump = 58,
|
||||
kJumpVich = 59,
|
||||
kPart = 60,
|
||||
kChapter = 61,
|
||||
kAvi = 62,
|
||||
kToMap = 63 // ??? MessageNumber
|
||||
};
|
||||
|
||||
class QMessageObject;
|
||||
|
||||
struct QMessage {
|
||||
QMessage() {
|
||||
objId = opcode = arg1 = arg2 = arg3 = 0;
|
||||
sender = nullptr;
|
||||
unk = 0;
|
||||
}
|
||||
QMessage(uint16 _objId, uint16 _opcode, uint16 _arg1, int16 _arg2, int16 _arg3, QMessageObject *_sender, int _unk) {
|
||||
this->objId = _objId;
|
||||
this->opcode = _opcode;
|
||||
this->arg1 = _arg1;
|
||||
this->arg2 = _arg2;
|
||||
this->arg3 = _arg3;
|
||||
this->sender = _sender;
|
||||
this->unk = _unk;
|
||||
}
|
||||
|
||||
uint16 objId;
|
||||
uint16 opcode;
|
||||
uint16 arg1;
|
||||
int16 arg2;
|
||||
int16 arg3;
|
||||
QMessageObject *sender;
|
||||
int32 unk;
|
||||
};
|
||||
|
||||
struct QReaction {
|
||||
uint16 opcode;
|
||||
int8 status;
|
||||
int16 senderId;
|
||||
Common::Array<QMessage> messages;
|
||||
};
|
||||
|
||||
} // End of namespace Petka
|
||||
|
||||
#endif
|
||||
480
engines/petka/big_dialogue.cpp
Normal file
480
engines/petka/big_dialogue.cpp
Normal file
@@ -0,0 +1,480 @@
|
||||
/* 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 "petka/base.h"
|
||||
#include "petka/big_dialogue.h"
|
||||
#include "petka/interfaces/main.h"
|
||||
#include "petka/interfaces/dialog_interface.h"
|
||||
#include "petka/objects/heroes.h"
|
||||
#include "petka/q_system.h"
|
||||
#include "petka/petka.h"
|
||||
|
||||
namespace Petka {
|
||||
|
||||
const uint16 kFallback = 0xFFFE;
|
||||
|
||||
BigDialogue::BigDialogue(PetkaEngine &vm)
|
||||
: _vm(vm) {
|
||||
_currOp = nullptr;
|
||||
_startOpIndex = 0;
|
||||
|
||||
Common::ScopedPtr<Common::SeekableReadStream> file(vm.openFile("dialogue.fix", true));
|
||||
if (!file)
|
||||
return;
|
||||
|
||||
_objDialogs.resize(file->readUint32LE());
|
||||
for (uint i = 0; i < _objDialogs.size(); ++i) {
|
||||
_objDialogs[i].objId = file->readUint32LE();
|
||||
_objDialogs[i].handlers.resize(file->readUint32LE());
|
||||
file->skip(4); // pointer
|
||||
}
|
||||
for (uint i = 0; i < _objDialogs.size(); ++i) {
|
||||
for (uint j = 0; j < _objDialogs[i].handlers.size(); ++j) {
|
||||
_objDialogs[i].handlers[j].opcode = file->readUint32LE();
|
||||
_objDialogs[i].handlers[j].dialogs.resize(file->readUint32LE());
|
||||
_objDialogs[i].handlers[j].startDialogIndex = file->readUint32LE();
|
||||
file->skip(4); // pointer
|
||||
}
|
||||
for (uint j = 0; j < _objDialogs[i].handlers.size(); ++j) {
|
||||
for (uint z = 0; z < _objDialogs[i].handlers[j].dialogs.size(); ++z) {
|
||||
_objDialogs[i].handlers[j].dialogs[z].startOpIndex = file->readUint32LE();
|
||||
file->skip(4 + 4); // opsCount + pointer
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
load(file.get());
|
||||
}
|
||||
|
||||
void BigDialogue::loadSpeechesInfo() {
|
||||
if (!_speeches.empty())
|
||||
return;
|
||||
|
||||
Common::ScopedPtr<Common::SeekableReadStream> file(_vm.openFile("dialogue.lod", true));
|
||||
if (!file)
|
||||
return;
|
||||
|
||||
_speeches.resize(file->readUint32LE());
|
||||
for (uint i = 0; i < _speeches.size(); ++i) {
|
||||
_speeches[i].speakerId = file->readUint32LE();
|
||||
file->read(_speeches[i].soundName, sizeof(_speeches[i].soundName));
|
||||
file->skip(4);
|
||||
}
|
||||
|
||||
char *str = new char[file->size() - file->pos()];
|
||||
char *curr = str;
|
||||
file->read(str, file->size() - file->pos());
|
||||
for (uint i = 0; i < _speeches.size(); ++i) {
|
||||
_speeches[i].text = Common::convertToU32String(curr, Common::kWindows1251);
|
||||
curr += strlen(curr) + 1;
|
||||
}
|
||||
delete[] str;
|
||||
}
|
||||
|
||||
const Common::U32String *BigDialogue::getSpeechInfo(int *talkerId, const char **soundName, int choice) {
|
||||
if (!_currOp)
|
||||
return nullptr;
|
||||
switch (_currOp->type) {
|
||||
case kOperationMenu: {
|
||||
Operation *menuOp = _currOp;
|
||||
uint bit = 1;
|
||||
if (_currOp->menu.bits <= choice || choice < 0) {
|
||||
break;
|
||||
}
|
||||
while (true) {
|
||||
_currOp += 1;
|
||||
if (choice == 0 && (bit & menuOp->menu.bitField))
|
||||
break;
|
||||
if (_currOp->type == kOperationBreak) {
|
||||
if (bit & menuOp->menu.bitField)
|
||||
choice--;
|
||||
bit *= 2;
|
||||
}
|
||||
}
|
||||
if (_currOp->type != kOperationPlay)
|
||||
next();
|
||||
if (_currOp->type != kOperationPlay) {
|
||||
_currOp = menuOp;
|
||||
break;
|
||||
}
|
||||
|
||||
uint index = _currOp->play.messageIndex;
|
||||
_currOp = menuOp;
|
||||
if (soundName)
|
||||
*soundName = _speeches[index].soundName;
|
||||
*talkerId = _speeches[index].speakerId;
|
||||
return &_speeches[index].text;
|
||||
}
|
||||
case kOperationCircle:
|
||||
circleMoveTo(_currOp->circle.curr);
|
||||
assert(_currOp->type == kOperationPlay);
|
||||
// fall through
|
||||
case kOperationPlay:
|
||||
if (soundName)
|
||||
*soundName = _speeches[_currOp->play.messageIndex].soundName;
|
||||
*talkerId = _speeches[_currOp->play.messageIndex].speakerId;
|
||||
return &_speeches[_currOp->play.messageIndex].text;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const DialogHandler *BigDialogue::findHandler(uint objId, uint opcode, bool *fallback) const {
|
||||
if (opcode == kEnd || opcode == kHalf) {
|
||||
return nullptr;
|
||||
}
|
||||
if (fallback) {
|
||||
*fallback = false;
|
||||
}
|
||||
for (uint i = 0; i < _objDialogs.size(); ++i) {
|
||||
if (_objDialogs[i].objId != objId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (uint j = 0; j < _objDialogs[i].handlers.size(); ++j) {
|
||||
if (_objDialogs[i].handlers[j].opcode == opcode) {
|
||||
return &_objDialogs[i].handlers[j];
|
||||
}
|
||||
}
|
||||
|
||||
if ((uint16)opcode != kObjectUse) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (uint j = 0; j < _objDialogs[i].handlers.size(); ++j) {
|
||||
if (_objDialogs[i].handlers[j].opcode == kFallback) {
|
||||
if (fallback)
|
||||
*fallback = true;
|
||||
return &_objDialogs[i].handlers[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
for (uint i = 0; i < _objDialogs.size(); ++i) {
|
||||
if (_objDialogs[i].objId != kFallback)
|
||||
continue;
|
||||
for (uint j = 0; j < _objDialogs[i].handlers.size(); ++j) {
|
||||
if (_objDialogs[i].handlers[j].opcode == opcode) {
|
||||
if (fallback)
|
||||
*fallback = true;
|
||||
return &_objDialogs[i].handlers[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void BigDialogue::setHandler(uint objId, uint opcode) {
|
||||
loadSpeechesInfo();
|
||||
const DialogHandler *h = findHandler(objId, opcode, nullptr);
|
||||
if (h) {
|
||||
_startOpIndex = h->dialogs[h->startDialogIndex].startOpIndex;
|
||||
_currOp = &_ops[_startOpIndex];
|
||||
}
|
||||
}
|
||||
|
||||
uint BigDialogue::opcode() {
|
||||
while (_currOp) {
|
||||
switch (_currOp->type) {
|
||||
case kOperationMenu:
|
||||
if (choicesCount() > 1)
|
||||
return kOpcodeMenu;
|
||||
next(0);
|
||||
break;
|
||||
case kOperationReturn:
|
||||
return kOpcodeEnd;
|
||||
case kOperationPlay:
|
||||
case kOperationCircle:
|
||||
return kOpcodePlay;
|
||||
case kOperationUserMessage:
|
||||
return kOpcodeUserMessage;
|
||||
default:
|
||||
next();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return kOpcodeEnd;
|
||||
}
|
||||
|
||||
void BigDialogue::load(Common::ReadStream *s) {
|
||||
uint32 opsCount = s->readUint32LE();
|
||||
_ops.resize(opsCount);
|
||||
for (uint i = 0; i < opsCount; ++i) {
|
||||
uint op = s->readUint32LE();
|
||||
_ops[i].type = (byte)(op >> 24);
|
||||
switch (_ops[i].type) {
|
||||
case kOperationBreak:
|
||||
break;
|
||||
case kOperationMenu:
|
||||
_ops[i].menu.bits = (byte)op;
|
||||
_ops[i].menu.bitField = (uint16)(op >> 8);
|
||||
break;
|
||||
case kOperationGoTo:
|
||||
_ops[i].goTo.opIndex = (uint16)(op);
|
||||
break;
|
||||
case kOperationDisableMenuItem:
|
||||
_ops[i].disableMenuItem.opIndex = (uint16)op;
|
||||
_ops[i].disableMenuItem.bit = (byte)(op >> 16);
|
||||
break;
|
||||
case kOperationEnableMenuItem:
|
||||
_ops[i].enableMenuItem.opIndex = (uint16)op;
|
||||
_ops[i].enableMenuItem.bit = (byte)(op >> 16);
|
||||
break;
|
||||
case kOperationReturn:
|
||||
break;
|
||||
case kOperationPlay:
|
||||
_ops[i].play.messageIndex = (uint16)op;
|
||||
break;
|
||||
case kOperationCircle:
|
||||
_ops[i].circle.count = (uint16)op;
|
||||
_ops[i].circle.curr = (byte)(op >> 16);
|
||||
break;
|
||||
case kOperationUserMessage:
|
||||
_ops[i].userMsg.arg = (uint16)op;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BigDialogue::save(Common::WriteStream *s) {
|
||||
s->writeUint32LE(_ops.size());
|
||||
for (uint i = 0; i < _ops.size(); ++i) {
|
||||
switch (_ops[i].type) {
|
||||
case kOperationBreak:
|
||||
s->writeUint32LE(MKTAG(kOperationBreak, 0, 0, 0));
|
||||
break;
|
||||
case kOperationMenu:
|
||||
s->writeByte(_ops[i].menu.bits);
|
||||
s->writeUint16LE(_ops[i].menu.bitField);
|
||||
s->writeByte(kOperationMenu);
|
||||
break;
|
||||
case kOperationGoTo:
|
||||
s->writeUint16LE(_ops[i].goTo.opIndex);
|
||||
s->writeUint16LE(MKTAG16(kOperationGoTo, 0));
|
||||
break;
|
||||
case kOperationDisableMenuItem:
|
||||
s->writeUint16LE(_ops[i].disableMenuItem.opIndex);
|
||||
s->writeUint16LE(MKTAG16(kOperationDisableMenuItem, _ops[i].disableMenuItem.bit));
|
||||
break;
|
||||
case kOperationEnableMenuItem:
|
||||
s->writeUint16LE(_ops[i].enableMenuItem.opIndex);
|
||||
s->writeUint16LE(MKTAG16(kOperationEnableMenuItem, _ops[i].enableMenuItem.bit));
|
||||
break;
|
||||
case kOperationReturn:
|
||||
s->writeUint32LE(MKTAG(kOperationReturn, 0, 0, 0));
|
||||
break;
|
||||
case kOperationPlay:
|
||||
s->writeUint16LE(_ops[i].play.messageIndex);
|
||||
s->writeUint16LE(MKTAG16(kOperationPlay, 0));
|
||||
break;
|
||||
case kOperationCircle:
|
||||
s->writeUint16LE(_ops[i].circle.count);
|
||||
s->writeUint16LE(MKTAG16(kOperationCircle, _ops[i].circle.curr));
|
||||
break;
|
||||
case kOperationUserMessage:
|
||||
s->writeUint16LE(_ops[i].userMsg.arg);
|
||||
s->writeUint16LE(MKTAG16(kOperationUserMessage, 0));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BigDialogue::next(int choice) {
|
||||
bool processed = true;
|
||||
|
||||
if (!_currOp)
|
||||
return;
|
||||
if (choice != -1 && _currOp->type != kOperationMenu) {
|
||||
choice = -1;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
switch (_currOp->type) {
|
||||
case kOperationBreak:
|
||||
while (_currOp->type != kOperationMenu && _currOp->type != kOperationCircle) {
|
||||
_currOp--;
|
||||
}
|
||||
next(choice);
|
||||
return;
|
||||
case kOperationMenu: {
|
||||
if (!processed)
|
||||
return;
|
||||
if (choice == -1)
|
||||
choice = 0;
|
||||
if (_currOp->menu.bits <= choice)
|
||||
choice = _currOp->menu.bits - 1;
|
||||
uint16 bitField = _currOp->menu.bitField;
|
||||
uint bits = _currOp->menu.bits;
|
||||
uint bit = 1;
|
||||
uint i = 0;
|
||||
while (bits) {
|
||||
if (_currOp->type == kOperationBreak) {
|
||||
bits--;
|
||||
if (!(bit & bitField) && i <= (uint)choice)
|
||||
choice++;
|
||||
bit *= 2;
|
||||
i++;
|
||||
}
|
||||
_currOp += 1;
|
||||
}
|
||||
_currOp += choice;
|
||||
processed = false;
|
||||
break;
|
||||
}
|
||||
case kOperationGoTo: {
|
||||
_currOp = &_ops[_currOp->goTo.opIndex];
|
||||
processed = false;
|
||||
break;
|
||||
}
|
||||
case kOperationDisableMenuItem:
|
||||
_ops[_currOp->disableMenuItem.opIndex].menu.bitField &= ~(1 << _currOp->disableMenuItem.bit); // disable menu item
|
||||
checkMenu(_startOpIndex);
|
||||
_currOp += 1;
|
||||
processed = false;
|
||||
break;
|
||||
case kOperationEnableMenuItem:
|
||||
_ops[_currOp->enableMenuItem.opIndex].menu.bitField |= (1 << _currOp->enableMenuItem.bit);
|
||||
_currOp += 1;
|
||||
processed = false;
|
||||
break;
|
||||
case kOperationReturn:
|
||||
return;
|
||||
case kOperationPlay:
|
||||
if (!processed)
|
||||
return;
|
||||
_currOp += 1;
|
||||
processed = false;
|
||||
break;
|
||||
case kOperationCircle:
|
||||
if (!processed)
|
||||
return;
|
||||
_currOp->circle.curr = (byte)((_currOp->circle.curr + 1) % _currOp->circle.count);
|
||||
circleMoveTo(_currOp->circle.count);
|
||||
processed = false;
|
||||
break;
|
||||
case kOperationUserMessage:
|
||||
if (processed)
|
||||
_currOp += 1;
|
||||
else {
|
||||
_vm.getQSystem()->_mainInterface->_dialog.startUserMsg(_currOp->userMsg.arg);
|
||||
}
|
||||
return;
|
||||
default:
|
||||
_currOp += 1;
|
||||
processed = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
uint BigDialogue::choicesCount() {
|
||||
if (!_currOp || _currOp->type != kOperationMenu)
|
||||
return 0;
|
||||
uint count = 0;
|
||||
uint bit = 1;
|
||||
for (uint i = 0; i < _currOp->menu.bits; ++i) {
|
||||
if (_currOp->menu.bitField & bit) {
|
||||
count++;
|
||||
}
|
||||
bit <<= 1;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
bool BigDialogue::findOperation(uint index, uint opType, uint *resIndex) {
|
||||
while (_ops[index].type != opType) {
|
||||
switch(_ops[index].type) {
|
||||
case kOperationGoTo:
|
||||
if (index >= _ops[index].goTo.opIndex)
|
||||
return false;
|
||||
index = _ops[index].goTo.opIndex;
|
||||
break;
|
||||
case kOperationReturn:
|
||||
return false;
|
||||
default:
|
||||
index++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
*resIndex = index;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BigDialogue::checkMenu(uint menuIndex) {
|
||||
if (_ops[menuIndex].type != kOperationMenu && !findOperation(menuIndex, kOperationMenu, &menuIndex)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
uint count = 0;
|
||||
uint bit = 1;
|
||||
uint opIndex = menuIndex + 1;
|
||||
for (uint i = 0; i < _ops[menuIndex].menu.bits; ++i) {
|
||||
if (_ops[menuIndex].menu.bitField & bit) {
|
||||
count++;
|
||||
}
|
||||
findOperation(opIndex, kOperationBreak, &opIndex);
|
||||
opIndex++;
|
||||
bit <<= 1;
|
||||
}
|
||||
|
||||
if (!count)
|
||||
return false;
|
||||
|
||||
bit = 1;
|
||||
for (uint i = 0; i < _ops[menuIndex].menu.bits; ++i) {
|
||||
uint subMenuIndex;
|
||||
if ((_ops[menuIndex].menu.bitField & bit) && findOperation(_ops[opIndex + i].goTo.opIndex, kOperationMenu, &subMenuIndex) && !checkMenu(subMenuIndex)) {
|
||||
_ops[menuIndex].menu.bitField &= ~bit;
|
||||
count--;
|
||||
if (count < 1)
|
||||
return false;
|
||||
}
|
||||
bit <<= 1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void BigDialogue::getMenuChoices(Common::Array<Common::U32String> &choices) {
|
||||
uint count = choicesCount();
|
||||
for (uint i = 0; i < count; ++i) {
|
||||
int id;
|
||||
choices.push_back(*getSpeechInfo(&id, nullptr, i));
|
||||
}
|
||||
}
|
||||
|
||||
void BigDialogue::circleMoveTo(byte index) {
|
||||
_currOp += 1;
|
||||
for (uint i = 0; i < index; ++i) {
|
||||
while (_currOp->type != kOperationBreak)
|
||||
_currOp += 1;
|
||||
_currOp += 1;
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Petka
|
||||
143
engines/petka/big_dialogue.h
Normal file
143
engines/petka/big_dialogue.h
Normal file
@@ -0,0 +1,143 @@
|
||||
/* 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_BIG_DIALOGUE_H
|
||||
#define PETKA_BIG_DIALOGUE_H
|
||||
|
||||
#include "common/array.h"
|
||||
#include "common/ustr.h"
|
||||
|
||||
namespace Petka {
|
||||
|
||||
class PetkaEngine;
|
||||
|
||||
enum {
|
||||
kOpcodePlay = 1,
|
||||
kOpcodeMenu,
|
||||
kOpcodeEnd,
|
||||
kOpcodeUserMessage
|
||||
};
|
||||
|
||||
enum {
|
||||
kOperationBreak = 1,
|
||||
kOperationMenu,
|
||||
kOperationGoTo,
|
||||
kOperationDisableMenuItem,
|
||||
kOperationEnableMenuItem,
|
||||
kOperationReturn,
|
||||
kOperationPlay,
|
||||
kOperationCircle,
|
||||
kOperationUserMessage
|
||||
};
|
||||
|
||||
struct Operation {
|
||||
union {
|
||||
struct {
|
||||
byte bits;
|
||||
uint16 bitField;
|
||||
} menu;
|
||||
struct {
|
||||
uint16 opIndex;
|
||||
} goTo;
|
||||
struct {
|
||||
uint16 opIndex;
|
||||
byte bit;
|
||||
} disableMenuItem;
|
||||
struct {
|
||||
uint16 opIndex;
|
||||
byte bit;
|
||||
} enableMenuItem;
|
||||
struct {
|
||||
uint16 messageIndex;
|
||||
} play;
|
||||
struct {
|
||||
uint16 count;
|
||||
byte curr;
|
||||
} circle;
|
||||
struct {
|
||||
uint16 arg;
|
||||
} userMsg;
|
||||
};
|
||||
byte type;
|
||||
};
|
||||
|
||||
struct Dialog {
|
||||
uint32 startOpIndex;
|
||||
// uint32 opsCount;
|
||||
// Operation *ops;
|
||||
};
|
||||
|
||||
struct DialogHandler {
|
||||
uint32 opcode;
|
||||
uint32 startDialogIndex;
|
||||
Common::Array<Dialog> dialogs;
|
||||
};
|
||||
|
||||
struct DialogGroup {
|
||||
uint32 objId;
|
||||
Common::Array<DialogHandler> handlers;
|
||||
};
|
||||
|
||||
struct SpeechInfo {
|
||||
uint32 speakerId;
|
||||
char soundName[16];
|
||||
Common::U32String text;
|
||||
};
|
||||
|
||||
class BigDialogue {
|
||||
public:
|
||||
BigDialogue(PetkaEngine &vm);
|
||||
|
||||
uint opcode();
|
||||
|
||||
uint choicesCount();
|
||||
|
||||
void next(int choice = -1);
|
||||
|
||||
const DialogHandler *findHandler(uint objId, uint opcode, bool *fallback) const;
|
||||
void setHandler(uint objId, uint opcode);
|
||||
|
||||
const Common::U32String *getSpeechInfo(int *talkerId, const char **soundName, int choice);
|
||||
void getMenuChoices(Common::Array<Common::U32String> &choices);
|
||||
|
||||
void load(Common::ReadStream *s);
|
||||
void save(Common::WriteStream *s);
|
||||
|
||||
private:
|
||||
void loadSpeechesInfo();
|
||||
bool checkMenu(uint opIndex);
|
||||
bool findOperation(uint startOpIndex, uint opType, uint *resIndex);
|
||||
void circleMoveTo(byte index);
|
||||
|
||||
private:
|
||||
PetkaEngine &_vm;
|
||||
|
||||
Operation *_currOp;
|
||||
Common::Array<Operation> _ops;
|
||||
uint _startOpIndex;
|
||||
|
||||
Common::Array<SpeechInfo> _speeches;
|
||||
Common::Array<DialogGroup> _objDialogs;
|
||||
};
|
||||
|
||||
} // End of namespace Petka
|
||||
|
||||
#endif
|
||||
3
engines/petka/configure.engine
Normal file
3
engines/petka/configure.engine
Normal file
@@ -0,0 +1,3 @@
|
||||
# This file is included from the main "configure" script
|
||||
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] [components]
|
||||
add_engine petka "Red Comrades" yes "" "" "highres 16bit freetype2"
|
||||
4
engines/petka/credits.pl
Normal file
4
engines/petka/credits.pl
Normal file
@@ -0,0 +1,4 @@
|
||||
begin_section("Petka");
|
||||
add_person("Andrei Prykhodko", "whiterandrek", "");
|
||||
add_person("Eugene Sandulenko", "sev", "");
|
||||
end_section();
|
||||
68
engines/petka/detection.cpp
Normal file
68
engines/petka/detection.cpp
Normal file
@@ -0,0 +1,68 @@
|
||||
/* 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 "base/plugins.h"
|
||||
|
||||
#include "engines/advancedDetector.h"
|
||||
|
||||
#include "petka/petka.h"
|
||||
|
||||
static const PlainGameDescriptor petkaGames[] = {
|
||||
{"petka1", "Red Comrades 1: Save the Galaxy"},
|
||||
{"petka2", "Red Comrades 2: For the Great Justice"},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
static const DebugChannelDef debugFlagList[] = {
|
||||
{Petka::kPetkaDebugGeneral, "general", "General issues"},
|
||||
{Petka::kPetkaDebugResources, "resources", "Resources"},
|
||||
{Petka::kPetkaDebugMessagingSystem, "message_system", "Engine message system"},
|
||||
{Petka::kPetkaDebugDialogs, "dialogs", "Dialogs"},
|
||||
DEBUG_CHANNEL_END
|
||||
};
|
||||
|
||||
#include "petka/detection_tables.h"
|
||||
|
||||
class PetkaMetaEngineDetection : public AdvancedMetaEngineDetection<ADGameDescription> {
|
||||
public:
|
||||
PetkaMetaEngineDetection() : AdvancedMetaEngineDetection(Petka::gameDescriptions, petkaGames) {
|
||||
_gameIds = petkaGames;
|
||||
_maxScanDepth = 2;
|
||||
}
|
||||
|
||||
const char *getName() const override {
|
||||
return "petka";
|
||||
}
|
||||
|
||||
const char *getEngineName() const override {
|
||||
return "Red Comrades";
|
||||
}
|
||||
|
||||
const char *getOriginalCopyright() const override {
|
||||
return "Red Comrades (C) S.K.I.F.";
|
||||
}
|
||||
|
||||
const DebugChannelDef *getDebugChannels() const override {
|
||||
return debugFlagList;
|
||||
}
|
||||
};
|
||||
|
||||
REGISTER_PLUGIN_STATIC(PETKA_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, PetkaMetaEngineDetection);
|
||||
92
engines/petka/detection_tables.h
Normal file
92
engines/petka/detection_tables.h
Normal 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef PETKA_DETECTION_TABLES_H
|
||||
#define PETKA_DETECTION_TABLES_H
|
||||
|
||||
namespace Petka {
|
||||
|
||||
static const ADGameDescription gameDescriptions[] = {
|
||||
|
||||
// Red Comrades Demo
|
||||
{
|
||||
"petka1",
|
||||
"Demo",
|
||||
AD_ENTRY2s("DEMO.EXE", "5ef1ceaba05413d04fd733a81e6adbae", 888832,
|
||||
"SCRIPT.DAT", "8712157c39daf8dd1f441a11d19b9e48", 1851),
|
||||
Common::RU_RUS,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_DROPPLATFORM | ADGF_DROPLANGUAGE | ADGF_DEMO,
|
||||
GUIO1(GUIO_NOMIDI)
|
||||
},
|
||||
|
||||
// Red Comrades 1: Save the Galaxy
|
||||
{
|
||||
"petka1",
|
||||
0,
|
||||
AD_ENTRY1s("MAIN.STR", "2523bf402ac8b7b2bf54e6e29a79831d", 27414919),
|
||||
Common::RU_RUS,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_DROPPLATFORM | ADGF_DROPLANGUAGE,
|
||||
GUIO1(GUIO_NOMIDI)
|
||||
},
|
||||
|
||||
// Red Comrades 1: Save the Galaxy (3 CD)
|
||||
{
|
||||
"petka1",
|
||||
"Compressed",
|
||||
AD_ENTRY2s("BGS1.STR", "99832accda859e2e1daeaae0a8561aeb", 20280804,
|
||||
"data1.cab", "7e73a644d8b15d2fd3781de5edce0c18", 228838940),
|
||||
Common::RU_RUS,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_DROPPLATFORM | ADGF_DROPLANGUAGE | GF_COMPRESSED,
|
||||
GUIO1(GUIO_NOMIDI)
|
||||
},
|
||||
|
||||
// Red Comrades 2: For the Great Justice
|
||||
{
|
||||
"petka2",
|
||||
0,
|
||||
AD_ENTRY1s("main.str", "4e515669c343609518277cab6e7d8c8f", 18992879),
|
||||
Common::RU_RUS,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_DROPPLATFORM | ADGF_DROPLANGUAGE,
|
||||
GUIO1(GUIO_NOMIDI)
|
||||
},
|
||||
|
||||
// Red Comrades 2: For the Great Justice (CD)
|
||||
{
|
||||
"petka2",
|
||||
"Compressed",
|
||||
AD_ENTRY2s("main.str", "4e515669c343609518277cab6e7d8c8f", 18992879,
|
||||
"data1.cab", "ac9ee2e481ee5a6389a6cd58faf5a358", 23287376),
|
||||
Common::RU_RUS,
|
||||
Common::kPlatformWindows,
|
||||
ADGF_DROPPLATFORM | ADGF_DROPLANGUAGE | GF_COMPRESSED,
|
||||
GUIO1(GUIO_NOMIDI)
|
||||
},
|
||||
|
||||
AD_TABLE_END_MARKER
|
||||
};
|
||||
|
||||
} // End of namespace Petka
|
||||
|
||||
#endif
|
||||
111
engines/petka/file_mgr.cpp
Normal file
111
engines/petka/file_mgr.cpp
Normal 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 "common/file.h"
|
||||
#include "common/substream.h"
|
||||
|
||||
#include "petka/petka.h"
|
||||
#include "petka/file_mgr.h"
|
||||
|
||||
namespace Petka {
|
||||
|
||||
bool FileMgr::openStore(const Common::String &name) {
|
||||
Common::SharedPtr<Common::File> file(new Common::File());
|
||||
if (name.empty() || !file->open(Common::Path(name)) || file->readUint32BE() != MKTAG('S', 't', 'O', 'R')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint32 tableOffset = file->readUint32LE();
|
||||
if (!file->seek(tableOffset)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint32 tableSize = file->size() - file->pos();
|
||||
|
||||
Common::ScopedPtr<Common::SeekableReadStream> stream(file->readStream(tableSize));
|
||||
if (stream->size() != (int)tableSize)
|
||||
return false;
|
||||
|
||||
_stores.push_back(Store());
|
||||
|
||||
Store &store = _stores.back();
|
||||
store.file = file;
|
||||
store.descriptions.resize(stream->readUint32LE());
|
||||
|
||||
for (auto &description : store.descriptions) {
|
||||
stream->skip(4);
|
||||
description.offset = stream->readUint32LE();
|
||||
description.size = stream->readUint32LE();
|
||||
}
|
||||
|
||||
for (auto &description : store.descriptions) {
|
||||
char ch;
|
||||
while ((ch = stream->readByte()) != '\0') {
|
||||
description.name += ch;
|
||||
}
|
||||
}
|
||||
|
||||
debugC(kPetkaDebugResources, "FileMgr: opened store %s (files count: %d)", name.c_str(), store.descriptions.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
void FileMgr::closeStore(const Common::String &name) {
|
||||
for (auto it = _stores.begin(); it != _stores.end(); ++it) {
|
||||
if (it->file->getName() == name) {
|
||||
_stores.erase(it);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FileMgr::closeAll() {
|
||||
_stores.clear();
|
||||
}
|
||||
|
||||
static Common::Path formPath(Common::String name) {
|
||||
for (uint i = 0; i < name.size(); ++i) {
|
||||
if (name[i] == '\\') {
|
||||
name.setChar('/', i);
|
||||
}
|
||||
}
|
||||
return Common::Path(name, '/');
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *FileMgr::getFileStream(const Common::String &name) {
|
||||
Common::ScopedPtr<Common::File> file(new Common::File());
|
||||
if (file->open(formPath(name))) {
|
||||
debugC(kPetkaDebugResources, "FileMgr: %s is opened from game directory", name.c_str());
|
||||
return file.release();
|
||||
}
|
||||
|
||||
for (auto &store : _stores) {
|
||||
for (auto &resource : store.descriptions) {
|
||||
if (resource.name.compareToIgnoreCase(name) == 0)
|
||||
return new Common::SafeSeekableSubReadStream(store.file.get(), resource.offset, resource.offset + resource.size);
|
||||
}
|
||||
}
|
||||
|
||||
debugC(kPetkaDebugResources, "FileMgr: %s not found", name.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // End of namespace Petka
|
||||
58
engines/petka/file_mgr.h
Normal file
58
engines/petka/file_mgr.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/* 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_FILE_MGR_H
|
||||
#define PETKA_FILE_MGR_H
|
||||
|
||||
#include "common/list.h"
|
||||
#include "common/ptr.h"
|
||||
|
||||
namespace Common {
|
||||
class File;
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace Petka {
|
||||
|
||||
class FileMgr {
|
||||
public:
|
||||
bool openStore(const Common::String &name);
|
||||
void closeStore(const Common::String &name);
|
||||
void closeAll();
|
||||
|
||||
Common::SeekableReadStream *getFileStream(const Common::String &name);
|
||||
|
||||
private:
|
||||
struct Resource {
|
||||
Common::String name;
|
||||
uint32 offset;
|
||||
uint32 size;
|
||||
};
|
||||
struct Store {
|
||||
Common::SharedPtr<Common::File> file;
|
||||
Common::Array<Resource> descriptions;
|
||||
};
|
||||
Common::Array<Store> _stores;
|
||||
};
|
||||
|
||||
} // End of namespace Petka
|
||||
|
||||
#endif
|
||||
220
engines/petka/flc.cpp
Normal file
220
engines/petka/flc.cpp
Normal 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/system.h"
|
||||
#include "common/stream.h"
|
||||
|
||||
#include "graphics/surface.h"
|
||||
|
||||
#include "petka/flc.h"
|
||||
#include "flc.h"
|
||||
|
||||
|
||||
namespace Petka {
|
||||
|
||||
const Graphics::Surface *FlicDecoder::getCurrentFrame() const {
|
||||
const Track *track = getTrack(0);
|
||||
if (track)
|
||||
return ((const FlicVideoTrack *)track)->getSurface();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void FlicDecoder::load(Common::SeekableReadStream *stream, Common::SeekableReadStream *mskStream) {
|
||||
close();
|
||||
|
||||
/* uint32 frameSize = */ stream->readUint32LE();
|
||||
uint16 frameType = stream->readUint16LE();
|
||||
|
||||
// Check FLC magic number
|
||||
if (frameType != 0xAF12) {
|
||||
warning("FlicDecoder::loadStream(): attempted to load non-FLC data (type = 0x%04X)", frameType);
|
||||
}
|
||||
|
||||
uint16 frameCount = stream->readUint16LE();
|
||||
uint16 width = stream->readUint16LE();
|
||||
uint16 height = stream->readUint16LE();
|
||||
uint16 colorDepth = stream->readUint16LE();
|
||||
if (colorDepth != 8) {
|
||||
warning("FlicDecoder::loadStream(): attempted to load an FLC with a palette of color depth %d. Only 8-bit color palettes are supported", colorDepth);
|
||||
}
|
||||
|
||||
FlicVideoTrack *track = new FlicVideoTrack(stream, frameCount, width, height);
|
||||
addTrack(track);
|
||||
decodeNextFrame();
|
||||
assert(track->getPalette());
|
||||
if (mskStream)
|
||||
track->loadMsk(*mskStream);
|
||||
delete mskStream;
|
||||
}
|
||||
|
||||
const Common::Rect FlicDecoder::getBounds() const {
|
||||
const Track *track = getTrack(0);
|
||||
if (track)
|
||||
return ((const FlicVideoTrack *)track)->getBounds();
|
||||
|
||||
return Common::Rect(0, 0);
|
||||
}
|
||||
|
||||
const Common::Array<Common::Rect> FlicDecoder::getMskRects() const {
|
||||
const Track *track = getTrack(0);
|
||||
if (track)
|
||||
return ((const FlicVideoTrack *)track)->getMskRects();
|
||||
|
||||
return Common::Array<Common::Rect>();
|
||||
}
|
||||
|
||||
uint32 FlicDecoder::getTransColor(const Graphics::PixelFormat &fmt) const {
|
||||
const Track *track = getTrack(0);
|
||||
if (track) {
|
||||
const FlicVideoTrack *flc = ((const FlicVideoTrack *)track);
|
||||
byte r = flc->getPalette()[0];
|
||||
byte g = flc->getPalette()[1];
|
||||
byte b = flc->getPalette()[2];
|
||||
return fmt.RGBToColor(r, g, b);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void FlicDecoder::setFrame(int frame) {
|
||||
FlicVideoTrack *flc = ((FlicVideoTrack *)getTrack(0));
|
||||
if (!flc || flc->getFrameCount() == 1 || flc->getCurFrame() + 1 == frame)
|
||||
return;
|
||||
|
||||
if (frame == -1) {
|
||||
if (flc->getCurFrame() + 1 == flc->getFrameCount()) {
|
||||
flc->rewind();
|
||||
}
|
||||
flc->decodeNextFrame();
|
||||
return;
|
||||
}
|
||||
|
||||
flc->rewind();
|
||||
do {
|
||||
flc->decodeNextFrame();
|
||||
} while (flc->getCurFrame() + 1 != frame);
|
||||
}
|
||||
|
||||
uint FlicDecoder::getDelay() const {
|
||||
const FlicVideoTrack *flc = ((const FlicVideoTrack *)getTrack(0));
|
||||
if (flc)
|
||||
return flc->getDelay();
|
||||
return 0;
|
||||
}
|
||||
|
||||
FlicDecoder::FlicVideoTrack::FlicVideoTrack(Common::SeekableReadStream *stream, uint16 frameCount, uint16 width, uint16 height, bool skipHeader)
|
||||
: Video::FlicDecoder::FlicVideoTrack(stream, frameCount, width, height, skipHeader) {}
|
||||
|
||||
bool FlicDecoder::FlicVideoTrack::loadMsk(Common::SeekableReadStream &stream) {
|
||||
_msk.resize(_frameCount);
|
||||
for (uint i = 0; i < _frameCount; ++i) {
|
||||
_msk[i].resize(stream.readUint32LE());
|
||||
for (uint j = 0; j < _msk[i].size(); ++j) {
|
||||
_msk[i][j].left = stream.readSint16LE();
|
||||
_msk[i][j].top = stream.readSint16LE();
|
||||
_msk[i][j].right = stream.readSint16LE();
|
||||
_msk[i][j].bottom = stream.readSint16LE();
|
||||
}
|
||||
}
|
||||
stream.skip(_frameCount * 4);
|
||||
_bounds.left = (int16)stream.readSint32LE();
|
||||
_bounds.top = (int16)stream.readSint32LE();
|
||||
_bounds.right = (int16)stream.readSint32LE();
|
||||
_bounds.bottom = (int16)stream.readSint32LE();
|
||||
|
||||
if (_surface->w <= _bounds.right) {
|
||||
_bounds.right = _surface->w - 1;
|
||||
}
|
||||
if (_surface->h <= _bounds.bottom) {
|
||||
_bounds.bottom = _surface->h - 1;
|
||||
}
|
||||
|
||||
if (_bounds.left > _bounds.right) {
|
||||
SWAP(_bounds.left, _bounds.right);
|
||||
}
|
||||
if (_bounds.top > _bounds.bottom) {
|
||||
SWAP(_bounds.top, _bounds.bottom);
|
||||
}
|
||||
|
||||
_bounds = _bounds.findIntersectingRect(Common::Rect(0, 0, _bounds.right, 479));
|
||||
_bounds.right++;
|
||||
_bounds.bottom++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const Common::Rect FlicDecoder::FlicVideoTrack::getBounds() const {
|
||||
return _bounds;
|
||||
}
|
||||
|
||||
const Graphics::Surface *FlicDecoder::FlicVideoTrack::getSurface() const {
|
||||
return _surface;
|
||||
}
|
||||
|
||||
const Common::Array<Common::Rect> FlicDecoder::FlicVideoTrack::getMskRects() const {
|
||||
assert(_curFrame >= 0);
|
||||
return _msk[_curFrame];
|
||||
}
|
||||
|
||||
uint FlicDecoder::FlicVideoTrack::getDelay() const {
|
||||
return _frameDelay;
|
||||
}
|
||||
|
||||
#define FRAME_TYPE 0xF1FA
|
||||
#define FLC_FILE_HEADER 0xAF12
|
||||
#define FLC_FILE_HEADER_SIZE 0x80
|
||||
|
||||
const Graphics::Surface *FlicDecoder::FlicVideoTrack::decodeNextFrame() {
|
||||
// attempt to fix broken flics
|
||||
while (true) {
|
||||
/*uint32 frameSize = */_fileStream->readUint32LE();
|
||||
uint16 frameType = _fileStream->readUint16LE();
|
||||
|
||||
bool processed = true;
|
||||
switch (frameType) {
|
||||
case FRAME_TYPE:
|
||||
handleFrame();
|
||||
break;
|
||||
case FLC_FILE_HEADER:
|
||||
// Skip 0x80 bytes of file header subtracting 6 bytes of header
|
||||
_fileStream->skip(FLC_FILE_HEADER_SIZE - 6);
|
||||
break;
|
||||
default:
|
||||
processed = false;
|
||||
_fileStream->seek(-5, SEEK_CUR);
|
||||
break;
|
||||
}
|
||||
if (processed)
|
||||
break;
|
||||
}
|
||||
|
||||
_curFrame++;
|
||||
_nextFrameStartTime += _frameDelay;
|
||||
|
||||
if (_atRingFrame) {
|
||||
// If we decoded the ring frame, seek to the second frame
|
||||
_atRingFrame = false;
|
||||
_fileStream->seek(_offsetFrame2);
|
||||
}
|
||||
|
||||
return _surface;
|
||||
}
|
||||
|
||||
} // End of namespace Petka
|
||||
63
engines/petka/flc.h
Normal file
63
engines/petka/flc.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/* 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_FLC_H
|
||||
#define PETKA_FLC_H
|
||||
|
||||
#include "video/flic_decoder.h"
|
||||
|
||||
namespace Petka {
|
||||
|
||||
class FlicDecoder : public Video::FlicDecoder {
|
||||
public:
|
||||
void load(Common::SeekableReadStream *flcStream, Common::SeekableReadStream *mskStream);
|
||||
|
||||
void setFrame(int frame);
|
||||
|
||||
uint getDelay() const;
|
||||
const Common::Rect getBounds() const;
|
||||
const Common::Array<Common::Rect> getMskRects() const;
|
||||
const Graphics::Surface *getCurrentFrame() const;
|
||||
uint32 getTransColor(const Graphics::PixelFormat &fmt) const;
|
||||
|
||||
protected:
|
||||
class FlicVideoTrack : public Video::FlicDecoder::FlicVideoTrack {
|
||||
public:
|
||||
FlicVideoTrack(Common::SeekableReadStream *stream, uint16 frameCount, uint16 width, uint16 height, bool skipHeader = false);
|
||||
|
||||
const Graphics::Surface *decodeNextFrame() override;
|
||||
|
||||
bool loadMsk(Common::SeekableReadStream &stream);
|
||||
|
||||
uint getDelay() const;
|
||||
const Common::Rect getBounds() const;
|
||||
const Common::Array<Common::Rect> getMskRects() const;
|
||||
const Graphics::Surface *getSurface() const;
|
||||
|
||||
private:
|
||||
Common::Rect _bounds;
|
||||
Common::Array<Common::Array<Common::Rect> > _msk;
|
||||
};
|
||||
};
|
||||
|
||||
} // End of namespace Petka
|
||||
|
||||
#endif
|
||||
268
engines/petka/interfaces/dialog_interface.cpp
Normal file
268
engines/petka/interfaces/dialog_interface.cpp
Normal 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 "common/system.h"
|
||||
|
||||
#include "petka/interfaces/dialog_interface.h"
|
||||
#include "petka/interfaces/main.h"
|
||||
#include "petka/petka.h"
|
||||
#include "petka/objects/object_cursor.h"
|
||||
#include "petka/objects/object_star.h"
|
||||
#include "petka/objects/heroes.h"
|
||||
#include "petka/q_system.h"
|
||||
#include "petka/big_dialogue.h"
|
||||
#include "petka/sound.h"
|
||||
#include "petka/flc.h"
|
||||
#include "petka/q_manager.h"
|
||||
|
||||
namespace Petka {
|
||||
|
||||
DialogInterface::DialogInterface() {
|
||||
_dialog = g_vm->getBigDialogue();
|
||||
_qsys = g_vm->getQSystem();
|
||||
_state = kIdle;
|
||||
_id = -1;
|
||||
_isUserMsg = false;
|
||||
_afterUserMsg = false;
|
||||
_talker = nullptr;
|
||||
_sender = nullptr;
|
||||
_reaction = nullptr;
|
||||
_firstTime = true;
|
||||
|
||||
_savedCursorId = _savedCursorActType = 0;
|
||||
_wasCursorAnim = _wasCursorShown = false;
|
||||
}
|
||||
|
||||
DialogInterface::~DialogInterface() {
|
||||
delete _reaction;
|
||||
}
|
||||
|
||||
void DialogInterface::start(uint id, QMessageObject *sender) {
|
||||
_id = id;
|
||||
_isUserMsg = false;
|
||||
_afterUserMsg = false;
|
||||
_talker = nullptr;
|
||||
_firstTime = true;
|
||||
_state = kIdle;
|
||||
_sender = sender;
|
||||
_soundName.clear();
|
||||
initCursor();
|
||||
next(-2);
|
||||
}
|
||||
|
||||
void DialogInterface::initCursor() {
|
||||
QObjectCursor *cursor = _qsys->getCursor();
|
||||
|
||||
_savedCursorId = cursor->_resourceId;
|
||||
_savedCursorActType = cursor->_actionType;
|
||||
_wasCursorAnim = cursor->_animate;
|
||||
_wasCursorShown = cursor->_isShown;
|
||||
|
||||
cursor->show(false);
|
||||
cursor->_animate = true;
|
||||
cursor->_resourceId = 5006;
|
||||
cursor->_actionType = kActionTalk;
|
||||
}
|
||||
|
||||
void DialogInterface::restoreCursor() {
|
||||
QObjectCursor *cursor = _qsys->getCursor();
|
||||
cursor->_isShown = _wasCursorShown;
|
||||
cursor->_animate = _wasCursorAnim;
|
||||
cursor->_resourceId = _savedCursorId;
|
||||
cursor->_actionType = _savedCursorActType;
|
||||
|
||||
// original bug fix
|
||||
g_vm->pushMouseMoveEvent();
|
||||
}
|
||||
|
||||
void DialogInterface::next(int choice) {
|
||||
if (_id == -1)
|
||||
return;
|
||||
|
||||
if ((choice == -1 && _state == kMenu) || (choice != -1 && _state == kPlaying))
|
||||
return;
|
||||
|
||||
int prevTalkerId = -1;
|
||||
if (choice == -1 && !_afterUserMsg) {
|
||||
_dialog->getSpeechInfo(&prevTalkerId, nullptr, -1);
|
||||
}
|
||||
_afterUserMsg = _isUserMsg;
|
||||
|
||||
_qsys->getCursor()->_isShown = false;
|
||||
if (_isUserMsg)
|
||||
return;
|
||||
if (_firstTime)
|
||||
_firstTime = false;
|
||||
else
|
||||
_dialog->next(choice);
|
||||
|
||||
switch (_dialog->opcode()) {
|
||||
case kOpcodePlay:
|
||||
onPlayOpcode(prevTalkerId);
|
||||
break;
|
||||
case kOpcodeMenu:
|
||||
onMenuOpcode();
|
||||
break;
|
||||
case kOpcodeEnd:
|
||||
onEndOpcode();
|
||||
break;
|
||||
case kOpcodeUserMessage:
|
||||
onUserMsgOpcode();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DialogInterface::sendMsg(uint16 opcode) {
|
||||
if (_talker) {
|
||||
_talker->processMessage(QMessage(_talker->_id, opcode, 0, 0, 0, nullptr, 0));
|
||||
}
|
||||
}
|
||||
|
||||
void DialogInterface::onEndOpcode() {
|
||||
g_vm->soundMgr()->removeSound(_soundName);
|
||||
sendMsg(kSaid);
|
||||
_talker = nullptr;
|
||||
_state = kIdle;
|
||||
_id = -1;
|
||||
_qsys->_currInterface->removeTexts();
|
||||
restoreCursor();
|
||||
if (_reaction) {
|
||||
QReaction *reaction = _reaction;
|
||||
_reaction = nullptr;
|
||||
_sender->processReaction(reaction);
|
||||
}
|
||||
_sender = nullptr;
|
||||
}
|
||||
|
||||
void DialogInterface::endUserMsg() {
|
||||
_isUserMsg = false;
|
||||
initCursor();
|
||||
next(-1);
|
||||
}
|
||||
|
||||
void DialogInterface::startUserMsg(uint16 arg) {
|
||||
sendMsg(kSaid);
|
||||
_isUserMsg = true;
|
||||
restoreCursor();
|
||||
_qsys->addMessage(_qsys->getChapay()->_id, kUserMsg, arg);
|
||||
}
|
||||
|
||||
bool DialogInterface::isActive() {
|
||||
return _state != kIdle;
|
||||
}
|
||||
|
||||
void DialogInterface::setSender(QMessageObject *sender) {
|
||||
_sender = sender;
|
||||
}
|
||||
|
||||
Sound *DialogInterface::findSound() {
|
||||
return g_vm->soundMgr()->findSound(_soundName);
|
||||
}
|
||||
|
||||
void DialogInterface::removeSound() {
|
||||
g_vm->soundMgr()->removeSound(_soundName);
|
||||
_soundName.clear();
|
||||
}
|
||||
|
||||
void DialogInterface::setReaction(QReaction *reaction) {
|
||||
delete _reaction;
|
||||
_reaction = reaction;
|
||||
}
|
||||
|
||||
void DialogInterface::playSound(const Common::String &name) {
|
||||
removeSound();
|
||||
_soundName = name;
|
||||
Sound *s = g_vm->soundMgr()->addSound(name, Audio::Mixer::kSpeechSoundType);
|
||||
if (s) {
|
||||
FlicDecoder *flc = g_vm->resMgr()->getFlic(_talker->_resourceId);
|
||||
if (flc) {
|
||||
Common::Rect bounds = flc->getBounds();
|
||||
s->setBalance(bounds.left + _talker->_x + bounds.width(), _qsys->_sceneWidth);
|
||||
}
|
||||
s->play(false);
|
||||
}
|
||||
}
|
||||
|
||||
void DialogInterface::setPhrase(const Common::U32String *text) {
|
||||
uint16 textColor;
|
||||
uint16 outlineColor;
|
||||
if (_talker->_dialogColor == -1) {
|
||||
textColor = g_system->getScreenFormat().RGBToColor(0xA, 0xA, 0xA);
|
||||
outlineColor = 0xFFFF;
|
||||
} else {
|
||||
textColor = _talker->_dialogColor;
|
||||
outlineColor = g_system->getScreenFormat().RGBToColor(0x7F, 0, 0);
|
||||
}
|
||||
_qsys->_currInterface->setTextPhrase(*text, textColor, outlineColor);
|
||||
}
|
||||
|
||||
void DialogInterface::onPlayOpcode(int prevTalkerId) {
|
||||
int currTalkerId;
|
||||
const char *soundName = nullptr;
|
||||
const Common::U32String *text = _dialog->getSpeechInfo(&currTalkerId, &soundName, -1);
|
||||
|
||||
if (prevTalkerId != currTalkerId) {
|
||||
sendMsg(kSaid);
|
||||
}
|
||||
|
||||
_talker = _qsys->findObject(currTalkerId);
|
||||
playSound(g_vm->getSpeechPath() + soundName);
|
||||
setPhrase(text);
|
||||
|
||||
if (prevTalkerId != currTalkerId) {
|
||||
sendMsg(kSay);
|
||||
}
|
||||
_state = kPlaying;
|
||||
}
|
||||
|
||||
void DialogInterface::onMenuOpcode() {
|
||||
removeSound();
|
||||
|
||||
sendMsg(kSaid);
|
||||
_talker = nullptr;
|
||||
|
||||
Common::Array<Common::U32String> choices;
|
||||
_dialog->getMenuChoices(choices);
|
||||
_qsys->_mainInterface->setTextChoice(choices,
|
||||
g_system->getScreenFormat().RGBToColor(0xFF, 0xFF, 0xFF),
|
||||
g_system->getScreenFormat().RGBToColor(0xA, 0xA, 0xA),
|
||||
g_system->getScreenFormat().RGBToColor(0, 0, 0xFF));
|
||||
|
||||
_qsys->getCursor()->_isShown = true;
|
||||
_state = kMenu;
|
||||
}
|
||||
|
||||
void DialogInterface::onUserMsgOpcode() {
|
||||
_qsys->_currInterface->setTextPhrase(Common::U32String(), 0, 0);
|
||||
removeSound();
|
||||
_talker = nullptr;
|
||||
_state = kPlaying;
|
||||
}
|
||||
|
||||
void DialogInterface::fixCursor() {
|
||||
_isUserMsg = false;
|
||||
_qsys->getCursor()->show(true);
|
||||
_qsys->getStar()->_isActive = true;
|
||||
}
|
||||
|
||||
} // End of namespace Petka
|
||||
96
engines/petka/interfaces/dialog_interface.h
Normal file
96
engines/petka/interfaces/dialog_interface.h
Normal 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 PETKA_DIALOG_INTERFACE_H
|
||||
#define PETKA_DIALOG_INTERFACE_H
|
||||
|
||||
#include "common/str.h"
|
||||
|
||||
namespace Petka {
|
||||
|
||||
enum DialogState {
|
||||
kPlaying = 1,
|
||||
kMenu = 2,
|
||||
kIdle = 3
|
||||
};
|
||||
|
||||
class Sound;
|
||||
class QMessageObject;
|
||||
class BigDialogue;
|
||||
class QSystem;
|
||||
struct QReaction;
|
||||
|
||||
class DialogInterface {
|
||||
public:
|
||||
DialogInterface();
|
||||
~DialogInterface();
|
||||
|
||||
void start(uint id, QMessageObject *sender);
|
||||
void next(int choice);
|
||||
|
||||
void startUserMsg(uint16 arg);
|
||||
void endUserMsg();
|
||||
|
||||
bool isActive();
|
||||
|
||||
Sound *findSound();
|
||||
|
||||
void setSender(QMessageObject *sender);
|
||||
void setReaction(QReaction *reaction);
|
||||
|
||||
void fixCursor();
|
||||
|
||||
private:
|
||||
void onPlayOpcode(int prevTalkerId);
|
||||
void onMenuOpcode();
|
||||
void onEndOpcode();
|
||||
void onUserMsgOpcode();
|
||||
|
||||
void removeSound();
|
||||
|
||||
void sendMsg(uint16 opcode);
|
||||
void setPhrase(const Common::U32String *text);
|
||||
void playSound(const Common::String &name);
|
||||
|
||||
void initCursor();
|
||||
void restoreCursor();
|
||||
|
||||
private:
|
||||
BigDialogue *_dialog;
|
||||
QSystem *_qsys;
|
||||
bool _isUserMsg;
|
||||
bool _afterUserMsg;
|
||||
bool _firstTime;
|
||||
int _id;
|
||||
DialogState _state;
|
||||
Common::String _soundName;
|
||||
QMessageObject *_talker;
|
||||
QMessageObject *_sender;
|
||||
QReaction *_reaction;
|
||||
int16 _savedCursorActType;
|
||||
int16 _savedCursorId;
|
||||
bool _wasCursorShown;
|
||||
bool _wasCursorAnim;
|
||||
};
|
||||
|
||||
} // End of namespace Petka
|
||||
|
||||
#endif
|
||||
154
engines/petka/interfaces/interface.cpp
Normal file
154
engines/petka/interfaces/interface.cpp
Normal file
@@ -0,0 +1,154 @@
|
||||
/* 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 "petka/objects/text.h"
|
||||
#include "petka/interfaces/interface.h"
|
||||
#include "petka/q_system.h"
|
||||
#include "petka/petka.h"
|
||||
#include "petka/objects/object_cursor.h"
|
||||
#include "petka/q_manager.h"
|
||||
#include "petka/video.h"
|
||||
|
||||
namespace Petka {
|
||||
|
||||
Interface::Interface()
|
||||
: _objUnderCursor(nullptr), _startIndex(0) {}
|
||||
|
||||
void Interface::stop() {
|
||||
setText(Common::U32String(), 0, 0);
|
||||
g_vm->videoSystem()->makeAllDirty();
|
||||
}
|
||||
|
||||
void Interface::setText(const Common::U32String &text, uint16 textColor, uint16 outlineColor) {
|
||||
removeTexts();
|
||||
if (!text.empty())
|
||||
_objs.push_back(new QText(text, textColor, outlineColor));
|
||||
}
|
||||
|
||||
void Interface::setTextPhrase(const Common::U32String &text, uint16 textColor, uint16 outlineColor) {
|
||||
removeTexts();
|
||||
_objUnderCursor = nullptr;
|
||||
_objs.push_back(new QTextPhrase(text, textColor, outlineColor));
|
||||
}
|
||||
|
||||
QVisibleObject *Interface::findObject(int resourceId) {
|
||||
for (uint i = 0; i < _objs.size(); ++i) {
|
||||
if (_objs[i]->_resourceId == resourceId) {
|
||||
return _objs[i];
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Interface::initCursor(int id, bool show, bool animate) {
|
||||
QObjectCursor *cursor = g_vm->getQSystem()->getCursor();
|
||||
_objs.push_back(cursor);
|
||||
cursor->_resourceId = id;
|
||||
cursor->_isShown = show;
|
||||
cursor->_animate = animate;
|
||||
cursor->_actionType = kActionLook;
|
||||
cursor->setPos(Common::Point(cursor->_x, cursor->_y), false);
|
||||
}
|
||||
|
||||
void Interface::removeTexts() {
|
||||
for (uint i = 0; i < _objs.size();) {
|
||||
if (_objs[i]->_resourceId == -2) {
|
||||
g_vm->videoSystem()->addDirtyRect(((QText *)_objs[i])->getRect());
|
||||
g_vm->resMgr()->removeResource((uint32)-2);
|
||||
delete _objs[i];
|
||||
_objs.remove_at(i);
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Interface::update(uint time) {
|
||||
for (uint i = _startIndex; i < _objs.size(); ++i) {
|
||||
_objs[i]->update(time);
|
||||
}
|
||||
|
||||
for (uint i = 0; i < _objs.size(); ++i) {
|
||||
_objs[i]->updateZ();
|
||||
}
|
||||
sort();
|
||||
}
|
||||
|
||||
void Interface::draw() {
|
||||
for (uint i = 0; i < _objs.size(); ++i) {
|
||||
_objs[i]->draw();
|
||||
}
|
||||
}
|
||||
|
||||
void Interface::sort() {
|
||||
for (uint i = 0; i < _objs.size() - 1; ++i) {
|
||||
uint minIndex = i;
|
||||
for (uint j = i + 1; j < _objs.size(); ++j) {
|
||||
if (_objs[j]->_z < _objs[minIndex]->_z) {
|
||||
minIndex = j;
|
||||
}
|
||||
}
|
||||
|
||||
if (i != minIndex) {
|
||||
SWAP(_objs[i], _objs[minIndex]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SubInterface::start(int id) {
|
||||
QSystem *sys = g_vm->getQSystem();
|
||||
QObjectCursor *cursor = sys->getCursor();
|
||||
|
||||
_savedCursorId = cursor->_resourceId;
|
||||
_savedCursorType = cursor->_actionType;
|
||||
|
||||
initCursor(4901, true, false);
|
||||
|
||||
_savedXOffset = sys->_xOffset;
|
||||
_savedSceneWidth = sys->_sceneWidth;
|
||||
|
||||
sys->_xOffset = 0;
|
||||
sys->_sceneWidth = 640;
|
||||
|
||||
g_vm->getQSystem()->_currInterface = this;
|
||||
g_vm->videoSystem()->updateTime();
|
||||
g_vm->videoSystem()->makeAllDirty();
|
||||
}
|
||||
|
||||
void SubInterface::stop() {
|
||||
QSystem *sys = g_vm->getQSystem();
|
||||
QObjectCursor *cursor = sys->getCursor();
|
||||
|
||||
sys->_xOffset = _savedXOffset;
|
||||
sys->_sceneWidth = _savedSceneWidth;
|
||||
|
||||
cursor->_resourceId = _savedCursorId;
|
||||
cursor->_actionType = _savedCursorType;
|
||||
|
||||
sys->_currInterface = g_vm->getQSystem()->_prevInterface;
|
||||
sys->_currInterface->onMouseMove(Common::Point(cursor->_x, cursor->_y));
|
||||
|
||||
_objs.clear();
|
||||
|
||||
Interface::stop();
|
||||
}
|
||||
|
||||
} // End of namespace Petka
|
||||
81
engines/petka/interfaces/interface.h
Normal file
81
engines/petka/interfaces/interface.h
Normal file
@@ -0,0 +1,81 @@
|
||||
/* 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_INTERFACE_H
|
||||
#define PETKA_INTERFACE_H
|
||||
|
||||
#include "common/ustr.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/array.h"
|
||||
|
||||
namespace Petka {
|
||||
|
||||
class QVisibleObject;
|
||||
class QText;
|
||||
|
||||
class Interface {
|
||||
public:
|
||||
Interface();
|
||||
virtual ~Interface() {}
|
||||
|
||||
virtual void start(int id) {};
|
||||
virtual void stop();
|
||||
|
||||
virtual void update(uint time);
|
||||
void draw();
|
||||
|
||||
virtual void onLeftButtonDown(Common::Point p) {};
|
||||
virtual void onRightButtonDown(Common::Point p) {};
|
||||
virtual void onMouseMove(Common::Point p) {};
|
||||
|
||||
void setText(const Common::U32String &text, uint16 textColor, uint16 outlineColor);
|
||||
void setTextPhrase(const Common::U32String &text, uint16 textColor, uint16 outlineColor);
|
||||
|
||||
void removeTexts();
|
||||
|
||||
QVisibleObject *findObject(int resourceId);
|
||||
|
||||
void initCursor(int id, bool show, bool animate);
|
||||
|
||||
private:
|
||||
void sort();
|
||||
|
||||
public:
|
||||
Common::Array<QVisibleObject *> _objs;
|
||||
QVisibleObject *_objUnderCursor;
|
||||
uint _startIndex;
|
||||
};
|
||||
|
||||
class SubInterface : public Interface {
|
||||
public:
|
||||
void start(int id) override;
|
||||
void stop() override;
|
||||
|
||||
private:
|
||||
int _savedXOffset;
|
||||
int _savedSceneWidth;
|
||||
int _savedCursorId;
|
||||
int _savedCursorType;
|
||||
};
|
||||
|
||||
} // End of namespace Petka
|
||||
|
||||
#endif
|
||||
305
engines/petka/interfaces/main.cpp
Normal file
305
engines/petka/interfaces/main.cpp
Normal file
@@ -0,0 +1,305 @@
|
||||
/* 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 "common/stream.h"
|
||||
#include "common/events.h"
|
||||
#include "common/formats/ini-file.h"
|
||||
|
||||
#include "petka/flc.h"
|
||||
#include "petka/objects/object_case.h"
|
||||
#include "petka/objects/object_cursor.h"
|
||||
#include "petka/objects/object_star.h"
|
||||
#include "petka/interfaces/main.h"
|
||||
#include "petka/q_system.h"
|
||||
#include "petka/q_manager.h"
|
||||
#include "petka/sound.h"
|
||||
#include "petka/petka.h"
|
||||
#include "petka/video.h"
|
||||
#include "petka/objects/object_case.h"
|
||||
#include "petka/objects/heroes.h"
|
||||
#include "petka/objects/text.h"
|
||||
#include "petka/walk.h"
|
||||
|
||||
namespace Petka {
|
||||
|
||||
InterfaceMain::InterfaceMain() {
|
||||
Common::ScopedPtr<Common::SeekableReadStream> stream(g_vm->openFile("backgrnd.bg", true));
|
||||
_hasTextDesc = false;
|
||||
_roomId = 0;
|
||||
|
||||
if (!stream)
|
||||
return;
|
||||
|
||||
_bgs.resize(stream->readUint32LE());
|
||||
for (uint i = 0; i < _bgs.size(); ++i) {
|
||||
_bgs[i].objId = stream->readUint16LE();
|
||||
_bgs[i].attachedObjIds.resize(stream->readUint32LE());
|
||||
for (uint j = 0; j < _bgs[i].attachedObjIds.size(); ++j) {
|
||||
_bgs[i].attachedObjIds[j] = stream->readUint16LE();
|
||||
QMessageObject *obj = g_vm->getQSystem()->findObject(_bgs[i].attachedObjIds[j]);
|
||||
obj->_x = stream->readSint32LE();
|
||||
obj->_y = stream->readSint32LE();
|
||||
obj->_z = stream->readSint32LE();
|
||||
obj->_walkX = stream->readSint32LE();
|
||||
obj->_walkY = stream->readSint32LE();
|
||||
}
|
||||
}
|
||||
|
||||
_objs.push_back(g_vm->getQSystem()->getCursor());
|
||||
_objs.push_back(g_vm->getQSystem()->getCase());
|
||||
_objs.push_back(g_vm->getQSystem()->getStar());
|
||||
}
|
||||
|
||||
void InterfaceMain::start(int id) {
|
||||
_objs.push_back(g_vm->getQSystem()->getPetka());
|
||||
_objs.push_back(g_vm->getQSystem()->getChapay());
|
||||
|
||||
Common::ScopedPtr<Common::SeekableReadStream> bgsStream(g_vm->openFile("BGs.ini", true));
|
||||
Common::INIFile bgsIni;
|
||||
bgsIni.allowNonEnglishCharacters();
|
||||
bgsIni.loadFromStream(*bgsStream);
|
||||
Common::String startRoom;
|
||||
bgsIni.getKey("StartRoom", "Settings", startRoom);
|
||||
if (g_vm->getSaveSlot() == -1)
|
||||
loadRoom(g_vm->getQSystem()->findObject(startRoom)->_id, false);
|
||||
}
|
||||
|
||||
void InterfaceMain::loadRoom(int id, bool fromSave) {
|
||||
QSystem *sys = g_vm->getQSystem();
|
||||
|
||||
sys->_currInterface->stop();
|
||||
if (_roomId == id)
|
||||
return;
|
||||
|
||||
unloadRoom(fromSave);
|
||||
|
||||
const BGInfo *info = findBGInfo(id);
|
||||
QObjectBG *room = (QObjectBG *)sys->findObject(id);
|
||||
QManager *resMgr = g_vm->resMgr();
|
||||
|
||||
_roomId = id;
|
||||
sys->_room = room;
|
||||
_objs.push_back(room);
|
||||
|
||||
const auto *surface = resMgr->getSurface(room->_resourceId);
|
||||
if (surface) {
|
||||
assert(surface->w >= 640);
|
||||
sys->_sceneWidth = MAX<int>(surface->w, 640);
|
||||
sys->_xOffset = 0;
|
||||
}
|
||||
|
||||
for (uint i = 0; i < info->attachedObjIds.size(); ++i) {
|
||||
QMessageObject *obj = sys->findObject(info->attachedObjIds[i]);
|
||||
obj->loadSound();
|
||||
if (obj->_isShown || obj->_isActive)
|
||||
g_vm->resMgr()->getFlic(obj->_resourceId);
|
||||
_objs.push_back(obj);
|
||||
}
|
||||
|
||||
auto petka = sys->getPetka();
|
||||
auto chapay = sys->getChapay();
|
||||
|
||||
auto bkgName = resMgr->findResourceName(room->_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);
|
||||
|
||||
playSound(room->_musicId, Audio::Mixer::kMusicSoundType);
|
||||
playSound(room->_fxId, Audio::Mixer::kSFXSoundType);
|
||||
|
||||
if (!fromSave)
|
||||
sys->addMessageForAllObjects(kInitBG, 0, 0, 0, 0, room);
|
||||
g_vm->videoSystem()->updateTime();
|
||||
}
|
||||
|
||||
void InterfaceMain::playSound(int id, Audio::Mixer::SoundType type) {
|
||||
int *sysId = (type == Audio::Mixer::kMusicSoundType) ? &g_vm->getQSystem()->_musicId : &g_vm->getQSystem()->_fxId;
|
||||
if (*sysId != id) {
|
||||
g_vm->soundMgr()->removeSound(g_vm->resMgr()->findSoundName(*sysId));
|
||||
Sound *sound = g_vm->soundMgr()->addSound(g_vm->resMgr()->findSoundName(id), Audio::Mixer::kMusicSoundType); // kMusicSoundType intended
|
||||
if (sound) {
|
||||
sound->play(true);
|
||||
}
|
||||
*sysId = id;
|
||||
}
|
||||
}
|
||||
|
||||
const BGInfo *InterfaceMain::findBGInfo(int id) const {
|
||||
for (uint i = 0; i < _bgs.size(); ++i) {
|
||||
if (_bgs[i].objId == id)
|
||||
return &_bgs[i];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void InterfaceMain::unloadRoom(bool fromSave) {
|
||||
if (_roomId == -1)
|
||||
return;
|
||||
QSystem *sys = g_vm->getQSystem();
|
||||
QObjectBG *room = (QObjectBG *)sys->findObject(_roomId);
|
||||
if (!room)
|
||||
return;
|
||||
|
||||
if (!fromSave)
|
||||
sys->addMessageForAllObjects(kLeaveBG, 0, 0, 0, 0, room);
|
||||
|
||||
g_vm->soundMgr()->removeSoundsWithType(Audio::Mixer::kSFXSoundType);
|
||||
g_vm->resMgr()->clearUnneeded();
|
||||
|
||||
_objs.clear();
|
||||
|
||||
_objs.push_back(sys->getCursor());
|
||||
_objs.push_back(sys->getCase());
|
||||
_objs.push_back(sys->getStar());
|
||||
_objs.push_back(sys->getPetka());
|
||||
_objs.push_back(sys->getChapay());
|
||||
}
|
||||
|
||||
void InterfaceMain::onLeftButtonDown(Common::Point p) {
|
||||
QObjectCursor *cursor = g_vm->getQSystem()->getCursor();
|
||||
if (!cursor->_isShown) {
|
||||
_dialog.next(-1);
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = _objs.size() - 1; i >= 0; --i) {
|
||||
if (_objs[i]->isInPoint(p)) {
|
||||
_objs[i]->onClick(p);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
switch (cursor->_actionType) {
|
||||
case kActionWalk: {
|
||||
QObjectPetka *petka = g_vm->getQSystem()->getPetka();
|
||||
if (petka->_heroReaction) {
|
||||
for (uint i = 0; i < petka->_heroReaction->messages.size(); ++i) {
|
||||
if (petka->_heroReaction->messages[i].opcode == kGoTo) {
|
||||
QObjectChapayev *chapay = g_vm->getQSystem()->getChapay();
|
||||
chapay->stopWalk();
|
||||
break;
|
||||
}
|
||||
}
|
||||
delete petka->_heroReaction;
|
||||
petka->_heroReaction = nullptr;
|
||||
}
|
||||
petka->walk(p.x, p.y);
|
||||
break;
|
||||
}
|
||||
case kActionObjUseChapayev: {
|
||||
QObjectChapayev *chapay = g_vm->getQSystem()->getChapay();
|
||||
chapay->walk(p.x, p.y);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void InterfaceMain::onRightButtonDown(Common::Point p) {
|
||||
QObjectStar *star = g_vm->getQSystem()->getStar();
|
||||
QObjectCase *objCase = g_vm->getQSystem()->getCase();
|
||||
QObjectCursor *cursor = g_vm->getQSystem()->getCursor();
|
||||
if (!star->_isActive)
|
||||
return;
|
||||
if (objCase->_isShown && cursor->_actionType == kActionObjUse) {
|
||||
cursor->setAction(kActionTake);
|
||||
} else {
|
||||
star->setPos(p, false);
|
||||
star->show(star->_isShown == 0);
|
||||
}
|
||||
}
|
||||
|
||||
void InterfaceMain::onMouseMove(Common::Point p) {
|
||||
QMessageObject *prevObj = (QMessageObject *)_objUnderCursor;
|
||||
_objUnderCursor = nullptr;
|
||||
|
||||
QObjectCursor *cursor = g_vm->getQSystem()->getCursor();
|
||||
if (cursor->_isShown) {
|
||||
for (int i = _objs.size() - 1; i >= 0; --i) {
|
||||
if (_objs[i]->isInPoint(p)) {
|
||||
_objs[i]->onMouseMove(p);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cursor->_animate = _objUnderCursor != nullptr;
|
||||
cursor->setPos(p, true);
|
||||
|
||||
if (prevObj != _objUnderCursor && _objUnderCursor && !_dialog.isActive()) {
|
||||
Graphics::PixelFormat fmt = g_system->getScreenFormat();
|
||||
QMessageObject *obj = (QMessageObject *)_objUnderCursor;
|
||||
if (!obj->_nameOnScreen.empty()) {
|
||||
setText(Common::convertToU32String(obj->_nameOnScreen.c_str(), Common::kWindows1251), fmt.RGBToColor(0xC0, 0xFF, 0xFF), fmt.RGBToColor(0xA, 0xA, 0xA));
|
||||
} else {
|
||||
setText(Common::convertToU32String(obj->_name.c_str(), Common::kWindows1251), fmt.RGBToColor(0x80, 0, 0), fmt.RGBToColor(0xA, 0xA, 0xA));
|
||||
}
|
||||
} else if (prevObj && !_objUnderCursor && !_dialog.isActive()) {
|
||||
setText(Common::U32String(), 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void InterfaceMain::setTextChoice(const Common::Array<Common::U32String> &choices, uint16 color, uint16 outlineColor, uint16 selectedColor) {
|
||||
removeTexts();
|
||||
_objUnderCursor = nullptr;
|
||||
_objs.push_back(new QTextChoice(choices, color, outlineColor, selectedColor));
|
||||
}
|
||||
|
||||
void InterfaceMain::setTextDescription(const Common::U32String &text, int frame) {
|
||||
removeTexts();
|
||||
QObjectStar *star = g_vm->getQSystem()->getStar();
|
||||
star->_isActive = false;
|
||||
_objUnderCursor = nullptr;
|
||||
_hasTextDesc = true;
|
||||
_objs.push_back(new QTextDescription(text, frame));
|
||||
}
|
||||
|
||||
void InterfaceMain::removeTextDescription() {
|
||||
_hasTextDesc = false;
|
||||
_objUnderCursor = nullptr;
|
||||
g_vm->getQSystem()->getStar()->_isActive = true;
|
||||
removeTexts();
|
||||
}
|
||||
|
||||
void InterfaceMain::update(uint time) {
|
||||
QSystem *sys = g_vm->getQSystem();
|
||||
int xOff = sys->_xOffset;
|
||||
int reqOffset = sys->_reqOffset;
|
||||
if (xOff != reqOffset && ((xOff != sys->_sceneWidth - 640 && xOff < reqOffset) || (xOff > 0 && xOff > reqOffset))) {
|
||||
if (xOff <= reqOffset) {
|
||||
xOff += 8;
|
||||
xOff = MIN<int>(xOff, reqOffset);
|
||||
} else {
|
||||
xOff -= 8;
|
||||
xOff = MAX<int>(xOff, reqOffset);
|
||||
}
|
||||
sys->_xOffset = CLIP(xOff, 0, sys->_sceneWidth - 640);
|
||||
g_vm->videoSystem()->makeAllDirty();
|
||||
}
|
||||
Interface::update(time);
|
||||
}
|
||||
|
||||
} // End of namespace Petka
|
||||
74
engines/petka/interfaces/main.h
Normal file
74
engines/petka/interfaces/main.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/* 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_MAIN_H
|
||||
#define PETKA_MAIN_H
|
||||
|
||||
#include "audio/mixer.h"
|
||||
|
||||
#include "petka/interfaces/interface.h"
|
||||
#include "petka/interfaces/dialog_interface.h"
|
||||
|
||||
namespace Petka {
|
||||
|
||||
struct BGInfo {
|
||||
uint16 objId;
|
||||
Common::Array<uint16> attachedObjIds;
|
||||
};
|
||||
|
||||
class Sound;
|
||||
|
||||
class InterfaceMain : public Interface {
|
||||
public:
|
||||
InterfaceMain();
|
||||
|
||||
void start(int id) override;
|
||||
|
||||
void update(uint time) override;
|
||||
|
||||
void loadRoom(int id, bool fromSave);
|
||||
|
||||
const BGInfo *findBGInfo(int id) const;
|
||||
|
||||
void unloadRoom(bool fromSave);
|
||||
|
||||
void onLeftButtonDown(Common::Point p) override;
|
||||
void onRightButtonDown(Common::Point p) override;
|
||||
void onMouseMove(Common::Point p) override;
|
||||
|
||||
void setTextChoice(const Common::Array<Common::U32String> &choices, uint16 color, uint16 outlineColor, uint16 selectedColor);
|
||||
void setTextDescription(const Common::U32String &text, int frame);
|
||||
|
||||
void removeTextDescription();
|
||||
|
||||
private:
|
||||
void playSound(int id, Audio::Mixer::SoundType type);
|
||||
|
||||
public:
|
||||
DialogInterface _dialog;
|
||||
Common::Array<BGInfo> _bgs;
|
||||
int _roomId;
|
||||
bool _hasTextDesc;
|
||||
};
|
||||
|
||||
} // End of namespace Petka
|
||||
|
||||
#endif
|
||||
126
engines/petka/interfaces/map.cpp
Normal file
126
engines/petka/interfaces/map.cpp
Normal file
@@ -0,0 +1,126 @@
|
||||
/* 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 "common/str-enc.h"
|
||||
|
||||
#include "petka/q_manager.h"
|
||||
#include "petka/flc.h"
|
||||
#include "petka/petka.h"
|
||||
#include "petka/q_system.h"
|
||||
#include "petka/interfaces/main.h"
|
||||
#include "petka/interfaces/map.h"
|
||||
#include "petka/objects/object_cursor.h"
|
||||
#include "petka/video.h"
|
||||
|
||||
namespace Petka {
|
||||
|
||||
const char *const mapName = "\xCA\xC0\xD0\xD2\xC0"; // КАРТА
|
||||
|
||||
void InterfaceMap::start(int id) {
|
||||
QSystem *sys = g_vm->getQSystem();
|
||||
if (!sys->_room->_showMap)
|
||||
return;
|
||||
|
||||
QObjectBG *bg = (QObjectBG *)sys->findObject(mapName);
|
||||
_roomResID = bg->_resourceId;
|
||||
_objs.push_back(bg);
|
||||
|
||||
const BGInfo *info = g_vm->getQSystem()->_mainInterface->findBGInfo(bg->_id);
|
||||
for (uint i = 0; i < info->attachedObjIds.size(); ++i) {
|
||||
QMessageObject *obj = sys->findObject(info->attachedObjIds[i]);
|
||||
FlicDecoder *flc = g_vm->resMgr()->getFlic(obj->_resourceId);
|
||||
if (flc) {
|
||||
flc->setFrame(1);
|
||||
}
|
||||
obj->_z = 1;
|
||||
obj->_x = 0;
|
||||
obj->_y = 0;
|
||||
obj->_frame = 1;
|
||||
obj->_animate = obj->_isShown;
|
||||
_objs.push_back(obj);
|
||||
}
|
||||
|
||||
sys->addMessageForAllObjects(kInitBG, 0, 0, 0, 0, bg);
|
||||
|
||||
SubInterface::start(id);
|
||||
}
|
||||
|
||||
void InterfaceMap::stop() {
|
||||
if (_objUnderCursor)
|
||||
((QMessageObject *)_objUnderCursor)->_isShown = false;
|
||||
SubInterface::stop();
|
||||
}
|
||||
|
||||
void InterfaceMap::onLeftButtonDown(Common::Point p) {
|
||||
for (int i = _objs.size() - 1; i >= 0; --i) {
|
||||
if (_objs[i]->isInPoint(p)) {
|
||||
_objs[i]->onClick(p);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InterfaceMap::onMouseMove(Common::Point p) {
|
||||
QVisibleObject *oldObj = _objUnderCursor;
|
||||
_objUnderCursor = nullptr;
|
||||
|
||||
bool found = false;
|
||||
for (int i = _objs.size() - 1; i > 0; --i) {
|
||||
QMessageObject *obj = (QMessageObject *)_objs[i];
|
||||
if (obj->_resourceId != 4901 && obj->_resourceId != _roomResID) {
|
||||
FlicDecoder *flc = g_vm->resMgr()->getFlic(obj->_resourceId);
|
||||
if (flc) {
|
||||
bool show = false;
|
||||
if (!found && obj->isInPoint(p)) {
|
||||
found = true;
|
||||
show = true;
|
||||
_objUnderCursor = obj;
|
||||
}
|
||||
if (obj->_isShown != show)
|
||||
obj->show(obj->_isShown == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QObjectCursor *cursor = g_vm->getQSystem()->getCursor();
|
||||
cursor->_animate = _objUnderCursor != nullptr;
|
||||
cursor->_isShown = true;
|
||||
cursor->setPos(p, false);
|
||||
|
||||
if (_objUnderCursor != oldObj && _objUnderCursor) {
|
||||
Graphics::PixelFormat fmt = g_system->getScreenFormat();
|
||||
QMessageObject *obj = (QMessageObject *)_objUnderCursor;
|
||||
if (!obj->_nameOnScreen.empty()) {
|
||||
setText(Common::convertToU32String(obj->_nameOnScreen.c_str(), Common::kWindows1251), fmt.RGBToColor(0xC0, 0xFF, 0xFF), fmt.RGBToColor(0xA, 0xA, 0xA));
|
||||
} else {
|
||||
setText(Common::convertToU32String(obj->_name.c_str(), Common::kWindows1251), fmt.RGBToColor(0x80, 0, 0), fmt.RGBToColor(0xA, 0xA, 0xA));
|
||||
}
|
||||
} else if (oldObj && !_objUnderCursor) {
|
||||
setText(Common::U32String(), 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void InterfaceMap::onRightButtonDown(Common::Point p) {
|
||||
stop();
|
||||
}
|
||||
|
||||
} // End of namespace Petka
|
||||
45
engines/petka/interfaces/map.h
Normal file
45
engines/petka/interfaces/map.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/* 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_MAP_H
|
||||
#define PETKA_MAP_H
|
||||
|
||||
#include "petka/interfaces/interface.h"
|
||||
|
||||
namespace Petka {
|
||||
|
||||
class InterfaceMap: public SubInterface {
|
||||
public:
|
||||
InterfaceMap() : _roomResID(0) {}
|
||||
void start(int id) override;
|
||||
void stop() override;
|
||||
|
||||
void onLeftButtonDown(Common::Point p) override;
|
||||
void onRightButtonDown(Common::Point p) override;
|
||||
void onMouseMove(Common::Point p) override;
|
||||
|
||||
private:
|
||||
int _roomResID;
|
||||
};
|
||||
|
||||
} // End of namespace Petka
|
||||
|
||||
#endif
|
||||
346
engines/petka/interfaces/panel.cpp
Normal file
346
engines/petka/interfaces/panel.cpp
Normal 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/config-manager.h"
|
||||
#include "common/system.h"
|
||||
|
||||
#include "petka/objects/object_cursor.h"
|
||||
#include "petka/objects/object_case.h"
|
||||
#include "petka/petka.h"
|
||||
#include "petka/q_system.h"
|
||||
#include "petka/flc.h"
|
||||
#include "petka/video.h"
|
||||
#include "petka/q_manager.h"
|
||||
#include "petka/interfaces/main.h"
|
||||
#include "petka/interfaces/save_load.h"
|
||||
#include "petka/interfaces/panel.h"
|
||||
|
||||
namespace {
|
||||
|
||||
// ПАНЕЛЬ УПРАВЛЕНИЯ
|
||||
const char *const kPanelObjName = "\xCF\xC0\xCD\xC5\xCB\xDC\x20\xD3\xCF\xD0\xC0\xC2\xCB\xC5\xCD\xC8\xDF";
|
||||
|
||||
const uint kNewGameButtonIndex = 1;
|
||||
const uint kLoadButtonIndex = 2;
|
||||
const uint kContinueButtonIndex = 3;
|
||||
const uint kExitButtonIndex = 4;
|
||||
const uint kSaveButtonIndex = 5;
|
||||
const uint kSafeObjectIndex = 6;
|
||||
const uint kSfxLabelIndex = 7;
|
||||
const uint kSubtitleButtonIndex = 8;
|
||||
const uint kSfxVolumeSliderIndex = 9;
|
||||
const uint kSpeedSliderIndex = 10;
|
||||
const uint kMusicLabelIndex = 11;
|
||||
const uint kSubtitleLabelIndex = 12;
|
||||
const uint kSpeechLabelIndex = 13;
|
||||
const uint kSpeedLabelIndex = 14;
|
||||
const uint kSpeechVolumeSliderIndex = 15;
|
||||
const uint kMusicVolumeSliderIndex = 16;
|
||||
const uint kDecSpeechButtonIndex = 17;
|
||||
const uint kIncSpeechButtonIndex = 18;
|
||||
const uint kDecMusicButtonIndex = 19;
|
||||
const uint kIncMusicButtonIndex = 20;
|
||||
const uint kDecSfxButtonIndex = 21;
|
||||
const uint kIncSfxButtonIndex = 22;
|
||||
const uint kDecSpeedButtonIndex = 23;
|
||||
const uint kIncSpeedButtonIndex = 24;
|
||||
|
||||
Common::Point getObjectPos(uint index)
|
||||
{
|
||||
switch (index) {
|
||||
case kNewGameButtonIndex:
|
||||
return {0, 2};
|
||||
case kLoadButtonIndex:
|
||||
return {5, 70};
|
||||
case kContinueButtonIndex:
|
||||
return {5, 136};
|
||||
case kExitButtonIndex:
|
||||
return {22, 328};
|
||||
case kSaveButtonIndex:
|
||||
return {87, 224};
|
||||
case kSafeObjectIndex:
|
||||
return {118, 395};
|
||||
case kSfxLabelIndex:
|
||||
return {467, 71};
|
||||
case kSubtitleButtonIndex:
|
||||
return {432, 144};
|
||||
case kSfxVolumeSliderIndex:
|
||||
return {428, 29};
|
||||
case kSpeedSliderIndex:
|
||||
return {434, 170};
|
||||
case kMusicLabelIndex:
|
||||
return {297, 214};
|
||||
case kSubtitleLabelIndex:
|
||||
return {470, 139};
|
||||
case kSpeechLabelIndex:
|
||||
return {318, 87};
|
||||
case kSpeedLabelIndex:
|
||||
return {468, 172};
|
||||
case kSpeechVolumeSliderIndex:
|
||||
return {262, 31};
|
||||
case kMusicVolumeSliderIndex:
|
||||
return {231, 137};
|
||||
// case kDecSpeechButtonIndex:
|
||||
// case kIncSpeechButtonIndex:
|
||||
// case kDecMusicButtonIndex:
|
||||
// case kIncMusicButtonIndex:
|
||||
// case kDecSfxButtonIndex:
|
||||
// case kIncSfxButtonIndex:
|
||||
// case kDecSpeedButtonIndex:
|
||||
// case kIncSpeedButtonIndex:
|
||||
default:
|
||||
return {0, 0};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace Petka {
|
||||
|
||||
InterfacePanel::InterfacePanel() {
|
||||
readSettings();
|
||||
}
|
||||
|
||||
void InterfacePanel::start(int id) {
|
||||
QSystem *sys = g_vm->getQSystem();
|
||||
|
||||
sys->getCase()->show(false);
|
||||
|
||||
g_vm->videoSystem()->makeAllDirty();
|
||||
g_vm->videoSystem()->update();
|
||||
|
||||
InterfaceSaveLoad::saveScreen();
|
||||
|
||||
QObjectBG *bg = (QObjectBG *)sys->findObject(kPanelObjName);
|
||||
const BGInfo *info = sys->_mainInterface->findBGInfo(bg->_id);
|
||||
|
||||
_objs.reserve(info->attachedObjIds.size() + 1);
|
||||
_objs.push_back(bg);
|
||||
|
||||
for (uint i = 0; i < info->attachedObjIds.size(); ++i) {
|
||||
QMessageObject *obj = sys->findObject(info->attachedObjIds[i]);
|
||||
FlicDecoder *flc = g_vm->resMgr()->getFlic(obj->_resourceId);
|
||||
flc->setFrame(1);
|
||||
|
||||
const auto pos = getObjectPos(i + 1);
|
||||
obj->_x = pos.x;
|
||||
obj->_y = pos.y;
|
||||
obj->_z = 1;
|
||||
obj->_frame = 1;
|
||||
obj->_animate = false;
|
||||
_objs.push_back(obj);
|
||||
}
|
||||
|
||||
SubInterface::start(id);
|
||||
onSettingsChanged();
|
||||
sys->getCursor()->_animate = true;
|
||||
}
|
||||
|
||||
void InterfacePanel::onLeftButtonDown(Common::Point p) {
|
||||
int i = 0;
|
||||
for (i = _objs.size() - 1; i > 0; --i) {
|
||||
if (_objs[i]->isInPoint(p)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
switch (i) {
|
||||
case kNewGameButtonIndex:
|
||||
g_vm->loadPart(1);
|
||||
break;
|
||||
case kLoadButtonIndex:
|
||||
stop();
|
||||
g_vm->getQSystem()->_saveLoadInterface->start(kLoadMode);
|
||||
break;
|
||||
case kContinueButtonIndex:
|
||||
stop();
|
||||
break;
|
||||
case kExitButtonIndex:
|
||||
g_system->quit();
|
||||
break;
|
||||
case kSaveButtonIndex:
|
||||
stop();
|
||||
g_vm->getQSystem()->_saveLoadInterface->start(kSaveMode);
|
||||
break;
|
||||
case kSubtitleButtonIndex:
|
||||
_subtitles = !_subtitles;
|
||||
updateSubtitles();
|
||||
break;
|
||||
case kDecSpeechButtonIndex:
|
||||
_speechFrame -= 5;
|
||||
updateSliders();
|
||||
break;
|
||||
case kIncSpeechButtonIndex:
|
||||
_speechFrame += 5;
|
||||
updateSliders();
|
||||
break;
|
||||
case kDecMusicButtonIndex:
|
||||
_musicFrame -= 5;
|
||||
updateSliders();
|
||||
break;
|
||||
case kIncMusicButtonIndex:
|
||||
_musicFrame += 5;
|
||||
updateSliders();
|
||||
break;
|
||||
case kDecSfxButtonIndex:
|
||||
_sfxFrame -= 5;
|
||||
updateSliders();
|
||||
break;
|
||||
case kIncSfxButtonIndex:
|
||||
_sfxFrame += 5;
|
||||
updateSliders();
|
||||
break;
|
||||
case kDecSpeedButtonIndex:
|
||||
_speedFrame -= 5;
|
||||
updateSliders();
|
||||
break;
|
||||
case kIncSpeedButtonIndex:
|
||||
_speedFrame += 5;
|
||||
updateSliders();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void InterfacePanel::onMouseMove(Common::Point p) {
|
||||
bool found = false;
|
||||
for (uint i = _objs.size() - 1; i > 0; --i) {
|
||||
QMessageObject *obj = (QMessageObject *)_objs[i];
|
||||
byte frame = 1;
|
||||
if (!found && obj->isInPoint(p)) {
|
||||
found = true;
|
||||
if ((i >= kNewGameButtonIndex && i <= kSaveButtonIndex) || (i >= kDecSpeechButtonIndex && i <= kIncSpeedButtonIndex)) {
|
||||
frame = 2;
|
||||
}
|
||||
}
|
||||
if (obj->_frame == frame)
|
||||
continue;
|
||||
obj->_frame = frame;
|
||||
|
||||
switch (i) {
|
||||
case kDecSpeechButtonIndex:
|
||||
case kIncSpeechButtonIndex:
|
||||
updateSprite(kSpeechLabelIndex, frame);
|
||||
break;
|
||||
case kDecMusicButtonIndex:
|
||||
case kIncMusicButtonIndex:
|
||||
updateSprite(kMusicLabelIndex, frame);
|
||||
break;
|
||||
case kDecSfxButtonIndex:
|
||||
case kIncSfxButtonIndex:
|
||||
updateSprite(kSfxLabelIndex, frame);
|
||||
break;
|
||||
case kIncSpeedButtonIndex:
|
||||
case kDecSpeedButtonIndex:
|
||||
updateSprite(kSpeedLabelIndex, frame);
|
||||
break;
|
||||
default:
|
||||
updateSprite(i, frame);
|
||||
break;
|
||||
}
|
||||
}
|
||||
QObjectCursor *cursor = g_vm->getQSystem()->getCursor();
|
||||
cursor->_isShown = true;
|
||||
cursor->setPos(p, false);
|
||||
}
|
||||
|
||||
void InterfacePanel::updateSliders() {
|
||||
applySettings();
|
||||
|
||||
updateSprite(kSpeechVolumeSliderIndex, _speechFrame);
|
||||
updateSprite(kMusicVolumeSliderIndex, _musicFrame);
|
||||
updateSprite(kSfxVolumeSliderIndex, _sfxFrame);
|
||||
updateSprite(kSpeedSliderIndex, _speedFrame);
|
||||
}
|
||||
|
||||
void InterfacePanel::updateSubtitles() {
|
||||
applySettings();
|
||||
|
||||
updateSprite(kSubtitleButtonIndex, _subtitles == 0 ? 1 : 7);
|
||||
updateSprite(kSubtitleLabelIndex, _subtitles == 0 ? 1 : 2);
|
||||
}
|
||||
|
||||
void InterfacePanel::readSettings() {
|
||||
_speechFrame = 1 + 30 * ConfMan.getInt("speech_volume") / 255;
|
||||
_musicFrame = 1 + 40 * ConfMan.getInt("music_volume") / 255;
|
||||
_sfxFrame = 1 + 30 * ConfMan.getInt("sfx_volume") / 255;
|
||||
_subtitles = ConfMan.getBool("subtitles");
|
||||
_speedFrame = 1 + ConfMan.getInt("petka_speed") / 4;
|
||||
}
|
||||
|
||||
void InterfacePanel::applySettings() {
|
||||
_speechFrame = CLIP<int>(_speechFrame, 1, 31);
|
||||
_musicFrame = CLIP<int>(_musicFrame, 1, 41);
|
||||
_sfxFrame = CLIP<int>(_sfxFrame, 1, 31);
|
||||
_speedFrame = CLIP<int>(_speedFrame, 1, 26);
|
||||
|
||||
const auto speechFrame = _speechFrame;
|
||||
const auto musicFrame = _musicFrame;
|
||||
const auto sfxFrame = _sfxFrame;
|
||||
const auto speedFrame = _speedFrame;
|
||||
const auto subtitles = _subtitles;
|
||||
|
||||
readSettings();
|
||||
|
||||
if (speechFrame != _speechFrame) {
|
||||
ConfMan.setInt("speech_volume", 255 * (speechFrame - 1) / (31 - 1));
|
||||
}
|
||||
if (musicFrame != _musicFrame) {
|
||||
ConfMan.setInt("music_volume", 255 * (musicFrame - 1) / (41 - 1));
|
||||
}
|
||||
if (sfxFrame != _sfxFrame) {
|
||||
ConfMan.setInt("sfx_volume", 255 * (sfxFrame - 1) / (31 - 1));
|
||||
}
|
||||
|
||||
ConfMan.setBool("subtitles", subtitles);
|
||||
|
||||
if (speedFrame != _speedFrame) {
|
||||
ConfMan.setInt("petka_speed", 4 * (speedFrame - 1));
|
||||
}
|
||||
|
||||
readSettings();
|
||||
|
||||
ConfMan.flushToDisk();
|
||||
g_vm->syncSoundSettings();
|
||||
}
|
||||
|
||||
void InterfacePanel::onRightButtonDown(Common::Point p) {
|
||||
stop();
|
||||
}
|
||||
|
||||
void InterfacePanel::updateSprite(uint index, uint frame) const {
|
||||
const auto *object = (QMessageObject *)(_objs[index]);
|
||||
FlicDecoder *flc = g_vm->resMgr()->getFlic(object->_resourceId);
|
||||
flc->setFrame(frame);
|
||||
g_vm->videoSystem()->addDirtyRect(Common::Point(object->_x, object->_y), *flc);
|
||||
}
|
||||
|
||||
int InterfacePanel::getHeroSpeed() {
|
||||
return (_speedFrame * 100 - 100) / 25;
|
||||
}
|
||||
|
||||
void InterfacePanel::onSettingsChanged() {
|
||||
readSettings();
|
||||
updateSliders();
|
||||
updateSubtitles();
|
||||
}
|
||||
|
||||
} // End of namespace Petka
|
||||
63
engines/petka/interfaces/panel.h
Normal file
63
engines/petka/interfaces/panel.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/* 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_PANEL_H
|
||||
#define PETKA_PANEL_H
|
||||
|
||||
#include "petka/interfaces/interface.h"
|
||||
|
||||
namespace Petka {
|
||||
|
||||
class InterfacePanel : public SubInterface {
|
||||
public:
|
||||
InterfacePanel();
|
||||
|
||||
void start(int id) override;
|
||||
|
||||
void onLeftButtonDown(Common::Point p) override;
|
||||
void onRightButtonDown(Common::Point p) override;
|
||||
void onMouseMove(Common::Point p) override;
|
||||
|
||||
void onSettingsChanged();
|
||||
|
||||
int getHeroSpeed();
|
||||
bool showSubtitles() const { return _subtitles; }
|
||||
|
||||
private:
|
||||
void readSettings();
|
||||
void applySettings();
|
||||
|
||||
void updateSliders();
|
||||
void updateSubtitles();
|
||||
|
||||
void updateSprite(uint index, uint frame) const;
|
||||
|
||||
private:
|
||||
bool _subtitles;
|
||||
int _speechFrame;
|
||||
int _musicFrame;
|
||||
int _sfxFrame;
|
||||
int _speedFrame;
|
||||
};
|
||||
|
||||
} // End of namespace Petka
|
||||
|
||||
#endif
|
||||
149
engines/petka/interfaces/save_load.cpp
Normal file
149
engines/petka/interfaces/save_load.cpp
Normal file
@@ -0,0 +1,149 @@
|
||||
/* 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 "engines/advancedDetector.h"
|
||||
|
||||
#include "graphics/fonts/ttf.h"
|
||||
#include "graphics/font.h"
|
||||
|
||||
#include "graphics/thumbnail.h"
|
||||
|
||||
#include "petka/petka.h"
|
||||
#include "petka/q_system.h"
|
||||
#include "petka/q_manager.h"
|
||||
#include "petka/video.h"
|
||||
#include "petka/objects/object_cursor.h"
|
||||
#include "petka/interfaces/save_load.h"
|
||||
|
||||
namespace Petka {
|
||||
|
||||
const uint kFirstSaveLoadPageId = 4990;
|
||||
|
||||
InterfaceSaveLoad::InterfaceSaveLoad() {
|
||||
_page = 0;
|
||||
_loadMode = false;
|
||||
|
||||
_saveRects[0] = Common::Rect(43, 84, 151, 166);
|
||||
_saveRects[1] = Common::Rect(43, 209, 151, 291);
|
||||
_saveRects[2] = Common::Rect(43, 335, 151, 417);
|
||||
_saveRects[3] = Common::Rect(358, 75, 466, 157);
|
||||
_saveRects[4] = Common::Rect(360, 200, 468, 282);
|
||||
_saveRects[5] = Common::Rect(359, 325, 467, 407);
|
||||
_nextPageRect = Common::Rect(596, 403, 624, 431);
|
||||
_prevPageRect = Common::Rect(10, 414, 38, 442);
|
||||
}
|
||||
|
||||
void InterfaceSaveLoad::start(int id) {
|
||||
QSystem *sys = g_vm->getQSystem();
|
||||
QManager *resMgr = g_vm->resMgr();
|
||||
QObjectBG *bg = (QObjectBG *)sys->findObject("SAVELOAD");
|
||||
|
||||
_loadMode = (id == kLoadMode);
|
||||
|
||||
_objs.push_back(bg);
|
||||
bg->_resourceId = kFirstSaveLoadPageId + _page + (_loadMode ? 0 : 5);
|
||||
|
||||
resMgr->removeResource(bg->_resourceId);
|
||||
auto bmp = resMgr->getSurface(bg->_resourceId);
|
||||
|
||||
Graphics::ManagedSurface surf(bmp->w, bmp->h, bmp->format);
|
||||
surf.blitFrom(*bmp);
|
||||
|
||||
// TODO: Which font did the original game use?
|
||||
Common::ScopedPtr<Graphics::Font> font(Graphics::loadTTFFontFromArchive("LiberationSans-Regular.ttf", 18));
|
||||
|
||||
MetaEngine *metaEngine = g_engine->getMetaEngine();
|
||||
for (int i = 0, j = _page * 6; i < 6; ++i, ++j) {
|
||||
SaveStateDescriptor save = metaEngine->querySaveMetaInfos(g_vm->_desc->gameId, j);
|
||||
|
||||
auto surface = save.getThumbnail();
|
||||
if (!surface)
|
||||
continue;
|
||||
|
||||
Common::ScopedPtr<Graphics::Surface, Graphics::SurfaceDeleter> thumbnail(surface->scale(108, 82, true));
|
||||
thumbnail.reset(thumbnail->convertTo(g_system->getOverlayFormat()));
|
||||
|
||||
surf.blitFrom(*thumbnail, Common::Point(_saveRects[i].left, _saveRects[i].top));
|
||||
|
||||
Common::Rect textRect(240, 30);
|
||||
textRect.translate(_saveRects[i].left, _saveRects[i].bottom + 1);
|
||||
|
||||
Common::ScopedPtr<Graphics::Surface, Graphics::SurfaceDeleter> text(new Graphics::Surface);
|
||||
text->create(textRect.width(), textRect.height(), g_system->getScreenFormat());
|
||||
font->drawString(text.get(), save.getSaveDate() + " " + save.getSaveTime(), 0, 0, textRect.width(), text->format.RGBToColor(0, 0x7F, 00));
|
||||
|
||||
surf.transBlitFrom(*text, Common::Point(textRect.left, textRect.top));
|
||||
}
|
||||
|
||||
bmp->copyFrom(surf.rawSurface());
|
||||
|
||||
SubInterface::start(id);
|
||||
}
|
||||
|
||||
void InterfaceSaveLoad::onLeftButtonDown(Common::Point p) {
|
||||
int index = findSaveLoadRectIndex(p);
|
||||
if (index == -1) {
|
||||
if (_prevPageRect.contains(p) && _page > 0) {
|
||||
_page--;
|
||||
stop();
|
||||
start(_loadMode ? kLoadMode : kSaveMode);
|
||||
} else if (_nextPageRect.contains(p) && _page < 2) {
|
||||
_page++;
|
||||
stop();
|
||||
start(_loadMode ? kLoadMode : kSaveMode);
|
||||
}
|
||||
} else {
|
||||
stop();
|
||||
|
||||
const uint slot = _page * 6 + index;
|
||||
_loadMode ? g_vm->loadGameState(slot) : g_vm->saveGameState(slot, Common::String::format("Save %d", slot + 1), false);
|
||||
}
|
||||
}
|
||||
|
||||
void InterfaceSaveLoad::onRightButtonDown(Common::Point p) {
|
||||
stop();
|
||||
}
|
||||
|
||||
void InterfaceSaveLoad::onMouseMove(Common::Point p) {
|
||||
QObjectCursor *cursor = g_vm->getQSystem()->getCursor();
|
||||
cursor->_animate = findSaveLoadRectIndex(p) != -1 || _nextPageRect.contains(p) || _prevPageRect.contains(p);
|
||||
cursor->setPos(p, false);
|
||||
}
|
||||
|
||||
int InterfaceSaveLoad::findSaveLoadRectIndex(Common::Point p) {
|
||||
for (uint i = 0; i < ARRAYSIZE(_saveRects); ++i) {
|
||||
if (_saveRects[i].contains(p)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void InterfaceSaveLoad::saveScreen() {
|
||||
Common::ScopedPtr<Common::MemoryWriteStreamDynamic> thumbnail(new Common::MemoryWriteStreamDynamic(DisposeAfterUse::NO));
|
||||
Graphics::saveThumbnail(*thumbnail);
|
||||
g_vm->_thumbnail.reset(new Common::MemoryReadStream(thumbnail->getData(), thumbnail->size(), DisposeAfterUse::YES));
|
||||
}
|
||||
|
||||
} // End of namespace Petka
|
||||
|
||||
61
engines/petka/interfaces/save_load.h
Normal file
61
engines/petka/interfaces/save_load.h
Normal 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_SAVE_LOAD_H
|
||||
#define PETKA_SAVE_LOAD_H
|
||||
|
||||
#include "petka/interfaces/interface.h"
|
||||
|
||||
namespace Petka {
|
||||
|
||||
enum {
|
||||
kSaveMode,
|
||||
kLoadMode
|
||||
};
|
||||
|
||||
class InterfaceSaveLoad : public SubInterface {
|
||||
public:
|
||||
InterfaceSaveLoad();
|
||||
|
||||
void start(int id) override;
|
||||
|
||||
bool loadMode() { return _loadMode; }
|
||||
|
||||
void onLeftButtonDown(Common::Point p) override;
|
||||
void onRightButtonDown(Common::Point p) override;
|
||||
void onMouseMove(Common::Point p) override;
|
||||
|
||||
static void saveScreen();
|
||||
|
||||
private:
|
||||
int findSaveLoadRectIndex(Common::Point p);
|
||||
|
||||
private:
|
||||
bool _loadMode;
|
||||
uint _page;
|
||||
Common::Rect _saveRects[6];
|
||||
Common::Rect _nextPageRect;
|
||||
Common::Rect _prevPageRect;
|
||||
};
|
||||
|
||||
} // End of namespace Petka
|
||||
|
||||
#endif
|
||||
127
engines/petka/interfaces/sequence.cpp
Normal file
127
engines/petka/interfaces/sequence.cpp
Normal 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 "common/system.h"
|
||||
|
||||
#include "petka/q_system.h"
|
||||
#include "petka/q_manager.h"
|
||||
#include "petka/sound.h"
|
||||
#include "petka/petka.h"
|
||||
#include "petka/video.h"
|
||||
#include "petka/objects/object.h"
|
||||
#include "petka/interfaces/main.h"
|
||||
#include "petka/interfaces/sequence.h"
|
||||
|
||||
namespace Petka {
|
||||
|
||||
InterfaceSequence::InterfaceSequence() {
|
||||
_fxId = 0;
|
||||
_musicId = 0;
|
||||
}
|
||||
|
||||
void InterfaceSequence::start(int id) {
|
||||
// original bug fix
|
||||
g_vm->getQSystem()->_mainInterface->removeTexts();
|
||||
|
||||
removeObjects();
|
||||
|
||||
g_system->getMixer()->pauseAll(true);
|
||||
|
||||
QObjectBG* bg = (QObjectBG *)g_vm->getQSystem()->findObject(id);
|
||||
_objs.push_back(bg);
|
||||
|
||||
const auto *surface = g_vm->resMgr()->getSurface(bg->_resourceId);
|
||||
if (surface) {
|
||||
assert(surface->w >= 640);
|
||||
g_vm->getQSystem()->_sceneWidth = MAX<int>(surface->w, 640);
|
||||
g_vm->getQSystem()->_xOffset = 0;
|
||||
}
|
||||
|
||||
playSound(bg->_musicId, Audio::Mixer::kMusicSoundType);
|
||||
playSound(bg->_fxId, Audio::Mixer::kSFXSoundType);
|
||||
|
||||
const BGInfo *info = g_vm->getQSystem()->_mainInterface->findBGInfo(id);
|
||||
if (info) {
|
||||
for (uint i = 0; i < info->attachedObjIds.size(); ++i) {
|
||||
QMessageObject *obj = g_vm->getQSystem()->findObject(info->attachedObjIds[i]);
|
||||
g_vm->resMgr()->getFlic(obj->_resourceId);
|
||||
obj->loadSound();
|
||||
_objs.push_back(obj);
|
||||
}
|
||||
}
|
||||
|
||||
g_vm->getQSystem()->_currInterface = this;
|
||||
g_vm->videoSystem()->makeAllDirty();
|
||||
}
|
||||
|
||||
void InterfaceSequence::stop() {
|
||||
removeObjects();
|
||||
|
||||
// original bug fix
|
||||
QObjectBG *room = g_vm->getQSystem()->_room;
|
||||
if (!room || room->_fxId != _fxId)
|
||||
g_vm->soundMgr()->removeSound(g_vm->resMgr()->findSoundName(_fxId));
|
||||
if (!room || room->_musicId != _musicId)
|
||||
g_vm->soundMgr()->removeSound(g_vm->resMgr()->findSoundName(_musicId));
|
||||
|
||||
_fxId = 0;
|
||||
_musicId = 0;
|
||||
|
||||
g_system->getMixer()->pauseAll(false);
|
||||
|
||||
g_vm->getQSystem()->_currInterface = g_vm->getQSystem()->_mainInterface.get();
|
||||
Interface::stop();
|
||||
}
|
||||
|
||||
void InterfaceSequence::onLeftButtonDown(Common::Point p) {
|
||||
QVisibleObject *obj = findObject(-2);
|
||||
if (obj) {
|
||||
obj->onClick(p);
|
||||
}
|
||||
}
|
||||
|
||||
void InterfaceSequence::removeObjects() {
|
||||
removeTexts();
|
||||
for (uint i = 0; i < _objs.size(); ++i) {
|
||||
QMessageObject *obj = (QMessageObject *)_objs[i];
|
||||
obj->removeSound();
|
||||
}
|
||||
_objs.clear();
|
||||
}
|
||||
|
||||
void InterfaceSequence::playSound(int id, Audio::Mixer::SoundType type) {
|
||||
int *soundId = (type == Audio::Mixer::kSFXSoundType) ? &_fxId : &_musicId;
|
||||
if (*soundId == id) {
|
||||
Sound *s = g_vm->soundMgr()->findSound(g_vm->resMgr()->findSoundName(id));
|
||||
if (s) {
|
||||
s->pause(false);
|
||||
}
|
||||
} else {
|
||||
g_vm->soundMgr()->removeSound(g_vm->resMgr()->findSoundName(*soundId));
|
||||
Sound *sound = g_vm->soundMgr()->addSound(g_vm->resMgr()->findSoundName(id), type);
|
||||
if (sound) {
|
||||
sound->play(true);
|
||||
}
|
||||
*soundId = id;
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Petka
|
||||
51
engines/petka/interfaces/sequence.h
Normal file
51
engines/petka/interfaces/sequence.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/* 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_SEQUENCE_H
|
||||
#define PETKA_SEQUENCE_H
|
||||
|
||||
#include "audio/mixer.h"
|
||||
|
||||
#include "petka/interfaces/interface.h"
|
||||
|
||||
namespace Petka {
|
||||
|
||||
class InterfaceSequence : public Interface {
|
||||
public:
|
||||
InterfaceSequence();
|
||||
|
||||
void start(int id) override;
|
||||
void stop() override;
|
||||
|
||||
void onLeftButtonDown(Common::Point p) override;
|
||||
|
||||
private:
|
||||
void removeObjects();
|
||||
void playSound(int id, Audio::Mixer::SoundType type);
|
||||
|
||||
private:
|
||||
int _fxId;
|
||||
int _musicId;
|
||||
};
|
||||
|
||||
} // End of namespace Petka
|
||||
|
||||
#endif
|
||||
129
engines/petka/interfaces/startup.cpp
Normal file
129
engines/petka/interfaces/startup.cpp
Normal file
@@ -0,0 +1,129 @@
|
||||
/* 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 "common/stream.h"
|
||||
#include "common/events.h"
|
||||
|
||||
#include "petka/flc.h"
|
||||
#include "petka/objects/object_cursor.h"
|
||||
#include "petka/interfaces/main.h"
|
||||
#include "petka/interfaces/save_load.h"
|
||||
#include "petka/interfaces/startup.h"
|
||||
#include "petka/q_system.h"
|
||||
#include "petka/q_manager.h"
|
||||
#include "petka/sound.h"
|
||||
#include "petka/petka.h"
|
||||
#include "petka/video.h"
|
||||
|
||||
namespace Petka {
|
||||
|
||||
const char *const kStartupObjName = "STARTUP";
|
||||
const char *const kCreditsVideoName = "credits.avi";
|
||||
|
||||
enum {
|
||||
kExit = 4981,
|
||||
kCredits = 4982,
|
||||
kLoad = 4983,
|
||||
kNewGame = 4984,
|
||||
kStartupCursorId = 4901,
|
||||
kBackgroundId = 4980
|
||||
};
|
||||
|
||||
void InterfaceStartup::start(int id) {
|
||||
QSystem *sys = g_vm->getQSystem();
|
||||
QObjectBG *bg = (QObjectBG *)sys->findObject(kStartupObjName);
|
||||
_objs.push_back(bg);
|
||||
|
||||
Sound *s = g_vm->soundMgr()->addSound(g_vm->resMgr()->findSoundName(bg->_musicId), Audio::Mixer::kMusicSoundType);
|
||||
s->play(true);
|
||||
|
||||
const BGInfo *info = sys->_mainInterface->findBGInfo(bg->_id);
|
||||
for (uint i = 0; i < info->attachedObjIds.size(); ++i) {
|
||||
QMessageObject *obj = sys->findObject(info->attachedObjIds[i]);
|
||||
obj->_z = 1;
|
||||
obj->_x = 0;
|
||||
obj->_y = 0;
|
||||
obj->_frame = 1;
|
||||
obj->_animate = false;
|
||||
obj->_isShown = false;
|
||||
_objs.push_back(obj);
|
||||
}
|
||||
|
||||
initCursor(kStartupCursorId, true, false);
|
||||
g_vm->videoSystem()->updateTime();
|
||||
}
|
||||
|
||||
void InterfaceStartup::onLeftButtonDown(Common::Point p) {
|
||||
if (!_objUnderCursor)
|
||||
return;
|
||||
switch (_objUnderCursor->_resourceId) {
|
||||
case kExit:
|
||||
Engine::quitGame();
|
||||
break;
|
||||
case kCredits:
|
||||
g_vm->playVideo(g_vm->openFile(kCreditsVideoName, false));
|
||||
break;
|
||||
case kLoad:
|
||||
g_vm->getQSystem()->_saveLoadInterface->start(kLoadMode);
|
||||
break;
|
||||
case kNewGame:
|
||||
g_vm->loadPart(1);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void InterfaceStartup::onMouseMove(Common::Point p) {
|
||||
_objUnderCursor = nullptr;
|
||||
bool found = false;
|
||||
for (int i = _objs.size() - 1; i > 0; --i) {
|
||||
QMessageObject *obj = (QMessageObject *)_objs[i];
|
||||
if (obj->_resourceId != kStartupCursorId && obj->_resourceId != kBackgroundId) {
|
||||
FlicDecoder *flc = g_vm->resMgr()->getFlic(obj->_resourceId);
|
||||
if (flc) {
|
||||
bool show = false;
|
||||
if (!found && obj->isInPoint(p)) {
|
||||
found = true;
|
||||
show = true;
|
||||
_objUnderCursor = obj;
|
||||
}
|
||||
if (obj->_isShown != show)
|
||||
obj->show(obj->_isShown == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QObjectCursor *cursor = g_vm->getQSystem()->getCursor();
|
||||
cursor->_animate = _objUnderCursor != nullptr;
|
||||
cursor->_isShown = true;
|
||||
cursor->setPos(p, false);
|
||||
}
|
||||
|
||||
void InterfaceStartup::stop() {
|
||||
QObjectBG *bg = (QObjectBG *)g_vm->getQSystem()->findObject(kStartupObjName);
|
||||
Common::String sound = g_vm->resMgr()->findSoundName(bg->_musicId);
|
||||
g_vm->soundMgr()->removeSound(sound);
|
||||
Interface::stop();
|
||||
}
|
||||
|
||||
} // End of namespace Petka
|
||||
40
engines/petka/interfaces/startup.h
Normal file
40
engines/petka/interfaces/startup.h
Normal file
@@ -0,0 +1,40 @@
|
||||
/* 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_STARTUP_H
|
||||
#define PETKA_STARTUP_H
|
||||
|
||||
#include "petka/interfaces/interface.h"
|
||||
|
||||
namespace Petka {
|
||||
|
||||
class InterfaceStartup : public Interface {
|
||||
public:
|
||||
void start(int id) override;
|
||||
void stop() override;
|
||||
|
||||
void onLeftButtonDown(Common::Point p) override;
|
||||
void onMouseMove(Common::Point p) override;
|
||||
};
|
||||
|
||||
} // End of namespace Petka
|
||||
|
||||
#endif
|
||||
215
engines/petka/metaengine.cpp
Normal file
215
engines/petka/metaengine.cpp
Normal file
@@ -0,0 +1,215 @@
|
||||
/* 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 "common/savefile.h"
|
||||
|
||||
#include "engines/advancedDetector.h"
|
||||
|
||||
#include "petka/petka.h"
|
||||
|
||||
#include "common/translation.h"
|
||||
#include "backends/keymapper/action.h"
|
||||
#include "backends/keymapper/keymapper.h"
|
||||
#include "backends/keymapper/standard-actions.h"
|
||||
|
||||
class PetkaMetaEngine : public AdvancedMetaEngine<ADGameDescription> {
|
||||
public:
|
||||
const char *getName() const override {
|
||||
return "petka";
|
||||
}
|
||||
|
||||
bool hasFeature(MetaEngineFeature f) const override;
|
||||
int getMaximumSaveSlot() const override { return 17; }
|
||||
SaveStateList listSaves(const char *target) const override;
|
||||
bool removeSaveState(const char *target, int slot) const override;
|
||||
SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const override;
|
||||
Common::Error createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const override;
|
||||
Common::KeymapArray initKeymaps(const char *target) const override;
|
||||
};
|
||||
|
||||
bool PetkaMetaEngine::hasFeature(MetaEngineFeature f) const {
|
||||
return
|
||||
(f == kSupportsListSaves) ||
|
||||
(f == kSupportsLoadingDuringStartup) ||
|
||||
(f == kSupportsDeleteSave) ||
|
||||
(f == kSavesSupportMetaInfo) ||
|
||||
(f == kSavesSupportThumbnail) ||
|
||||
(f == kSavesSupportCreationDate) ||
|
||||
(f == kSavesSupportPlayTime);
|
||||
}
|
||||
|
||||
SaveStateList PetkaMetaEngine::listSaves(const char *target) const {
|
||||
Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
|
||||
Common::String pattern = Common::String::format("%s.s##", target);
|
||||
Common::StringArray filenames = saveFileMan->listSavefiles(pattern);
|
||||
|
||||
SaveStateList saveList;
|
||||
for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); ++file) {
|
||||
// Obtain the last 2 digits of the filename, since they correspond to the save slot
|
||||
int slotNum = atoi(file->c_str() + file->size() - 2);
|
||||
if (slotNum >= 0 && slotNum <= getMaximumSaveSlot()) {
|
||||
Common::ScopedPtr<Common::InSaveFile> in(saveFileMan->openForLoading(*file));
|
||||
if (in) {
|
||||
SaveStateDescriptor desc;
|
||||
desc.setSaveSlot(slotNum);
|
||||
if (Petka::readSaveHeader(*in.get(), desc))
|
||||
saveList.push_back(desc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort saves based on slot number.
|
||||
Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
|
||||
return saveList;
|
||||
}
|
||||
|
||||
bool PetkaMetaEngine::removeSaveState(const char *target, int slot) const {
|
||||
return g_system->getSavefileManager()->removeSavefile(Petka::generateSaveName(slot, target));
|
||||
}
|
||||
|
||||
SaveStateDescriptor PetkaMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
|
||||
Common::ScopedPtr<Common::InSaveFile> f(g_system->getSavefileManager()->openForLoading(Petka::generateSaveName(slot, target)));
|
||||
|
||||
if (f) {
|
||||
SaveStateDescriptor desc;
|
||||
if (!Petka::readSaveHeader(*f.get(), desc, false))
|
||||
return SaveStateDescriptor();
|
||||
|
||||
desc.setSaveSlot(slot);
|
||||
return desc;
|
||||
}
|
||||
|
||||
return SaveStateDescriptor();
|
||||
}
|
||||
|
||||
Common::Error PetkaMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
|
||||
*engine = new Petka::PetkaEngine(syst, desc);
|
||||
return Common::kNoError;
|
||||
}
|
||||
|
||||
Common::KeymapArray PetkaMetaEngine::initKeymaps(const char *target) const {
|
||||
using namespace Common;
|
||||
using namespace Petka;
|
||||
|
||||
Keymap *engineKeymap = new Keymap(Keymap::kKeymapTypeGame, "petka-default", _("Default keymappings"));
|
||||
|
||||
Action *act;
|
||||
|
||||
act = new Action(kStandardActionLeftClick, _("Interact / Skip dialog"));
|
||||
act->setLeftClickEvent();
|
||||
act->addDefaultInputMapping("MOUSE_LEFT");
|
||||
act->addDefaultInputMapping("JOY_A");
|
||||
engineKeymap->addAction(act);
|
||||
|
||||
// I18N: (Game Name: Red Comrades 1: Save the Galaxy) The game has multiple actions that you can perform(take, use, etc.), the action menu allows you to select one of them.
|
||||
act = new Action(kStandardActionRightClick, _("Open action menu / Close current interface"));
|
||||
act->setRightClickEvent();
|
||||
act->addDefaultInputMapping("MOUSE_RIGHT");
|
||||
act->addDefaultInputMapping("JOY_B");
|
||||
engineKeymap->addAction(act);
|
||||
|
||||
act = new Action("LOOK", _("Look"));
|
||||
act->setCustomEngineActionEvent(kActionCursorLook);
|
||||
act->addDefaultInputMapping("1");
|
||||
act->addDefaultInputMapping("l");
|
||||
act->addDefaultInputMapping("JOY_DOWN");
|
||||
engineKeymap->addAction(act);
|
||||
|
||||
act = new Action("WALK", _("Walk"));
|
||||
act->setCustomEngineActionEvent(kActionCursorWalk);
|
||||
act->addDefaultInputMapping("2");
|
||||
act->addDefaultInputMapping("w");
|
||||
act->addDefaultInputMapping("JOY_LEFT");
|
||||
engineKeymap->addAction(act);
|
||||
|
||||
act = new Action("TAKE", _("Take"));
|
||||
act->setCustomEngineActionEvent(kActionCursorTake);
|
||||
act->addDefaultInputMapping("3");
|
||||
act->addDefaultInputMapping("g");
|
||||
act->addDefaultInputMapping("JOY_UP");
|
||||
engineKeymap->addAction(act);
|
||||
|
||||
act = new Action("USE", _("Use"));
|
||||
act->setCustomEngineActionEvent(kActionCursorUse);
|
||||
act->addDefaultInputMapping("4");
|
||||
act->addDefaultInputMapping("u");
|
||||
act->addDefaultInputMapping("JOY_RIGHT");
|
||||
engineKeymap->addAction(act);
|
||||
|
||||
act = new Action("TALK", _("Talk"));
|
||||
act->setCustomEngineActionEvent(kActionCursorTalk);
|
||||
act->addDefaultInputMapping("5");
|
||||
act->addDefaultInputMapping("t");
|
||||
act->addDefaultInputMapping("JOY_X");
|
||||
engineKeymap->addAction(act);
|
||||
|
||||
// I18N: (Game Name: Red Comrades 1: Save the Galaxy) The game features two characters, but all actions are normally performed by Petka. This action allows you to "use" an object specifically with Chapayev instead.
|
||||
act = new Action("CHAPAYEV", _("Chapayev use object"));
|
||||
act->setCustomEngineActionEvent(kActionCursorChapayev);
|
||||
act->addDefaultInputMapping("6");
|
||||
act->addDefaultInputMapping("c");
|
||||
act->addDefaultInputMapping("JOY_RIGHT_TRIGGER");
|
||||
engineKeymap->addAction(act);
|
||||
|
||||
act = new Action("INVENTORY", _("Inventory"));
|
||||
act->setCustomEngineActionEvent(kActionInventory);
|
||||
act->addDefaultInputMapping("i");
|
||||
act->addDefaultInputMapping("JOY_LEFT_SHOULDER");
|
||||
engineKeymap->addAction(act);
|
||||
|
||||
act = new Action("MAP", _("Map"));
|
||||
act->setCustomEngineActionEvent(kActionMap);
|
||||
act->addDefaultInputMapping("TAB");
|
||||
act->addDefaultInputMapping("m");
|
||||
act->addDefaultInputMapping("JOY_RIGHT_SHOULDER");
|
||||
engineKeymap->addAction(act);
|
||||
|
||||
act = new Action("OPTIONS", _("Show / Hide options screen"));
|
||||
act->setCustomEngineActionEvent(kActionOptions);
|
||||
act->addDefaultInputMapping("o");
|
||||
act->addDefaultInputMapping("JOY_LEFT_TRIGGER");
|
||||
engineKeymap->addAction(act);
|
||||
|
||||
act = new Action("PREVIOUS_INTERFACE", _("Close current interface"));
|
||||
act->setCustomEngineActionEvent(kActionPrevInterface);
|
||||
act->addDefaultInputMapping("ESCAPE");
|
||||
act->addDefaultInputMapping("JOY_Y");
|
||||
engineKeymap->addAction(act);
|
||||
|
||||
act = new Action("SAVE", _("Save game"));
|
||||
act->setCustomEngineActionEvent(kActionSave);
|
||||
act->addDefaultInputMapping("F2");
|
||||
engineKeymap->addAction(act);
|
||||
|
||||
act = new Action("LOAD", _("Load game"));
|
||||
act->setCustomEngineActionEvent(kActionLoad);
|
||||
act->addDefaultInputMapping("F3");
|
||||
engineKeymap->addAction(act);
|
||||
|
||||
return Keymap::arrayOf(engineKeymap);
|
||||
}
|
||||
|
||||
#if PLUGIN_ENABLED_DYNAMIC(PETKA)
|
||||
REGISTER_PLUGIN_DYNAMIC(PETKA, PLUGIN_TYPE_ENGINE, PetkaMetaEngine);
|
||||
#else
|
||||
REGISTER_PLUGIN_STATIC(PETKA, PLUGIN_TYPE_ENGINE, PetkaMetaEngine);
|
||||
#endif
|
||||
40
engines/petka/module.mk
Normal file
40
engines/petka/module.mk
Normal file
@@ -0,0 +1,40 @@
|
||||
MODULE := engines/petka
|
||||
|
||||
MODULE_OBJS = \
|
||||
big_dialogue.o \
|
||||
file_mgr.o \
|
||||
flc.o \
|
||||
metaengine.o \
|
||||
petka.o \
|
||||
saveload.o \
|
||||
q_manager.o \
|
||||
q_system.o \
|
||||
sound.o \
|
||||
video.o \
|
||||
walk.o \
|
||||
interfaces/dialog_interface.o \
|
||||
interfaces/interface.o \
|
||||
interfaces/main.o \
|
||||
interfaces/map.o \
|
||||
interfaces/panel.o \
|
||||
interfaces/save_load.o \
|
||||
interfaces/sequence.o \
|
||||
interfaces/startup.o \
|
||||
objects/object.o \
|
||||
objects/object_bg.o \
|
||||
objects/object_case.o \
|
||||
objects/object_cursor.o \
|
||||
objects/object_star.o \
|
||||
objects/heroes.o \
|
||||
objects/text.o
|
||||
|
||||
# This module can be built as a plugin
|
||||
ifeq ($(ENABLE_PETKA), DYNAMIC_PLUGIN)
|
||||
PLUGIN := 1
|
||||
endif
|
||||
|
||||
# Include common rules
|
||||
include $(srcdir)/rules.mk
|
||||
|
||||
# Detection objects
|
||||
DETECT_OBJS += $(MODULE)/detection.o
|
||||
428
engines/petka/objects/heroes.cpp
Normal file
428
engines/petka/objects/heroes.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
84
engines/petka/objects/heroes.h
Normal file
84
engines/petka/objects/heroes.h
Normal 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
|
||||
603
engines/petka/objects/object.cpp
Normal file
603
engines/petka/objects/object.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
117
engines/petka/objects/object.h
Normal file
117
engines/petka/objects/object.h
Normal 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
|
||||
175
engines/petka/objects/object_bg.cpp
Normal file
175
engines/petka/objects/object_bg.cpp
Normal 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
|
||||
60
engines/petka/objects/object_bg.h
Normal file
60
engines/petka/objects/object_bg.h
Normal 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
|
||||
292
engines/petka/objects/object_case.cpp
Normal file
292
engines/petka/objects/object_case.cpp
Normal 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
|
||||
61
engines/petka/objects/object_case.h
Normal file
61
engines/petka/objects/object_case.h
Normal 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
|
||||
145
engines/petka/objects/object_cursor.cpp
Normal file
145
engines/petka/objects/object_cursor.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
59
engines/petka/objects/object_cursor.h
Normal file
59
engines/petka/objects/object_cursor.h
Normal 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
|
||||
100
engines/petka/objects/object_star.cpp
Normal file
100
engines/petka/objects/object_star.cpp
Normal 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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
47
engines/petka/objects/object_star.h
Normal file
47
engines/petka/objects/object_star.h
Normal 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
|
||||
270
engines/petka/objects/text.cpp
Normal file
270
engines/petka/objects/text.cpp
Normal 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
|
||||
97
engines/petka/objects/text.h
Normal file
97
engines/petka/objects/text.h
Normal 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
|
||||
405
engines/petka/petka.cpp
Normal file
405
engines/petka/petka.cpp
Normal file
@@ -0,0 +1,405 @@
|
||||
/* 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/substream.h"
|
||||
#include "common/config-manager.h"
|
||||
#include "common/debug-channels.h"
|
||||
#include "common/error.h"
|
||||
#include "common/events.h"
|
||||
#include "common/formats/ini-file.h"
|
||||
#include "common/system.h"
|
||||
#include "common/file.h"
|
||||
#include "common/compression/installshield_cab.h"
|
||||
|
||||
#include "engines/advancedDetector.h"
|
||||
#include "engines/util.h"
|
||||
|
||||
#include "graphics/surface.h"
|
||||
#include "graphics/font.h"
|
||||
#include "graphics/fonts/ttf.h"
|
||||
|
||||
#include "video/avi_decoder.h"
|
||||
|
||||
#include "petka/file_mgr.h"
|
||||
#include "petka/video.h"
|
||||
#include "petka/sound.h"
|
||||
#include "petka/petka.h"
|
||||
#include "petka/q_manager.h"
|
||||
#include "petka/interfaces/interface.h"
|
||||
#include "petka/interfaces/panel.h"
|
||||
#include "petka/q_system.h"
|
||||
#include "petka/big_dialogue.h"
|
||||
|
||||
namespace Petka {
|
||||
|
||||
PetkaEngine *g_vm;
|
||||
|
||||
PetkaEngine::PetkaEngine(OSystem *system, const ADGameDescription *desc)
|
||||
: Engine(system), _console(nullptr), _fileMgr(nullptr), _resMgr(nullptr),
|
||||
_qsystem(nullptr), _vsys(nullptr), _desc(desc), _videoDec(nullptr), _rnd("petka") {
|
||||
|
||||
_part = 0xFF;
|
||||
_chapter = 0;
|
||||
_shouldChangePart = false;
|
||||
_nextPart = 0;
|
||||
_saveSlot = -1;
|
||||
g_vm = this;
|
||||
|
||||
debug("PetkaEngine::ctor");
|
||||
}
|
||||
|
||||
PetkaEngine::~PetkaEngine() {
|
||||
debug("PetkaEngine::dtor");
|
||||
}
|
||||
|
||||
Common::Error PetkaEngine::run() {
|
||||
debug("PetkaEngine::run");
|
||||
const Graphics::PixelFormat format(2, 5, 6, 5, 0, 11, 5, 0, 0);
|
||||
initGraphics(640, 480, &format);
|
||||
syncSoundSettings();
|
||||
|
||||
if (_desc->flags & GF_COMPRESSED) {
|
||||
Common::Archive *cabinet = Common::makeInstallShieldArchive("data");
|
||||
if (cabinet)
|
||||
SearchMan.add("data1.cab", cabinet);
|
||||
}
|
||||
|
||||
const char *const videos[] = {"buka.avi", "skif.avi", "adv.avi"};
|
||||
for (uint i = 0; i < sizeof(videos) / sizeof(char *); ++i) {
|
||||
Common::ScopedPtr<Common::File> file(new Common::File);
|
||||
if (file->open(videos[i])) {
|
||||
playVideo(file.release());
|
||||
} else {
|
||||
debugC(kPetkaDebugResources, "Video file %s can't be opened", videos[i]);
|
||||
}
|
||||
}
|
||||
|
||||
_console.reset(new Console(this));
|
||||
_fileMgr.reset(new FileMgr());
|
||||
_soundMgr.reset(new SoundMgr(*this));
|
||||
_vsys.reset(new VideoSystem(*this));
|
||||
_resMgr.reset(new QManager(*this));
|
||||
|
||||
// TODO: Which fonts did the original game use?
|
||||
_textFont.reset(Graphics::loadTTFFontFromArchive("LiberationSans-Bold.ttf", 18, Graphics::kTTFSizeModeCell));
|
||||
_descriptionFont.reset(Graphics::loadTTFFontFromArchive("LiberationSans-Bold.ttf", 14, Graphics::kTTFSizeModeCell));
|
||||
|
||||
loadPart(isDemo() ? 1 : 0);
|
||||
|
||||
if (ConfMan.hasKey("save_slot")) {
|
||||
loadGameState(ConfMan.getInt("save_slot"));
|
||||
}
|
||||
|
||||
while (!shouldQuit()) {
|
||||
Common::Event event;
|
||||
while (_eventMan->pollEvent(event)) {
|
||||
switch (event.type) {
|
||||
case Common::EVENT_QUIT:
|
||||
case Common::EVENT_RETURN_TO_LAUNCHER:
|
||||
return Common::kNoError;
|
||||
default:
|
||||
_qsystem->onEvent(event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
_qsystem->update();
|
||||
|
||||
if (_shouldChangePart) {
|
||||
loadPart(_nextPart);
|
||||
if (_saveSlot != -1)
|
||||
loadGameState(_saveSlot);
|
||||
_saveSlot = -1;
|
||||
_shouldChangePart = false;
|
||||
_vsys->makeAllDirty();
|
||||
}
|
||||
|
||||
_vsys->update();
|
||||
_system->delayMillis(20);
|
||||
}
|
||||
return Common::kNoError;
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *PetkaEngine::openFile(const Common::String &name, bool addCurrentPath) {
|
||||
if (name.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
return _fileMgr->getFileStream(addCurrentPath ? _currentPath + name : name);
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *PetkaEngine::openIniFile(const Common::String &name) {
|
||||
// Some lines in ini files have null terminators befoen CR+LF
|
||||
class IniReadStream : public Common::SeekableSubReadStream
|
||||
{
|
||||
public:
|
||||
IniReadStream(SeekableReadStream *parentStream, uint32 begin, uint32 end, DisposeAfterUse::Flag disposeParentStream = DisposeAfterUse::NO)
|
||||
: SeekableSubReadStream(parentStream, begin, end, disposeParentStream) {}
|
||||
|
||||
char *readLine(char *buf, size_t bufSize, bool handleCR = true) override
|
||||
{
|
||||
memset(buf, '\0', bufSize);
|
||||
|
||||
if (!Common::SeekableSubReadStream::readLine(buf, bufSize, handleCR)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
char *null_term = nullptr;
|
||||
for (uint i = 0; i < bufSize; ++i) {
|
||||
if (buf[i] == '\n') {
|
||||
if (null_term) {
|
||||
null_term[0] = '\n';
|
||||
null_term[1] = '\0';
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
if (buf[i] == '\0' && !null_term) {
|
||||
null_term = &buf[i];
|
||||
}
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
auto *file_stream = openFile(name, true);
|
||||
if (!file_stream)
|
||||
return nullptr;
|
||||
return new IniReadStream(file_stream, 0, file_stream->size(), DisposeAfterUse::YES);
|
||||
}
|
||||
|
||||
void PetkaEngine::loadStores() {
|
||||
debug("PetkaEngine::loadStores");
|
||||
_fileMgr->closeAll();
|
||||
|
||||
_fileMgr->openStore("patch.str");
|
||||
_fileMgr->openStore("main.str");
|
||||
|
||||
Common::INIFile parts;
|
||||
Common::ScopedPtr<Common::SeekableReadStream> stream(_fileMgr->getFileStream("PARTS.INI"));
|
||||
|
||||
if (!stream || !parts.loadFromStream(*stream)) {
|
||||
debugC(kPetkaDebugResources, "PARTS.INI opening failed");
|
||||
return;
|
||||
}
|
||||
|
||||
const char *const names[] = {"Background", "Flics", "Wav", "SFX", "Music", "Speech"};
|
||||
const Common::String section = Common::String::format("Part %d", _part);
|
||||
|
||||
parts.getKey("CurrentPath", section, _currentPath);
|
||||
parts.getKey("PathSpeech", section, _speechPath);
|
||||
|
||||
Common::String storeName;
|
||||
for (uint i = 0; i < sizeof(names) / sizeof(char *); ++i) {
|
||||
parts.getKey(names[i], section, storeName);
|
||||
_fileMgr->openStore(storeName);
|
||||
}
|
||||
|
||||
parts.getKey("Chapter", Common::String::format("Part %d Chapter %d", _part, _chapter), _chapterStoreName);
|
||||
_fileMgr->openStore(_chapterStoreName);
|
||||
}
|
||||
|
||||
QSystem *PetkaEngine::getQSystem() const {
|
||||
return _qsystem.get();
|
||||
}
|
||||
|
||||
Common::RandomSource &PetkaEngine::getRnd() {
|
||||
return _rnd;
|
||||
}
|
||||
|
||||
void PetkaEngine::playVideo(Common::SeekableReadStream *stream) {
|
||||
if (!stream)
|
||||
return;
|
||||
|
||||
PauseToken token = pauseEngine();
|
||||
Graphics::PixelFormat fmt = _system->getScreenFormat();
|
||||
|
||||
_videoDec.reset(new Video::AVIDecoder);
|
||||
if (!_videoDec->loadStream(stream)) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
_videoDec->start();
|
||||
|
||||
while (!_videoDec->endOfVideo() && !shouldQuit()) {
|
||||
Common::Event event;
|
||||
while (_eventMan->pollEvent(event)) {
|
||||
switch (event.type) {
|
||||
case Common::EVENT_RETURN_TO_LAUNCHER:
|
||||
case Common::EVENT_QUIT:
|
||||
case Common::EVENT_LBUTTONDOWN:
|
||||
case Common::EVENT_RBUTTONDOWN:
|
||||
case Common::EVENT_KEYDOWN:
|
||||
case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
|
||||
goto end;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (_videoDec->needsUpdate()) {
|
||||
const Graphics::Surface *frame = _videoDec->decodeNextFrame();
|
||||
if (frame) {
|
||||
Common::ScopedPtr<Graphics::Surface, Graphics::SurfaceDeleter> f(frame->convertTo(fmt));
|
||||
_system->copyRectToScreen(f->getPixels(), f->pitch, 0, 0, f->w, f->h);
|
||||
}
|
||||
}
|
||||
|
||||
_system->updateScreen();
|
||||
_system->delayMillis(15);
|
||||
}
|
||||
|
||||
end:
|
||||
if (_vsys) {
|
||||
_vsys->makeAllDirty();
|
||||
}
|
||||
_videoDec.reset();
|
||||
}
|
||||
|
||||
bool PetkaEngine::isDemo() const {
|
||||
return _desc->flags & ADGF_DEMO;
|
||||
}
|
||||
|
||||
bool PetkaEngine::isPetka2() const {
|
||||
return strcmp(_desc->gameId, "petka2") == 0;
|
||||
}
|
||||
|
||||
SoundMgr *PetkaEngine::soundMgr() const {
|
||||
return _soundMgr.get();
|
||||
}
|
||||
|
||||
QManager *PetkaEngine::resMgr() const {
|
||||
return _resMgr.get();
|
||||
}
|
||||
|
||||
VideoSystem *PetkaEngine::videoSystem() const {
|
||||
return _vsys.get();
|
||||
}
|
||||
|
||||
byte PetkaEngine::getPart() {
|
||||
return _part;
|
||||
}
|
||||
|
||||
void PetkaEngine::loadPart(byte part) {
|
||||
debug("PetkaEngine::loadPart %d", part);
|
||||
_part = part;
|
||||
|
||||
_soundMgr->removeAll();
|
||||
loadStores();
|
||||
|
||||
_resMgr.reset(new QManager(*this));
|
||||
_resMgr->init();
|
||||
_dialogMan.reset(new BigDialogue(*this));
|
||||
_qsystem.reset(new QSystem(*this));
|
||||
_qsystem->init();
|
||||
}
|
||||
|
||||
void PetkaEngine::loadPartAtNextFrame(byte part) {
|
||||
_shouldChangePart = true;
|
||||
_nextPart = part;
|
||||
_chapter = 1;
|
||||
_saveSlot = -1;
|
||||
}
|
||||
|
||||
void PetkaEngine::loadChapter(byte chapter) {
|
||||
Common::INIFile parts;
|
||||
Common::ScopedPtr<Common::SeekableReadStream> stream(_fileMgr->getFileStream("PARTS.INI"));
|
||||
|
||||
if (!stream || !parts.loadFromStream(*stream)) {
|
||||
debugC(kPetkaDebugResources, "PARTS.INI opening failed");
|
||||
return;
|
||||
}
|
||||
|
||||
_fileMgr->closeStore(_chapterStoreName);
|
||||
|
||||
const Common::String section = Common::String::format("Part %d Chapter %d", _part, chapter);
|
||||
parts.getKey("Chapter", section, _chapterStoreName);
|
||||
if (_chapterStoreName.empty())
|
||||
return;
|
||||
|
||||
_fileMgr->openStore(_chapterStoreName);
|
||||
|
||||
Common::ScopedPtr<Common::SeekableReadStream> namesStream(openIniFile("Names.ini"));
|
||||
Common::ScopedPtr<Common::SeekableReadStream> castStream(openIniFile("Cast.ini"));
|
||||
|
||||
Common::INIFile namesIni;
|
||||
Common::INIFile castIni;
|
||||
|
||||
namesIni.allowNonEnglishCharacters();
|
||||
castIni.allowNonEnglishCharacters();
|
||||
|
||||
if (namesStream)
|
||||
namesIni.loadFromStream(*namesStream);
|
||||
if (castStream)
|
||||
castIni.loadFromStream(*castStream);
|
||||
|
||||
for (uint i = 0; i < _qsystem->_allObjects.size(); ++i) {
|
||||
QMessageObject *obj = _qsystem->_allObjects[i];
|
||||
obj->readInisData(namesIni, castIni, nullptr);
|
||||
}
|
||||
_chapter = chapter;
|
||||
}
|
||||
|
||||
BigDialogue *PetkaEngine::getBigDialogue() const {
|
||||
return _dialogMan.get();
|
||||
}
|
||||
|
||||
const Common::String &PetkaEngine::getSpeechPath() {
|
||||
return _speechPath;
|
||||
}
|
||||
|
||||
bool PetkaEngine::hasFeature(EngineFeature f) const {
|
||||
return
|
||||
f == kSupportsReturnToLauncher ||
|
||||
f == kSupportsLoadingDuringRuntime ||
|
||||
f == kSupportsSavingDuringRuntime ||
|
||||
f == kSupportsChangingOptionsDuringRuntime ||
|
||||
f == kSupportsSubtitleOptions;
|
||||
}
|
||||
|
||||
void PetkaEngine::pauseEngineIntern(bool pause) {
|
||||
if (!pause && _vsys)
|
||||
_vsys->updateTime();
|
||||
|
||||
if (_videoDec)
|
||||
_videoDec->pauseVideo(pause);
|
||||
|
||||
Engine::pauseEngineIntern(pause);
|
||||
}
|
||||
|
||||
void PetkaEngine::pushMouseMoveEvent() {
|
||||
Common::Event ev;
|
||||
ev.type = Common::EVENT_MOUSEMOVE;
|
||||
ev.mouse = g_system->getEventManager()->getMousePos();
|
||||
_eventMan->pushEvent(ev);
|
||||
}
|
||||
|
||||
void PetkaEngine::applyGameSettings() {
|
||||
if (_qsystem->_currInterface == _qsystem->_panelInterface.get())
|
||||
{
|
||||
_qsystem->_panelInterface->onSettingsChanged();
|
||||
}
|
||||
|
||||
Engine::applyGameSettings();
|
||||
}
|
||||
|
||||
} // End of namespace Petka
|
||||
197
engines/petka/petka.h
Normal file
197
engines/petka/petka.h
Normal file
@@ -0,0 +1,197 @@
|
||||
/* 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_PETKA_H
|
||||
#define PETKA_PETKA_H
|
||||
|
||||
#include "common/random.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/memstream.h"
|
||||
#include "common/savefile.h"
|
||||
|
||||
#include "engines/engine.h"
|
||||
#include "engines/savestate.h"
|
||||
|
||||
#include "gui/debugger.h"
|
||||
|
||||
#include "graphics/surface.h"
|
||||
|
||||
/*
|
||||
* This is the namespace of the Petka engine.
|
||||
*
|
||||
* Status of this engine: In Development
|
||||
*
|
||||
* Games using this engine:
|
||||
* - Red Comrades Demo
|
||||
* - Red Comrades Save the Galaxy - Fully playable
|
||||
* - Red Comrades 2: For the Great Justice - Fully playable
|
||||
*/
|
||||
|
||||
struct ADGameDescription;
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace Graphics {
|
||||
class Font;
|
||||
}
|
||||
|
||||
namespace Video {
|
||||
class VideoDecoder;
|
||||
}
|
||||
|
||||
namespace Petka {
|
||||
|
||||
class Console;
|
||||
class BigDialogue;
|
||||
class FileMgr;
|
||||
class SoundMgr;
|
||||
class QManager;
|
||||
class QSystem;
|
||||
class VideoSystem;
|
||||
|
||||
enum {
|
||||
kPetkaDebugGeneral = 1,
|
||||
kPetkaDebugResources,
|
||||
kPetkaDebugMessagingSystem,
|
||||
kPetkaDebugDialogs,
|
||||
};
|
||||
|
||||
enum {
|
||||
GF_COMPRESSED = (1 << 0),
|
||||
};
|
||||
|
||||
enum PETKAActions {
|
||||
kActionNone,
|
||||
kActionCursorLook,
|
||||
kActionCursorWalk,
|
||||
kActionCursorTake,
|
||||
kActionCursorUse,
|
||||
kActionCursorTalk,
|
||||
kActionCursorChapayev,
|
||||
kActionInventory,
|
||||
kActionMap,
|
||||
kActionOptions,
|
||||
kActionPrevInterface,
|
||||
kActionSave,
|
||||
kActionLoad,
|
||||
kActionSkip,
|
||||
};
|
||||
|
||||
class PetkaEngine : public Engine {
|
||||
public:
|
||||
PetkaEngine(OSystem *syst, const ADGameDescription *desc);
|
||||
~PetkaEngine() override;
|
||||
|
||||
bool isDemo() const;
|
||||
bool isPetka2() const;
|
||||
|
||||
void loadPart(byte part);
|
||||
void loadPartAtNextFrame(byte part);
|
||||
|
||||
byte getPart();
|
||||
int getSaveSlot();
|
||||
|
||||
void loadChapter(byte chapter);
|
||||
|
||||
Common::Error run() override;
|
||||
|
||||
bool hasFeature(EngineFeature f) const override;
|
||||
|
||||
void applyGameSettings() override;
|
||||
|
||||
Common::SeekableReadStream *openFile(const Common::String &name, bool addCurrentPath);
|
||||
Common::SeekableReadStream *openIniFile(const Common::String &name);
|
||||
|
||||
void playVideo(Common::SeekableReadStream *stream);
|
||||
QSystem *getQSystem() const;
|
||||
BigDialogue *getBigDialogue() const;
|
||||
SoundMgr *soundMgr() const;
|
||||
QManager *resMgr() const;
|
||||
|
||||
VideoSystem *videoSystem() const;
|
||||
|
||||
Common::RandomSource &getRnd();
|
||||
const Common::String &getSpeechPath();
|
||||
|
||||
Graphics::Font *getTextFont() const { return _textFont.get(); }
|
||||
Graphics::Font *getDescriptionFont() const { return _descriptionFont.get(); }
|
||||
|
||||
void pushMouseMoveEvent();
|
||||
|
||||
Common::Error loadGameState(int slot) override;
|
||||
bool canLoadGameStateCurrently(Common::U32String *msg = nullptr) override;
|
||||
|
||||
Common::Error saveGameState(int slot, const Common::String &desc, bool isAutosave) override;
|
||||
bool canSaveGameStateCurrently(Common::U32String *msg = nullptr) override;
|
||||
|
||||
int getAutosaveSlot() const override { return - 1;}
|
||||
|
||||
const ADGameDescription *const _desc;
|
||||
Common::ScopedPtr<Common::MemoryReadStream> _thumbnail;
|
||||
|
||||
protected:
|
||||
void pauseEngineIntern(bool pause) override;
|
||||
|
||||
private:
|
||||
void loadStores();
|
||||
|
||||
private:
|
||||
Common::ScopedPtr<Console> _console;
|
||||
Common::ScopedPtr<FileMgr> _fileMgr;
|
||||
Common::ScopedPtr<QManager> _resMgr;
|
||||
Common::ScopedPtr<SoundMgr> _soundMgr;
|
||||
Common::ScopedPtr<QSystem> _qsystem;
|
||||
Common::ScopedPtr<VideoSystem> _vsys;
|
||||
Common::ScopedPtr<BigDialogue> _dialogMan;
|
||||
Common::ScopedPtr<Video::VideoDecoder> _videoDec;
|
||||
Common::ScopedPtr<Graphics::Font> _textFont;
|
||||
Common::ScopedPtr<Graphics::Font> _descriptionFont;
|
||||
|
||||
Common::RandomSource _rnd;
|
||||
|
||||
Common::String _currentPath;
|
||||
Common::String _speechPath;
|
||||
|
||||
Common::String _chapterStoreName;
|
||||
|
||||
uint8 _part;
|
||||
uint8 _nextPart;
|
||||
uint8 _chapter;
|
||||
bool _shouldChangePart;
|
||||
int _saveSlot;
|
||||
};
|
||||
|
||||
class Console : public GUI::Debugger {
|
||||
public:
|
||||
Console(PetkaEngine *vm) {}
|
||||
virtual ~Console() {}
|
||||
};
|
||||
|
||||
extern PetkaEngine *g_vm;
|
||||
|
||||
WARN_UNUSED_RESULT bool readSaveHeader(Common::InSaveFile &in, SaveStateDescriptor &desc, bool skipThumbnail = true);
|
||||
Common::String generateSaveName(int slot, const char *gameId);
|
||||
|
||||
} // End of namespace Petka
|
||||
|
||||
#endif
|
||||
233
engines/petka/q_manager.cpp
Normal file
233
engines/petka/q_manager.cpp
Normal file
@@ -0,0 +1,233 @@
|
||||
/* 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/memstream.h"
|
||||
#include "common/system.h"
|
||||
#include "common/substream.h"
|
||||
#include "common/tokenizer.h"
|
||||
|
||||
#include "graphics/surface.h"
|
||||
|
||||
#include "image/bmp.h"
|
||||
|
||||
#include "petka/flc.h"
|
||||
#include "petka/q_manager.h"
|
||||
#include "petka/petka.h"
|
||||
|
||||
namespace Petka {
|
||||
|
||||
QManager::QManager(PetkaEngine &vm)
|
||||
: _vm(vm) {}
|
||||
|
||||
bool QManager::init() {
|
||||
clear();
|
||||
|
||||
Common::ScopedPtr<Common::SeekableReadStream> stream(_vm.openFile("resource.qrc", true));
|
||||
if (!stream) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while (!stream->eos()) {
|
||||
Common::StringTokenizer tokenizer(stream->readLine());
|
||||
if (tokenizer.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const uint32 id = (uint32)atoi(tokenizer.nextToken().c_str());
|
||||
_isAlwaysNeededMap.setVal(id, tokenizer.nextToken() == "==");
|
||||
_nameMap.setVal(id, tokenizer.nextToken());
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Common::String QManager::findResourceName(uint32 id) const {
|
||||
return _nameMap.contains(id) ? _nameMap.getVal(id) : "";
|
||||
}
|
||||
|
||||
Common::String QManager::findSoundName(uint32 id) const {
|
||||
Common::String name = findResourceName(id);
|
||||
name.toUppercase();
|
||||
if (name.empty() || name.hasSuffix(".WAV")) {
|
||||
return name;
|
||||
}
|
||||
name.erase(name.size() - 3, 3);
|
||||
return name += "WAV";
|
||||
}
|
||||
|
||||
void QManager::removeResource(uint32 id) {
|
||||
if (_resourceMap.contains(id)) {
|
||||
_resourceMap.erase(id);
|
||||
}
|
||||
}
|
||||
|
||||
void QManager::clearUnneeded() {
|
||||
for (auto it = _resourceMap.begin(); it != _resourceMap.end(); ++it) {
|
||||
if (!_isAlwaysNeededMap.getValOrDefault(it->_key, false)) {
|
||||
_resourceMap.erase(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Graphics::Surface *QManager::getSurface(uint32 id, uint16 w, uint16 h) {
|
||||
if (_resourceMap.contains(id)) {
|
||||
const QResource &res = _resourceMap.getVal(id);
|
||||
return res.type == QResource::kSurface ? res.surface : nullptr;
|
||||
}
|
||||
|
||||
QResource &res = _resourceMap.getOrCreateVal(id);
|
||||
res.type = QResource::kSurface;
|
||||
res.surface = new Graphics::Surface;
|
||||
res.surface->create(w, h, _vm._system->getScreenFormat());
|
||||
|
||||
return res.surface;
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *QManager::loadFileStream(uint32 id) const {
|
||||
const Common::String &name = findResourceName(id);
|
||||
return name.empty() ? nullptr : _vm.openFile(name, false);
|
||||
}
|
||||
|
||||
Graphics::Surface *QManager::getSurface(uint32 id) {
|
||||
if (_resourceMap.contains(id)) {
|
||||
const QResource &res = _resourceMap.getVal(id);
|
||||
return res.type == QResource::kSurface ? res.surface : nullptr;
|
||||
}
|
||||
|
||||
Common::ScopedPtr<Common::SeekableReadStream> stream(loadFileStream(id));
|
||||
if (!stream) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Common::ScopedPtr<Common::SeekableReadStream> preloaded_stream (stream->readStream(stream->size()));
|
||||
Graphics::Surface *s = loadBitmapSurface(*preloaded_stream);
|
||||
if (s) {
|
||||
QResource &res = _resourceMap.getOrCreateVal(id);
|
||||
res.type = QResource::kSurface;
|
||||
res.surface = s;
|
||||
return res.surface;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FlicDecoder *QManager::getFlic(uint32 id) {
|
||||
if (_resourceMap.contains(id)) {
|
||||
const QResource &res = _resourceMap.getVal(id);
|
||||
return res.type == QResource::kFlic ? res.flcDecoder : nullptr;
|
||||
}
|
||||
|
||||
Common::String name = findResourceName(id);
|
||||
Common::SeekableReadStream *stream = _vm.openFile(name, false);
|
||||
if (!stream) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
name.erase(name.size() - 3, 3);
|
||||
name.toUppercase();
|
||||
name += "MSK";
|
||||
|
||||
FlicDecoder *flc = new FlicDecoder;
|
||||
flc->load(stream, _vm.openFile(name, false));
|
||||
|
||||
QResource &res = _resourceMap.getOrCreateVal(id);
|
||||
res.type = QResource::kFlic;
|
||||
res.flcDecoder = flc;
|
||||
|
||||
return res.flcDecoder;
|
||||
}
|
||||
|
||||
void QManager::clear() {
|
||||
_resourceMap.clear();
|
||||
_nameMap.clear();
|
||||
_isAlwaysNeededMap.clear();
|
||||
}
|
||||
|
||||
Graphics::Surface *QManager::loadBitmapSurface(Common::SeekableReadStream &stream) {
|
||||
const uint32 kHeaderSize = 14 + 40;
|
||||
const uint32 kAdditionalDataSize = 8;
|
||||
|
||||
if (stream.readByte() != 'B')
|
||||
return nullptr;
|
||||
|
||||
if (stream.readByte() != 'M')
|
||||
return nullptr;
|
||||
|
||||
uint32 realFileSize = stream.readUint32LE();
|
||||
|
||||
stream.skip(12);
|
||||
|
||||
uint32 width = stream.readUint32LE();
|
||||
uint32 height = stream.readUint32LE();
|
||||
|
||||
stream.skip(2);
|
||||
|
||||
uint16 bitsPerPixel = stream.readUint16LE();
|
||||
if (bitsPerPixel != 16 && bitsPerPixel != 1) {
|
||||
stream.seek(0, SEEK_SET);
|
||||
Image::BitmapDecoder decoder;
|
||||
if (!decoder.loadStream(stream))
|
||||
return nullptr;
|
||||
return decoder.getSurface()->convertTo(g_system->getScreenFormat(), decoder.getPalette().data(), decoder.getPalette().size());
|
||||
}
|
||||
else if (bitsPerPixel == 1) {
|
||||
Graphics::Surface *s = new Graphics::Surface;
|
||||
s->create(width, height, Graphics::PixelFormat(2, 5, 6, 5, 0, 0, 5, 11, 0));
|
||||
return s;
|
||||
}
|
||||
|
||||
stream.seek(0, SEEK_SET);
|
||||
byte *convertedBmp = new byte[realFileSize];
|
||||
|
||||
stream.read(convertedBmp, kHeaderSize);
|
||||
WRITE_LE_INT16(convertedBmp + 28, 24); // bitsPerPixel
|
||||
|
||||
uint32 align = stream.readUint32LE();
|
||||
uint32 fileSize = stream.readUint32LE();
|
||||
|
||||
byte *pixels = convertedBmp + kHeaderSize;
|
||||
uint32 pixelsCount = (fileSize - (kHeaderSize + kAdditionalDataSize) - align + 1) / 2;
|
||||
|
||||
Graphics::PixelFormat fmt(2, 5, 6, 5, 0, 0, 5, 11, 0);
|
||||
while (pixelsCount) {
|
||||
fmt.colorToRGB(stream.readUint16BE(), *(pixels + 2), *(pixels + 1), *pixels);
|
||||
pixels += 3;
|
||||
pixelsCount--;
|
||||
}
|
||||
|
||||
Common::MemoryReadStream convBmpStream(convertedBmp, realFileSize, DisposeAfterUse::YES);
|
||||
Image::BitmapDecoder decoder;
|
||||
if (!decoder.loadStream(convBmpStream))
|
||||
return nullptr;
|
||||
|
||||
return decoder.getSurface()->convertTo(g_system->getScreenFormat(), decoder.getPalette().data(), decoder.getPalette().size());
|
||||
}
|
||||
|
||||
QManager::QResource::~QResource() {
|
||||
if (type == QResource::kSurface && surface) {
|
||||
surface->free();
|
||||
delete surface;
|
||||
} else {
|
||||
delete flcDecoder;
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Petka
|
||||
85
engines/petka/q_manager.h
Normal file
85
engines/petka/q_manager.h
Normal 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 PETKA_QMANAGER_H
|
||||
#define PETKA_QMANAGER_H
|
||||
|
||||
#include "common/hashmap.h"
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace Graphics {
|
||||
struct Surface;
|
||||
}
|
||||
|
||||
namespace Petka {
|
||||
|
||||
class PetkaEngine;
|
||||
|
||||
class FlicDecoder;
|
||||
|
||||
class QManager {
|
||||
public:
|
||||
explicit QManager(PetkaEngine &vm);
|
||||
|
||||
bool init();
|
||||
|
||||
Common::String findResourceName(uint32 id) const;
|
||||
Common::String findSoundName(uint32 id) const;
|
||||
|
||||
Graphics::Surface *getSurface(uint32 id, uint16 w, uint16 h);
|
||||
Graphics::Surface *getSurface(uint32 id);
|
||||
FlicDecoder *getFlic(uint32 id);
|
||||
|
||||
void removeResource(uint32 id);
|
||||
void clearUnneeded();
|
||||
void clear();
|
||||
|
||||
private:
|
||||
struct QResource {
|
||||
union {
|
||||
Graphics::Surface *surface;
|
||||
FlicDecoder *flcDecoder;
|
||||
};
|
||||
enum ResType {
|
||||
kSurface,
|
||||
kFlic
|
||||
} type;
|
||||
|
||||
~QResource();
|
||||
};
|
||||
|
||||
Common::SeekableReadStream *loadFileStream(uint32 id) const;
|
||||
|
||||
static Graphics::Surface *loadBitmapSurface(Common::SeekableReadStream &stream);
|
||||
|
||||
private:
|
||||
PetkaEngine &_vm;
|
||||
Common::HashMap<uint32, QResource> _resourceMap;
|
||||
Common::HashMap<uint32, Common::String> _nameMap;
|
||||
Common::HashMap<uint32, bool> _isAlwaysNeededMap;
|
||||
};
|
||||
|
||||
} // End of namespace Petka
|
||||
|
||||
#endif
|
||||
453
engines/petka/q_system.cpp
Normal file
453
engines/petka/q_system.cpp
Normal 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/formats/ini-file.h"
|
||||
#include "common/substream.h"
|
||||
#include "common/system.h"
|
||||
|
||||
#include "graphics/thumbnail.h"
|
||||
|
||||
#include "petka/petka.h"
|
||||
#include "petka/interfaces/startup.h"
|
||||
#include "petka/interfaces/main.h"
|
||||
#include "petka/interfaces/save_load.h"
|
||||
#include "petka/interfaces/sequence.h"
|
||||
#include "petka/interfaces/panel.h"
|
||||
#include "petka/interfaces/map.h"
|
||||
#include "petka/objects/object_cursor.h"
|
||||
#include "petka/objects/object_case.h"
|
||||
#include "petka/objects/object_star.h"
|
||||
#include "petka/objects/heroes.h"
|
||||
#include "petka/big_dialogue.h"
|
||||
#include "petka/q_system.h"
|
||||
#include "petka/video.h"
|
||||
#include "petka/q_manager.h"
|
||||
#include "petka/flc.h"
|
||||
|
||||
namespace Petka {
|
||||
|
||||
QSystem::QSystem(PetkaEngine &vm)
|
||||
: _vm(vm), _mainInterface(nullptr), _currInterface(nullptr), _prevInterface(nullptr),
|
||||
_totalInit(false), _sceneWidth(640), _room(nullptr), _xOffset(0), _reqOffset(0),
|
||||
_fxId(0), _musicId(0) {}
|
||||
|
||||
QSystem::~QSystem() {
|
||||
for (uint i = 0; i < _allObjects.size(); ++i) {
|
||||
delete _allObjects[i];
|
||||
}
|
||||
}
|
||||
|
||||
bool QSystem::init() {
|
||||
Common::ScopedPtr<Common::SeekableReadStream> stream(_vm.openFile("script.dat", true));
|
||||
if (!stream)
|
||||
return false;
|
||||
Common::ScopedPtr<Common::SeekableReadStream> namesStream(_vm.openIniFile("Names.ini"));
|
||||
Common::ScopedPtr<Common::SeekableReadStream> castStream(_vm.openIniFile("Cast.ini"));
|
||||
Common::ScopedPtr<Common::SeekableReadStream> bgsStream(_vm.openIniFile("BGs.ini"));
|
||||
|
||||
Common::INIFile namesIni;
|
||||
Common::INIFile castIni;
|
||||
Common::INIFile bgsIni;
|
||||
|
||||
namesIni.allowNonEnglishCharacters();
|
||||
castIni.allowNonEnglishCharacters();
|
||||
bgsIni.allowNonEnglishCharacters();
|
||||
|
||||
if (namesStream)
|
||||
namesIni.loadFromStream(*namesStream);
|
||||
if (castStream)
|
||||
castIni.loadFromStream(*castStream);
|
||||
if (bgsStream)
|
||||
bgsIni.loadFromStream(*bgsStream);
|
||||
|
||||
uint32 objsCount = stream->readUint32LE();
|
||||
uint32 bgsCount = stream->readUint32LE();
|
||||
_allObjects.reserve(objsCount + bgsCount + 3);
|
||||
for (uint i = 0; i < objsCount + bgsCount; ++i) {
|
||||
QMessageObject *obj = nullptr;
|
||||
if (i == 0) {
|
||||
obj = new QObjectPetka;
|
||||
} else if (i == 1) {
|
||||
obj = new QObjectChapayev;
|
||||
} else if (i < objsCount) {
|
||||
obj = new QObject;
|
||||
} else {
|
||||
obj = new QObjectBG;
|
||||
}
|
||||
obj->readScriptData(*stream);
|
||||
obj->readInisData(namesIni, castIni, &bgsIni);
|
||||
_allObjects.push_back(obj);
|
||||
}
|
||||
|
||||
_allObjects.push_back(new QObjectCursor);
|
||||
_allObjects.push_back(new QObjectCase);
|
||||
_allObjects.push_back(new QObjectStar);
|
||||
|
||||
_mainInterface.reset(new InterfaceMain());
|
||||
_startupInterface.reset(new InterfaceStartup());
|
||||
_saveLoadInterface.reset(new InterfaceSaveLoad());
|
||||
_sequenceInterface.reset(new InterfaceSequence());
|
||||
_panelInterface.reset(new InterfacePanel());
|
||||
_mapInterface.reset(new InterfaceMap());
|
||||
|
||||
if (_vm.getPart() == 0) {
|
||||
_prevInterface = _currInterface = _startupInterface.get();
|
||||
} else {
|
||||
_prevInterface = _currInterface = _mainInterface.get();
|
||||
}
|
||||
|
||||
_totalInit = true;
|
||||
|
||||
addMessageForAllObjects(kTotalInit);
|
||||
update();
|
||||
|
||||
_totalInit = false;
|
||||
|
||||
_currInterface->start(0);
|
||||
return true;
|
||||
}
|
||||
|
||||
void QSystem::addMessage(const QMessage &msg) {
|
||||
_messages.push_back(msg);
|
||||
}
|
||||
|
||||
void QSystem::addMessage(uint16 objId, uint16 opcode, int16 arg1, int16 arg2, int16 arg3, int32 unk, QMessageObject *sender) {
|
||||
_messages.push_back(QMessage(objId, opcode, arg1, arg2, arg3, sender, unk));
|
||||
}
|
||||
|
||||
void QSystem::addMessageForAllObjects(uint16 opcode, int16 arg1, int16 arg2, int16 arg3, int32 unk, QMessageObject *sender) {
|
||||
for (uint i = 0; i < _allObjects.size(); ++i) {
|
||||
_messages.push_back(QMessage(_allObjects[i]->_id, opcode, arg1, arg2, arg3, sender, unk));
|
||||
}
|
||||
}
|
||||
|
||||
QMessageObject *QSystem::findObject(int16 id) {
|
||||
for (uint i = 0; i < _allObjects.size(); ++i) {
|
||||
if (_allObjects[i]->_id == id)
|
||||
return _allObjects[i];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QMessageObject *QSystem::findObject(const Common::String &name) {
|
||||
for (uint i = 0; i < _allObjects.size(); ++i) {
|
||||
if (_allObjects[i]->_name == name)
|
||||
return _allObjects[i];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void QSystem::update() {
|
||||
for (Common::List<QMessage>::iterator it = _messages.begin(); it != _messages.end();) {
|
||||
QMessageObject *obj = findObject(it->objId);
|
||||
if (obj && !obj->_holdMessages) {
|
||||
obj->processMessage(*it);
|
||||
it = _messages.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QSystem::togglePanelInterface() {
|
||||
if (_currInterface != _startupInterface.get() && getStar()->_isActive) {
|
||||
getCase()->show(0);
|
||||
if (_currInterface == _panelInterface.get()) {
|
||||
_currInterface->stop();
|
||||
} else if (_currInterface == _mainInterface.get()) {
|
||||
_panelInterface->start(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QSystem::toggleMapInterface() {
|
||||
if (_currInterface != _startupInterface.get() && getStar()->_isActive && _room->_showMap) {
|
||||
getCase()->show(false);
|
||||
if (_currInterface == _mapInterface.get()) {
|
||||
_currInterface->stop();
|
||||
} else if (_currInterface == _mainInterface.get()) {
|
||||
_currInterface->setText(Common::U32String(), 0, 0);
|
||||
_mapInterface->start(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QSystem::setCursorAction(int action) {
|
||||
if (getStar()->_isActive && _currInterface == _mainInterface.get()) {
|
||||
if (action != kActionObjUseChapayev || getChapay()->_isShown) {
|
||||
getCursor()->setAction(action);
|
||||
|
||||
// original bug fix
|
||||
_mainInterface->onMouseMove(g_system->getEventManager()->getMousePos());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Common::String readString(Common::ReadStream *s) {
|
||||
Common::String string;
|
||||
uint32 len = s->readUint32LE();
|
||||
char *buffer = (char *)malloc(len);
|
||||
s->read(buffer, len);
|
||||
string = Common::String(buffer, len);
|
||||
free(buffer);
|
||||
return string;
|
||||
}
|
||||
|
||||
static void writeString(Common::WriteStream *s, const Common::String &string) {
|
||||
s->writeUint32LE(string.size());
|
||||
s->write(string.c_str(), string.size());
|
||||
}
|
||||
|
||||
void QSystem::load(Common::ReadStream *s) {
|
||||
uint count = s->readUint32LE();
|
||||
for (uint i = 0; i < count; ++i) {
|
||||
QMessageObject *obj = findObject(readString(s));
|
||||
obj->_holdMessages = s->readUint32LE();
|
||||
obj->_status = s->readUint32LE();
|
||||
obj->_resourceId = s->readUint32LE();
|
||||
/*obj->_z =*/ s->readUint32LE();
|
||||
obj->_x = s->readUint32LE();
|
||||
obj->_y = s->readUint32LE();
|
||||
obj->_isShown = s->readUint32LE();
|
||||
obj->_isActive = s->readUint32LE();
|
||||
obj->_animate = s->readUint32LE();
|
||||
}
|
||||
|
||||
uint itemSize = s->readUint32LE();
|
||||
QObjectCase *objCase = getCase();
|
||||
objCase->_items.clear();
|
||||
for (uint i = 0; i < itemSize; ++i) {
|
||||
objCase->_items.push_back(s->readSint32LE());
|
||||
}
|
||||
|
||||
_room = (QObjectBG *)findObject(readString(s));
|
||||
if (_room) {
|
||||
_mainInterface->loadRoom(_room->_id, true);
|
||||
}
|
||||
|
||||
QObjectPetka *petka = getPetka();
|
||||
QObjectChapayev *chapayev = getChapay();
|
||||
|
||||
Common::Point pos;
|
||||
pos.x = s->readSint32LE();
|
||||
pos.y = s->readSint32LE();
|
||||
|
||||
petka->setPos(pos, false);
|
||||
|
||||
_xOffset = CLIP<int>(pos.x - 320, 0, _sceneWidth - 640);
|
||||
|
||||
pos.x = s->readSint32LE();
|
||||
pos.y = s->readSint32LE();
|
||||
|
||||
chapayev->setPos(pos, false);
|
||||
|
||||
_vm.getBigDialogue()->load(s);
|
||||
|
||||
QObjectCursor *cursor = getCursor();
|
||||
cursor->_resourceId = s->readUint32LE();
|
||||
cursor->_actionType = s->readUint32LE();
|
||||
int invObjId = s->readSint32LE();
|
||||
if (invObjId != -1) {
|
||||
cursor->_invObj = findObject(invObjId);
|
||||
} else {
|
||||
cursor->_invObj = nullptr;
|
||||
}
|
||||
|
||||
int imageId = s->readSint32LE();
|
||||
if (imageId != -1 && !(imageId % 100)) {
|
||||
addMessage(petka->_id, kImage, imageId, 1);
|
||||
}
|
||||
|
||||
imageId = s->readSint32LE();
|
||||
if (imageId != -1 && !(imageId % 100)) {
|
||||
addMessage(chapayev->_id, kImage, imageId, 1);
|
||||
}
|
||||
|
||||
getStar()->_isActive = true;
|
||||
|
||||
_vm.videoSystem()->makeAllDirty();
|
||||
}
|
||||
|
||||
void QSystem::save(Common::WriteStream *s) {
|
||||
s->writeUint32LE(_allObjects.size() - 3);
|
||||
for (uint i = 0; i < _allObjects.size() - 3; ++i) {
|
||||
writeString(s, _allObjects[i]->_name);
|
||||
s->writeUint32LE(_allObjects[i]->_holdMessages);
|
||||
s->writeUint32LE(_allObjects[i]->_status);
|
||||
s->writeUint32LE(_allObjects[i]->_resourceId);
|
||||
s->writeUint32LE(_allObjects[i]->_z);
|
||||
s->writeUint32LE(_allObjects[i]->_x);
|
||||
s->writeUint32LE(_allObjects[i]->_y);
|
||||
s->writeUint32LE(_allObjects[i]->_isShown);
|
||||
s->writeUint32LE(_allObjects[i]->_isActive);
|
||||
s->writeUint32LE(_allObjects[i]->_animate);
|
||||
}
|
||||
|
||||
QObjectCase *objCase = getCase();
|
||||
s->writeUint32LE(objCase->_items.size());
|
||||
for (uint i = 0; i < objCase->_items.size(); ++i) {
|
||||
s->writeSint32LE(objCase->_items[i]);
|
||||
}
|
||||
|
||||
writeString(s, _room->_name);
|
||||
|
||||
QObjectPetka *petka = getPetka();
|
||||
QObjectChapayev *chapayev = getChapay();
|
||||
|
||||
FlicDecoder *petkaFlc = _vm.resMgr()->getFlic(petka->_resourceId);
|
||||
FlicDecoder *chapayFlc = _vm.resMgr()->getFlic(chapayev->_resourceId);
|
||||
|
||||
s->writeSint32LE(petka->_x - petkaFlc->getCurrentFrame()->w * petka->_k * -0.5);
|
||||
s->writeSint32LE(petka->_y + petkaFlc->getCurrentFrame()->h * petka->_k);
|
||||
|
||||
s->writeSint32LE(chapayev->_x - chapayFlc->getCurrentFrame()->w * chapayev->_k * -0.5);
|
||||
s->writeSint32LE(chapayev->_y + chapayFlc->getCurrentFrame()->h * chapayev->_k);
|
||||
|
||||
_vm.getBigDialogue()->save(s);
|
||||
|
||||
QObjectCursor *cursor = getCursor();
|
||||
s->writeUint32LE(cursor->_resourceId);
|
||||
s->writeUint32LE(cursor->_actionType);
|
||||
if (cursor->_invObj) {
|
||||
s->writeSint32LE(cursor->_invObj->_id);
|
||||
} else {
|
||||
s->writeSint32LE(-1);
|
||||
}
|
||||
|
||||
s->writeSint32LE(petka->_imageId);
|
||||
s->writeSint32LE(chapayev->_imageId);
|
||||
}
|
||||
|
||||
QObjectPetka *QSystem::getPetka() const {
|
||||
return (QObjectPetka *)_allObjects[0];
|
||||
}
|
||||
|
||||
QObjectChapayev *QSystem::getChapay() const {
|
||||
return (QObjectChapayev *)_allObjects[1];
|
||||
}
|
||||
|
||||
QObjectCursor *QSystem::getCursor() const {
|
||||
return (QObjectCursor *)_allObjects[_allObjects.size() - 3];
|
||||
}
|
||||
|
||||
QObjectCase *QSystem::getCase() const {
|
||||
return (QObjectCase *)_allObjects[_allObjects.size() - 2];
|
||||
}
|
||||
|
||||
QObjectStar *QSystem::getStar() const {
|
||||
return (QObjectStar *)_allObjects.back();
|
||||
}
|
||||
|
||||
void QSystem::onEvent(const Common::Event &event) {
|
||||
switch (event.type) {
|
||||
case Common::EVENT_MOUSEMOVE: {
|
||||
Common::Point p = event.mouse;
|
||||
p.x += _xOffset;
|
||||
_currInterface->onMouseMove(p);
|
||||
break;
|
||||
}
|
||||
case Common::EVENT_LBUTTONDOWN: {
|
||||
Common::Point p = event.mouse;
|
||||
p.x += _xOffset;
|
||||
_currInterface->onLeftButtonDown(p);
|
||||
break;
|
||||
}
|
||||
case Common::EVENT_RBUTTONDOWN: {
|
||||
Common::Point p = event.mouse;
|
||||
p.x += _xOffset;
|
||||
_currInterface->onRightButtonDown(p);
|
||||
break;
|
||||
}
|
||||
case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
|
||||
switch (event.customType) {
|
||||
case kActionCursorLook:
|
||||
setCursorAction(kActionLook);
|
||||
break;
|
||||
case kActionCursorWalk:
|
||||
setCursorAction(kActionWalk);
|
||||
break;
|
||||
case kActionCursorTake:
|
||||
setCursorAction(kActionTake);
|
||||
break;
|
||||
case kActionCursorUse:
|
||||
setCursorAction(kActionUse);
|
||||
break;
|
||||
case kActionCursorTalk:
|
||||
setCursorAction(kActionTalk);
|
||||
break;
|
||||
case kActionCursorChapayev:
|
||||
setCursorAction(kActionObjUseChapayev);
|
||||
break;
|
||||
case kActionInventory:
|
||||
toggleCase();
|
||||
break;
|
||||
case kActionMap:
|
||||
toggleMapInterface();
|
||||
break;
|
||||
case kActionOptions:
|
||||
togglePanelInterface();
|
||||
break;
|
||||
case kActionPrevInterface:
|
||||
goPrevInterface();
|
||||
break;
|
||||
case kActionSave: {
|
||||
InterfaceSaveLoad::saveScreen();
|
||||
startSaveLoad(kSaveMode);
|
||||
break;
|
||||
}
|
||||
case kActionLoad:
|
||||
startSaveLoad(kLoadMode);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Common::EVENT_KEYDOWN:
|
||||
if (event.kbd.keycode == Common::KEYCODE_r)
|
||||
if (event.kbd.flags & Common::KBD_ALT) {
|
||||
_mainInterface->_dialog.fixCursor(); // Buggy in original
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void QSystem::goPrevInterface() {
|
||||
getCase()->show(false);
|
||||
if (_currInterface != _startupInterface.get() && _currInterface != _sequenceInterface.get())
|
||||
_currInterface->stop();
|
||||
}
|
||||
|
||||
void QSystem::toggleCase() {
|
||||
if (_currInterface == _mainInterface.get() && getStar()->_isActive) {
|
||||
QObjectCase *obj = getCase();
|
||||
obj->show(obj->_isShown == 0);
|
||||
}
|
||||
}
|
||||
|
||||
void QSystem::startSaveLoad(int id) {
|
||||
if (_currInterface == _mainInterface.get() && getStar()->_isActive) {
|
||||
_saveLoadInterface->start(id);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
113
engines/petka/q_system.h
Normal file
113
engines/petka/q_system.h
Normal file
@@ -0,0 +1,113 @@
|
||||
/* 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_Q_SYSTEM_H
|
||||
#define PETKA_Q_SYSTEM_H
|
||||
|
||||
#include "common/ptr.h"
|
||||
#include "common/events.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/list.h"
|
||||
#include "common/hashmap.h"
|
||||
#include "common/hash-str.h"
|
||||
|
||||
#include "petka/objects/object_bg.h"
|
||||
|
||||
namespace Petka {
|
||||
|
||||
class PetkaEngine;
|
||||
class QObjectCase;
|
||||
class QObjectCursor;
|
||||
class QObjectStar;
|
||||
class QObjectPetka;
|
||||
class QObjectChapayev;
|
||||
class InterfaceSaveLoad;
|
||||
class InterfaceSequence;
|
||||
class InterfaceMain;
|
||||
class InterfaceStartup;
|
||||
class InterfacePanel;
|
||||
class InterfaceMap;
|
||||
class Interface;
|
||||
|
||||
class QSystem {
|
||||
public:
|
||||
QSystem(PetkaEngine &vm);
|
||||
~QSystem();
|
||||
|
||||
bool init();
|
||||
void load(Common::ReadStream *s);
|
||||
void save(Common::WriteStream *s);
|
||||
|
||||
void update();
|
||||
|
||||
void addMessage(const QMessage &msg);
|
||||
void addMessage(uint16 objId, uint16 opcode, int16 arg1 = 0, int16 arg2 = 0, int16 arg3 = 0, int32 unk = 0, QMessageObject *sender = nullptr);
|
||||
void addMessageForAllObjects(uint16 opcode, int16 arg1 = 0, int16 arg2 = 0, int16 arg3 = 0, int32 unk = 0, QMessageObject *sender = nullptr);
|
||||
|
||||
QMessageObject *findObject(int16 id);
|
||||
QMessageObject *findObject(const Common::String &name);
|
||||
|
||||
QObjectPetka *getPetka() const;
|
||||
QObjectChapayev *getChapay() const;
|
||||
QObjectCursor *getCursor() const;
|
||||
QObjectCase *getCase() const;
|
||||
QObjectStar *getStar() const;
|
||||
|
||||
void startSaveLoad(int id);
|
||||
|
||||
void togglePanelInterface();
|
||||
void toggleMapInterface();
|
||||
void toggleCase();
|
||||
|
||||
void goPrevInterface();
|
||||
|
||||
void setCursorAction(int action);
|
||||
|
||||
void onEvent(const Common::Event &event);
|
||||
|
||||
public:
|
||||
PetkaEngine &_vm;
|
||||
|
||||
Common::Array<QMessageObject *> _allObjects;
|
||||
Common::List<QMessage> _messages;
|
||||
Common::ScopedPtr<InterfaceMain> _mainInterface;
|
||||
Common::ScopedPtr<InterfaceSaveLoad> _saveLoadInterface;
|
||||
Common::ScopedPtr<InterfaceSequence> _sequenceInterface;
|
||||
Common::ScopedPtr<InterfaceStartup> _startupInterface;
|
||||
Common::ScopedPtr<InterfacePanel> _panelInterface;
|
||||
Common::ScopedPtr<InterfaceMap> _mapInterface;
|
||||
Interface *_currInterface;
|
||||
Interface *_prevInterface;
|
||||
|
||||
bool _totalInit;
|
||||
int _fxId;
|
||||
int _musicId;
|
||||
|
||||
int _sceneWidth;
|
||||
int _xOffset;
|
||||
int _reqOffset;
|
||||
|
||||
QObjectBG *_room;
|
||||
};
|
||||
|
||||
} // End of namespace Petka
|
||||
|
||||
#endif
|
||||
166
engines/petka/saveload.cpp
Normal file
166
engines/petka/saveload.cpp
Normal file
@@ -0,0 +1,166 @@
|
||||
/* 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 "common/savefile.h"
|
||||
#include "common/translation.h"
|
||||
|
||||
#include "engines/savestate.h"
|
||||
|
||||
#include "graphics/thumbnail.h"
|
||||
|
||||
#include "petka/petka.h"
|
||||
#include "petka/objects/object_star.h"
|
||||
#include "petka/q_system.h"
|
||||
#include "petka/interfaces/panel.h"
|
||||
#include "petka/interfaces/save_load.h"
|
||||
#include "petka/interfaces/main.h"
|
||||
|
||||
namespace Petka {
|
||||
|
||||
Common::Error PetkaEngine::loadGameState(int slot) {
|
||||
Common::ScopedPtr<Common::SeekableReadStream> in(_saveFileMan->openForLoading(generateSaveName(slot, _targetName.c_str())));
|
||||
if (!in)
|
||||
return Common::kNoGameDataFoundError;
|
||||
|
||||
SaveStateDescriptor desc;
|
||||
if (!readSaveHeader(*in, desc))
|
||||
return Common::kUnknownError;
|
||||
|
||||
setTotalPlayTime(desc.getPlayTimeMSecs());
|
||||
|
||||
_nextPart = in->readUint32LE();
|
||||
_chapter = in->readUint32LE();
|
||||
if (_nextPart == _part) {
|
||||
loadChapter(_chapter);
|
||||
_qsystem->load(in.get());
|
||||
} else {
|
||||
_shouldChangePart = true;
|
||||
_saveSlot = slot;
|
||||
}
|
||||
|
||||
return Common::kNoError;
|
||||
}
|
||||
|
||||
Common::Error PetkaEngine::saveGameState(int slot, const Common::String &desci, bool isAutosave) {
|
||||
Common::ScopedPtr<Common::OutSaveFile> out(_saveFileMan->openForSaving(generateSaveName(slot, _targetName.c_str())));
|
||||
if (!out)
|
||||
return Common::kUnknownError;
|
||||
|
||||
if (_qsystem->_currInterface == _qsystem->_panelInterface.get() ||
|
||||
_qsystem->_currInterface == _qsystem->_saveLoadInterface.get())
|
||||
{
|
||||
_qsystem->goPrevInterface();
|
||||
}
|
||||
|
||||
out->writeUint32BE(MKTAG('p', 'e', 't', 'k'));
|
||||
out->writeByte(desci.size());
|
||||
out->writeString(desci);
|
||||
|
||||
TimeDate curTime;
|
||||
_system->getTimeAndDate(curTime);
|
||||
|
||||
out->writeUint32LE(((curTime.tm_mday & 0xFF) << 24) | (((curTime.tm_mon + 1) & 0xFF) << 16) | ((curTime.tm_year + 1900) & 0xFFFF));
|
||||
out->writeUint16LE(((curTime.tm_hour & 0xFF) << 8) | ((curTime.tm_min) & 0xFF));
|
||||
|
||||
out->writeUint32LE(getTotalPlayTime() / 1000);
|
||||
|
||||
if (!_thumbnail)
|
||||
return Common::kUnknownError;
|
||||
|
||||
out->writeStream(_thumbnail.get());
|
||||
|
||||
out->writeUint32LE(_part);
|
||||
out->writeUint32LE(_chapter);
|
||||
_qsystem->save(out.get());
|
||||
|
||||
return Common::kNoError;
|
||||
}
|
||||
|
||||
bool PetkaEngine::canSaveGameStateCurrently(Common::U32String *msg) {
|
||||
if (isDemo()) {
|
||||
if (msg)
|
||||
*msg = _("This game does not support saving");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_qsystem)
|
||||
return false;
|
||||
|
||||
Interface *panel = _qsystem->_panelInterface.get();
|
||||
InterfaceSaveLoad *saveLoad = _qsystem->_saveLoadInterface.get();
|
||||
|
||||
Interface *curr = _qsystem->_currInterface;
|
||||
Interface *prev = _qsystem->_prevInterface;
|
||||
|
||||
return prev == _qsystem->_mainInterface.get() && (curr == saveLoad || curr == panel);
|
||||
}
|
||||
|
||||
bool PetkaEngine::canLoadGameStateCurrently(Common::U32String *msg) {
|
||||
if (isDemo()) {
|
||||
if (msg)
|
||||
*msg = _("This game does not support loading");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return _qsystem;
|
||||
}
|
||||
|
||||
int PetkaEngine::getSaveSlot() {
|
||||
return _saveSlot;
|
||||
}
|
||||
|
||||
bool readSaveHeader(Common::InSaveFile &in, SaveStateDescriptor &desc, bool skipThumbnail) {
|
||||
if (in.readUint32BE() != MKTAG('p', 'e', 't', 'k'))
|
||||
return false;
|
||||
|
||||
const Common::String description = in.readPascalString();
|
||||
uint32 date = in.readUint32LE();
|
||||
uint16 time = in.readUint16LE();
|
||||
uint32 playTime = in.readUint32LE();
|
||||
|
||||
Graphics::Surface *thumbnail = nullptr;
|
||||
if (!Graphics::loadThumbnail(in, thumbnail, skipThumbnail))
|
||||
return false;
|
||||
|
||||
int day = (date >> 24) & 0xFF;
|
||||
int month = (date >> 16) & 0xFF;
|
||||
int year = date & 0xFFFF;
|
||||
|
||||
int hour = (time >> 8) & 0xFF;
|
||||
int minutes = time & 0xFF;
|
||||
|
||||
desc.setSaveDate(year, month, day);
|
||||
desc.setSaveTime(hour, minutes);
|
||||
desc.setPlayTime(playTime * 1000);
|
||||
desc.setDescription(description);
|
||||
desc.setThumbnail(thumbnail);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Common::String generateSaveName(int slot, const char *gameId) {
|
||||
return Common::String::format("%s.s%02d", gameId, slot);
|
||||
}
|
||||
|
||||
}
|
||||
119
engines/petka/sound.cpp
Normal file
119
engines/petka/sound.cpp
Normal file
@@ -0,0 +1,119 @@
|
||||
/* 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 "audio/audiostream.h"
|
||||
#include "audio/decoders/wave.h"
|
||||
|
||||
#include "common/substream.h"
|
||||
#include "common/system.h"
|
||||
#include "common/debug.h"
|
||||
|
||||
#include "petka/petka.h"
|
||||
#include "petka/sound.h"
|
||||
|
||||
namespace Petka {
|
||||
|
||||
Sound::Sound(Common::SeekableReadStream *stream, Audio::Mixer::SoundType type)
|
||||
: _type(type), _stream(stream->readStream(stream->size())) {
|
||||
delete stream;
|
||||
}
|
||||
|
||||
Sound::~Sound() {
|
||||
stop();
|
||||
}
|
||||
|
||||
void Sound::play(bool isLoop) {
|
||||
if (!_stream)
|
||||
return;
|
||||
stop();
|
||||
|
||||
Audio::AudioStream *audioStream;
|
||||
Audio::SeekableAudioStream *wavStream = Audio::makeWAVStream(_stream.get(), DisposeAfterUse::NO);
|
||||
if (isLoop)
|
||||
audioStream = Audio::makeLoopingAudioStream(wavStream, 0, 0, 0);
|
||||
else
|
||||
audioStream = wavStream;
|
||||
|
||||
g_system->getMixer()->playStream(_type, &_handle , audioStream);
|
||||
}
|
||||
|
||||
bool Sound::isPlaying() {
|
||||
return g_system->getMixer()->isSoundHandleActive(_handle);
|
||||
}
|
||||
|
||||
void Sound::stop() {
|
||||
g_system->getMixer()->stopHandle(_handle);
|
||||
_stream->seek(0, SEEK_SET);
|
||||
}
|
||||
|
||||
void Sound::setBalance(uint16 x, uint16 width) {
|
||||
// original scales from -12.5 db to 12.5 db
|
||||
g_system->getMixer()->setChannelBalance(_handle, (int8)(255 * (2 * x - width) / (2 * width)));
|
||||
}
|
||||
|
||||
Audio::Mixer::SoundType Sound::type() {
|
||||
return _type;
|
||||
}
|
||||
|
||||
void Sound::pause(bool p) {
|
||||
g_system->getMixer()->pauseHandle(_handle, p);
|
||||
}
|
||||
|
||||
|
||||
Sound *SoundMgr::addSound(const Common::String &name, Audio::Mixer::SoundType type) {
|
||||
Sound *sound = findSound(name);
|
||||
if (sound)
|
||||
return sound;
|
||||
Common::SeekableReadStream *s = _vm.openFile(name, false);
|
||||
if (s) {
|
||||
debug("SoundMgr: added sound %s", name.c_str());
|
||||
sound = new Sound(s, type);
|
||||
_sounds.getOrCreateVal(name).reset(sound);
|
||||
}
|
||||
return sound;
|
||||
}
|
||||
|
||||
Sound *SoundMgr::findSound(const Common::String &name) const {
|
||||
SoundsMap::iterator it = _sounds.find(name);
|
||||
return it != _sounds.end() ? it->_value.get() : nullptr;
|
||||
}
|
||||
|
||||
void SoundMgr::removeSound(const Common::String &name) {
|
||||
debug("SoundMgr::removeSound %s", name.c_str());
|
||||
_sounds.erase(name);
|
||||
}
|
||||
|
||||
void SoundMgr::removeAll() {
|
||||
debug("SoundMgr::removeAll");
|
||||
_sounds.clear(false);
|
||||
}
|
||||
|
||||
void SoundMgr::removeSoundsWithType(Audio::Mixer::SoundType type) {
|
||||
SoundsMap::iterator it;
|
||||
for (it = _sounds.begin(); it != _sounds.end(); ++it) {
|
||||
Sound *s = it->_value.get();
|
||||
if (s->type() == type) {
|
||||
_sounds.erase(it); // it is safe to inc iterator after erasing in our hashmap impl
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Petka
|
||||
79
engines/petka/sound.h
Normal file
79
engines/petka/sound.h
Normal 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 PETKA_SOUND_H
|
||||
#define PETKA_SOUND_H
|
||||
|
||||
#include "audio/mixer.h"
|
||||
|
||||
#include "common/hash-str.h"
|
||||
#include "common/ptr.h"
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace Petka {
|
||||
|
||||
class Sound {
|
||||
public:
|
||||
Sound(Common::SeekableReadStream *stream, Audio::Mixer::SoundType type);
|
||||
~Sound();
|
||||
|
||||
Audio::Mixer::SoundType type();
|
||||
|
||||
void play(bool isLoop = false);
|
||||
void stop();
|
||||
|
||||
void pause(bool p);
|
||||
|
||||
bool isPlaying();
|
||||
|
||||
void setBalance(uint16 x, uint16 width);
|
||||
|
||||
private:
|
||||
Common::ScopedPtr<Common::SeekableReadStream> _stream;
|
||||
Audio::Mixer::SoundType _type;
|
||||
Audio::SoundHandle _handle;
|
||||
};
|
||||
|
||||
class PetkaEngine;
|
||||
|
||||
class SoundMgr {
|
||||
public:
|
||||
SoundMgr(PetkaEngine &vm) : _vm(vm) {}
|
||||
|
||||
Sound *addSound(const Common::String &name, Audio::Mixer::SoundType type);
|
||||
Sound *findSound(const Common::String &name) const;
|
||||
|
||||
void removeSound(const Common::String &name);
|
||||
void removeSoundsWithType(Audio::Mixer::SoundType type);
|
||||
void removeAll();
|
||||
|
||||
private:
|
||||
typedef Common::HashMap<Common::String, Common::ScopedPtr<Sound>, Common::CaseSensitiveString_Hash> SoundsMap;
|
||||
PetkaEngine &_vm;
|
||||
SoundsMap _sounds;
|
||||
};
|
||||
|
||||
} // End of namespace Petka
|
||||
|
||||
#endif
|
||||
119
engines/petka/video.cpp
Normal file
119
engines/petka/video.cpp
Normal file
@@ -0,0 +1,119 @@
|
||||
/* 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/flc.h"
|
||||
#include "petka/petka.h"
|
||||
#include "petka/q_system.h"
|
||||
#include "petka/interfaces/interface.h"
|
||||
#include "petka/video.h"
|
||||
|
||||
namespace Petka {
|
||||
|
||||
const uint kShakeTime = 30;
|
||||
const int kShakeOffset = 3;
|
||||
|
||||
VideoSystem::VideoSystem(PetkaEngine &vm)
|
||||
: _vm(vm) {
|
||||
_shakeTime = 0;
|
||||
_time = g_system->getMillis();
|
||||
_shake = false;
|
||||
_shift = false;
|
||||
_allowAddingRects = true;
|
||||
}
|
||||
|
||||
void VideoSystem::update() {
|
||||
QSystem *sys = _vm.getQSystem();
|
||||
Interface *interface = sys->_currInterface;
|
||||
uint32 time = g_system->getMillis();
|
||||
|
||||
assert(sys);
|
||||
assert(interface);
|
||||
|
||||
interface->update(time - _time);
|
||||
|
||||
mergeDirtyRects();
|
||||
|
||||
_allowAddingRects = false;
|
||||
interface->draw();
|
||||
_allowAddingRects = true;
|
||||
|
||||
for (Common::Rect &r : _dirtyRects) {
|
||||
const byte *srcP = (const byte *)getBasePtr(r.left, r.top);
|
||||
g_system->copyRectToScreen(srcP, pitch, r.left, r.top, r.width(), r.height());
|
||||
}
|
||||
|
||||
_dirtyRects.clear();
|
||||
|
||||
_time = time;
|
||||
|
||||
if (_shake) {
|
||||
g_system->setShakePos(_shift ? kShakeOffset : 0, 0);
|
||||
if (time - _shakeTime > kShakeTime) {
|
||||
_shift = !_shift;
|
||||
_shakeTime = time;
|
||||
}
|
||||
}
|
||||
|
||||
g_system->updateScreen();
|
||||
}
|
||||
|
||||
void VideoSystem::addDirtyRect(const Common::Rect &rect) {
|
||||
if (_allowAddingRects) {
|
||||
Graphics::Screen::addDirtyRect(rect);
|
||||
}
|
||||
}
|
||||
|
||||
void VideoSystem::addDirtyRect(Common::Point pos, Common::Rect rect) {
|
||||
rect.translate(pos.x, pos.y);
|
||||
addDirtyRect(rect);
|
||||
}
|
||||
|
||||
void VideoSystem::addDirtyRect(Common::Point pos, FlicDecoder &flc) {
|
||||
pos.x = pos.x - _vm.getQSystem()->_xOffset;
|
||||
addDirtyRect(pos, flc.getBounds());
|
||||
}
|
||||
|
||||
void VideoSystem::addDirtyMskRects(Common::Point pos, FlicDecoder &flc) {
|
||||
for (auto &rect : flc.getMskRects()) {
|
||||
addDirtyRect(pos, rect);
|
||||
}
|
||||
}
|
||||
|
||||
void VideoSystem::addDirtyMskRects(FlicDecoder &flc) {
|
||||
addDirtyMskRects(Common::Point(0, 0), flc);
|
||||
}
|
||||
|
||||
const Common::List<Common::Rect> &VideoSystem::rects() const {
|
||||
return _dirtyRects;
|
||||
}
|
||||
|
||||
void VideoSystem::updateTime() {
|
||||
_time = g_system->getMillis();
|
||||
}
|
||||
|
||||
void VideoSystem::setShake(bool shake) {
|
||||
_shake = shake;
|
||||
g_system->setShakePos(0, 0);
|
||||
}
|
||||
|
||||
} // End of namespace Petka
|
||||
62
engines/petka/video.h
Normal file
62
engines/petka/video.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/* 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_SCREEN_H
|
||||
#define PETKA_SCREEN_H
|
||||
|
||||
#include "graphics/screen.h"
|
||||
|
||||
namespace Petka {
|
||||
|
||||
class FlicDecoder;
|
||||
|
||||
class PetkaEngine;
|
||||
|
||||
class VideoSystem : public Graphics::Screen {
|
||||
public:
|
||||
explicit VideoSystem(PetkaEngine &vm);
|
||||
|
||||
void updateTime();
|
||||
void update() override;
|
||||
|
||||
void addDirtyRect(const Common::Rect &rect) override;
|
||||
void addDirtyRect(Common::Point pos, Common::Rect rect);
|
||||
void addDirtyRect(Common::Point pos, FlicDecoder &flc);
|
||||
|
||||
void addDirtyMskRects(Common::Point pos, FlicDecoder &flc);
|
||||
void addDirtyMskRects(FlicDecoder &flc);
|
||||
|
||||
void setShake(bool shake);
|
||||
|
||||
const Common::List<Common::Rect> &rects() const;
|
||||
|
||||
private:
|
||||
PetkaEngine &_vm;
|
||||
uint32 _shakeTime;
|
||||
uint32 _time;
|
||||
bool _shake;
|
||||
bool _shift;
|
||||
bool _allowAddingRects;
|
||||
};
|
||||
|
||||
} // End of namespace Petka
|
||||
|
||||
#endif
|
||||
810
engines/petka/walk.cpp
Normal file
810
engines/petka/walk.cpp
Normal file
@@ -0,0 +1,810 @@
|
||||
/* 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 "math/utils.h"
|
||||
|
||||
#include "petka/walk.h"
|
||||
#include "petka/petka.h"
|
||||
#include "petka/q_manager.h"
|
||||
#include "petka/q_system.h"
|
||||
#include "petka/objects/heroes.h"
|
||||
|
||||
namespace Petka {
|
||||
|
||||
const char *const wayPrefixes[] = {"-w-n.", "-w-ne.", "-w-e.", "-w-se.", "-w-s.", "-w-sw.", "-w-w.", "-w-nw."};
|
||||
|
||||
const double kPI = M_PI;
|
||||
const double k2PI = M_PI * 2;
|
||||
const double kHalfPI = M_PI_2;
|
||||
|
||||
const double kPiArray[] = {M_PI_2, M_PI_4, 0.0, -M_PI_4, -M_PI_2, -3 * M_PI_4, M_PI, 3 * M_PI_4};
|
||||
|
||||
Walk::Walk(int id) { // CHECKED
|
||||
QManager *mgr = g_vm->resMgr();
|
||||
|
||||
Common::String res = mgr->findResourceName(id);
|
||||
res.toLowercase();
|
||||
res = res.substr(0, res.find(wayPrefixes[0]));
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
waysSizes[i] = readWayFile(res + Common::String(wayPrefixes[i]) + "off", &_off1[i], &_off2[i]) - 1;
|
||||
readWayFile(res + Common::String(wayPrefixes[i]) + "leg", &_leg1[i], &_leg2[i]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
_offleg1[i] = new int[waysSizes[i] + 1];
|
||||
_offleg2[i] = new int[waysSizes[i] + 1];
|
||||
for (int j = 1; j <= waysSizes[i]; ++j) {
|
||||
_offleg1[i][j] = _leg1[i][j] + _off1[i][j] - _leg1[i][j - 1];
|
||||
_offleg2[i][j] = _leg2[i][j] + _off2[i][j] - _leg2[i][j - 1];
|
||||
}
|
||||
_offleg1[i][0] = _offleg1[i][waysSizes[i]];
|
||||
_offleg2[i][0] = _offleg2[i][waysSizes[i]];
|
||||
}
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
int v16 = 0;
|
||||
int v18 = 0;
|
||||
int idx = 1;
|
||||
for (int j = 0; j < 150; ++j) {
|
||||
v16 += _offleg1[i][idx];
|
||||
v18 += _offleg2[i][idx];
|
||||
idx = (idx + 1) % waysSizes[i];
|
||||
}
|
||||
field_D0[i] = (double)v18 / v16;
|
||||
}
|
||||
|
||||
currX = 0.0;
|
||||
currY = 0.0;
|
||||
_bkg3Count = 0;
|
||||
_edgesCount = 0;
|
||||
field_134 = 0;
|
||||
_verticesCount = 0;
|
||||
_bkg3_1 = nullptr;
|
||||
_bkg3_2 = nullptr;
|
||||
_edges = nullptr;
|
||||
_bkg3_3 = nullptr;
|
||||
_bkg3_4 = nullptr;
|
||||
_bkg3_5 = nullptr;
|
||||
_vertices = nullptr;
|
||||
field_190 = 0;
|
||||
resId = 0;
|
||||
|
||||
field_170 = field_178 = 0.0;
|
||||
field_180 = field_181 = field_182 = field_183 = field_184 = field_185 =
|
||||
field_186 = field_187 = field_188 = field_189 = field_18A = field_18B =
|
||||
field_18C = field_18D = field_18E = field_18F = 0;
|
||||
field_194 = 0;
|
||||
field_198 = 0.0;
|
||||
destX = destY = 0;
|
||||
field_1C4 = field_1C5 = field_1C6 = field_1C7 = 0;
|
||||
}
|
||||
|
||||
Walk::~Walk() { // CHECKED
|
||||
clearBackground();
|
||||
reset();
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
delete[] _leg1[i];
|
||||
delete[] _leg2[i];
|
||||
delete[] _off1[i];
|
||||
delete[] _off2[i];
|
||||
delete[] _offleg1[i];
|
||||
delete[] _offleg2[i];
|
||||
|
||||
_leg1[i] = nullptr;
|
||||
_leg2[i] = nullptr;
|
||||
_off1[i] = nullptr;
|
||||
_off2[i] = nullptr;
|
||||
_offleg1[i] = nullptr;
|
||||
_offleg2[i] = nullptr;
|
||||
|
||||
waysSizes[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Walk::init(Point start, Point end) {
|
||||
reset();
|
||||
field_134 = 1;
|
||||
|
||||
int v5 = sub_424230(&end) ? moveInside(&end) : sub_423600(end);
|
||||
_bkg3_4[0] = sub_424230(&start) ? moveInside(&start) : sub_423600(start);
|
||||
|
||||
|
||||
destX = end.x;
|
||||
destY = end.y;
|
||||
|
||||
currX = end.x;
|
||||
currY = end.y;
|
||||
|
||||
if (start == end)
|
||||
return;
|
||||
|
||||
if (sub_424160(&start, &end) || v5 != _bkg3_4[0]) {
|
||||
for (int i = 0; i < _bkg3Count; ++i)
|
||||
_bkg3_5[i] = -1;
|
||||
|
||||
field_134 = 1;
|
||||
if (_bkg3_4[0] != v5) {
|
||||
do {
|
||||
_bkg3_4[field_134] = sub_423970(_bkg3_4[field_134 - 1], _bkg3_5[field_134 - 1]);
|
||||
if (_bkg3_4[field_134] >= 0) {
|
||||
_bkg3_5[field_134 - 1] = _bkg3_4[field_134];
|
||||
if (field_134 <= 1 || _bkg3_4[field_134 - 2] != _bkg3_4[field_134])
|
||||
field_134++;
|
||||
} else {
|
||||
field_134--;
|
||||
_bkg3_4[field_134] = -1;
|
||||
_bkg3_5[field_134] = -1;
|
||||
}
|
||||
|
||||
} while (_bkg3_4[field_134 - 1] != v5);
|
||||
}
|
||||
|
||||
int v20 = 1;
|
||||
int v21 = 4;
|
||||
int v22 = 1;
|
||||
int a2 = 4;
|
||||
_bkg3_3[0] = start;
|
||||
if (field_134 > 1) {
|
||||
do {
|
||||
int v23 = sub_423A30(_bkg3_4[(v21 - 4) / 4], _bkg3_4[v21 / 4]);
|
||||
_bkg3_3[v22].x = (_vertices[_edges[v23].x].x + _vertices[_edges[v23].y].x) / 2;
|
||||
_bkg3_3[v22].y = (_vertices[_edges[v23].x].y + _vertices[_edges[v23].y].y) / 2;
|
||||
|
||||
if (v22 > 1 && !sub_424160(&_bkg3_3[v22 - 2], &_bkg3_3[v22])) {
|
||||
v20--;
|
||||
|
||||
_bkg3_3[v22 - 1] = _bkg3_3[v22];
|
||||
|
||||
v22--;
|
||||
field_134--;
|
||||
}
|
||||
|
||||
v21 = a2 + 4;
|
||||
v20++;
|
||||
v22++;
|
||||
a2 += 4;
|
||||
} while (v20 < field_134);
|
||||
}
|
||||
|
||||
_bkg3_3[v20] = end;
|
||||
if (v20 > 1 && !sub_424160(_bkg3_3 + v20 - 2, _bkg3_3 + v20)) {
|
||||
_bkg3_3[v20 - 1] = _bkg3_3[v20];
|
||||
field_134--;
|
||||
}
|
||||
field_134++;
|
||||
} else {
|
||||
_bkg3_3[0] = start;
|
||||
_bkg3_3[1] = end;
|
||||
field_134 = 2;
|
||||
}
|
||||
|
||||
sub_422EA0(_bkg3_3[0], _bkg3_3[1]);
|
||||
field_14C = 1;
|
||||
}
|
||||
|
||||
void Walk::clearBackground() { // CHECKED
|
||||
delete[] _vertices;
|
||||
_vertices = nullptr;
|
||||
_verticesCount = 0;
|
||||
|
||||
|
||||
if (_bkg3_1) {
|
||||
if (_bkg3_2) {
|
||||
for (int i = 0; i < _bkg3Count; ++i) {
|
||||
delete[] _bkg3_2[i];
|
||||
_bkg3_2[i] = nullptr;
|
||||
}
|
||||
delete[] _bkg3_2;
|
||||
_bkg3_2 = nullptr;
|
||||
}
|
||||
delete[] _bkg3_1;
|
||||
_bkg3_1 = nullptr;
|
||||
_bkg3Count = 0;
|
||||
}
|
||||
|
||||
delete[] _edges;
|
||||
_edges = nullptr;
|
||||
_edgesCount = 0;
|
||||
|
||||
delete[] _bkg3_3;
|
||||
_bkg3_3 = nullptr;
|
||||
|
||||
delete[] _bkg3_4;
|
||||
_bkg3_4 = nullptr;
|
||||
|
||||
delete[] _bkg3_5;
|
||||
_bkg3_5 = nullptr;
|
||||
}
|
||||
|
||||
void Walk::setBackground(Common::String name) { // CHECKED
|
||||
clearBackground();
|
||||
|
||||
name.toLowercase();
|
||||
name.replace(name.size() - 3, 3, "cvx");
|
||||
|
||||
Common::SeekableReadStream *stream = g_vm->openFile(name, false);
|
||||
if (!stream)
|
||||
return;
|
||||
|
||||
_verticesCount = stream->readUint32LE();
|
||||
_vertices = new Point[_verticesCount];
|
||||
|
||||
for (int i = 0; i < _verticesCount; ++i) {
|
||||
_vertices[i].x = stream->readUint32LE();
|
||||
_vertices[i].y = stream->readUint32LE();
|
||||
}
|
||||
|
||||
_edgesCount = stream->readUint32LE();
|
||||
_edges = new Point[_edgesCount];
|
||||
|
||||
for (int i = 0; i < _edgesCount; ++i) {
|
||||
_edges[i].x = stream->readUint32LE();
|
||||
_edges[i].y = stream->readUint32LE();
|
||||
}
|
||||
|
||||
_bkg3Count = stream->readUint32LE();
|
||||
_bkg3_1 = new int[_bkg3Count];
|
||||
|
||||
stream->read(_bkg3_1, 4 * _bkg3Count);
|
||||
|
||||
_bkg3_2 = new int*[_bkg3Count];
|
||||
|
||||
for (int i = 0; i < _bkg3Count; ++i) {
|
||||
_bkg3_2[i] = new int[_bkg3_1[i]];
|
||||
stream->read(_bkg3_2[i], 4 * _bkg3_1[i]);
|
||||
}
|
||||
|
||||
delete stream;
|
||||
_bkg3_3 = new Point[_bkg3Count + 1];
|
||||
_bkg3_4 = new int[_bkg3Count + 1];
|
||||
_bkg3_5 = new int[_bkg3Count + 1];
|
||||
|
||||
}
|
||||
|
||||
void Walk::reset() { // CHECKED
|
||||
field_140 = 0.0;
|
||||
field_138 = 0.0;
|
||||
currX = 0;
|
||||
currY = 0;
|
||||
resId = 0;
|
||||
field_14C = 0;
|
||||
field_190 = 0;
|
||||
field_194 = 0;
|
||||
}
|
||||
|
||||
Common::Point Walk::currPos() { // CHECKED
|
||||
return Common::Point(currX, currY);
|
||||
}
|
||||
|
||||
int Walk::getSpriteId() { // CHECKED
|
||||
return resId;
|
||||
}
|
||||
|
||||
int Walk::commonPoint(int idx1, int idx2) { // CHECKED
|
||||
if (_edges[idx1].x == _edges[idx2].x || _edges[idx1].x == _edges[idx2].y)
|
||||
return _edges[idx1].x;
|
||||
|
||||
if (_edges[idx1].y == _edges[idx2].x || _edges[idx1].y == _edges[idx2].y)
|
||||
return _edges[idx1].y;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Walk::readWayFile(const Common::String &name, int **p1, int **p2) { // CHECKED
|
||||
Common::SeekableReadStream *stream = g_vm->openFile(name, false);
|
||||
if (!stream) {
|
||||
p1 = nullptr;
|
||||
p2 = nullptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const uint items = (uint)stream->size() / 8;
|
||||
|
||||
*p1 = new int[items];
|
||||
*p2 = new int[items];
|
||||
|
||||
stream->skip(4);
|
||||
for (uint i = 0; i < items; ++i) {
|
||||
stream->read(&(*p1)[i], 4);
|
||||
stream->read(&(*p2)[i], 4);
|
||||
}
|
||||
|
||||
delete stream;
|
||||
return items;
|
||||
}
|
||||
|
||||
int Walk::sub_422EA0(Point p1, Point p2) {
|
||||
if (p1 == p2)
|
||||
return 0;
|
||||
|
||||
Point p = p1;
|
||||
p.x += 150;
|
||||
|
||||
double v5 = angle(p1, p, p2);
|
||||
double v6;
|
||||
if (v5 >= 0.0)
|
||||
v6 = k2PI - v5;
|
||||
else
|
||||
v6 = v5 + k2PI;
|
||||
|
||||
double v30 = 4.0;
|
||||
for (uint i = 0; i < ARRAYSIZE(kPiArray); ++i) {
|
||||
double v9 = v5 - kPiArray[i];
|
||||
if (v9 < 0.0)
|
||||
v9 = -v9;
|
||||
if (v9 < v30) {
|
||||
v30 = v9;
|
||||
resId = i;
|
||||
}
|
||||
|
||||
double v10 = v6 - kPiArray[i];
|
||||
if (v10 < 0.0)
|
||||
v10 = -v10;
|
||||
if (v10 < v30) {
|
||||
v30 = v10;
|
||||
resId = i;
|
||||
}
|
||||
}
|
||||
|
||||
double v28 = p2.x - p1.x;
|
||||
double v26 = p2.y - p1.y;
|
||||
double v12 = Math::hypotenuse(p2.x - p1.x, p2.y - p1.y);
|
||||
|
||||
double v39 = 1.0 / sqrt(field_D0[resId] * field_D0[resId] - -1.0);
|
||||
if (v39 == 0.0)
|
||||
field_140 = v28 / v12;
|
||||
else
|
||||
field_140 = (field_D0[resId] - -1.0 / (v26 / v28)) * (v26 / v12) * v39;
|
||||
|
||||
DBLPoint a1;
|
||||
DBLPoint a2;
|
||||
DBLPoint a3;
|
||||
|
||||
a1.x = p1.x;
|
||||
a1.y = p1.y;
|
||||
|
||||
a2.x = p2.x;
|
||||
a2.y = p2.y;
|
||||
|
||||
a3.x = p2.x;
|
||||
a3.y = field_D0[resId] * v28 + p1.y;
|
||||
|
||||
double v13 = angle(a1, a2, a3);
|
||||
field_140 = cos(v13);
|
||||
field_138 = sin(v13);
|
||||
|
||||
double v16 = v13;
|
||||
if (v13 < -kHalfPI)
|
||||
v16 = v13 + kPI;
|
||||
if (v13 > kHalfPI)
|
||||
v16 = v13 - kPI;
|
||||
|
||||
field_140 = cos(v16);
|
||||
field_138 = sin(v16);
|
||||
|
||||
int v32 = 1;
|
||||
double v34 = 0.0;
|
||||
double v35 = 0.0;
|
||||
double v36 = p1.y;
|
||||
v39 = v28 * v28 + v26 * v26 - -1.0;
|
||||
|
||||
int j = 0;
|
||||
for (int i = 0; i < 10;) {
|
||||
double k = g_vm->getQSystem()->getPetka()->calcPerspective(v36);
|
||||
|
||||
v34 += _offleg1[resId][v32] * k;
|
||||
v35 += _offleg2[resId][v32] * k;
|
||||
|
||||
j++;
|
||||
|
||||
v32 = (v32 + 1) % waysSizes[resId];
|
||||
|
||||
v36 = v35 * field_140 + v34 * field_138 + p1.y;
|
||||
|
||||
double v22 = v34 * field_140 - v35 * field_138 + p1.x - p2.x;
|
||||
double v38 = v36 - p2.y;
|
||||
|
||||
double v23 = v22 * v22 + v38 * v38;
|
||||
if (v23 >= v39) {
|
||||
i++;
|
||||
} else {
|
||||
v39 = v23;
|
||||
field_194 = j;
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
|
||||
field_170 = 0;
|
||||
field_178 = 0;
|
||||
|
||||
currX = p1.x;
|
||||
currY = p1.y;
|
||||
|
||||
field_150 = p1.x;
|
||||
field_158 = p1.y;
|
||||
|
||||
field_190 = 0;
|
||||
field_198 = g_vm->getQSystem()->getPetka()->calcPerspective(p1.y);
|
||||
return resId;
|
||||
}
|
||||
|
||||
int Walk::sub_423350() { // CHECKED
|
||||
field_190 = (field_190 + 1) % waysSizes[resId];
|
||||
|
||||
--field_194;
|
||||
if (field_194 < 0) {
|
||||
field_14C++;
|
||||
if (field_14C < field_134) {
|
||||
int t = field_190;
|
||||
int id = resId;
|
||||
if (id == sub_422EA0(_bkg3_3[field_14C - 1], _bkg3_3[field_14C])) {
|
||||
field_190 = t;
|
||||
return 1;
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
field_198 = g_vm->getQSystem()->getPetka()->calcPerspective(currY);
|
||||
field_170 = _offleg1[resId][field_190] * field_198 + field_170;
|
||||
field_178 = _offleg2[resId][field_190] * field_198 + field_178;
|
||||
|
||||
currX = field_140 * field_170 - field_178 * field_138 + field_150;
|
||||
currY = field_140 * field_178 + field_138 * field_170 + field_158;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
Common::Point Walk::sub_4234B0() { // CHECKED
|
||||
Common::Point p;
|
||||
field_198 = g_vm->getQSystem()->getPetka()->calcPerspective(currY);
|
||||
p.x = currX - _leg1[resId][field_190] * field_198;
|
||||
p.y = currY - _leg2[resId][field_190] * field_198;
|
||||
return p;
|
||||
}
|
||||
|
||||
bool Walk::areEdgesAdjacent(int first_index, int second_index) { // CHECKED
|
||||
if (first_index != second_index) {
|
||||
if (_edges[first_index].x == _edges[second_index].x ||
|
||||
_edges[first_index].x == _edges[second_index].y ||
|
||||
_edges[first_index].y == _edges[second_index].x ||
|
||||
_edges[first_index].y == _edges[second_index].y) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int Walk::sub_423600(Point p) {
|
||||
int j = 0;
|
||||
for (int i = 0; i < _bkg3Count; ++i, ++j) {
|
||||
int *v4 = new int[_bkg3_1[j]];
|
||||
v4[0] = _bkg3_2[j][0];
|
||||
|
||||
for (int k = 0; k < _bkg3_1[j]; ++k) {
|
||||
if (areEdgesAdjacent(v4[0], _bkg3_2[j][k])) {
|
||||
v4[1] = _bkg3_2[j][k];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int k = 2; k < _bkg3_1[j]; ++k) {
|
||||
for (int l = 0; l < _bkg3_1[j]; ++l) {
|
||||
if (areEdgesAdjacent(v4[k - 1], _bkg3_2[j][l]) && v4[k - 2] != _bkg3_2[j][l]) {
|
||||
v4[k] = _bkg3_2[j][l];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int v11 = commonPoint(v4[_bkg3_1[j] - 1], v4[0]);
|
||||
int v31 = commonPoint(v4[0], v4[1]);
|
||||
|
||||
double v12 = angle(p, _vertices[v11], _vertices[v31]);
|
||||
if (p == _vertices[v11] || p == _vertices[v31]) {
|
||||
delete[] v4;
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
int k;
|
||||
for (k = 1; k < _bkg3_1[j] - 1; ++k) {
|
||||
int v16 = commonPoint(v4[k - 1], v4[k]);
|
||||
int v32 = commonPoint(v4[k], v4[k + 1]);
|
||||
|
||||
v12 += angle(p, _vertices[v16], _vertices[v32]);
|
||||
if (p == _vertices[v16] || p == _vertices[v32]) {
|
||||
delete[] v4;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
int v19 = commonPoint(v4[k - 1], v4[k]);
|
||||
int v20 = commonPoint(v4[k], v4[0]);
|
||||
|
||||
delete[] v4;
|
||||
|
||||
double v23 = angle(p, _vertices[v19], _vertices[v20]);
|
||||
v12 += v23;
|
||||
|
||||
if (p == _vertices[v19] || p == _vertices[v20])
|
||||
return i;
|
||||
|
||||
if (v12 < 0.0)
|
||||
v12 = -v12;
|
||||
|
||||
if (v12 > kPI)
|
||||
return i;
|
||||
}
|
||||
debug("Walk bug: Point doesn't belong to any convex");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Walk::sub_423970(int a1, int a2) { // CHECKED
|
||||
int index = 0;
|
||||
if (a2 >= 0) {
|
||||
int v5 = sub_423A30(a1, a2);
|
||||
for (int i = 0; i < _bkg3_1[a1]; ++i) {
|
||||
if (_bkg3_2[a1][i] == v5) {
|
||||
index = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = index; i < _bkg3_1[a1]; ++i) {
|
||||
for (int j = 0; j < _bkg3Count; ++j) {
|
||||
if (j == a1)
|
||||
continue;
|
||||
|
||||
for (int k = 0; k < _bkg3_1[j]; ++k) {
|
||||
if (_bkg3_2[j][k] == _bkg3_2[a1][i]) {
|
||||
return j;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int Walk::sub_423A30(int idx1, int idx2) { // CHECKED
|
||||
for (int i = 0; i < _bkg3_1[idx1]; ++i) {
|
||||
for (int j = 0; j < _bkg3_1[idx2]; ++j) {
|
||||
if (_bkg3_2[idx1][i] == _bkg3_2[idx2][j])
|
||||
return _bkg3_2[idx1][i];
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
double Walk::angle(Point p1, Point p2, Point p3) { // CHECKED
|
||||
return angle(DBLPoint(p1), DBLPoint(p2), DBLPoint(p3));
|
||||
}
|
||||
|
||||
double Walk::angle(DBLPoint p1, DBLPoint p2, DBLPoint p3) { // CHECKED
|
||||
if (p1 == p2 || p1 == p3)
|
||||
return 0.0;
|
||||
|
||||
double xv1 = p2.x - p1.x;
|
||||
double xv2 = p3.x - p1.x;
|
||||
|
||||
double yv1 = p2.y - p1.y;
|
||||
double yv2 = p3.y - p1.y;
|
||||
|
||||
double mv1 = Math::hypotenuse(xv1, yv1);
|
||||
double mv2 = Math::hypotenuse(xv2, yv2);
|
||||
double v13 = (xv1 * xv2 + yv1 * yv2) / (mv1 * mv2);
|
||||
if ((xv2 / mv2 * (yv1 / mv1) - yv2 / mv2 * (xv1 / mv1)) < 0.0) // Not sure
|
||||
return -acos(v13);
|
||||
return acos(v13);
|
||||
}
|
||||
|
||||
int Walk::sub_423E00(Point p1, Point p2, Point p3, Point p4, Point &p5) {
|
||||
if (p1.x > p2.x) {
|
||||
SWAP(p2, p1);
|
||||
}
|
||||
|
||||
if (p3.x > p4.x) {
|
||||
SWAP(p3, p4);
|
||||
}
|
||||
|
||||
double v11 = (p2.y - p1.y) * (p4.x - p3.x);
|
||||
double v12 = (p2.x - p1.x) * (p4.y - p3.y);
|
||||
if (v11 == v12)
|
||||
return 0;
|
||||
|
||||
double v30;
|
||||
if (p2.x - p1.x) {
|
||||
if (p4.x - p3.x) {
|
||||
v30 = ((double)(p3.y - p1.y) * (p4.x - p3.x) * (p2.x - p1.x) + (v11 * p1.x) - (v12 * p3.x)) / (v11 - v12);
|
||||
if (v30 < p1.x || p3.x > v30 || p2.x < v30 || p4.x < v30)
|
||||
return 0;
|
||||
} else {
|
||||
v30 = p3.x;
|
||||
if (p3.x < p1.x || p2.x < p3.x)
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
v30 = p1.x;
|
||||
if (p1.x < p3.x)
|
||||
return 0;
|
||||
if (p4.x < p1.x)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (p1.y > p2.y) {
|
||||
SWAP(p1, p2);
|
||||
}
|
||||
|
||||
if (p3.y > p4.y) {
|
||||
SWAP(p3, p4);
|
||||
}
|
||||
|
||||
if (p2.y - p1.y) {
|
||||
if (p4.y - p3.y) {
|
||||
double v21;
|
||||
if (p2.x - p1.x) {
|
||||
v21 = (v30 - p1.x) * (p2.y - p1.y) / (p2.x - p1.x) + p1.y;
|
||||
} else {
|
||||
v21 = (v30 - p3.x) * (p4.y - p3.y) / (p4.x - p3.x) + p3.y;
|
||||
}
|
||||
|
||||
if (v21 >= p1.y && v21 >= p3.y && v21 <= p2.y && v21 <= p4.y) {
|
||||
p5.x = v30;
|
||||
p5.y = v21;
|
||||
return 1;
|
||||
}
|
||||
|
||||
} else {
|
||||
if (p3.y >= p1.y) {
|
||||
if (p3.y > p2.y)
|
||||
return 0;
|
||||
p5.x = v30;
|
||||
p5.y = p3.y;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (p1.y >= p3.y) {
|
||||
if (p1.y <= p4.y) {
|
||||
p5.x = v30;
|
||||
p5.y = p1.y;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool Walk::sub_424160(Point *p1, Point *p2) { // CHECKED
|
||||
if (*p1 == *p2)
|
||||
return false;
|
||||
|
||||
Point p;
|
||||
int v = 1;
|
||||
if (_verticesCount <= 1)
|
||||
return sub_423E00(_vertices[v - 1], _vertices[0], *p1, *p2, p) != 0;
|
||||
|
||||
while (!sub_423E00(_vertices[v - 1], _vertices[v], *p1, *p2, p)) {
|
||||
if (++v >= _verticesCount)
|
||||
return sub_423E00(_vertices[v - 1], _vertices[0], *p1, *p2, p) != 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Walk::sub_424230(Point *p1) { // CHECKED
|
||||
Point p(0, 0);
|
||||
int v = sub_424320(p1, &p);
|
||||
|
||||
p.y = p1->y;
|
||||
v = (sub_424320(p1, &p) & 1) + (v & 1);
|
||||
|
||||
p.y = 480;
|
||||
v = (sub_424320(p1, &p) & 1) + v;
|
||||
v = (sub_424320(p1, &p) & 1) + v;
|
||||
|
||||
p.x = 640;
|
||||
v = (sub_424320(p1, &p) & 1) + v;
|
||||
|
||||
p.y = p1->y;
|
||||
v = (sub_424320(p1, &p) & 1) + v;
|
||||
|
||||
p.y = 0;
|
||||
v = (sub_424320(p1, &p) & 1) + v;
|
||||
|
||||
p.x = p1->x;
|
||||
return (sub_424320(p1, &p) & 1) + v < 4;
|
||||
}
|
||||
|
||||
int Walk::sub_424320(Point *p1, Point *p2) { // CHECKED
|
||||
if (*p1 == *p2)
|
||||
return 0;
|
||||
|
||||
int ret = 0;
|
||||
|
||||
Point p;
|
||||
|
||||
int i;
|
||||
for (i = 1; i < _verticesCount; ++i) {
|
||||
if (sub_423E00(_vertices[i - 1], _vertices[i], *p1, *p2, p) && *p1 != p && *p2 != p) {
|
||||
ret++;
|
||||
}
|
||||
}
|
||||
|
||||
if (sub_423E00(_vertices[i - 1], _vertices[0], *p1, *p2, p) && *p1 != p && *p2 != p) {
|
||||
ret++;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int Walk::moveInside(Point *p) { // CHECKED
|
||||
DBLPoint dp = sub_424610(_vertices[_edges->x], _vertices[_edges->y].x, _vertices[_edges->y].y, *p);
|
||||
|
||||
int index = 0;
|
||||
|
||||
double min = (dp.y - p->y) * (dp.y - p->y) + (dp.x - p->x) * (dp.x - p->x);
|
||||
for (int i = 1; i < _verticesCount; ++i) {
|
||||
DBLPoint dp1 = sub_424610(_vertices[_edges[i].x], _vertices[_edges[i].y].x, _vertices[_edges[i].y].y, *p);
|
||||
double curr = (dp1.y - p->y) * (dp1.y - p->y) + (dp1.x - p->x) * (dp1.x - p->x);
|
||||
if (curr < min) {
|
||||
dp = dp1;
|
||||
min = curr;
|
||||
index = i;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
p->x = dp.x;
|
||||
p->y = dp.y;
|
||||
|
||||
|
||||
for (int i = 0; i < _verticesCount; ++i) {
|
||||
for (int j = 0; j < _bkg3_1[i]; ++j) {
|
||||
if (_bkg3_2[i][j] == index)
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DBLPoint Walk::sub_424610(Point p1, int x, int y, Point p2) { // CHECKED
|
||||
DBLPoint p;
|
||||
double v13;
|
||||
double v14;
|
||||
if (p1.x == x) {
|
||||
v13 = p1.x;
|
||||
v14 = p2.y;
|
||||
} else {
|
||||
double v6 = (double)(y - p1.y) / (x - p1.x);
|
||||
double v7 = p1.y - v6 * p1.x;
|
||||
v13 = ((p2.y - v7) * v6 + p2.x) / (v6 * v6 - -1.0);
|
||||
v14 = v13 * v6 + v7;
|
||||
}
|
||||
p.x = CLIP<double>(v13, MIN<int>(x, p1.x), MAX<int>(x, p1.x));
|
||||
p.y = CLIP<double>(v14, MIN<int>(y, p1.y), MAX<int>(y, p1.y));
|
||||
return p;
|
||||
}
|
||||
|
||||
} // End of namespace Petka
|
||||
155
engines/petka/walk.h
Normal file
155
engines/petka/walk.h
Normal file
@@ -0,0 +1,155 @@
|
||||
/* 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_WALK_H
|
||||
#define PETKA_WALK_H
|
||||
|
||||
#include "common/rect.h"
|
||||
|
||||
namespace Petka {
|
||||
|
||||
struct Point {
|
||||
int x;
|
||||
int y;
|
||||
|
||||
Point() : x(0), y(0) {}
|
||||
Point(int x_, int y_) : x(x_), y(y_) {}
|
||||
Point(Common::Point p) : x(p.x), y(p.y) {}
|
||||
bool operator==(const Point &p) { return x == p.x && y == p.y; }
|
||||
bool operator!=(const Point &p) { return !(*this == p); }
|
||||
};
|
||||
|
||||
struct DBLPoint {
|
||||
double x;
|
||||
double y;
|
||||
|
||||
DBLPoint() : x(0.0), y(0.0) {}
|
||||
DBLPoint(double x1, double y1) : x(x1), y(y1) {}
|
||||
DBLPoint(Point p) : x(p.x), y(p.y) {}
|
||||
|
||||
bool operator==(const DBLPoint &p) { return x == p.x && y == p.y; }
|
||||
};
|
||||
|
||||
class Walk {
|
||||
public:
|
||||
Walk(int id);
|
||||
~Walk();
|
||||
|
||||
void init(Point start, Point end);
|
||||
|
||||
void clearBackground();
|
||||
void setBackground(Common::String name);
|
||||
|
||||
void reset();
|
||||
|
||||
Common::Point currPos();
|
||||
|
||||
int getSpriteId();
|
||||
|
||||
int commonPoint(int, int);
|
||||
|
||||
int readWayFile(const Common::String &name, int **, int **);
|
||||
|
||||
int sub_422EA0(Point p1, Point p2);
|
||||
int sub_423350();
|
||||
Common::Point sub_4234B0();
|
||||
bool areEdgesAdjacent(int, int);
|
||||
|
||||
int sub_423600(Point p);
|
||||
int sub_423970(int, int);
|
||||
int sub_423A30(int, int);
|
||||
|
||||
static double angle(Point p1, Point p2, Point p3);
|
||||
static double angle(DBLPoint p1, DBLPoint p2, DBLPoint p3);
|
||||
|
||||
static int sub_423E00(Point p1, Point p2, Point p3, Point p4, Point &p5);
|
||||
|
||||
bool sub_424160(Point *p1, Point *p2);
|
||||
bool sub_424230(Point *p1);
|
||||
int sub_424320(Point *p1, Point *p2);
|
||||
|
||||
int moveInside(Point *p);
|
||||
DBLPoint sub_424610(Point p1, int x, int y, Point p4);
|
||||
|
||||
public:
|
||||
char field_4;
|
||||
char field_5;
|
||||
char field_6;
|
||||
char field_7;
|
||||
int *_leg1[8];
|
||||
int *_leg2[8];
|
||||
int *_off1[8];
|
||||
int *_off2[8];
|
||||
int *_offleg1[8];
|
||||
int *_offleg2[8];
|
||||
int *_bkg3_4;
|
||||
int *_bkg3_5;
|
||||
double field_D0[8];
|
||||
int waysSizes[8];
|
||||
Point *_bkg3_3;
|
||||
int field_134;
|
||||
double field_138;
|
||||
double field_140;
|
||||
int resId;
|
||||
int field_14C;
|
||||
double field_150;
|
||||
double field_158;
|
||||
double currX;
|
||||
double currY;
|
||||
double field_170;
|
||||
double field_178;
|
||||
char field_180;
|
||||
char field_181;
|
||||
char field_182;
|
||||
char field_183;
|
||||
char field_184;
|
||||
char field_185;
|
||||
char field_186;
|
||||
char field_187;
|
||||
char field_188;
|
||||
char field_189;
|
||||
char field_18A;
|
||||
char field_18B;
|
||||
char field_18C;
|
||||
char field_18D;
|
||||
char field_18E;
|
||||
char field_18F;
|
||||
int field_190;
|
||||
int field_194;
|
||||
double field_198;
|
||||
int destX;
|
||||
int destY;
|
||||
int _verticesCount;
|
||||
Point *_vertices;
|
||||
int _edgesCount;
|
||||
int _bkg3Count;
|
||||
int *_bkg3_1;
|
||||
int **_bkg3_2;
|
||||
Point *_edges;
|
||||
char field_1C4;
|
||||
char field_1C5;
|
||||
char field_1C6;
|
||||
char field_1C7;
|
||||
};
|
||||
|
||||
} // End of namespace Petka
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user