Initial commit

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

2
engines/petka/POTFILES Normal file
View File

@@ -0,0 +1,2 @@
engines/petka/metaengine.cpp
engines/petka/saveload.cpp

132
engines/petka/base.h Normal file
View 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

View 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

View 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

View 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
View File

@@ -0,0 +1,4 @@
begin_section("Petka");
add_person("Andrei Prykhodko", "whiterandrek", "");
add_person("Eugene Sandulenko", "sev", "");
end_section();

View 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);

View File

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

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

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

View File

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

View File

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

View 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

View 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

View 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

View 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

View 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

View 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

View File

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

View 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

View 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

View File

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

View File

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

View 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

View 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

View 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

View 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
View 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

View File

@@ -0,0 +1,428 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/system.h"
#include "petka/petka.h"
#include "petka/q_manager.h"
#include "petka/q_system.h"
#include "petka/flc.h"
#include "petka/video.h"
#include "petka/sound.h"
#include "petka/objects/heroes.h"
#include "petka/interfaces/panel.h"
namespace Petka {
QObjectPetka::QObjectPetka()
: _walk(nullptr) {
_field7C = 1;
_reaction = nullptr;
_heroReaction = nullptr;
_sender = nullptr;
_isWalking = false;
_x = 574;
_y = 444;
_z = 200;
// _surfId = -5;
_surfH = 0;
_surfW = 0;
_k = 1.0;
_x_ = _y_ = 0;
_destX = _destY = 0;
_imageId = 0;
_fieldB4 = 0;
}
void QObjectPetka::processMessage(const QMessage &arg) {
QMessage msg = arg;
if (msg.opcode == kImage) {
msg.opcode = kSet;
_imageId = msg.arg1;
_walk.reset(new Walk(_imageId + 10));
QObjectBG *room = g_vm->getQSystem()->_room;
if (room)
_walk->setBackground(g_vm->resMgr()->findResourceName(room->_resourceId));
}
if (msg.opcode == kSaid || msg.opcode == kStand) {
msg.opcode = kSet;
msg.arg1 = _imageId;
msg.arg2 = 1;
}
if (msg.opcode == kSay) {
msg.opcode = kSet;
msg.arg1 = _imageId + 1;
msg.arg2 = 1;
}
if (msg.opcode == kSet || msg.opcode == kPlay) {
_field7C = msg.arg1 == _imageId || msg.opcode == kPlay;
}
if (msg.opcode != kWalk) {
if (msg.opcode == kWalked && _heroReaction) {
QReaction *reaction = _heroReaction;
_heroReaction = nullptr;
_sender->processReaction(reaction);
}
QMessageObject::processMessage(msg);
if (msg.opcode == kSet || msg.opcode == kPlay) {
initSurface();
if (!g_vm->getQSystem()->_totalInit) {
setPos(Common::Point(_x_, _y_), false);
}
}
}
}
void QObjectPetka::initSurface() {
QManager *resMgr = g_vm->resMgr();
FlicDecoder *flc = resMgr->getFlic(_resourceId);
_surfW = flc->getWidth() * _k;
_surfH = flc->getHeight() * _k;
}
void QObjectPetka::walk(int x, int y) {
Common::Point walkPos(x, y);
if (!_isShown) {
setPos(walkPos, false);
return;
}
Common::Point currPos;
if (_isWalking) {
currPos = _walk->currPos();
} else {
currPos.x = _x_;
currPos.y = _y_;
}
if (currPos.sqrDist(walkPos) >= 25) {
_walk->init(currPos, walkPos);
_destX = x;
_destY = y;
_resourceId = _imageId + _walk->getSpriteId() + 10;
_isWalking = true;
_animate = true;
initSurface();
FlicDecoder *flc = g_vm->resMgr()->getFlic(_resourceId);
flc->setFrame(1);
sub_408940();
g_vm->videoSystem()->makeAllDirty();
_field7C = 0;
_time = 0;
_holdMessages = true;
}
}
void QObjectPetka::draw() {
if (!_isShown || _resourceId == -1) {
return;
}
if (_animate && _startSound) {
if (_sound) {
_sound->play(_loopedSound);
if (_loopedSound) {
_sound = nullptr;
}
}
_startSound = false;
}
FlicDecoder *flc = g_vm->resMgr()->getFlic(_resourceId);
if (!flc) {
return;
}
Graphics::Surface *conv = flc->getCurrentFrame()->convertTo(g_system->getScreenFormat(), flc->getPalette());
Common::Rect srcRect(0, 0, conv->w, conv->h);
Common::Rect dstRect(0, 0, _surfW, _surfH);
dstRect.translate(_x - g_vm->getQSystem()->_xOffset, _y);
g_vm->videoSystem()->transBlitFrom(*conv, srcRect, dstRect, flc->getTransColor(conv->format));
conv->free();
delete conv;
}
void QObjectPetka::setPos(Common::Point p, bool) {
QSystem *sys = g_vm->getQSystem();
int xOff = sys->_xOffset;
Common::Rect dirty(_x - xOff, _y, _surfW + _x - xOff, _surfH + _y);
g_vm->videoSystem()->addDirtyRect(dirty);
p.y = MIN<int16>(p.y, 480);
FlicDecoder *flc = g_vm->resMgr()->getFlic(_resourceId);
_k = calcPerspective(p.y);
_surfH = flc->getHeight() * _k;
_surfW = flc->getWidth() * _k;
_x_ = p.x;
_y_ = p.y;
_x = p.x - _surfW / 2;
_y = p.y - _surfH;
recalcOffset();
g_vm->videoSystem()->addDirtyRect(Common::Rect(_x - xOff, _y, _surfW + _x - xOff, _surfH + _y));
}
double QObjectPetka::calcPerspective(int y) {
QSystem *qsys = g_vm->getQSystem();
y = MIN(y, 480);
const Perspective &pers = qsys->_room->_persp;
double res = (y - pers.y0) * pers.k / (pers.y1 - pers.y0);
if (res < 0.0)
res = 0.0;
if (res + pers.f0 > pers.f1)
return pers.f1;
return res + pers.f0;
}
void QObjectPetka::updateWalk() {
if (!_isWalking)
return;
int v = _walk->sub_423350();
switch (v) {
case 0: {
_isWalking = false;
setPos(Common::Point(_walk->destX, _walk->destY), false);
QMessage msg(_id, kSet, (uint16) _imageId, 1, 0, nullptr, 0);
if (_heroReaction) {
uint i;
for (i = 0; i < _heroReaction->messages.size(); ++i) {
if (_heroReaction->messages[i].opcode == kGoTo || _heroReaction->messages[i].opcode == kSetSeq) {
_resourceId = _imageId + _walk->getSpriteId() + 10;
FlicDecoder *flc = g_vm->resMgr()->getFlic(_resourceId);
flc->setFrame(1);
initSurface();
processMessage(QMessage(_id, kAnimate, 0, 0, 0, nullptr, 0));
_heroReaction->messages.push_back(msg);
_heroReaction->messages.push_back(QMessage(_id, kAnimate, 1, 0, 0, nullptr, 0));
break;
}
}
if (i == _heroReaction->messages.size())
processMessage(msg);
} else {
processMessage(msg);
}
_holdMessages = false;
g_vm->videoSystem()->makeAllDirty();
break;
}
case 1:
sub_408940();
break;
case 2: {
_resourceId = _walk->getSpriteId() + _imageId + 10;
FlicDecoder *flc = g_vm->resMgr()->getFlic(_resourceId);
flc->setFrame(1);
_time = flc->getDelay();
initSurface();
g_vm->videoSystem()->makeAllDirty();
break;
}
default:
break;
}
}
void QObjectPetka::setReactionAfterWalk(uint index, QReaction *reaction, QMessageObject *sender, bool deleteReaction) {
_heroReaction = nullptr;
stopWalk();
QMessage msg(_id, kWalked, 0, 0, 0, sender, 0);
g_vm->getQSystem()->addMessage(msg);
_heroReaction = new QReaction();
_sender = sender;
for (uint i = index + 1; i < reaction->messages.size(); ++i) {
_heroReaction->messages.push_back(reaction->messages[i]);
}
if (deleteReaction) {
delete reaction;
}
}
void QObjectPetka::stopWalk() {
_isWalking = false;
_holdMessages = false;
Common::List<QMessage> &list = g_vm->getQSystem()->_messages;
for (Common::List<QMessage>::iterator it = list.begin(); it != list.end(); ++it) {
if (it->opcode == kWalked && it->objId == _id) {
it->objId = (uint16)-1;
}
}
delete _heroReaction;
_heroReaction = nullptr;
if (!_field7C) {
Common::Point p = _walk->sub_4234B0();
_x = p.x;
_y = p.y;
QMessage msg(_id, kSet, (uint16)_imageId, 1, 0, nullptr, 0);
processMessage(msg);
}
}
void QObjectPetka::update(int time) {
if (!_animate || !_isShown)
return;
if (_isWalking)
_time += time * (g_vm->getQSystem()->_panelInterface->getHeroSpeed() + 50) / 50;
else
_time += time;
FlicDecoder *flc = g_vm->resMgr()->getFlic(_resourceId);
if (flc && flc->getFrameCount() != 1) {
if (_sound) {
Common::Rect bounds = flc->getBounds();
_sound->setBalance(bounds.left + bounds.width() / 2 - g_vm->getQSystem()->_xOffset, 640);
}
while (_time >= (int)flc->getDelay()) {
if (_sound && flc->getCurFrame() == 0) {
_startSound = true;
}
flc->setFrame(-1);
if (flc->getCurFrame() == (int32)flc->getFrameCount() - 1) {
g_vm->getQSystem()->addMessage(_id, kEnd, _resourceId, 0, 0, 0, nullptr);
}
if (flc->getCurFrame() + 1 == (int32)flc->getFrameCount() / 2) {
g_vm->getQSystem()->addMessage(_id, kHalf, _resourceId, 0, 0, 0, nullptr);
}
if (_field7C && flc->getCurFrame() == 0)
_time = -10000;
updateWalk();
flc = g_vm->resMgr()->getFlic(_resourceId);
_surfH = flc->getHeight() * _k;
_surfW = flc->getWidth() * _k;
_time -= flc->getDelay();
g_vm->videoSystem()->addDirtyRect(Common::Rect(_x, _y, _surfW + _x, _surfH + _y));
}
}
}
bool QObjectPetka::isInPoint(Common::Point p) {
if (!_isActive)
return false;
FlicDecoder *flc = g_vm->resMgr()->getFlic(_resourceId);
const Graphics::Surface *flcSurface = flc->getCurrentFrame();
Common::Rect bounds(_surfW, _surfH);
Graphics::ManagedSurface s(_surfW, _surfH, flcSurface->format);
s.transBlitFrom(*flcSurface, Common::Rect(0, 0, flcSurface->w, flcSurface->h), bounds);
p.x -= _x;
p.y -= _y;
if (!bounds.contains(p.x, p.y))
return false;
return *(uint16 *)s.getBasePtr(p.x, p.y) != 0;
}
void QObjectPetka::updateZ() {
if (_animate && _isShown && _updateZ) {
FlicDecoder *flc = g_vm->resMgr()->getFlic(_resourceId);
if (_isWalking) {
_z = _walk->currPos().y;
} else {
_z = _y + flc->getHeight() * _k;
}
}
}
void QObjectPetka::sub_408940() {
FlicDecoder *flc = g_vm->resMgr()->getFlic(_resourceId);
QSystem *sys = g_vm->getQSystem();
int xOff = sys->_xOffset;
Common::Rect dirty(_x - xOff, _y, _surfW + _x - xOff, _surfH + _y);
g_vm->videoSystem()->addDirtyRect(dirty);
Common::Point currPos = _walk->currPos();
_k = calcPerspective(currPos.y);
_surfW = flc->getWidth() * _k;
_surfH = flc->getHeight() * _k;
Common::Point p = _walk->sub_4234B0();
_x = p.x;
_y = p.y;
_x_ = currPos.x;
_y_ = currPos.y;
recalcOffset();
g_vm->videoSystem()->addDirtyRect(Common::Rect(_x - xOff, _y, _surfW + _x - xOff, _surfH + _y));
}
void QObjectPetka::recalcOffset() {
QSystem *sys = g_vm->getQSystem();
int xOff = sys->_xOffset;
if (_x_ < xOff + 160 || _x_ > xOff + 480) {
sys->_reqOffset = _x_ - 320;
}
sys->_reqOffset = CLIP<int>(sys->_reqOffset, 0, sys->_sceneWidth - 640);
}
QObjectChapayev::QObjectChapayev() {
_x = 477;
_y = 350;
// _surfId = -6;
}
}

View File

@@ -0,0 +1,84 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef PETKA_HEROES_H
#define PETKA_HEROES_H
#include "common/ptr.h"
#include "petka/objects/object.h"
#include "petka/walk.h"
namespace Petka {
class QObjectPetka : public QObject {
public:
QObjectPetka();
void processMessage(const QMessage &msg) override;
void initSurface();
void walk(int x, int y);
void stopWalk();
void updateWalk();
void setReactionAfterWalk(uint index, QReaction *reaction, QMessageObject *sender, bool deleteReaction);
void draw() override;
bool isInPoint(Common::Point p) override;
void update(int time) override;
void setPos(Common::Point p, bool ) override;
double calcPerspective(int y);
void updateZ() override;
void sub_408940();
private:
virtual void recalcOffset();
public:
int _field7C;
int _surfW;
int _surfH;
int _x_;
int _y_;
// int _surfId;
int _imageId;
double _k;
Common::ScopedPtr<Walk> _walk;
int _destX;
int _destY;
bool _isWalking;
QReaction *_heroReaction;
QMessageObject *_sender;
int _fieldB4;
};
class QObjectChapayev : public QObjectPetka {
public:
QObjectChapayev();
void recalcOffset() override {}
};
} // End of namespace Petka
#endif

View File

@@ -0,0 +1,603 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/formats/ini-file.h"
#include "common/stream.h"
#include "common/system.h"
#include "common/events.h"
#include "graphics/surface.h"
#include "petka/big_dialogue.h"
#include "petka/flc.h"
#include "petka/sound.h"
#include "petka/petka.h"
#include "petka/video.h"
#include "petka/q_system.h"
#include "petka/q_manager.h"
#include "petka/objects/object_star.h"
#include "petka/objects/object_cursor.h"
#include "petka/interfaces/main.h"
#include "petka/objects/heroes.h"
#include "petka/objects/object_case.h"
namespace Petka {
QReaction *createReaction(QMessage *messages, QMessage *end) {
QReaction *reaction = new QReaction();
while (messages != end) {
reaction->messages.push_back(*messages++);
}
return reaction;
}
QVisibleObject::QVisibleObject()
: _resourceId(-1), _z(240) {}
QMessageObject::QMessageObject() {
_id = (uint16)-1;
_status = 0;
_time = 0;
_dialogColor = -1;
_animate = true;
_isShown = true;
_isActive = true;
_updateZ = false;
_holdMessages = false;
_loopedSound = false;
_startSound = false;
_reaction = nullptr;
_x = _y = _walkX = _walkY = 0;
_frame = 0;
_sound = nullptr;
_reactionId = 0;
}
void QMessageObject::processMessage(const QMessage &msg) {
bool reacted = false;
int opcode = (msg.opcode == kObjectUse) ? (msg.sender->_id << 16) | kObjectUse : msg.opcode;
for (uint i = 0; i < _reactions.size(); ++i) {
QReaction *r = &_reactions[i];
if (r->opcode != msg.opcode ||
(r->status != -1 && r->status != _status) ||
(r->senderId != -1 && r->senderId != msg.sender->_id)) {
continue;
}
bool fallback;
if (g_vm->getBigDialogue()->findHandler(_id, opcode, &fallback) && !fallback) {
g_vm->getBigDialogue()->setHandler(_id, opcode);
g_vm->getQSystem()->_mainInterface->_dialog.setSender(this);
}
processReaction(r, &msg);
reacted = true;
}
if (reacted || !g_vm->getBigDialogue()->findHandler(_id, opcode, nullptr)) {
switch (msg.opcode) {
case kAddInv:
g_vm->getQSystem()->getCase()->addItem(msg.objId);
// original bug fix
g_vm->pushMouseMoveEvent();
break;
case kDelInv:
g_vm->getQSystem()->getCase()->removeItem(msg.objId);
break;
case kSetInv:
g_vm->getQSystem()->getCase()->transformItem(msg.sender->_id, msg.objId);
break;
case kAvi: {
Common::String videoName = g_vm->resMgr()->findResourceName((uint16) msg.arg1);
g_vm->playVideo(g_vm->openFile(videoName, false));
break;
}
case kContinue:
g_vm->getQSystem()->_mainInterface->_dialog.endUserMsg();
break;
case kCursor:
g_vm->getQSystem()->getCursor()->setInvItem(this, msg.arg1);
g_vm->videoSystem()->makeAllDirty();
break;
case kDialog:
g_vm->getQSystem()->_mainInterface->_dialog.start(msg.arg1, this);
break;
case kSetPos:
setPos(Common::Point(msg.arg1, msg.arg2), false);
break;
case kSet:
case kPlay:
play(msg.arg1, msg.arg2);
break;
case kAnimate:
_animate = msg.arg1;
break;
case kEnd:
if (_reaction && _reactionId == msg.arg1) {
QReaction *reaction = _reaction;
_reaction = nullptr;
processReaction(reaction);
}
break;
case kStatus:
_status = (int8) msg.arg1;
break;
case kOn:
_isActive = true;
show(true);
break;
case kOff:
_isActive = false;
show(false);
break;
case kStop:
g_vm->getQSystem()->getCursor()->show(msg.arg1);
g_vm->getQSystem()->getStar()->_isActive = msg.arg1;
break;
case kShow:
show(msg.arg1);
break;
case kShake:
g_vm->videoSystem()->setShake(msg.arg1);
break;
case kSystem:
switch (msg.arg1){
case 0:
g_vm->getQSystem()->getStar()->_isActive = false;
break;
case 1:
g_vm->getQSystem()->getStar()->_isActive = true;
break;
case 242:
Engine::quitGame();
break;
default:
break;
}
break;
case kHide:
show(false);
break;
case kZBuffer:
_updateZ = msg.arg1;
_z = (msg.arg2 != -1) ? msg.arg2 : _z;
break;
case kActive:
_isActive = msg.arg1;
break;
case kPassive:
_isActive = false;
break;
case kJump: {
Common::Point p;
p.x = (msg.arg1 == 0xffff ? _walkX : msg.arg1);
p.y = (msg.arg2 == -1 ? _walkY : msg.arg2);
g_vm->getQSystem()->getPetka()->setPos(p, false);
break;
}
case kJumpVich: {
Common::Point p;
p.x = (msg.arg1 == 0xffff ? _walkX : msg.arg1);
p.y = (msg.arg2 == -1 ? _walkY : msg.arg2);
g_vm->getQSystem()->getChapay()->setPos(p, false);
break;
}
case kWalk:
if (!reacted) {
if (_walkX == -1) {
g_vm->getQSystem()->getPetka()->walk(msg.arg1, msg.arg2);
} else {
g_vm->getQSystem()->getPetka()->walk(_walkX, _walkY);
}
}
break;
case kWalkTo: {
int destX = msg.arg1;
int destY = msg.arg2;
if (destX == -1 || destY == -1) {
destX = _walkX;
destY = _walkY;
}
if (destX != -1) {
g_vm->getQSystem()->getPetka()->walk(destX, destY);
QReaction *r = g_vm->getQSystem()->getPetka()->_heroReaction;
if (r) {
for (uint i = 0; i < r->messages.size(); ++i) {
if (r->messages[i].opcode == kGoTo) {
g_vm->getQSystem()->getChapay()->walk(destX, destY);
break;
}
}
}
}
break;
}
case kWalkVich: {
int destX = msg.arg1;
int destY = msg.arg2;
if (destX == -1 || destY == -1) {
destX = _walkX;
destY = _walkY;
}
if (destX != -1)
g_vm->getQSystem()->getChapay()->walk(destX, destY);
break;
}
case kDescription: {
Common::ScopedPtr<Common::SeekableReadStream> invStream(g_vm->openFile("invntr.txt", true));
if (invStream) {
Common::String desc;
Common::INIFile invIni;
invIni.allowNonEnglishCharacters();
invIni.loadFromStream(*invStream);
invIni.getKey(_name, "ALL", desc);
g_vm->getQSystem()->_mainInterface->setTextDescription(Common::convertToU32String(desc.c_str(), Common::kWindows1251), msg.arg1);
}
break;
}
case kPart:
g_vm->loadPartAtNextFrame(msg.arg1);
break;
case kChapter:
g_vm->loadChapter(msg.arg1);
break;
case kToMap:
g_vm->getQSystem()->toggleMapInterface();
break;
default:
break;
}
} else {
for (uint i = 0; i < _reactions.size(); ++i) {
QReaction &r = _reactions[i];
if (r.opcode != msg.opcode ||
(r.status != -1 && r.status != _status) ||
(r.senderId != -1 && r.senderId != msg.sender->_id)) {
continue;
}
g_vm->getQSystem()->_mainInterface->_dialog.setReaction(createReaction(r.messages.data(), r.messages.end()));
}
g_vm->getBigDialogue()->setHandler(_id, opcode);
g_vm->getQSystem()->_mainInterface->_dialog.start(msg.arg1, this);
}
}
void QMessageObject::show(bool v) {
_isShown = v;
}
void QMessageObject::setReaction(int16 id, QReaction *reaction) {
delete _reaction;
_reaction = reaction;
_reactionId = id;
}
void QMessageObject::processReaction(QReaction *r, const QMessage *msg) {
bool deleteReaction = (msg == nullptr);
for (uint j = 0; j < r->messages.size(); ++j) {
QMessage &rMsg = r->messages[j];
if (rMsg.opcode == kCheck && g_vm->getQSystem()->findObject(rMsg.objId)->_status != rMsg.arg1) {
break;
}
if (msg && rMsg.opcode == kIf &&
((rMsg.arg1 != 0xffff && rMsg.arg1 != msg->arg1) ||
(rMsg.arg2 != -1 && rMsg.arg2 != msg->arg2) ||
(rMsg.arg3 != -1 && rMsg.arg3 != msg->arg3))) {
break;
}
if (msg && rMsg.opcode == kRandom && rMsg.arg2 != -1) {
rMsg.arg1 = (int16) g_vm->getRnd().getRandomNumber((uint) (rMsg.arg2 - 1));
}
g_vm->getQSystem()->addMessage(rMsg.objId, rMsg.opcode, rMsg.arg1, rMsg.arg2, rMsg.arg3, rMsg.unk, this);
bool processed = true;
switch (rMsg.opcode) {
case kDialog: {
g_vm->getQSystem()->_mainInterface->_dialog.setReaction(createReaction(r->messages.data() + j + 1, r->messages.end()));
break;
}
case kPlay: {
QMessageObject *obj = g_vm->getQSystem()->findObject(rMsg.objId);
obj->setReaction(rMsg.arg1, createReaction(r->messages.data() + j + 1, r->messages.end()));
break;
}
case kWalk:
case kWalkTo:
g_vm->getQSystem()->getPetka()->setReactionAfterWalk(j, r, this, deleteReaction);
return;
case kWalkVich:
g_vm->getQSystem()->getChapay()->setReactionAfterWalk(j, r, this, deleteReaction);
return;
default:
processed = false;
break;
}
if (processed)
break;
}
if (deleteReaction)
delete r;
}
void QMessageObject::play(int id, int type) {
if (g_vm->getQSystem()->_totalInit) {
_resourceId = id;
_loopedSound = (type == 5);
return;
}
if (_loopedSound || g_vm->isDemo()) {
removeSound();
}
FlicDecoder *flc = g_vm->resMgr()->getFlic(_resourceId);
if (flc) {
g_vm->videoSystem()->addDirtyRect(Common::Point(_x, _y), *flc);
}
_resourceId = id;
loadSound();
flc = g_vm->resMgr()->getFlic(id);
flc->setFrame(1);
_time = 0;
_loopedSound = (type == 5);
}
void QMessageObject::loadSound() {
Common::String name = g_vm->resMgr()->findSoundName(_resourceId);
_sound = g_vm->soundMgr()->addSound(name, Audio::Mixer::kSFXSoundType);
_startSound = false;
}
void QMessageObject::removeSound() {
Common::String name = g_vm->resMgr()->findSoundName(_resourceId);
g_vm->soundMgr()->removeSound(name);
_sound = nullptr;
}
static Common::String readString(Common::ReadStream &readStream) {
uint32 stringSize = readStream.readUint32LE();
byte *data = (byte *)malloc(stringSize + 1);
readStream.read(data, stringSize);
data[stringSize] = '\0';
Common::String str((char *)data);
free(data);
return str;
}
void QMessageObject::readScriptData(Common::SeekableReadStream &stream) {
_id = stream.readUint16LE();
_name = readString(stream);
_reactions.resize(stream.readUint32LE());
for (uint i = 0; i < _reactions.size(); ++i) {
QReaction *reaction = &_reactions[i];
reaction->opcode = stream.readUint16LE();
reaction->status = stream.readByte();
reaction->senderId = stream.readUint16LE();
reaction->messages.resize(stream.readUint32LE());
for (uint j = 0; j < reaction->messages.size(); ++j) {
QMessage *msg = &reaction->messages[j];
msg->objId = stream.readUint16LE();
msg->opcode = stream.readUint16LE();
msg->arg1 = stream.readUint16LE();
msg->arg2 = stream.readUint16LE();
msg->arg3 = stream.readUint16LE();
}
}
}
void QMessageObject::readInisData(Common::INIFile &names, Common::INIFile &cast, Common::INIFile *bgs) {
names.getKey(_name, "all", _nameOnScreen);
Common::String rgbString;
if (cast.getKey(_name, "all", rgbString)) {
int r, g, b;
sscanf(rgbString.c_str(), "%d %d %d", &r, &g, &b);
_dialogColor = g_vm->_system->getScreenFormat().RGBToColor((byte)r, (byte)g, (byte)b);
}
}
QObject::QObject() {
_animate = true;
_updateZ = true;
_frame = 1;
_sound = nullptr;
_x = 0;
_y = 0;
_walkX = -1;
_walkY = -1;
}
bool QObject::isInPoint(Common::Point p) {
if (!_isActive)
return false;
FlicDecoder *flc = g_vm->resMgr()->getFlic(_resourceId);
if (!flc || !flc->getBounds().contains(p.x - _x, p.y - _y))
return false;
const Graphics::Surface *s = flc->getCurrentFrame();
auto format = g_system->getScreenFormat();
byte index = *(const byte *)s->getBasePtr(p.x - _x, p.y - _y);
const byte *pal = flc->getPalette();
return format.RGBToColor(pal[0], pal[1], pal[2]) != format.RGBToColor(pal[index * 3], pal[index * 3 + 1], pal[index * 3 + 2]);
}
void QObject::draw() {
if (!_isShown || _resourceId == -1) {
return;
}
FlicDecoder *flc = g_vm->resMgr()->getFlic(_resourceId);
if (!flc) {
return;
}
if (_animate && _startSound) {
if (_sound) {
_sound->play(_loopedSound);
if (_loopedSound) {
_sound = nullptr;
}
}
_startSound = false;
}
int xOff = g_vm->getQSystem()->_xOffset;
VideoSystem *videoSys = g_vm->videoSystem();
Common::Rect screen(640 + xOff, 480);
Common::Rect flcBounds(flc->getBounds());
Common::Rect objBounds(flcBounds);
objBounds.translate(_x, _y);
Common::Rect intersect(screen.findIntersectingRect(objBounds));
if (intersect.isEmpty())
return;
Graphics::Surface *surface = flc->getCurrentFrame()->getSubArea(flcBounds).convertTo(g_system->getScreenFormat(), flc->getPalette());
for (Common::Rect dirty : videoSys->rects()) {
dirty.translate(xOff, 0);
Common::Rect destRect(intersect.findIntersectingRect(dirty));
if (destRect.isEmpty())
continue;
Common::Rect srcRect(destRect);
srcRect.translate(-_x, -_y);
srcRect.translate(-flcBounds.left, -flcBounds.top);
destRect.translate(-xOff, 0);
videoSys->transBlitFrom(*surface, srcRect, destRect, flc->getTransColor(surface->format));
}
surface->free();
delete surface;
}
void QObject::updateZ() {
if (!_animate || !_isShown || !_updateZ)
return;
FlicDecoder *flc = g_vm->resMgr()->getFlic(_resourceId);
if (flc) {
_z = 1;
const Common::Array<Common::Rect> &rects = flc->getMskRects();
for (uint i = 0; i < rects.size(); ++i) {
if (_y + rects[i].bottom > _z)
_z = _y + rects[i].bottom;
}
}
}
void QObject::show(bool v) {
FlicDecoder *flc = g_vm->resMgr()->getFlic(_resourceId);
if (flc) {
g_vm->videoSystem()->addDirtyRect(Common::Point(_x, _y), *flc);
}
QMessageObject::show(v);
}
void QObject::update(int time) {
if (!_animate || !_isShown)
return;
_time += time;
FlicDecoder *flc = g_vm->resMgr()->getFlic(_resourceId);
if (flc && flc->getFrameCount() != 1) {
if (_sound) {
Common::Rect bounds = flc->getBounds();
_sound->setBalance(bounds.left + bounds.width() / 2 - g_vm->getQSystem()->_xOffset, 640);
}
while (_time >= (int32)flc->getDelay()) {
if (_sound && flc->getCurFrame() == 0) {
_startSound = true;
}
g_vm->videoSystem()->addDirtyRect(Common::Point(_x, _y), *flc);
flc->setFrame(-1);
if (flc->getCurFrame() == (int32)flc->getFrameCount() - 1) {
g_vm->getQSystem()->addMessage(_id, kEnd, _resourceId, 0, 0, 0, nullptr);
}
if (flc->getCurFrame() + 1 == (int32)flc->getFrameCount() / 2) {
g_vm->getQSystem()->addMessage(_id, kHalf, _resourceId, 0, 0, 0, nullptr);
}
g_vm->videoSystem()->addDirtyRect(Common::Point(_x, _y), *flc);
_time -= flc->getDelay();
}
}
}
void QObject::setPos(Common::Point p, bool) {
FlicDecoder *flc = g_vm->resMgr()->getFlic(_resourceId);
if (flc) {
g_vm->videoSystem()->addDirtyMskRects(Common::Point(_x, _y), *flc);
g_vm->videoSystem()->addDirtyMskRects(p, *flc);
_x = p.x;
_y = p.y;
}
}
void QObject::onClick(Common::Point p) {
QSystem *sys = g_vm->getQSystem();
QObjectCursor *cursor = g_vm->getQSystem()->getCursor();
sys->getPetka()->stopWalk();
sys->getChapay()->stopWalk();
switch (cursor->_actionType) {
case kActionLook:
g_vm->getQSystem()->addMessage(_id, kLook, 0, 0, 0, 0, this);
break;
case kActionWalk:
g_vm->getQSystem()->addMessage(_id, kWalk, p.x, p.y, 0, 0, this);
break;
case kActionUse:
g_vm->getQSystem()->addMessage(_id, kUse, 0, 0, 0, 0, this);
break;
case kActionTake:
g_vm->getQSystem()->addMessage(_id, kTake, 0, 0, 0, 0, this);
break;
case kActionTalk:
g_vm->getQSystem()->addMessage(_id, kTalk, 0, 0, 0, 0, this);
break;
case kActionObjUseChapayev:
g_vm->getQSystem()->addMessage(_id, kObjectUse, p.x, p.y, 0, 0, g_vm->getQSystem()->getChapay());
break;
case kActionObjUse:
g_vm->getQSystem()->addMessage(_id, kObjectUse, 0, 0, 0, 0, cursor->_invObj);
break;
default:
break;
}
}
void QObject::onMouseMove(Common::Point p) {
g_vm->getQSystem()->_mainInterface->_objUnderCursor = this;
}
}

View File

@@ -0,0 +1,117 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef PETKA_OBJECT_H
#define PETKA_OBJECT_H
#include "common/rect.h"
#include "common/str.h"
#include "petka/base.h"
namespace Common {
class INIFile;
class SeekableReadStream;
}
namespace Petka {
class QVisibleObject {
public:
QVisibleObject();
virtual ~QVisibleObject() {};
virtual void draw() {};
virtual void update(int time) {};
virtual void updateZ() {};
virtual void show(bool v) {};
virtual void setPos(Common::Point p, bool center) {};
virtual bool isInPoint(Common::Point p) { return false; }
virtual void onMouseMove(Common::Point p) {}
virtual void onClick(Common::Point p) {}
public:
int32 _resourceId;
int32 _z;
};
class Sound;
class QMessageObject : public QVisibleObject {
public:
QMessageObject();
void show(bool v) override;
void setReaction(int16 id, QReaction *reaction);
virtual void processMessage(const QMessage &msg);
void processReaction(QReaction *reaction, const QMessage *msg = nullptr);
virtual void play(int id, int type);
void loadSound();
void removeSound();
void readScriptData(Common::SeekableReadStream &stream);
virtual void readInisData(Common::INIFile &names, Common::INIFile &cast, Common::INIFile *bgs);
public:
int32 _x;
int32 _y;
int32 _walkX;
int32 _walkY;
int32 _time;
byte _frame;
bool _isShown;
bool _animate;
bool _updateZ;
bool _holdMessages;
bool _isActive;
bool _startSound;
bool _loopedSound;
Sound *_sound;
int8 _status;
uint16 _id;
Common::String _name;
Common::String _nameOnScreen;
int32 _dialogColor;
Common::Array<QReaction> _reactions;
QReaction *_reaction;
int16 _reactionId;
};
class QObject : public QMessageObject {
public:
QObject();
void draw() override;
void update(int time) override;
void updateZ() override;
bool isInPoint(Common::Point p) override;
void setPos(Common::Point p, bool center) override;
void show(bool v) override;
void onClick(Common::Point p) override;
void onMouseMove(Common::Point p) override;
};
} // End of namespace Petka
#endif

View File

@@ -0,0 +1,175 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/formats/ini-file.h"
#include "common/stream.h"
#include "common/system.h"
#include "common/events.h"
#include "graphics/surface.h"
#include "petka/flc.h"
#include "petka/walk.h"
#include "petka/petka.h"
#include "petka/video.h"
#include "petka/q_system.h"
#include "petka/q_manager.h"
#include "petka/interfaces/main.h"
#include "petka/interfaces/sequence.h"
#include "petka/objects/object_bg.h"
#include "petka/objects/heroes.h"
namespace Petka {
QObjectBG::QObjectBG() {
_x = 0;
_y = 0;
_z = 0;
_showMap = true;
_fxId = 0;
_musicId = 0;
}
void QObjectBG::processMessage(const QMessage &msg) {
QMessageObject::processMessage(msg);
switch (msg.opcode) {
case kSet: {
_resourceId = msg.arg1;
QSystem *sys = g_vm->getQSystem();
if (g_vm->isPetka2() && !sys->_totalInit && sys->_mainInterface->_roomId == _id) {
auto petka = sys->getPetka();
auto chapay = sys->getChapay();
auto bkgName = g_vm->resMgr()->findResourceName(_resourceId);
petka->_walk->setBackground(bkgName);
chapay->_walk->setBackground(bkgName);
petka->setPos(Common::Point(petka->_x_, petka->_y_), false);
chapay->setPos(Common::Point(chapay->_x_, chapay->_y_), false);
}
break;
}
case kMusic:
_musicId = msg.arg1;
break;
case kBGsFX:
_fxId = msg.arg1;
break;
case kMap:
_showMap = (msg.arg1 != 0);
break;
case kNoMap:
_showMap = false;
break;
case kGoTo:
goTo();
break;
case kSetSeq:
g_vm->getQSystem()->_sequenceInterface->start(_id);
break;
case kEndSeq:
g_vm->getQSystem()->_sequenceInterface->stop();
break;
default:
break;
}
}
void QObjectBG::draw() {
Graphics::Surface *s = g_vm->resMgr()->getSurface(_resourceId);
if (!s)
return;
int xOffset = g_vm->getQSystem()->_xOffset;
for (auto rect : g_vm->videoSystem()->rects()) {
rect.translate(xOffset, 0);
g_vm->videoSystem()->blitFrom(*s, rect, Common::Point(rect.left - xOffset, rect.top));
}
}
void QObjectBG::goTo() {
QSystem *sys = g_vm->getQSystem();
sys->getPetka()->stopWalk();
sys->getChapay()->stopWalk();
int oldRoomId = sys->_mainInterface->_roomId;
sys->_mainInterface->loadRoom(_id, false);
QMessageObject *oldRoom = sys->findObject(oldRoomId);
Common::ScopedPtr<Common::SeekableReadStream> bgsStream(g_vm->openFile("BGs.ini", true));
Common::INIFile bgsIni;
bgsIni.allowNonEnglishCharacters();
bgsIni.loadFromStream(*bgsStream);
Common::String entranceName;
if (bgsIni.getKey(oldRoom->_name, _name, entranceName)) {
setEntrance(entranceName);
return;
}
for (auto o : sys->_allObjects) {
QObjectBG *bg = dynamic_cast<QObjectBG *>(o);
if (bg && bgsIni.getKey(bg->_name, _name, entranceName)) {
setEntrance(entranceName);
}
}
}
void QObjectBG::setEntrance(const Common::String &name) {
QSystem *sys = g_vm->getQSystem();
QMessageObject *entrance = sys->findObject(name);
if (entrance) {
sys->getPetka()->_z = 0;
sys->getChapay()->_z = 0;
sys->getPetka()->setPos(Common::Point(entrance->_walkX, entrance->_walkY), false);
sys->getChapay()->setPos(Common::Point(entrance->_walkX, entrance->_walkY - 2), false);
sys->_xOffset = CLIP<int32>(entrance->_walkX - 320, 0, sys->_sceneWidth - 640);
sys->_reqOffset = sys->_xOffset;
}
g_vm->videoSystem()->makeAllDirty();
}
void QObjectBG::readInisData(Common::INIFile &names, Common::INIFile &cast, Common::INIFile *bgs) {
if (bgs) {
Common::String perspective;
bgs->getKey(_name, "Settings", perspective);
if (!perspective.empty()) {
sscanf(perspective.c_str(), "%lf %lf %d %d %lf", &_persp.f0, &_persp.k, &_persp.y0, &_persp.y1, &_persp.f1);
} else {
_persp.f0 = 1.0;
_persp.f1 = 1.0;
_persp.k = 0.0;
_persp.y0 = 0;
_persp.y1 = 480;
}
}
QMessageObject::readInisData(names, cast, bgs);
}
} // End of namespace Petka

View File

@@ -0,0 +1,60 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef PETKA_OBJECT_BG_H
#define PETKA_OBJECT_BG_H
#include "petka/objects/object.h"
namespace Petka {
// Linear Interpolation
struct Perspective {
double f0;
double k; // seems to be (f1 - f0) always in files
int y0;
int y1;
double f1;
Perspective() { f0 = f1 = 1.0; k = 0.0; y0 = 0; y1 = 480; }
};
class QObjectBG : public QMessageObject {
public:
QObjectBG();
void processMessage(const QMessage &msg) override;
void draw() override;
void goTo();
void setEntrance(const Common::String &name);
void play(int id, int type) override {}
void readInisData(Common::INIFile &names, Common::INIFile &cast, Common::INIFile *bgs) override;
public:
bool _showMap;
int _fxId;
int _musicId;
Perspective _persp;
};
} // End of namespace Petka
#endif

View File

@@ -0,0 +1,292 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/system.h"
#include "petka/interfaces/main.h"
#include "petka/objects/object_case.h"
#include "petka/objects/object_cursor.h"
#include "petka/flc.h"
#include "petka/petka.h"
#include "petka/q_manager.h"
#include "petka/video.h"
#include "petka/objects/heroes.h"
#include "petka/q_system.h"
namespace Petka {
// Полоска в чемодане
const char *const kPoloska = "\xCF\xEE\xEB\xEE\xF1\xEA\xE0\x20\xE2\x20\xF7\xE5\xEC\xEE\xE4\xE0\xED\xE5";
const uint kCaseZ = 980;
const uint kItemZ = kCaseZ + 1;
const uint kPoloskaZ = kCaseZ + 2;
const uint kButtonsCount = 6;
const uint kItemsOnPage = 6;
const uint kObjectCaseId = 4099;
const uint kCaseResourceId = 6000;
const uint kFirstButtonResourceId = 6001;
const uint kExitCaseResourceId = 6009;
enum {
kChapayevButton = 0,
kPanelButton,
kMapButton,
kCloseButton,
kNextPageButton,
kPrevPageButton,
kInvalidButton
};
QObjectCase::QObjectCase() {
_id = kObjectCaseId;
_resourceId = kCaseResourceId;
_z = kCaseZ;
_itemIndex = 0;
_clickedObjIndex = kInvalidButton;
_isShown = false;
_updateZ = false;
_itemsLocation[0] = Common::Point(120, 145);
_itemsLocation[1] = Common::Point(240, 145);
_itemsLocation[2] = Common::Point(360, 145);
_itemsLocation[3] = Common::Point(100, 220);
_itemsLocation[4] = Common::Point(240, 220);
_itemsLocation[5] = Common::Point(380, 220);
}
void QObjectCase::update(int time) {
if (!_isShown || _clickedObjIndex == kInvalidButton)
return;
_time += time;
FlicDecoder *flc = g_vm->resMgr()->getFlic(kFirstButtonResourceId + _clickedObjIndex);
if (flc) {
while (_time >= (int32)flc->getDelay()) {
flc->setFrame(-1);
_time -= flc->getDelay();
g_vm->videoSystem()->addDirtyMskRects(*flc);
}
}
}
void QObjectCase::draw() {
if (!_isShown)
return;
QObject::draw();
if (_clickedObjIndex != kInvalidButton) {
FlicDecoder *flc = g_vm->resMgr()->getFlic(kFirstButtonResourceId + _clickedObjIndex);
Graphics::Surface *s = flc->getCurrentFrame()->convertTo(g_system->getScreenFormat(), flc->getPalette());
QSystem *sys = g_vm->getQSystem();
const Common::List<Common::Rect> &dirty = g_vm->videoSystem()->rects();
const Common::Array<Common::Rect> &mskRects = flc->getMskRects();
for (Common::List<Common::Rect>::const_iterator it = dirty.begin(); it != dirty.end(); ++it) {
for (uint i = 0; i < mskRects.size(); ++i) {
Common::Rect destRect = mskRects[i].findIntersectingRect(*it);
Common::Rect srcRect = destRect;
srcRect.translate(-_x + sys->_xOffset, -_y);
g_vm->videoSystem()->transBlitFrom(*s, srcRect, destRect, flc->getTransColor(s->format));
}
}
s->free();
delete s;
}
}
void QObjectCase::show(bool v) {
QSystem *sys = g_vm->getQSystem();
_x = sys->_xOffset;
QObject::show(v);
if (v) {
addItemObjects();
QMessageObject *obj = sys->findObject(kPoloska);
obj->_z = kPoloskaZ;
obj->_x = sys->_xOffset;
sys->_mainInterface->_objs.push_back(obj);
} else {
removeObjects(true);
sys->_currInterface->_startIndex = 0;
}
}
bool QObjectCase::isInPoint(Common::Point p) {
return _isShown;
}
void QObjectCase::onMouseMove(Common::Point p) {
p.x -= _x;
FlicDecoder *flc = g_vm->resMgr()->getFlic(kExitCaseResourceId);
if (*(const byte *)flc->getCurrentFrame()->getBasePtr(p.x, p.y) != 0) {
if (_clickedObjIndex != kCloseButton && _clickedObjIndex != kInvalidButton) {
flc = g_vm->resMgr()->getFlic(kFirstButtonResourceId + _clickedObjIndex);
flc->setFrame(1);
g_vm->videoSystem()->addDirtyMskRects(*flc);
}
_clickedObjIndex = kCloseButton;
} else {
uint i;
for (i = 0; i < kButtonsCount; ++i) {
flc = g_vm->resMgr()->getFlic(kFirstButtonResourceId + i);
if (flc->getMskRects()[0].contains(p)) {
break;
}
}
if (_clickedObjIndex != i && _clickedObjIndex != kInvalidButton) {
flc = g_vm->resMgr()->getFlic(kFirstButtonResourceId + _clickedObjIndex);
flc->setFrame(1);
g_vm->videoSystem()->addDirtyMskRects(*flc);
}
if (i == kButtonsCount && _clickedObjIndex != kInvalidButton) {
_clickedObjIndex = kInvalidButton;
} else if (i != _clickedObjIndex) {
if ((i != kChapayevButton || g_vm->getQSystem()->getChapay()->_isShown) && (i != kMapButton || g_vm->getQSystem()->_room->_showMap)) {
flc = g_vm->resMgr()->getFlic(kFirstButtonResourceId + i);
g_vm->videoSystem()->addDirtyMskRects(*flc);
_clickedObjIndex = i;
} else {
_clickedObjIndex = kInvalidButton;
}
}
}
}
void QObjectCase::onClick(Common::Point p) {
switch (_clickedObjIndex) {
case kChapayevButton:
g_vm->getQSystem()->setCursorAction(kActionObjUseChapayev);
break;
case kPanelButton:
g_vm->getQSystem()->togglePanelInterface();
break;
case kMapButton:
g_vm->getQSystem()->toggleMapInterface();
break;
case kCloseButton:
show(false);
break;
case kNextPageButton:
nextPage();
break;
case kPrevPageButton:
prevPage();
break;
default:
break;
}
}
void QObjectCase::addItemObjects() {
QSystem *sys = g_vm->getQSystem();
Common::Array<QVisibleObject *> &objs = sys->_mainInterface->_objs;
removeObjects(false);
for (uint i = 0; i < objs.size(); ++i) {
if (objs[i]->_resourceId == kCaseResourceId) {
sys->_currInterface->_startIndex = i;
}
}
const uint size = (_itemIndex + kItemsOnPage >= _items.size()) ? _items.size() : (_itemIndex + kItemsOnPage);
for (uint i = _itemIndex; i < size; ++i) {
QMessageObject *obj = sys->findObject(_items[i]);
obj->_x = sys->_xOffset + _itemsLocation[i - _itemIndex].x;
obj->_y = _itemsLocation[i - _itemIndex].y;
obj->_z = kItemZ;
g_vm->resMgr()->getFlic(obj->_resourceId);
objs.push_back(obj);
}
}
void QObjectCase::addItem(uint16 id) {
_items.push_back(id);
reshow();
}
void QObjectCase::removeItem(uint16 id) {
for (uint i = 0; i < _items.size(); ++i) {
if (_items[i] == id) {
_items.remove_at(i);
}
}
_itemIndex = (_items.size() < kItemsOnPage) ? 0 : (_items.size() - kItemsOnPage);
reshow();
}
void QObjectCase::transformItem(uint16 oldItem, uint16 newItem) {
for (uint i = 0; i < _items.size(); ++i) {
if (_items[i] == oldItem) {
_items[i] = newItem;
}
}
reshow();
}
void QObjectCase::nextPage() {
if (_items.size() > _itemIndex + kItemsOnPage) {
_itemIndex += kItemsOnPage;
addItemObjects();
g_vm->videoSystem()->makeAllDirty();
}
}
void QObjectCase::prevPage() {
if (_itemIndex > 0) {
_itemIndex = (_itemIndex <= kItemsOnPage) ? 0 : _itemIndex - kItemsOnPage;
addItemObjects();
g_vm->videoSystem()->makeAllDirty();
}
}
void QObjectCase::removeObjects(bool removePoloska) {
Common::Array<QVisibleObject *> &objs = g_vm->getQSystem()->_mainInterface->_objs;
for (uint i = 0; i < objs.size();) {
if (objs[i]->_z == kItemZ || (removePoloska && objs[i]->_z == kPoloskaZ)) {
objs.remove_at(i);
} else {
++i;
}
}
}
void QObjectCase::reshow() {
if (_isShown) {
show(false);
show(true);
}
}
} // End of namespace Petka

View File

@@ -0,0 +1,61 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef PETKA_OBJECT_CASE_H
#define PETKA_OBJECT_CASE_H
#include "petka/objects/object.h"
namespace Petka {
class QObjectCase : public QObject {
public:
QObjectCase();
void draw() override;
void update(int time) override;
void show(bool v) override;
bool isInPoint(Common::Point p) override;
void onMouseMove(Common::Point p) override;
void onClick(Common::Point p) override;
void addItem(uint16 id);
void removeItem(uint16 id);
void transformItem(uint16 oldItem, uint16 newItem);
private:
void reshow();
void nextPage();
void prevPage();
void addItemObjects();
void removeObjects(bool removePoloska);
public:
Common::Array<uint16> _items;
Common::Point _itemsLocation[6];
uint _clickedObjIndex;
uint _itemIndex;
};
} // End of namespace Petka
#endif

View File

@@ -0,0 +1,145 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/formats/ini-file.h"
#include "common/stream.h"
#include "common/system.h"
#include "common/events.h"
#include "graphics/surface.h"
#include "petka/flc.h"
#include "petka/petka.h"
#include "petka/video.h"
#include "petka/q_system.h"
#include "petka/q_manager.h"
#include "petka/objects/object_cursor.h"
namespace Petka {
const int kCursorLook = 5002;
QObjectCursor::QObjectCursor() {
_id = 4097;
_z = 1000;
_resourceId = kCursorLook;
Common::Point pos = g_vm->getEventManager()->getMousePos();
_x = pos.x;
_y = pos.y;
g_vm->resMgr()->getFlic(kCursorLook);
_actionType = kActionLook;
_invObj = nullptr;
_name = "Cursor";
}
void QObjectCursor::draw() {
if (!_isShown) {
return;
}
FlicDecoder *flc = g_vm->resMgr()->getFlic(_resourceId);
const Graphics::Surface *frame = flc->getCurrentFrame();
if (frame) {
Graphics::Surface *s = frame->convertTo(g_system->getScreenFormat(), flc->getPalette());
Common::Rect destRect(flc->getBounds());
destRect.translate(_x, _y);
destRect.clip(640, 480);
Common::Rect srcRect(destRect);
srcRect.translate(-_x, -_y);
g_vm->videoSystem()->transBlitFrom(*s, srcRect, destRect, flc->getTransColor(s->format));
s->free();
delete s;
}
}
void QObjectCursor::update(int time) {
if (!_isShown || !_animate)
return;
FlicDecoder *flc = g_vm->resMgr()->getFlic(_resourceId);
_time += time;
while (flc && _time >= (int32)flc->getDelay()) {
flc->setFrame(-1);
g_vm->videoSystem()->addDirtyRect(Common::Point(_x, _y), flc->getBounds());
_time -= flc->getDelay();
}
}
void QObjectCursor::setPos(Common::Point p, bool center) {
FlicDecoder *flc = g_vm->resMgr()->getFlic(_resourceId);
if (!_animate) {
flc->setFrame(1);
}
p.x = p.x - g_vm->getQSystem()->_xOffset;
g_vm->videoSystem()->addDirtyRect(Common::Point(_x, _y), flc->getBounds());
if (center) {
Common::Rect bounds = flc->getBounds();
p.x = p.x - bounds.left - bounds.width() / 2;
p.y = p.y - bounds.top - bounds.height() / 2;
}
_x = p.x;
_y = p.y;
g_vm->videoSystem()->addDirtyRect(Common::Point(_x, _y), flc->getBounds());
}
void QObjectCursor::show(bool v) {
FlicDecoder *flc = g_vm->resMgr()->getFlic(_resourceId);
g_vm->videoSystem()->addDirtyRect(Common::Point(_x, _y), flc->getBounds());
QMessageObject::show(v);
}
void QObjectCursor::returnInvItem() {
if (_actionType == kActionObjUse) {
_invObj->show(true);
_invObj->_isActive = true;
}
}
void QObjectCursor::setInvItem(QMessageObject *item, uint16 resourceId) {
returnInvItem();
if (resourceId != 0xffff) {
_resourceId = resourceId;
_actionType = kActionObjUse;
_invObj = item;
item->_isShown = false;
item->_isActive = false;
} else {
_resourceId = kCursorLook;
_actionType = kActionLook;
_invObj = nullptr;
}
}
void QObjectCursor::setAction(int actionType) {
show(false);
returnInvItem();
_resourceId = kCursorLook + actionType;
_actionType = actionType;
_invObj = nullptr;
show(true);
}
}

View File

@@ -0,0 +1,59 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef PETKA_OBJECT_CURSOR_H
#define PETKA_OBJECT_CURSOR_H
#include "petka/objects/object.h"
namespace Petka {
enum ActionType {
kActionLook,
kActionWalk,
kActionUse,
kActionTake,
kActionTalk,
kActionObjUseChapayev,
kActionObjUse
};
class QObjectCursor : public QMessageObject {
public:
QObjectCursor();
void setPos(Common::Point p, bool center) override;
void update(int time) override;
void draw() override;
void show(bool v) override;
bool isInPoint(Common::Point p) override { return false; }
void setAction(int actionType);
void setInvItem(QMessageObject *item, uint16 resourceId);
void returnInvItem();
public:
int _actionType;
QMessageObject *_invObj;
};
} // End of namespace Petka
#endif

View File

@@ -0,0 +1,100 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/rect.h"
#include "petka/objects/object_case.h"
#include "petka/objects/object_cursor.h"
#include "petka/objects/object_star.h"
#include "petka/q_system.h"
#include "petka/flc.h"
#include "petka/q_manager.h"
#include "petka/video.h"
#include "petka/petka.h"
namespace Petka {
//const uint kFirstCursorId = 5001;
const uint kCaseButtonIndex = 0;
QObjectStar::QObjectStar() {
_isShown = false;
_id = 4098;
_resourceId = 5000;
_z = 999;
_updateZ = false;
_isActive = true;
_buttonRects[0] = Common::Rect(70, 74, 112, 112);
_buttonRects[1] = Common::Rect(68, 0, 114, 41);
_buttonRects[2] = Common::Rect(151, 51, 180, 97);
_buttonRects[3] = Common::Rect(138, 125, 179, 166);
_buttonRects[4] = Common::Rect(55, 145, 96, 175);
_buttonRects[5] = Common::Rect(11, 79, 40, 118);
}
bool QObjectStar::isInPoint(Common::Point p) {
return _isShown;
}
void QObjectStar::onMouseMove(Common::Point p) {
uint frame = (findButtonIndex(p.x - _x, p.y - _y) + 1) % 7 + 1;
FlicDecoder *flc = g_vm->resMgr()->getFlic(_resourceId);
if (flc && flc->getCurFrame() + 1 != (int32)frame) {
g_vm->videoSystem()->addDirtyRect(Common::Point(_x, _y), *flc);
flc->setFrame(frame);
}
}
void QObjectStar::onClick(Common::Point p) {
uint button = findButtonIndex(p.x - _x, p.y - _y);
if (button == kCaseButtonIndex) {
g_vm->getQSystem()->getCase()->show(true);
} else if (button < ARRAYSIZE(_buttonRects)) {
QObjectCursor *cursor = g_vm->getQSystem()->getCursor();
cursor->setAction(button - 1);
}
show(false);
}
uint QObjectStar::findButtonIndex(int16 x, int16 y) const {
uint i = 0;
for (i = 0; i < ARRAYSIZE(_buttonRects); ++i) {
if (_buttonRects[i].contains(x, y))
return i;
}
return i;
}
void QObjectStar::setPos(Common::Point p, bool) {
if (!_isShown) {
QSystem *sys = g_vm->getQSystem();
FlicDecoder *flc = g_vm->resMgr()->getFlic(_resourceId);
p.x = MAX<int16>(p.x - sys->_xOffset - flc->getWidth() / 2, 0);
p.y = MAX<int16>(p.y - flc->getHeight() / 2, 0);
_x = MIN<int16>(p.x, 639 - flc->getWidth()) + sys->_xOffset;
_y = MIN<int16>(p.y, 479 - flc->getHeight());
}
}
}

View File

@@ -0,0 +1,47 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef PETKA_OBJECT_STAR_H
#define PETKA_OBJECT_STAR_H
#include "petka/objects/object.h"
namespace Petka {
class QObjectStar : public QObject {
public:
QObjectStar();
void update(int time) override {}
bool isInPoint(Common::Point p) override;
void onMouseMove(Common::Point p) override;
void onClick(Common::Point p) override;
void setPos(Common::Point p, bool center) override;
private:
uint findButtonIndex(int16 x, int16 y) const;
private:
Common::Rect _buttonRects[6];
};
} // End of namespace Petka
#endif

View File

@@ -0,0 +1,270 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "graphics/fonts/ttf.h"
#include "graphics/fontman.h"
#include "petka/q_manager.h"
#include "petka/petka.h"
#include "petka/video.h"
#include "petka/objects/text.h"
#include "petka/q_system.h"
#include "petka/interfaces/panel.h"
#include "petka/sound.h"
#include "petka/interfaces/main.h"
#include "petka/flc.h"
namespace Petka {
QText::QText(const Common::U32String &text, uint16 textColor, uint16 outlineColor) {
_resourceId = -2;
_z = 3000;
auto *font = g_vm->getTextFont();
Common::Rect rect = calculateBoundingBoxForText(text, *font);
rect.right += 10;
rect.bottom += 4;
_rect = Common::Rect((640 - rect.width()) / 2, 479 - rect.height(), 639 - (640 - rect.width()) / 2, 479);
Graphics::Surface *s = g_vm->resMgr()->getSurface((uint32)-2, rect.width(), rect.height());
drawText(*s, 0, 630, text, textColor, *font, Graphics::kTextAlignCenter);
drawOutline(s, outlineColor);
}
void QText::draw() {
const Graphics::Surface *s = g_vm->resMgr()->getSurface((uint32)-2);
if (s) {
g_vm->videoSystem()->transBlitFrom(*s, Common::Point((640 - s->w) / 2, 479 - s->h));
}
}
const Common::Rect &QText::getRect() {
return _rect;
}
void QText::drawOutline(Graphics::Surface *s, uint16 color) {
for (int y = 0; y < s->h; ++y) {
for (int x = 1; x < s->w - 1; ++x) {
uint16 *pixel = (uint16 *)s->getBasePtr(x, y);
if (*pixel && *pixel != color) {
if (!pixel[-1])
pixel[-1] = (uint16)color;
if (!pixel[1])
pixel[1] = (uint16)color;
}
}
}
for (int x = 0; x < s->w; ++x) {
for (int y = 0; y < s->h - 1; ++y) {
uint16 *pixel = (uint16 *)s->getBasePtr(x, y);
if (*pixel && *pixel != color) {
pixel = (uint16 *)s->getBasePtr(x, y - 1);
if (*pixel == 0)
*pixel = color;
pixel = (uint16 *)s->getBasePtr(x, y + 1);
if (*pixel == 0)
*pixel = color;
}
}
}
}
QText::QText() {
_resourceId = -2;
_z = 3000;
}
void QText::update(int) {
g_vm->videoSystem()->addDirtyRect(_rect);
}
QTextPhrase::QTextPhrase(const Common::U32String &text, uint16 textColor, uint16 outlineColor)
: QText(text, textColor, outlineColor), _phrase(text), _time(0) {}
void QTextPhrase::draw() {
if (g_vm->getQSystem()->_panelInterface->showSubtitles()) {
QText::draw();
}
}
void QTextPhrase::update(int time) {
DialogInterface &dialog = g_vm->getQSystem()->_mainInterface->_dialog;
_time += time;
QText::update(time);
Sound *sound = dialog.findSound();
if (sound) {
if (!sound->isPlaying()) {
_time = 0;
dialog.next(-1);
}
} else if (_time > _phrase.size() * 30 + 1000 || !g_vm->getQSystem()->_panelInterface->showSubtitles()) {
_time = 0;
dialog.next(-1);
}
}
void QTextPhrase::onClick(Common::Point p) {
DialogInterface &dialog = g_vm->getQSystem()->_mainInterface->_dialog;
dialog.next(-1);
}
QTextDescription::QTextDescription(const Common::U32String &desc, uint32 frame) {
_z = 999;
_resourceId = -2;
_rect = Common::Rect(0, 0, 640, 480);
FlicDecoder *flc = g_vm->resMgr()->getFlic(6008);
flc->setFrame(frame);
const Graphics::Surface *frameS = flc->getCurrentFrame();
Graphics::Surface *s = g_vm->resMgr()->getSurface((uint32)-2, 640, 480);
Graphics::Surface *convS = frameS->convertTo(s->format, flc->getPalette());
s->copyRectToSurface(*convS, 0, 0, _rect);
convS->free();
delete convS;
Common::Rect textArea(160, 275, 598, 376);
auto *font = g_vm->getDescriptionFont();
auto textSurface = s->getSubArea(textArea);
drawText(textSurface, 0, textArea.width(), desc, 0, *font, Graphics::kTextAlignLeft);
g_vm->videoSystem()->addDirtyRect(_rect);
}
void QTextDescription::onClick(Common::Point p) {
g_vm->getQSystem()->_mainInterface->removeTextDescription();
}
void QTextDescription::draw() {
QManager *resMgr = g_vm->resMgr();
VideoSystem *videoSys = g_vm->videoSystem();
Graphics::Surface *s = resMgr->getSurface((uint32)-2);
FlicDecoder *flc = resMgr->getFlic(6008);
for (auto &dirty : videoSys->rects()) {
videoSys->transBlitFrom(*s, dirty, dirty, flc->getTransColor(s->format));
}
}
QTextChoice::QTextChoice(const Common::Array<Common::U32String> &choices, uint16 color, uint16 outlineColor, uint16 selectedColor) {
_activeChoice = 0;
_choiceColor = color;
_outlineColor = outlineColor;
_selectedColor = selectedColor;
_choices = choices;
int w = 0;
int h = 0;
auto *font = g_vm->getTextFont();
_rects.resize(choices.size());
for (uint i = 0; i < _choices.size(); ++i) {
_rects[i] = calculateBoundingBoxForText(_choices[i], *font);
w = MAX<int>(w, _rects[i].width());
_rects[i].setWidth(w);
_rects[i].setHeight(font->getFontHeight());
h += font->getFontHeight();
}
w += 10;
h += 4;
_rect = Common::Rect((640 - w) / 2, 479 - h, 639 - (640 - w) / 2, 479);
Graphics::Surface *s = g_vm->resMgr()->getSurface((uint32)-2, w, h);
int y = 0;
for (uint i = 0; i < _choices.size(); ++i) {
drawText(*s, y, 630, _choices[i], _choiceColor, *font, Graphics::TextAlign::kTextAlignLeft);
_rects[i].moveTo(0, y);
y += font->getFontHeight();
}
drawOutline(s, outlineColor);
}
void QTextChoice::onMouseMove(Common::Point p) {
p.x = p.x - _rect.left - g_vm->getQSystem()->_xOffset;
p.y = p.y - _rect.top;
uint newChoice;
for (newChoice = 0; newChoice < _rects.size(); ++newChoice) {
if (_rects[newChoice].contains(p)) {
break;
}
}
if (newChoice != _activeChoice) {
Graphics::Surface *s = g_vm->resMgr()->getSurface((uint32)-2);
auto *font = g_vm->getTextFont();
s->fillRect(Common::Rect(s->w, s->h), 0);
for (uint i = 0; i < _choices.size(); ++i) {
uint color = (i == newChoice) ? _selectedColor : _choiceColor;
drawText(*s, _rects[i].top, 630, _choices[i], color, *font, Graphics::kTextAlignLeft);
}
drawOutline(s, _outlineColor);
_activeChoice = newChoice;
}
}
void QTextChoice::onClick(Common::Point p) {
if (_activeChoice < _choices.size()) {
g_vm->getQSystem()->_mainInterface->_dialog.next(_activeChoice);
}
}
Common::Rect QText::calculateBoundingBoxForText(const Common::U32String &text, Graphics::Font &font) {
if (text.empty())
return {};
Common::Array<Common::U32String> lines;
font.wordWrapText(text, 630, lines);
Common::Rect rect = font.getBoundingBox(lines[0]);
rect.setHeight(font.getFontHeight());
for (uint j = 1; j < lines.size(); ++j) {
auto box = font.getBoundingBox(lines[j]);
rect.setHeight(rect.height() + font.getFontHeight());
if (box.width() > rect.width())
rect.setWidth(box.width());
}
return rect;
}
void QText::drawText(Graphics::Surface &s, int y, int maxWidth, const Common::U32String &text, uint color, Graphics::Font &font, Graphics::TextAlign alignment) {
Common::Array<Common::U32String> lines;
font.wordWrapText(text, maxWidth, lines);
int h = 0;
for (uint i = 0; i < lines.size(); ++i) {
font.drawString(&s, lines[i], 0, y + h, s.w, color, alignment);
h += font.getFontHeight();
}
}
} // End of namespace Petka

View File

@@ -0,0 +1,97 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef PETKA_TEXT_H
#define PETKA_TEXT_H
#include "common/rect.h"
#include "common/ustr.h"
#include "common/str-array.h"
#include "graphics/font.h"
#include "petka/objects/object.h"
namespace Petka {
class QText : public QVisibleObject {
public:
QText(const Common::U32String &text, uint16 textColor, uint16 outlineColor);
void draw();
void update(int time);
const Common::Rect &getRect();
protected:
QText();
static void drawOutline(Graphics::Surface *surface, uint16 color);
static Common::Rect calculateBoundingBoxForText(const Common::U32String &text, Graphics::Font &font);
static void drawText(Graphics::Surface &s, int y, int maxWidth, const Common::U32String &text, uint color, Graphics::Font &font, Graphics::TextAlign alignment);
protected:
Common::Rect _rect;
};
class QTextPhrase : public QText {
public:
QTextPhrase(const Common::U32String &phrase, uint16 textColor, uint16 outlineColor);
void draw() override;
void update(int time) override;
void onClick(Common::Point p) override;
bool isInPoint(Common::Point p) override { return true; }
private:
Common::U32String _phrase;
uint _time;
};
class QTextDescription : public QText {
public:
QTextDescription(const Common::U32String &desc, uint32 frame);
void draw() override;
void onClick(Common::Point p) override;
bool isInPoint(Common::Point p) override { return true; }
void update(int t) override {}
};
class QTextChoice : public QText {
public:
QTextChoice(const Common::Array<Common::U32String> &choices, uint16 color, uint16 outlineColor, uint16 selectedColor);
void onMouseMove(Common::Point p) override;
void onClick(Common::Point p) override;
bool isInPoint(Common::Point p) override { return true; }
private:
Common::Array<Common::Rect> _rects;
Common::Array<Common::U32String> _choices;
uint _activeChoice;
uint16 _outlineColor;
uint16 _choiceColor;
uint16 _selectedColor;
};
} // End of namespace Petka
#endif

405
engines/petka/petka.cpp Normal file
View 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
View 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
View 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
View File

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

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

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