Initial commit

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

View File

@@ -0,0 +1,3 @@
engines/darkseed/darkseed.cpp
engines/darkseed/dialogs.cpp
engines/darkseed/metaengine.cpp

View File

@@ -0,0 +1,232 @@
/* 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 "darkseed/adlib_dsf.h"
namespace Darkseed {
// F-num values used for the 12 octave notes.
const uint16 MidiDriver_DarkSeedFloppy_AdLib::OPL_NOTE_FREQUENCIES[12] = {
0x157, 0x16B, 0x181, 0x198, 0x1B0, 0x1CA, 0x1E5, 0x202, 0x220, 0x241, 0x263, 0x287
};
MidiDriver_DarkSeedFloppy_AdLib::MidiDriver_DarkSeedFloppy_AdLib(OPL::Config::OplType oplType, int timerFrequency) :
MidiDriver_ADLIB_Multisource::MidiDriver_ADLIB_Multisource(oplType, timerFrequency) {
_dsfInstrumentBank = new OplInstrumentDefinition[128];
_instrumentBank = _dsfInstrumentBank;
Common::fill(_sourcePriority, _sourcePriority + sizeof(_sourcePriority), 0);
_defaultChannelVolume = 0x7F;
// Dark Seed uses rhythm instrument definitions with instrument numbers 0x0 - 0xE
_rhythmInstrumentMode = RHYTHM_INSTRUMENT_MODE_RHYTHM_TYPE;
_instrumentWriteMode = INSTRUMENT_WRITE_MODE_FIRST_NOTE_ON;
}
MidiDriver_DarkSeedFloppy_AdLib::~MidiDriver_DarkSeedFloppy_AdLib() {
delete[] _dsfInstrumentBank;
}
int MidiDriver_DarkSeedFloppy_AdLib::open() {
int result = MidiDriver_ADLIB_Multisource::open();
if (result == 0)
// Dark Seed has the OPL rhythm mode always on
setRhythmMode(true);
return result;
}
void MidiDriver_DarkSeedFloppy_AdLib::deinitSource(uint8 source) {
MidiDriver_ADLIB_Multisource::deinitSource(source);
_sourcePriority[source] = 0;
}
void MidiDriver_DarkSeedFloppy_AdLib::setSourcePriority(uint8 source, uint8 priority) {
assert(source < MAXIMUM_SOURCES);
_sourcePriority[source] = priority;
}
void MidiDriver_DarkSeedFloppy_AdLib::loadInstrumentBank(uint8 *instrumentBankData) {
// Dark Seed stores instruments in SIT files. Most music tracks have their
// own instrument bank, but there are only 2 significantly different banks.
// START loads the instrument bank for each of the 8 tracks it plays, but
// each bank is effectively the same. TOS only loads the TOS1.SIT
// instrument bank; the SIT files from the music tracks it plays are
// ignored.
//
// All SIT files contain 256 instruments in the 16 byte AdLib BNK format.
// Only the first 128 instruments are actually loaded; the rest is usually
// empty.
for (int i = 0; i < 128; i++) {
AdLibIbkInstrumentDefinition _ibkInstrument;
_ibkInstrument.o0FreqMultMisc = *instrumentBankData++;
_ibkInstrument.o1FreqMultMisc = *instrumentBankData++;
_ibkInstrument.o0Level = *instrumentBankData++;
_ibkInstrument.o1Level = *instrumentBankData++;
_ibkInstrument.o0DecayAttack = *instrumentBankData++;
_ibkInstrument.o1DecayAttack = *instrumentBankData++;
_ibkInstrument.o0ReleaseSustain = *instrumentBankData++;
_ibkInstrument.o1ReleaseSustain = *instrumentBankData++;
_ibkInstrument.o0WaveformSelect = *instrumentBankData++;
_ibkInstrument.o1WaveformSelect = *instrumentBankData++;
_ibkInstrument.connectionFeedback = *instrumentBankData++;
// The first 15 instruments are rhythm instrument definitions, meant
// for the 5 OPL rhythm mode instruments. 0-2 are bass drum instruments,
// 3-5 are snare drum instruments, etc.
uint8 rhythmType = 0;
if (i < 15) {
rhythmType = 6 + (i / 3);
}
_ibkInstrument.rhythmType = rhythmType;
_ibkInstrument.rhythmNote = 0;
_ibkInstrument.transpose = 0;
_ibkInstrument.toOplInstrumentDefinition(_dsfInstrumentBank[i]);
// Skip padding bytes
instrumentBankData += 5;
}
}
uint8 MidiDriver_DarkSeedFloppy_AdLib::allocateOplChannel(uint8 channel, uint8 source, InstrumentInfo &instrumentInfo) {
uint8 allocatedChannel = 0xFF;
_allocationMutex.lock();
_activeNotesMutex.lock();
if (instrumentInfo.instrumentId <= 0xE) {
// The first 15 instruments are rhythm instruments. These get assigned
// to the corresponding OPL rhythm instruments.
// Note: original code also processes instrument 0xF, leading to
// undefined behavior.
// The order of the rhythm instruments is flipped compared to the order
// in the _activeRhythmNotes array.
uint8 rhythmInstType = 4 - (instrumentInfo.instrumentId / 3);
allocatedChannel = OPL_RHYTHM_INSTRUMENT_CHANNELS[rhythmInstType];
if (_activeRhythmNotes[rhythmInstType].channelAllocated && _activeRhythmNotes[rhythmInstType].source != source) {
// OPL rhythm instrument is already allocated
if (_sourcePriority[_activeRhythmNotes[rhythmInstType].source] >= _sourcePriority[source]) {
// Current source priority is equal to or higher than the new
// source priority. Do not re-allocate this rhythm instrument.
allocatedChannel = 0xFF;
} else {
// Current source priority is lower than the new source
// priority. Deallocate the channel from the current source.
if (_activeRhythmNotes[rhythmInstType].noteActive)
writeKeyOff(allocatedChannel, static_cast<OplInstrumentRhythmType>(rhythmInstType + 1));
_channelAllocations[_activeRhythmNotes[rhythmInstType].source][_activeRhythmNotes[rhythmInstType].channel] = 0xFF;
}
}
if (allocatedChannel != 0xFF) {
// Allocate the OPL channel to the source and rhythm instrument.
_activeRhythmNotes[rhythmInstType].channelAllocated = true;
_activeRhythmNotes[rhythmInstType].source = source;
_activeRhythmNotes[rhythmInstType].channel = channel;
}
}
else {
// For melodic instruments, the following OPL channel is allocated:
// - The OPL channel already allocated to this data channel.
// - The OPL channel with the lowest priority, if it is lower than the
// priority of the new source.
uint8 lowestPriority = 0x64;
for (int i = 0; i < _numMelodicChannels; i++) {
uint8 oplChannel = _melodicChannels[i];
if (_activeNotes[oplChannel].channelAllocated && _activeNotes[oplChannel].source == source && _activeNotes[oplChannel].channel == channel) {
// This OPL channel is already allocated to this source and
// data channel. Use this OPL channel.
allocatedChannel = oplChannel;
lowestPriority = 0;
break;
}
// Unallocated channels are treated as having priority 0, the
// lowest priority.
uint8 currentChannelPriority = !_activeNotes[oplChannel].channelAllocated ? 0 : _sourcePriority[_activeNotes[oplChannel].source];
if (currentChannelPriority < lowestPriority) {
// Found an OPL channel with a lower priority than the
// previously found OPL channel.
allocatedChannel = i;
lowestPriority = currentChannelPriority;
}
}
if (_sourcePriority[source] <= lowestPriority) {
// New source priority is lower than the lowest found OPL channel
// priority. Do not re-allocate this OPL channel.
allocatedChannel = 0xFF;
}
if (allocatedChannel != 0xFF) {
if (_activeNotes[allocatedChannel].channelAllocated && _activeNotes[allocatedChannel].source != source) {
// OPL channel is already allocated. De-allocate it.
if (_activeNotes[allocatedChannel].noteActive)
writeKeyOff(allocatedChannel);
_channelAllocations[_activeRhythmNotes[allocatedChannel].source][_activeRhythmNotes[allocatedChannel].channel] = 0xFF;
}
// Allocate the OPL channel to the source and data channel.
_activeNotes[allocatedChannel].channelAllocated = true;
_activeNotes[allocatedChannel].source = source;
_activeNotes[allocatedChannel].channel = channel;
}
}
if (allocatedChannel != 0xFF) {
_channelAllocations[source][channel] = allocatedChannel;
}
_allocationMutex.unlock();
_activeNotesMutex.unlock();
return allocatedChannel;
}
uint16 MidiDriver_DarkSeedFloppy_AdLib::calculateFrequency(uint8 channel, uint8 source, uint8 note) {
uint8 octaveNote = ((note >= 120) ? 11 : (note % 12));
uint8 block;
if (note < 12) {
block = 0;
} else if (note >= 108) {
block = 7;
} else {
block = (note / 12) - 1;
}
uint16 fnum = OPL_NOTE_FREQUENCIES[octaveNote];
return fnum | (block << 10);
}
uint8 MidiDriver_DarkSeedFloppy_AdLib::calculateUnscaledVolume(uint8 channel, uint8 source, uint8 velocity, const OplInstrumentDefinition &instrumentDef, uint8 operatorNum) {
uint8 instrumentLevel = instrumentDef.getOperatorDefinition(operatorNum).level & 0x3F;
if (instrumentDef.getNumberOfOperators() >= 2 && operatorNum == 0) {
// For operator 0 of a 2 operator instrument, the level from the
// instrument definition is used without scaling, even if the
// connection type is additive.
return instrumentLevel;
}
return 0x3F - (((velocity + 0x80) * (0x3F - instrumentLevel)) >> 8);
}
} // namespace Darkseed

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 DARKSEED_ADLIB_DSF_H
#define DARKSEED_ADLIB_DSF_H
#include "audio/adlib_ms.h"
namespace Darkseed {
/**
* Implementation of the AdLib code used by Dark Seed (floppy version).
* The game uses a combination of the Creative FM-Music Functions library and
* its own sound code.
*/
class MidiDriver_DarkSeedFloppy_AdLib : public MidiDriver_ADLIB_Multisource {
protected:
static const uint16 OPL_NOTE_FREQUENCIES[12];
public:
MidiDriver_DarkSeedFloppy_AdLib(OPL::Config::OplType oplType, int timerFrequency = OPL::OPL::kDefaultCallbackFrequency);
~MidiDriver_DarkSeedFloppy_AdLib();
int open() override;
void deinitSource(uint8 source) override;
void setSourcePriority(uint8 source, uint8 priority);
void loadInstrumentBank(uint8 *instrumentBankData);
protected:
uint8 allocateOplChannel(uint8 channel, uint8 source, InstrumentInfo &instrumentInfo) override;
uint16 calculateFrequency(uint8 channel, uint8 source, uint8 note) override;
uint8 calculateUnscaledVolume(uint8 channel, uint8 source, uint8 velocity, const OplInstrumentDefinition &instrumentDef, uint8 operatorNum) override;
uint8 _sourcePriority[MAXIMUM_SOURCES];
OplInstrumentDefinition *_dsfInstrumentBank;
};
} // namespace Darkseed
#endif // DARKSEED_ADLIB_DSF_H

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 "darkseed/adlib_worx.h"
namespace Darkseed {
const AdLibIbkInstrumentDefinition MidiDriver_Worx_AdLib::WORX_INSTRUMENT_BANK[128] = {
// 0x00
{ 0x01, 0x01, 0x4f, 0x12, 0xf1, 0xd3, 0x50, 0x7c, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x02, 0x01, 0x50, 0x12, 0xf1, 0xd2, 0x50, 0x76, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x01, 0x01, 0x4b, 0x17, 0xf1, 0xd2, 0x50, 0x76, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x13, 0x01, 0x50, 0x11, 0xf1, 0xd2, 0x50, 0x76, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x32, 0x01, 0x92, 0x8f, 0xff, 0xff, 0x11, 0x13, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x34, 0x03, 0x92, 0x0f, 0xff, 0xff, 0x10, 0x04, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x34, 0x03, 0x92, 0x14, 0xff, 0xff, 0x10, 0x04, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x53, 0x51, 0x4e, 0x00, 0xf1, 0xd2, 0x00, 0x86, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00 },
// 0x08
{ 0x28, 0x21, 0xcf, 0x0d, 0xf8, 0xc0, 0xe5, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0xe2, 0xe1, 0xca, 0x15, 0xf8, 0xc0, 0xe5, 0x0e, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x2c, 0xa1, 0xd4, 0x1c, 0xf9, 0xc0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x2b, 0x21, 0xca, 0x13, 0xf8, 0xc0, 0xe5, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x29, 0x21, 0xcd, 0x14, 0xf0, 0xe0, 0x91, 0x86, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x24, 0x21, 0xd0, 0x14, 0xf0, 0xe0, 0x01, 0x86, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x23, 0x21, 0xc8, 0x10, 0xf0, 0xe0, 0x01, 0x86, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x64, 0x61, 0xc9, 0x14, 0xb0, 0xf0, 0x01, 0x86, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00 },
// 0x10
{ 0x33, 0x15, 0x85, 0x94, 0xa1, 0x72, 0x10, 0x23, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x31, 0x15, 0x85, 0x94, 0xa1, 0x73, 0x10, 0x33, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x31, 0x16, 0x81, 0x94, 0xa1, 0xc2, 0x30, 0x74, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x03, 0x02, 0x8a, 0x94, 0xf0, 0xf4, 0x7b, 0x7b, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x03, 0x01, 0x8a, 0x99, 0xf0, 0xf4, 0x7b, 0x7b, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x23, 0x01, 0x8a, 0x94, 0xf2, 0xf4, 0x7b, 0x7b, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x32, 0x12, 0x80, 0x95, 0x01, 0x72, 0x10, 0x33, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x32, 0x14, 0x80, 0x90, 0x01, 0x73, 0x10, 0x33, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 },
// 0x18
{ 0x31, 0x21, 0x16, 0x14, 0x73, 0x80, 0x8e, 0x9e, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x30, 0x21, 0x16, 0x10, 0x73, 0x80, 0x7e, 0x9e, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x31, 0x21, 0x94, 0x15, 0x33, 0xa0, 0x73, 0x97, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x31, 0x21, 0x94, 0x13, 0xd3, 0xa0, 0x73, 0x97, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x31, 0x32, 0x45, 0x11, 0xf1, 0xf2, 0x53, 0x27, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x13, 0x15, 0x0c, 0x1a, 0xf2, 0xf2, 0x01, 0xb6, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x11, 0x11, 0x0c, 0x15, 0xf2, 0xf2, 0x01, 0xb6, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x11, 0x11, 0x0a, 0x10, 0xfe, 0xf2, 0x04, 0xbd, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 },
// 0x20
{ 0x16, 0xe1, 0x4d, 0x11, 0xfa, 0xf1, 0x11, 0xf1, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x16, 0xf1, 0x40, 0x17, 0xba, 0x24, 0x11, 0x31, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x61, 0xe1, 0xa7, 0x8e, 0x72, 0x50, 0x8e, 0x1a, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x18, 0xe1, 0x4d, 0x13, 0x32, 0x51, 0x13, 0xe3, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x17, 0x31, 0xc0, 0x92, 0x12, 0x13, 0x41, 0x31, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x03, 0x21, 0x8f, 0x90, 0xf5, 0xf3, 0x55, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x13, 0xe1, 0x4d, 0x12, 0xfa, 0xf1, 0x11, 0xf1, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x11, 0xf1, 0x43, 0x10, 0x20, 0x31, 0x15, 0xf8, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 },
// 0x28
{ 0x11, 0xe4, 0x03, 0x52, 0x82, 0xf0, 0x97, 0xf2, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x05, 0x14, 0x40, 0x0f, 0xd1, 0x51, 0x53, 0x71, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0xf1, 0x21, 0x01, 0x12, 0x77, 0x81, 0x17, 0x18, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0xf1, 0xe1, 0x18, 0x17, 0x32, 0xf1, 0x11, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x73, 0x71, 0x48, 0x13, 0xf1, 0xf1, 0x53, 0x06, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x71, 0x61, 0x8d, 0x53, 0x71, 0x72, 0x11, 0x15, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0xd7, 0xd2, 0x4f, 0x14, 0xf2, 0xf1, 0x61, 0xb2, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x01, 0x01, 0x11, 0x13, 0xf0, 0xf0, 0xff, 0xf8, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00 },
// 0x30
{ 0x31, 0x61, 0x8b, 0x10, 0x41, 0x22, 0x11, 0x13, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x31, 0x61, 0x8b, 0x10, 0xff, 0x44, 0x21, 0x15, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x31, 0x61, 0x8b, 0x10, 0x41, 0x32, 0x11, 0x15, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x71, 0x21, 0x1c, 0x10, 0xfd, 0xe7, 0x13, 0xd6, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x71, 0x21, 0x1c, 0x10, 0x51, 0x54, 0x03, 0x67, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x71, 0x21, 0x1c, 0x10, 0x51, 0x54, 0x03, 0x17, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x71, 0x21, 0x1c, 0x10, 0x54, 0x53, 0x15, 0x49, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x71, 0x61, 0x56, 0x10, 0x51, 0x54, 0x03, 0x17, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 },
// 0x38
{ 0x71, 0x21, 0x1c, 0x10, 0x51, 0x54, 0x03, 0x17, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x02, 0x01, 0x29, 0x90, 0xf5, 0xf2, 0x75, 0xf3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x02, 0x01, 0x29, 0x90, 0xf0, 0xf4, 0x75, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x01, 0x11, 0x49, 0x10, 0xf1, 0xf1, 0x53, 0x74, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x01, 0x11, 0x89, 0x10, 0xf1, 0xf1, 0x53, 0x74, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x02, 0x11, 0x89, 0x10, 0xf1, 0xf1, 0x53, 0x74, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x02, 0x11, 0x80, 0x10, 0xf1, 0xf1, 0x53, 0x74, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x01, 0x08, 0x40, 0x50, 0xf1, 0xf1, 0x53, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
// 0x40
{ 0x21, 0x21, 0x15, 0x90, 0xd3, 0xc3, 0x2c, 0x2c, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x01, 0x21, 0x18, 0x90, 0xd4, 0xc4, 0xf2, 0x8a, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x01, 0x11, 0x4e, 0x10, 0xf0, 0xf4, 0x7b, 0xc8, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x01, 0x11, 0x44, 0x10, 0xf0, 0xf3, 0xab, 0xab, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x53, 0x11, 0x0e, 0x10, 0xf4, 0xf1, 0xc8, 0xbb, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x53, 0x11, 0x0b, 0x10, 0xf2, 0xf2, 0xc8, 0xc5, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x21, 0x21, 0x15, 0x10, 0xb4, 0x94, 0x4c, 0xac, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x21, 0x21, 0x15, 0x10, 0x94, 0x64, 0x1c, 0xac, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00 },
// 0x48
{ 0x21, 0xa1, 0x16, 0x90, 0x77, 0x60, 0x8f, 0x2a, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x21, 0xa1, 0x19, 0x90, 0x77, 0x60, 0xbf, 0x2a, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0xa1, 0xe2, 0x13, 0x90, 0xd6, 0x60, 0xaf, 0x2a, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0xa2, 0xe2, 0x1d, 0x90, 0x95, 0x60, 0x24, 0x2a, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x32, 0x61, 0x9a, 0x90, 0x51, 0x60, 0x19, 0x39, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0xa4, 0xe2, 0x12, 0x90, 0xf4, 0x60, 0x30, 0x2a, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x21, 0x21, 0x16, 0x10, 0x63, 0x63, 0x0e, 0x0e, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x31, 0x21, 0x16, 0x10, 0x63, 0x63, 0x0a, 0x0b, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00 },
// 0x50
{ 0x21, 0x21, 0x1b, 0x10, 0x63, 0x63, 0x0a, 0x0b, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x20, 0x21, 0x1b, 0x10, 0x63, 0x63, 0x0a, 0x0b, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x32, 0x61, 0x1c, 0x90, 0x82, 0x60, 0x18, 0x07, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x32, 0xe1, 0x18, 0x90, 0x51, 0x62, 0x14, 0x36, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x31, 0x22, 0xc3, 0x10, 0x87, 0x8b, 0x17, 0x0e, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x71, 0x22, 0xc3, 0x14, 0x8e, 0x8b, 0x17, 0x0e, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x70, 0x22, 0x8d, 0x10, 0x6e, 0x6b, 0x17, 0x0e, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x24, 0x31, 0x4f, 0x10, 0xf2, 0x52, 0x06, 0x06, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 },
// 0x58
{ 0x31, 0x61, 0x1b, 0x10, 0x64, 0xd0, 0x07, 0x67, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x31, 0x61, 0x1b, 0x10, 0x61, 0xd2, 0x06, 0x36, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x31, 0x61, 0x1f, 0x10, 0x31, 0x50, 0x06, 0x36, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x31, 0x61, 0x1f, 0x10, 0x41, 0xa0, 0x06, 0x36, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x21, 0x21, 0x9a, 0x90, 0x53, 0xa0, 0x56, 0x16, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x21, 0x21, 0x9a, 0x90, 0x53, 0xa0, 0x56, 0x16, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x61, 0x21, 0x19, 0x10, 0x53, 0xa0, 0x58, 0x18, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x61, 0x21, 0x19, 0x10, 0x73, 0xa0, 0x57, 0x17, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00 },
// 0x60
{ 0x21, 0x21, 0x1b, 0x10, 0x71, 0xa1, 0xa6, 0x96, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x85, 0xa1, 0x91, 0x10, 0xf5, 0xf0, 0x44, 0x45, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x07, 0x61, 0x51, 0x10, 0xf5, 0xf0, 0x33, 0x25, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x13, 0x11, 0x8c, 0x90, 0xff, 0xff, 0x21, 0x03, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x38, 0xb1, 0x8c, 0x50, 0xf3, 0xf5, 0x0d, 0x33, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x87, 0x22, 0x91, 0x10, 0xf5, 0xf0, 0x55, 0x54, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0xb3, 0x90, 0x4a, 0x10, 0xb6, 0xd1, 0x32, 0x31, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x04, 0xc2, 0x00, 0x10, 0xfe, 0xf6, 0xf0, 0xb5, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 },
// 0x68
{ 0x05, 0x01, 0x4e, 0x90, 0xda, 0xf0, 0x15, 0x13, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x31, 0x32, 0x44, 0x10, 0xf2, 0xf0, 0x9a, 0x27, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0xb0, 0xd7, 0xc4, 0x90, 0xa4, 0x40, 0x02, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0xca, 0xcc, 0x84, 0x10, 0xf0, 0x59, 0xf0, 0x62, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x30, 0x35, 0x35, 0x10, 0xf5, 0xf0, 0xf0, 0x9b, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0xb4, 0xd7, 0x87, 0x90, 0xa4, 0x40, 0x02, 0x42, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x07, 0x05, 0x40, 0x00, 0x09, 0xf6, 0x53, 0x96, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x09, 0x01, 0x4e, 0x10, 0xda, 0xf1, 0x25, 0x15, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00 },
// 0x70
{ 0x06, 0x00, 0x09, 0x10, 0xf4, 0xf6, 0xa0, 0x46, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x07, 0x00, 0x00, 0x10, 0xf0, 0x5c, 0xf0, 0xdc, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x1c, 0x0c, 0x1e, 0x10, 0xe5, 0x5d, 0x5b, 0xfa, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x11, 0x01, 0x8a, 0x50, 0xf1, 0xf1, 0x11, 0xb3, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x00, 0x00, 0x40, 0x10, 0xd1, 0xf2, 0x53, 0x56, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x32, 0x11, 0x44, 0x10, 0xf8, 0xf5, 0xff, 0x7f, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x00, 0x02, 0x40, 0x10, 0x09, 0xf7, 0x53, 0x94, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x11, 0x01, 0x86, 0x90, 0xf2, 0xa0, 0xa8, 0xa8, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 },
// 0x78
{ 0x00, 0x13, 0x50, 0x10, 0xf2, 0xf2, 0x70, 0x72, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0xf0, 0xe0, 0x00, 0xd0, 0x11, 0x11, 0x11, 0x11, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x07, 0x12, 0x4f, 0x10, 0xf2, 0xf2, 0x60, 0x72, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x00, 0x00, 0x0b, 0x10, 0xa8, 0xd6, 0x4c, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x00, 0x00, 0x0d, 0x10, 0xe8, 0xa5, 0xef, 0xff, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x31, 0x16, 0x87, 0x90, 0xa1, 0x7d, 0x11, 0x46, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x30, 0x10, 0x90, 0x10, 0xf4, 0xf4, 0x49, 0x33, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x24, 0x31, 0x54, 0x10, 0x55, 0x50, 0xfd, 0x2d, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 }
};
const uint16 MidiDriver_Worx_AdLib::OPL_NOTE_FREQUENCIES[12] = {
0x16B, 0x181, 0x198, 0x1B0, 0x1CA, 0x1E5, 0x202, 0x220, 0x241, 0x263, 0x287, 0x2AE
};
MidiDriver_Worx_AdLib::MidiDriver_Worx_AdLib(OPL::Config::OplType oplType, int timerFrequency) :
MidiDriver_ADLIB_Multisource::MidiDriver_ADLIB_Multisource(oplType, timerFrequency) {
OplInstrumentDefinition *instrumentBank = new OplInstrumentDefinition[128];
for (int i = 0; i < 128; i++) {
WORX_INSTRUMENT_BANK[i].toOplInstrumentDefinition(instrumentBank[i]);
// The original code does not add the key scale level bits (bits 6 and 7)
// from the instrument definition to the level before it writes the 0x4x
// register value, so effectively, KSL is always disabled for operator 1.
// This is probably an oversight, but this behavior is implemented here
// by clearing the KSL bits of operator 1 in the instrument definition.
instrumentBank[i].operator1.level &= 0x3F;
}
// Set the const class variable with our just allocated bank
_instrumentBank = instrumentBank;
_defaultChannelVolume = 0x7F;
_rhythmInstrumentMode = RHYTHM_INSTRUMENT_MODE_RHYTHM_TYPE;
_instrumentWriteMode = INSTRUMENT_WRITE_MODE_FIRST_NOTE_ON;
}
MidiDriver_Worx_AdLib::~MidiDriver_Worx_AdLib() {
delete[] _instrumentBank;
}
uint8 MidiDriver_Worx_AdLib::allocateOplChannel(uint8 channel, uint8 source, InstrumentInfo &instrumentInfo) {
uint8 allocatedChannel = 0xFF;
_allocationMutex.lock();
uint8 unusedChannel = 0xFF, unallocatedChannel = 0xFF, unusedAllocatedChannel = 0xFF;
for (int i = 0; i < _numMelodicChannels; i++) {
uint8 oplChannel = _melodicChannels[i];
if (_activeNotes[oplChannel].channelAllocated && _activeNotes[oplChannel].source == source && _activeNotes[oplChannel].channel == channel &&
!_activeNotes[oplChannel].noteActive && unusedChannel == 0xFF) {
// This OPL channel is already allocated to this source and MIDI
// channel, but it is not playing a note. Use this channel.
unusedChannel = oplChannel;
break;
}
if (!_activeNotes[oplChannel].channelAllocated && unallocatedChannel == 0xFF) {
// This channel is unallocated. If no unallocated channel has been
// found yet, register this channel.
unallocatedChannel = oplChannel;
}
if (_activeNotes[oplChannel].channelAllocated && !(_activeNotes[oplChannel].source == source && _activeNotes[oplChannel].channel == channel) &&
!_activeNotes[oplChannel].noteActive && unusedAllocatedChannel == 0xFF) {
// This channel is allocated to a different source and/or MIDI
// channel, but it is not playing a note. If a channel of this
// type has not yet been found, register it.
unusedAllocatedChannel = oplChannel;
}
}
if (unusedChannel != 0xFF) {
// Found an allocated but unused channel.
allocatedChannel = unusedChannel;
}
else if (unallocatedChannel != 0xFF) {
// Found an unallocated channel.
allocatedChannel = unallocatedChannel;
}
else if (unusedAllocatedChannel != 0xFF) {
// Found an unused channel allocated to a different source / MIDI channel.
allocatedChannel = unusedAllocatedChannel;
}
else {
// All channels are playing notes. No channel is freed, so this will
// result in the note not being played. Return 0xFF.
_allocationMutex.unlock();
return allocatedChannel;
}
// Allocate the OPL channel to the source / MIDI channel.
_activeNotes[allocatedChannel].channelAllocated = true;
_activeNotes[allocatedChannel].source = source;
_activeNotes[allocatedChannel].channel = channel;
_allocationMutex.unlock();
return allocatedChannel;
}
uint16 MidiDriver_Worx_AdLib::calculateFrequency(uint8 channel, uint8 source, uint8 note) {
// Notes for melodic instruments are transposed down by 13 semitones.
uint8 transposedNote = MAX(note - 12 - 1, 0);
// TODO Implement transpose based on transpose controllers
// Get F-num based on octave note. Note: Worx does not support pitch bend.
uint8 octaveNote = transposedNote % 12;
uint16 oplFrequency = OPL_NOTE_FREQUENCIES[octaveNote];
// Get block (octave).
uint8 block = transposedNote / 12;
block = MIN(block, (uint8) 7);
// Combine the block and frequency in the OPL Ax and Bx register format.
return oplFrequency | (block << 10);
}
uint8 MidiDriver_Worx_AdLib::calculateUnscaledVolume(uint8 channel, uint8 source, uint8 velocity, const OplInstrumentDefinition &instrumentDef, uint8 operatorNum) {
// Worx calculates volume by scaling the instrument operator volume by the
// channel volume. Note velocity is not used.
uint8 operatorVolume = 0x3F - (instrumentDef.getOperatorDefinition(operatorNum).level & 0x3F);
uint8 channelVolume = _controlData[source][channel].volume;
// Note: the original code loses 1 bit of precision here (bit 0 is always 0).
uint8 unscaledVolume = (operatorVolume * channelVolume) >> 7;
return 0x3F - unscaledVolume;
}
} // namespace Darkseed

View File

@@ -0,0 +1,65 @@
/* 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 DARKSEED_ADLIB_WORX_H
#define DARKSEED_ADLIB_WORX_H
#include "audio/adlib_ms.h"
namespace Darkseed {
/**
* Implementation of the AdLib code from the Worx Toolkit library,
* based on version 2.1, as used by Dark Seed (CD version).
* Implements the OPL channel allocation algorithm and note frequency
* and volume calculation.
*
* TODO Implementation is incomplete, because Dark Seed does not use
* some functionality of the library:
* - OPL rhythm mode
* - Transpose controllers 0x68 and 0x69 (seems to be buggy)
*/
class MidiDriver_Worx_AdLib : public MidiDriver_ADLIB_Multisource {
private:
/**
* The OPL instrument bank of the Worx Toolkit.
* This was taken from the Dark Seed executable and might have been
* customized for the game.
*/
static const AdLibIbkInstrumentDefinition WORX_INSTRUMENT_BANK[];
/**
* The OPL frequency (F-num) for each octave note.
*/
static const uint16 OPL_NOTE_FREQUENCIES[];
public:
MidiDriver_Worx_AdLib(OPL::Config::OplType oplType, int timerFrequency = OPL::OPL::kDefaultCallbackFrequency);
~MidiDriver_Worx_AdLib();
protected:
uint8 allocateOplChannel(uint8 channel, uint8 source, InstrumentInfo &instrumentInfo) override;
uint16 calculateFrequency(uint8 channel, uint8 source, uint8 note) override;
uint8 calculateUnscaledVolume(uint8 channel, uint8 source, uint8 velocity, const OplInstrumentDefinition &instrumentDef, uint8 operatorNum) override;
};
} // namespace Darkseed
#endif // DARKSEED_ADLIB_WORX_H

File diff suppressed because it is too large Load Diff

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/>.
*
*/
#ifndef DARKSEED_ANIMATION_H
#define DARKSEED_ANIMATION_H
#include "darkseed/objects.h"
#include "darkseed/player.h"
namespace Darkseed {
class Animation {
Player *_player = nullptr;
Objects &_objectVar;
public:
int _animIndexTbl[30];
int _spriteAnimCountdownTimer[30];
bool _isPlayingAnimation_maybe = false;
uint16 _otherNspAnimationType_maybe = 0;
bool _scaleSequence = false;
bool _objRestarted = false;
bool _frameAdvanced = false;
int _nsp_sprite_scaling_y_position = 0;
public:
explicit Animation(Player *player, Objects &objectVar) : _player(player), _objectVar(objectVar) {}
void updateAnimation();
void advanceAnimationFrame(int nspAminIdx);
void dCopAnim();
void sargoAnim();
void keeperAdmin();
void gancAnim();
void stuffPlayer();
void runDrekethSequence();
void libAnim(bool pickingUpReservedBook);
void setupOtherNspAnimation(int nspAnimIdx, int animId);
private:
void adddrekbutt();
void wonGame();
};
} // End of namespace Darkseed
#endif // DARKSEED_ANIMATION_H

61
engines/darkseed/anm.cpp Normal file
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/>.
*
*/
#include "common/debug.h"
#include "darkseed/anm.h"
namespace Darkseed {
bool Anm::load(const Common::Path &filename, int deltaOffset) {
close();
if (!_file.open(filename)) {
return false;
}
_numRecords = _file.readUint16LE();
_assetFileId = _file.readUint16LE();
_deltaOffset = deltaOffset;
return true;
}
void Anm::close() {
if (_file.isOpen()) {
_file.close();
}
}
bool Anm::getImg(uint16 index, Img &img, bool includesPosition) {
_file.seek(4 + index * 2);
int offset = _file.readUint16LE() + _deltaOffset;
_file.seek((offset * 16) + (4 + _numRecords * 2));
if (includesPosition) {
img.load(_file);
} else {
img.loadWithoutPosition(_file);
}
debug("Loaded %d (%d,%d) (%d,%d) %x", index, img.getX(), img.getY(), img.getWidth(), img.getHeight(), 0);
return false;
}
int Anm::numImages() const {
return _numRecords;
}
} // namespace Darkseed

46
engines/darkseed/anm.h Normal file
View File

@@ -0,0 +1,46 @@
/* 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 DARKSEED_ANM_H
#define DARKSEED_ANM_H
#include "common/array.h"
#include "darkseed/img.h"
namespace Darkseed {
class Anm {
private:
Common::File _file;
uint16 _numRecords = 0;
uint16 _assetFileId = 0;
int _deltaOffset = 0;
public:
bool load(const Common::Path &filename, int deltaOffset = 0);
void close();
bool getImg(uint16 index, Img &img, bool includesPosition = true);
int numImages() const;
};
} // namespace Darkseed
#endif // DARKSEED_ANM_H

View File

@@ -0,0 +1,82 @@
/* 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 "darkseed/darkseed.h"
#include "darkseed/big5font.h"
#include "graphics/fonts/dosfont.h"
namespace Darkseed {
Big5Font::Big5Font() {
Common::File fontData;
if (!fontData.open("big5font_game.dat")) {
error("Error: failed to open big5font_game.dat");
}
_big5.loadPrefixedRaw(fontData, 15);
fontData.close();
}
int Big5Font::getFontHeight() const {
return 15;
}
int Big5Font::getMaxCharWidth() const {
return 17;
}
int Big5Font::getCharWidth(uint32 chr) const {
if (_big5.hasGlyphForBig5Char(chr)) {
return getMaxCharWidth();
}
return 9;
}
void Big5Font::drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const {
Common::Point charPos = {(int16)x, (int16)y};
if (_big5.drawBig5Char(g_engine->_screen->surfacePtr(), chr, charPos, 0xf)) {
charPos.x++;
_big5.drawBig5Char(g_engine->_screen->surfacePtr(), chr, charPos, 0xc);
charPos.x += Graphics::Big5Font::kChineseTraditionalWidth + 1;
} else if (chr < 128) {
drawBiosFontGlyph(chr, x, y, 0xf);
drawBiosFontGlyph(chr, x+1, y, 0xc);
}
}
void Big5Font::drawBiosFontGlyph(uint8 chr, int x, int y, uint8 color) const {
byte *ptr = (byte *)g_engine->_screen->getBasePtr(x, y);
int srcPixel = chr * 8;
for (int sy = 0; sy < 8; sy++) {
for (int sx = 0; sx < 8; sx++) {
if (Graphics::DosFont::fontData_PCBIOS[srcPixel] & 1 << (7 - sx)) {
*ptr = color;
ptr[g_engine->_screen->pitch] = color;
}
ptr++;
}
srcPixel++;
ptr -= 8;
ptr += (g_engine->_screen->pitch * 2);
}
}
} // namespace Darkseed

View File

@@ -0,0 +1,48 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef DARKSEED_BIG5FONT_H
#define DARKSEED_BIG5FONT_H
#include "graphics/font.h"
#include "graphics/big5.h"
#include "graphics/surface.h"
namespace Darkseed {
class Big5Font : public Graphics::Font {
private:
Graphics::Big5Font _big5;
public:
Big5Font();
int getFontHeight() const override;
int getMaxCharWidth() const override;
int getCharWidth(uint32 chr) const override;
void drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const override;
private:
void drawBiosFontGlyph(uint8 chr, int x, int y, uint8 color) const;
};
} // namespace Darkseed
#endif // DARKSEED_BIG5FONT_H

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 darkseed "Darkseed" yes "" "" "highres" "midi"

View File

@@ -0,0 +1,139 @@
/* 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 "darkseed/big5font.h"
#include "darkseed/kofont.h"
#include "darkseed/console.h"
#include "darkseed/darkseed.h"
namespace Darkseed {
static constexpr Common::Rect consoleArea = {{0x70, 277}, 416, 51};
Console::Console(TosText *tosText, Sound *sound) : _tosText(tosText), _sound(sound) {
switch (g_engine->getLanguage()) {
case Common::ZH_ANY :
_font = new Big5Font();
_lineHeight = 17;
_numLines = 3;
_isCJKLanguage = true;
break;
case Common::KO_KOR :
_font = new KoFont();
_lineHeight = 18;
_numLines = 3;
_isCJKLanguage = true;
break;
default:
_font = new GameFont();
_lineHeight = 11;
_numLines = 4;
break;
}
_text.resize(10);
}
Console::~Console() {
delete _font;
}
void Console::clear() {
_text.clear();
_text.resize(10);
_startIdx = 0;
_redrawRequired = true;
}
void Console::printTosText(int tosIndex, bool shouldAddToCurrentLine) {
if (g_engine->isDosDemo()) {
debug("TosIndex: %d", tosIndex);
}
const Common::U32String &text = _tosText->getText(tosIndex);
if (!_isCJKLanguage) {
debug("%s", text.encode().c_str());
} else {
debugN("tos %d: ", tosIndex);
for (uint i = 0; i < text.size(); i++) {
debugN("%02x,", (unsigned char)text[i]);
}
debug("%s", "");
}
if (shouldAddToCurrentLine) {
addToCurrentLine(text);
} else {
addTextLine(text);
}
_sound->playTosSpeech(tosIndex);
}
void Console::addToCurrentLine(const Common::String &text) {
addToCurrentLineU32(Common::U32String(text));
}
void Console::addTextLine(const Common::U32String &text) {
Common::U32StringArray lines;
_font->wordWrapText(text, consoleArea.width(), lines);
for (auto &line : lines) {
addLine(line);
}
}
void Console::addToCurrentLineU32(const Common::U32String &text) {
int curIdx = _startIdx == 0 ? _text.size() - 1 : _startIdx - 1;
_startIdx = curIdx;
addTextLine(_text[_startIdx] + text);
}
void Console::addI18NText(const I18nText &text) {
addTextLine(getI18NText(text));
}
void Console::draw(bool forceRedraw) {
if (!_redrawRequired && !forceRedraw) {
return;
}
g_engine->_screen->fillRect(consoleArea, 0);
int curIdx = _startIdx == 0 ? _text.size() - 1 : _startIdx - 1;
int y = 0x139;
for (int i = 0; i < _numLines && curIdx != _startIdx && !_text[curIdx].empty(); i++) {
drawStringAt(0x70, y, _text[curIdx]);
y -= _lineHeight;
curIdx = curIdx == 0 ? _text.size() - 1 : curIdx - 1;
}
_redrawRequired = false;
g_engine->_screen->addDirtyRect(consoleArea);
}
void Console::drawStringAt(const int x, const int y, const Common::U32String &text) const {
_font->drawString(g_engine->_screen, text, x, y, consoleArea.width(), 0);
}
void Console::addLine(const Common::U32String &line) {
_text[_startIdx] = line;
_startIdx = (_startIdx + 1) % _text.size();
_redrawRequired = true;
}
} // End of namespace Darkseed

View File

@@ -0,0 +1,67 @@
/* 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 DARKSEED_CONSOLE_H
#define DARKSEED_CONSOLE_H
#include "graphics/font.h"
#include "darkseed/sound.h"
#include "darkseed/tostext.h"
#include "darkseed/langtext.h"
namespace Darkseed {
class Console {
private:
TosText *_tosText;
Graphics::Font *_font;
Sound *_sound;
Common::U32StringArray _text;
int _startIdx = 0;
bool _redrawRequired = false;
int _numLines = 4;
int _lineHeight = 11;
bool _isCJKLanguage = false;
public:
Console(TosText *tostext, Sound *sound);
~Console();
void clear();
void printTosText(int tosIndex, bool shouldAddToCurrentLine = false);
void addTextLine(const Common::U32String &text);
void addToCurrentLine(const Common::String &text);
void addToCurrentLineU32(const Common::U32String &text);
void addI18NText(const I18nText &text);
void draw(bool forceRedraw = false);
void drawStringAt(int x, int y, const Common::U32String &text) const;
private:
void addLine(const Common::U32String &line);
};
} // End of namespace Darkseed
#endif // DARKSEED_CONSOLE_H

View File

@@ -0,0 +1,3 @@
begin_section("Darkseed");
add_person("Eric Fry", "Yuv422", "");
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 "graphics/cursorman.h"
#include "darkseed/cursor.h"
#include "darkseed/darkseed.h"
namespace Darkseed {
void Cursor::setCursorType(enum CursorType newType) {
bool loadCursor = !_hasLoadedCursor || _currentCursorType != newType;
_currentCursorType = newType;
if (loadCursor) {
const Sprite cursorSprite = g_engine->_baseSprites.getSpriteAt(_currentCursorType);
Graphics::Surface surf;
surf.create(cursorSprite._width, cursorSprite._height, Graphics::PixelFormat::createFormatCLUT8());
surf.copyRectToSurface(cursorSprite._pixels.data(), cursorSprite._pitch, 0, 0, cursorSprite._width, cursorSprite._height);
CursorMan.replaceCursor(surf, 0, 0, 0xf);
surf.free();
}
}
void Cursor::updatePosition(int16 x, int16 y) {
_position.x = x;
_position.y = y;
// debug("mouse at (%d,%d)", _x, _y);
}
int Cursor::getWidth() const {
return g_engine->_baseSprites.getSpriteAt(_currentCursorType)._width;
}
int Cursor::getHeight() const {
return g_engine->_baseSprites.getSpriteAt(_currentCursorType)._height;
}
const Sprite &Darkseed::Cursor::getSprite() const {
return g_engine->_baseSprites.getSpriteAt(_currentCursorType);
}
const Sprite &Darkseed::Cursor::getSpriteForType(Darkseed::CursorType cursorType) const {
return g_engine->_baseSprites.getSpriteAt(cursorType);
}
void Cursor::showCursor(bool showCursor) {
CursorMan.showMouse(showCursor);
}
} // End of namespace Darkseed

81
engines/darkseed/cursor.h Normal file
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 DARKSEED_CURSOR_H
#define DARKSEED_CURSOR_H
#include "common/rect.h"
#include "darkseed/nsp.h"
namespace Darkseed {
enum CursorType {
Pointer = 0,
Hand = 2,
Look = 3,
HourGlass = 91,
ConnectorEntrance = 92,
HandPointing = 93,
ExclamationMark = 94,
};
class Cursor {
private:
Common::Point _position;
enum CursorType _currentCursorType = Pointer;
bool _hasLoadedCursor = false;
public:
void showCursor(bool showCursor);
void setCursorType(enum CursorType newType);
CursorType getCursorType() const {
return _currentCursorType;
}
Common::Point &getPosition() {
return _position;
}
void setPosition(Common::Point &position) {
_position = position;
}
int16 getX() const {
return _position.x;
}
int16 getY() const {
return _position.y;
}
Common::Rect getRect() const {
return Common::Rect(_position, getWidth(), getHeight());
}
int getWidth() const;
int getHeight() const;
const Sprite &getSprite() const;
const Sprite &getSpriteForType(CursorType cursorType) const;
void updatePosition(int16 x, int16 y);
};
} // End of namespace Darkseed
#endif // DARKSEED_CURSOR_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,87 @@
/* 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 DARKSEED_CUTSCENE_H
#define DARKSEED_CUTSCENE_H
#include "darkseed/morph.h"
#include "darkseed/pal.h"
#include "darkseed/titlefont.h"
#include "zhmenufont.h"
namespace Darkseed {
struct I18NTextWithPosition;
class Cutscene {
char _cutsceneId = 0;
uint16 _movieStep = 9999;
TitleFont *_titleFont = nullptr;
ZhMenuFont *_zhFont = nullptr;
Pal _palette;
Anm _animation;
int _animIdx = 0;
int _animCount = 0;
int _animDelayCount = 0;
int _animDirection = 0;
uint32 _startTime = 0;
Morph *_morph = nullptr;
int _valvesIdx = 0;
int _faceIdx = 0;
public:
Cutscene() {}
virtual ~Cutscene();
void play(char cutsceneId);
bool isPlaying() const {
return _movieStep != 9999;
}
void update();
private:
bool introScene();
bool embryoInsertedScene();
bool shipLaunchScene();
bool alienBornScene();
bool babyDollScene();
bool bookScene();
bool nightmare2Scene();
bool nightmare3Scene();
void runAnim(int direction = 1);
bool stepAnim(int drawMode = 1);
bool stepValveAnim(bool doFaceAnim);
void putHouse();
void registTime();
bool waitTime(int16 duration) const;
void freeMorph();
void displayTitleText(const I18NTextWithPosition &text);
void displayZhString(const char *text, int y);
};
} // namespace Darkseed
#endif // DARKSEED_CUTSCENE_H

File diff suppressed because it is too large Load Diff

298
engines/darkseed/darkseed.h Normal file
View File

@@ -0,0 +1,298 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef DARKSEED_H
#define DARKSEED_H
#include "common/error.h"
#include "common/fs.h"
#include "common/keyboard.h"
#include "common/random.h"
#include "common/scummsys.h"
#include "common/serializer.h"
#include "common/system.h"
#include "common/util.h"
#include "engines/engine.h"
#include "graphics/screen.h"
#include "darkseed/animation.h"
#include "darkseed/console.h"
#include "darkseed/cursor.h"
#include "darkseed/cutscene.h"
#include "darkseed/detection.h"
#include "darkseed/inventory.h"
#include "darkseed/menu.h"
#include "darkseed/nsp.h"
#include "darkseed/objects.h"
#include "darkseed/player.h"
#include "darkseed/room.h"
#include "darkseed/sound.h"
#include "darkseed/sprites.h"
#include "darkseed/tostext.h"
#include "darkseed/usecode.h"
namespace Darkseed {
struct DarkseedGameDescription;
enum DarkseedAction {
kDarkseedActionNone,
kDarkseedActionSelect,
kDarkseedActionChangeCommand,
kDarkseedActionTimeAdvance,
kDarkseedActionQuit,
kDarkseedActionSkipCutscene
};
enum ActionMode : uint8 {
kPointerAction = 0,
kHandAction = 2,
kLookAction = 3,
kUseStickAction = 19,
kUseHammerAction = 27,
};
enum class FadeDirection : uint8 {
NONE,
IN,
OUT
};
class DarkseedEngine : public Engine {
const ADGameDescription *_gameDescription;
Common::RandomSource _randomSource;
Pic *_fullscreenPic = nullptr;
bool _timeAdvanceEventSelected = false;
uint8 _delbertspeech = 0;
int16 _yvec = 0; //delbert throw stick related.
bool _normalWorldSpritesLoaded = true;
bool _redrawFrame = true;
bool _restartGame = false;
bool _canSaveGame = false;
FadeDirection _fadeDirection = FadeDirection::NONE;
uint8 _fadeStepCounter = 0;
Pal _fadeTempPalette;
Pal _fadeTargetPalette;
protected:
// Engine APIs
Common::Error run() override;
public:
Pic _frame;
bool _ct_voice_status = false;
bool _isRightMouseClicked = false;
bool _isLeftMouseClicked = false;
Common::KeyCode _lastKeyPressed = Common::KeyCode::KEYCODE_INVALID;
Sound *_sound = nullptr;
Nsp _baseSprites;
Cursor _cursor;
Graphics::Screen *_screen = nullptr;
TosText *_tosText = nullptr;
Console *_console = nullptr;
Room *_room = nullptr;
int _actionMode = kPointerAction;
Player *_player = nullptr;
Sprites _sprites;
Objects _objectVar;
Inventory _inventory;
UseCode *_useCode = nullptr;
Cutscene _cutscene;
Animation *_animation = nullptr;
Menu *_menu = nullptr;
uint8 _currentDay = 1;
int _currentTimeInSeconds = 0x7e8e;
int _fttime = 0;
uint8 _previousRoomNumber = 0;
uint16 _targetRoomNumber = 0;
uint16 _headAcheMessageCounter = 0;
uint8 _headacheMessageIdx = 0;
int _sprite_y_scaling_threshold_maybe = 0xf0;
int _scaledWalkSpeed_maybe = 0;
uint16 _scaledSpriteWidth = 0;
uint16 _scaledSpriteHeight = 0;
int _frameBottom = 0;
// Unknown variables
bool _doorEnabled = false;
bool _useDoorTarget = false;
int16 _counter_2c85_888b = 0;
uint8 _targetPlayerDirection = 0; // related to changing rooms.
uint8 _systemTimerCounter = 0;
bool _debugShowWalkPath = false;
int _phoneStatus = 0;
int16 _soundTimer = 0;
bool _printedcomeheredawson = false;
void zeroMouseButtons();
void updateEvents();
void gotoNextMorning();
void playDayChangeCutscene();
void removeFullscreenPic();
void wait();
void waitForSpeech();
void waitForSpeechOrSfx();
void syncSoundSettings() override;
void pauseEngineIntern(bool pause) override;
DarkseedEngine(OSystem *syst, const ADGameDescription *gameDesc);
~DarkseedEngine() override;
uint32 getFeatures() const;
Common::Language getLanguage() const {
return _gameDescription->language;
}
/**
* Returns the game Id
*/
Common::String getGameId() const;
/**
* Gets a random number
*/
uint32 getRandomNumber(uint maxNum) {
return _randomSource.getRandomNumber(maxNum);
}
bool isDosVersion() const {
return _gameDescription->platform == Common::kPlatformDOS;
}
bool isCdVersion() const {
return getFeatures() & ADGF_CD;
}
bool isDosFloppy() const {
return isDosVersion() && !isCdVersion();
}
bool isDosDemo() const {
return isDosVersion() && getFeatures() & ADGF_DEMO;
}
bool hasFeature(EngineFeature f) const override {
return
(f == kSupportsLoadingDuringRuntime) ||
(f == kSupportsSavingDuringRuntime) ||
(f == kSupportsReturnToLauncher);
};
bool canLoadGameStateCurrently(Common::U32String *msg) override {
return !isDosDemo() && !_animation->_isPlayingAnimation_maybe && !_player->_isAutoWalkingToBed && !_player->_heroWaiting && !_cutscene.isPlaying();
}
bool canSaveGameStateCurrently(Common::U32String *msg) override {
return _canSaveGame && !_animation->_isPlayingAnimation_maybe && !_player->_isAutoWalkingToBed && !_player->_heroWaiting && !_cutscene.isPlaying() && !_menu->isOpen();
}
/**
* Uses a serializer to allow implementing savegame
* loading and saving using a single method
*/
Common::Error syncGame(Common::Serializer &s);
Common::Error saveGameStream(Common::WriteStream *stream, bool isAutosave = false) override {
Common::Serializer s(nullptr, stream);
return syncGame(s);
}
Common::Error loadGameStream(Common::SeekableReadStream *stream) override {
Common::Serializer s(stream, nullptr);
Common::Error syncResult = syncGame(s);
if (syncResult.getCode() == Common::kNoError) {
changeToRoom(_room->_roomNumber);
}
_canSaveGame = true;
return syncResult;
}
Common::Path getRoomFilePath(const Common::Path &filename) const;
Common::Path getPictureFilePath(const Common::Path &filename) const;
void fadeIn(const Pal &palette);
void fadeOut();
bool fadeStep();
void restartGame();
void newGame();
void updateDisplay();
void debugTeleportToRoom(int newRoomNumber, int entranceNumber);
void showFullscreenPic(const Common::Path &filename);
void drawFullscreenPic();
void lookCode(int objNum);
void handleObjCollision(int targetObjNum);
void playSound(uint8 sfxId, uint8 priority, int16 unk2);
void nextFrame(int nspAminIdx);
void throwmikeinjail();
void runObjects();
void getPackageObj(int packageType);
void printTime();
void changeToRoom(int newRoomNumber, bool placeDirectly = false);
void waitxticks(int ticks);
void doCircles();
private:
void updateBaseSprites();
void gameLoop();
void handleInput();
void handlePointerAction();
void loadRoom(int roomNumber);
void gotoSleepInJail();
void updateHeadache();
void closeShops();
void initDelbertAtSide();
void movePlayerToDelbert();
void delbertThrowStick(int16 spriteNum);
void leavePackage();
void copyLine(const Graphics::Surface &surface, int16 x1, int16 x2, int16 y);
};
extern DarkseedEngine *g_engine;
#define SHOULD_QUIT ::Darkseed::g_engine->shouldQuit()
} // End of namespace Darkseed
#endif // DARKSEED_H

View File

@@ -0,0 +1,291 @@
/* 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 "darkseed/darkseed.h"
#include "darkseed/debugconsole.h"
namespace Darkseed {
DebugConsole::DebugConsole(TosText *tosText) : _tosText(tosText) {
registerCmd("tostext", WRAP_METHOD(DebugConsole, Cmd_tostext));
registerCmd("dt", WRAP_METHOD(DebugConsole, Cmd_dt));
registerCmd("getvar", WRAP_METHOD(DebugConsole, Cmd_getvar));
registerCmd("setvar", WRAP_METHOD(DebugConsole, Cmd_setvar));
registerCmd("enablePathfinderOverlay", WRAP_METHOD(DebugConsole, Cmd_enablePathfinderOverlay));
registerCmd("info", WRAP_METHOD(DebugConsole, Cmd_info));
registerCmd("gotoRoom", WRAP_METHOD(DebugConsole, Cmd_gotoRoom));
registerCmd("invAdd", WRAP_METHOD(DebugConsole, Cmd_invAdd));
registerCmd("invRemove", WRAP_METHOD(DebugConsole, Cmd_invRemove));
registerCmd("changeDay", WRAP_METHOD(DebugConsole, Cmd_changeDay));
registerCmd("searchTos", WRAP_METHOD(DebugConsole, Cmd_searchTos));
registerCmd("playMusic", WRAP_METHOD(DebugConsole, Cmd_playMusic));
registerCmd("stopMusic", WRAP_METHOD(DebugConsole, Cmd_stopMusic));
registerCmd("playSfx", WRAP_METHOD(DebugConsole, Cmd_playSfx));
registerCmd("playFloppySfx", WRAP_METHOD(DebugConsole, Cmd_playFloppySfx));
registerCmd("playSpeech", WRAP_METHOD(DebugConsole, Cmd_playSpeech));
}
DebugConsole::~DebugConsole() {
}
bool DebugConsole::Cmd_tostext(int argc, const char **argv) {
if (argc != 2) {
debugPrintf("Usage: tostext <index>\n");
return true;
}
uint16 textIdx = atoi(argv[1]);
if (textIdx < _tosText->getNumEntries()) {
debugPrintf("%s\n", _tosText->getText(textIdx).encode().c_str());
} else {
debugPrintf("index too large!\n");
}
return true;
}
bool DebugConsole::Cmd_dt(int argc, const char **argv) {
printDayAndTime();
return true;
}
bool DebugConsole::Cmd_getvar(int argc, const char **argv) {
if (argc != 2) {
debugPrintf("Usage: getvar <index>\n");
return true;
}
int16 varIdx = (int16)atoi(argv[1]);
if (validateObjVarIndex(varIdx)) {
debugPrintf("Object Var: %d\n", g_engine->_objectVar.getVar(varIdx));
}
return true;
}
bool DebugConsole::Cmd_setvar(int argc, const char **argv) {
if (argc != 3) {
debugPrintf("Usage: setvar <index> <newValue>\n");
return true;
}
int16 varIdx = (int16)atoi(argv[1]);
int16 newValue = (int16)atoi(argv[2]);
if (validateObjVarIndex(varIdx)) {
g_engine->_objectVar[varIdx] = newValue;
}
return true;
}
bool DebugConsole::Cmd_enablePathfinderOverlay(int argc, const char **argv) {
if (argc != 2) {
debugPrintf("Usage: enablePathfinderOverlay <true | t | false | f>\n");
return true;
}
if (!strcmp(argv[1], "true") || !strcmp(argv[1], "t")) {
g_engine->_debugShowWalkPath = true;
} else if (!strcmp(argv[1], "false") || !strcmp(argv[1], "f")) {
g_engine->_debugShowWalkPath = false;
}
return true;
}
bool DebugConsole::validateObjVarIndex(int16 varIdx) {
if (varIdx >= Objects::MAX_OBJECTS) {
debugPrintf("Index must be less than %d\n", Objects::MAX_OBJECTS);
return false;
}
if (varIdx < 0) {
debugPrintf("Index cannot be negative\n");
return false;
}
return true;
}
bool DebugConsole::Cmd_info(int argc, const char **argv) {
printDayAndTime();
debugPrintf("\nRoom info:\n");
debugPrintf("Room number: %d\n", g_engine->_room->_roomNumber);
return true;
}
bool DebugConsole::Cmd_gotoRoom(int argc, const char **argv) {
if (argc < 2 || argc > 3) {
debugPrintf("Usage: gotoRoom <roomNumber> <entranceNumber>\n");
return true;
}
int16 roomNumber = (int16)atoi(argv[1]);
int entranceNumber = 0;
if (argc == 3) {
entranceNumber = (int16)atoi(argv[2]);
}
g_engine->debugTeleportToRoom(roomNumber, entranceNumber);
return true;
}
bool DebugConsole::Cmd_invAdd(int argc, const char **argv) {
if (argc != 2) {
debugPrintf("Usage: invAdd <objNum>\n");
return true;
}
uint8 objNum = (uint8)atoi(argv[1]);
g_engine->_inventory.addItem(objNum);
return true;
}
bool DebugConsole::Cmd_invRemove(int argc, const char **argv) {
if (argc != 2) {
debugPrintf("Usage: invRemove <objNum>\n");
return true;
}
uint8 objNum = (uint8)atoi(argv[1]);
g_engine->_inventory.removeItem(objNum);
return true;
}
bool DebugConsole::Cmd_changeDay(int argc, const char **argv) {
if (argc != 2) {
debugPrintf("Usage: changeDay <newDay>\n");
return true;
}
uint8 newDay = (uint8)atoi(argv[1]);
if (newDay < 1 || newDay > 3) {
debugPrintf("Error: Day must be in range of 1 .. 3\n");
return true;
}
g_engine->_currentDay = newDay;
debugPrintf("Current day changed.\n");
printDayAndTime();
return true;
}
bool DebugConsole::Cmd_searchTos(int argc, const char **argv) {
if (argc != 2) {
debugPrintf("Usage: searchTos \"search string\"\n");
return true;
}
Common::String searchString = Common::String(argv[1]);
searchString.toLowercase();
for (int i = 0; i < g_engine->_tosText->getNumEntries(); i++) {
Common::String entry = g_engine->_tosText->getText(i);
entry.toLowercase();
if (entry.contains(searchString)) {
debugPrintf("% 3d: %s\n", i, entry.c_str());
}
}
return true;
}
bool DebugConsole::Cmd_playMusic(int argc, const char **argv) {
if (argc != 2) {
debugPrintf("Usage: playMusic <trackNumber>\n");
return true;
}
uint8 trackNumber = (uint8)atoi(argv[1]);
if (trackNumber > 19) {
debugPrintf("Error: track number must be in range of 0 .. 19\n");
return true;
}
if (trackNumber < 8) {
g_engine->_sound->playMusic(static_cast<StartMusicId>(trackNumber));
}
else {
g_engine->_sound->playMusic(static_cast<MusicId>(trackNumber - 7), false);
}
return true;
}
bool DebugConsole::Cmd_stopMusic(int argc, const char** argv) {
g_engine->_sound->stopMusic();
return true;
}
bool DebugConsole::Cmd_playSfx(int argc, const char** argv) {
if (argc != 2) {
debugPrintf("Usage: playSfx <sfxNumber>\n");
return true;
}
uint8 sfxId = (uint8)atoi(argv[1]);
if (sfxId > 59) {
debugPrintf("Error: SFX number must be in range of 0 .. 59\n");
return true;
}
g_engine->_sound->playSfx(sfxId, 1, -1);
return true;
}
bool DebugConsole::Cmd_playFloppySfx(int argc, const char **argv) {
if (argc != 2) {
debugPrintf("Usage: playFloppySfx <sfxNumber>\n");
return true;
}
uint8 sfxId = (uint8)atoi(argv[1]);
if (sfxId < 10 || sfxId > 119) {
debugPrintf("Error: SFX number must be in range of 10 .. 119\n");
return true;
}
g_engine->_sound->playDosFloppySfx(sfxId, 1);
return true;
}
bool DebugConsole::Cmd_playSpeech(int argc, const char **argv) {
if (argc != 2) {
debugPrintf("Usage: playSpeech <speechNumber>\n");
return true;
}
int speechId = atoi(argv[1]);
if (speechId < 0 || speechId > 998) {
debugPrintf("Error: speech number must be in range of 0 .. 998\n");
return true;
}
g_engine->_sound->playTosSpeech(speechId);
return true;
}
void DebugConsole::printDayAndTime() {
int hour = g_engine->_currentTimeInSeconds / 3600;
debugPrintf("Day %d at %d:%02d%s (%d seconds)\n",
g_engine->_currentDay,
hour > 12 ? hour - 12 : hour,
(g_engine->_currentTimeInSeconds / 60) % 60,
hour < 12 ? "AM" : "PM", g_engine->_currentTimeInSeconds);
}
} // End of namespace Darkseed

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 DARKSEED_DEBUGCONSOLE_H
#define DARKSEED_DEBUGCONSOLE_H
#include "gui/debugger.h"
#include "darkseed/tostext.h"
namespace Darkseed {
class DebugConsole : public GUI::Debugger {
TosText *_tosText;
bool Cmd_tostext(int argc, const char **argv);
bool Cmd_dt(int argc, const char **argv);
bool Cmd_getvar(int argc, const char **argv);
bool Cmd_setvar(int argc, const char **argv);
bool Cmd_enablePathfinderOverlay(int argc, const char **argv);
bool Cmd_info(int argc, const char **argv);
bool Cmd_gotoRoom(int argc, const char **argv);
bool Cmd_invAdd(int argc, const char **argv);
bool Cmd_invRemove(int argc, const char **argv);
bool Cmd_changeDay(int argc, const char **argv);
bool Cmd_searchTos(int argc, const char **argv);
bool Cmd_playMusic(int argc, const char **argv);
bool Cmd_stopMusic(int argc, const char **argv);
bool Cmd_playSfx(int argc, const char **argv);
bool Cmd_playFloppySfx(int argc, const char **argv);
bool Cmd_playSpeech(int argc, const char **argv);
bool validateObjVarIndex(int16 varIdx);
void printDayAndTime();
public:
DebugConsole(TosText *tostext);
~DebugConsole() override;
};
} // End of namespace Darkseed
#endif // DARKSEED_DEBUGCONSOLE_H

View File

@@ -0,0 +1,39 @@
/* 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 "common/file.h"
#include "darkseed/detection.h"
#include "darkseed/detection_tables.h"
const DebugChannelDef DarkseedMetaEngineDetection::debugFlagList[] = {
{ Darkseed::kDebugGraphics, "Graphics", "Graphics debug level" },
{ Darkseed::kDebugPath, "Path", "Pathfinding debug level" },
{ Darkseed::kDebugFilePath, "FilePath", "File path debug level" },
{ Darkseed::kDebugScan, "Scan", "Scan for unrecognised games" },
{ Darkseed::kDebugScript, "Script", "Enable debug script dump" },
DEBUG_CHANNEL_END
};
DarkseedMetaEngineDetection::DarkseedMetaEngineDetection() : AdvancedMetaEngineDetection(Darkseed::gameDescriptions, Darkseed::darkseedGames) {
}
REGISTER_PLUGIN_STATIC(DARKSEED_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, DarkseedMetaEngineDetection);

View File

@@ -0,0 +1,77 @@
/* 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 DARKSEED_DETECTION_H
#define DARKSEED_DETECTION_H
#include "engines/advancedDetector.h"
namespace Darkseed {
enum DarkseedDebugChannels {
kDebugGraphics = 1,
kDebugPath,
kDebugScan,
kDebugFilePath,
kDebugScript,
};
extern const PlainGameDescriptor darkseedGames[];
extern const ADGameDescription gameDescriptions[];
#define GAMEOPTION_ORIGINAL_SAVELOAD GUIO_GAMEOPTIONS1
#define GAMEOPTION_FLOPPY_MUSIC GUIO_GAMEOPTIONS2
#define GAMEOPTION_SFX_MODE GUIO_GAMEOPTIONS3
} // End of namespace Darkseed
class DarkseedMetaEngineDetection : public AdvancedMetaEngineDetection<ADGameDescription> {
static const DebugChannelDef debugFlagList[];
public:
DarkseedMetaEngineDetection();
~DarkseedMetaEngineDetection() override {}
const char *getName() const override {
return "darkseed";
}
const char *getEngineName() const override {
return "Darkseed";
}
const char *getOriginalCopyright() const override {
return "(C) 1992 CYBERDREAMS, INC.";
}
const DebugChannelDef *getDebugChannels() const override {
return debugFlagList;
}
};
enum DarkseedSfxMode {
SFX_MODE_CD_ONLY,
SFX_MODE_CD_PLUS_FLOPPY,
SFX_MODE_FLOPPY_ONLY
};
#endif // DARKSEED_DETECTION_H

View File

@@ -0,0 +1,150 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace Darkseed {
const PlainGameDescriptor darkseedGames[] = {
{ "darkseed", "Darkseed" },
{ 0, 0 }
};
const ADGameDescription gameDescriptions[] = {
{
"darkseed",
nullptr,
AD_ENTRY1s("TOS.EXE", "e20ca609f2acb623e0076ef2288673b2", 147016),
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GUIO_NONE)
},
{ // version 1.2
"darkseed",
nullptr,
AD_ENTRY1s("TOS.EXE", "9fad9d0a4b5e7d0657172593750c7b81", 214784),
Common::DE_DEU,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GUIO_NONE)
},
{
"darkseed",
nullptr,
AD_ENTRY1s("TOS.EXE", "73854f950819beb0eb0d73bd52cc9030", 149412),
Common::ES_ESP,
Common::kPlatformDOS,
ADGF_UNSTABLE,
GUIO1(GUIO_NONE)
},
{
"darkseed",
nullptr,
AD_ENTRY1s("TOS.EXE", "ba87f00c3a51ca3e3bb218fc58f128eb", 121662),
Common::ZH_ANY,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GUIO_NONE)
},
{ // unpacked exe
"darkseed",
"unpacked",
AD_ENTRY1s("TOS.EXE", "0fc2751aa16cac26ad3aa9d1cbbb5c7b", 209208),
Common::ZH_ANY,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GUIO_NONE)
},
{
"darkseed",
nullptr,
AD_ENTRY1s("TOS.EXE", "62a636d3d6b19336d059cd2f8b1a365f", 154450),
Common::KO_KOR,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GUIO_NONE)
},
{
"darkseed",
"CD",
AD_ENTRY1s("TOS.EXE", "679abf5829b2453d30b17caabafea168", 168432),
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_CD,
GUIO2(GAMEOPTION_FLOPPY_MUSIC, GAMEOPTION_SFX_MODE)
},
{ // 1.51 according to DS.BAT, 1.5P according to intro
"darkseed",
"CD",
AD_ENTRY1s("TOS.EXE", "afaeb490ef8e7625008867aa8f20c703", 168480),
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_CD,
GUIO2(GAMEOPTION_FLOPPY_MUSIC, GAMEOPTION_SFX_MODE)
},
{
"darkseed",
"CD",
AD_ENTRY1s("TOS.EXE", "57581682c29fc7d242b463210b6e54b4", 144422),
Common::DE_DEU,
Common::kPlatformDOS,
ADGF_CD,
GUIO2(GAMEOPTION_FLOPPY_MUSIC, GAMEOPTION_SFX_MODE)
},
{
"darkseed",
"CD",
AD_ENTRY1s("TOS.EXE", "9b8cdd3b4268d18babf7629fca6a271e", 143534),
Common::FR_FRA,
Common::kPlatformDOS,
ADGF_CD,
GUIO2(GAMEOPTION_FLOPPY_MUSIC, GAMEOPTION_SFX_MODE)
},
{
"darkseed",
"CD",
AD_ENTRY1s("TOS.EXE", "3c00f3e80fa2c40641278243f96b8398", 170944),
Common::ES_ESP,
Common::kPlatformDOS,
ADGF_CD,
GUIO2(GAMEOPTION_FLOPPY_MUSIC, GAMEOPTION_SFX_MODE)
},
{
"darkseed",
"CD",
AD_ENTRY1s("DARKSEED.EXE", "27321d178a553c4dc17d1a2a601a9a6f", 1432140),
Common::JA_JPN,
Common::kPlatformWindows,
ADGF_UNSTABLE | ADGF_CD,
GUIO1(GUIO_NONE)
},
{
"darkseed",
"DEMO",
AD_ENTRY1s("TOS.EXE", "43014b73a2cc549bd13d65b18a6aefe4", 161184),
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_DEMO,
GUIO1(GUIO_NONE)
},
AD_TABLE_END_MARKER
};
} // End of namespace Darkseed

View File

@@ -0,0 +1,144 @@
/* 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 "common/file.h"
#include "common/hashmap.h"
#include "common/ptr.h"
#include "common/translation.h"
#include "engines/advancedDetector.h"
#include "darkseed/detection.h"
#include "darkseed/dialogs.h"
namespace Darkseed {
OptionsWidget::OptionsWidget(GuiObject *boss, const Common::String &name, const Common::String &domain) : OptionsContainerWidget(boss, name, "DarkseedGameOptionsDialog", domain) {
_guiOptions = ConfMan.get("guioptions", domain);
for (const ADExtraGuiOptionsMap *entry = optionsList; entry->guioFlag; ++entry)
if (checkGameGUIOption(entry->guioFlag, _guiOptions))
_checkboxes[entry->option.configOption] = new GUI::CheckboxWidget(widgetsBoss(), _dialogLayout + "." + entry->option.configOption, _(entry->option.label), _(entry->option.tooltip));
for (const PopUpOptionsMap *entry = popUpOptionsList; entry->guioFlag; ++entry)
if (checkGameGUIOption(entry->guioFlag, _guiOptions)) {
GUI::StaticTextWidget *textWidget = new GUI::StaticTextWidget(widgetsBoss(), _dialogLayout + "." + entry->configOption + "_desc", _(entry->label), _(entry->tooltip));
textWidget->setAlign(Graphics::kTextAlignRight);
_popUps[entry->configOption] = new GUI::PopUpWidget(widgetsBoss(), _dialogLayout + "." + entry->configOption);
for (uint i = 0; entry->items[i].label; ++i)
_popUps[entry->configOption]->appendEntry(_(entry->items[i].label), entry->items[i].configValue);
}
}
void OptionsWidget::defineLayout(GUI::ThemeEval &layouts, const Common::String &layoutName, const Common::String &overlayedLayout) const {
layouts.addDialog(layoutName, overlayedLayout);
layouts.addLayout(GUI::ThemeLayout::kLayoutVertical).addPadding(0, 0, 0, 0);
for (const ADExtraGuiOptionsMap *entry = optionsList; entry->guioFlag; ++entry)
layouts.addWidget(entry->option.configOption, "Checkbox");
for (const PopUpOptionsMap *entry = popUpOptionsList; entry->guioFlag; ++entry) {
layouts.addLayout(GUI::ThemeLayout::kLayoutHorizontal).addPadding(0, 0, 0, 0);
layouts.addWidget(Common::String(entry->configOption) + "_desc", "OptionsLabel");
layouts.addWidget(entry->configOption, "PopUp").closeLayout();
}
layouts.closeLayout().closeDialog();
}
void OptionsWidget::load() {
for (const ADExtraGuiOptionsMap *entry = optionsList; entry->guioFlag; ++entry)
if (checkGameGUIOption(entry->guioFlag, _guiOptions))
_checkboxes[entry->option.configOption]->setState(ConfMan.getBool(entry->option.configOption, _domain));
for (const PopUpOptionsMap *entry = popUpOptionsList; entry->guioFlag; ++entry)
if (checkGameGUIOption(entry->guioFlag, _guiOptions))
_popUps[entry->configOption]->setSelectedTag(ConfMan.getInt(entry->configOption, _domain));
}
bool OptionsWidget::save() {
for (const ADExtraGuiOptionsMap *entry = optionsList; entry->guioFlag; ++entry)
if (checkGameGUIOption(entry->guioFlag, _guiOptions))
ConfMan.setBool(entry->option.configOption, _checkboxes[entry->option.configOption]->getState(), _domain);
for (const PopUpOptionsMap *entry = popUpOptionsList; entry->guioFlag; ++entry)
if (checkGameGUIOption(entry->guioFlag, _guiOptions))
ConfMan.setInt(entry->configOption, _popUps[entry->configOption]->getSelectedTag(), _domain);
return true;
}
const ADExtraGuiOptionsMap optionsList[] = {
{
GAMEOPTION_ORIGINAL_SAVELOAD,
{
_s("Use original save/load screens"),
_s("Use the original save/load screens instead of the ScummVM ones"),
"original_menus",
false,
0,
0
}
},
{
GAMEOPTION_FLOPPY_MUSIC,
{
_s("Use floppy version music"),
_s("Use the music from the floppy version. The floppy version's music files must be copied to the SOUND directory."),
"use_floppy_music",
false,
0,
0
}
},
AD_EXTRA_GUI_OPTIONS_TERMINATOR
};
const PopUpOptionsMap popUpOptionsList[] = {
{
GAMEOPTION_SFX_MODE,
_s("SFX mode:"),
_s("Determines if the game should use CD version SFX only, CD SFX with additional floppy SFX, or floppy SFX only. Floppy SFX are only available if floppy music is used."),
"sfx_mode",
SFX_MODE_CD_ONLY,
{
{
_s("CD version SFX only"),
SFX_MODE_CD_ONLY
},
{
_s("CD + extra floppy SFX"),
SFX_MODE_CD_PLUS_FLOPPY
},
{
_s("Floppy version SFX only"),
SFX_MODE_FLOPPY_ONLY
},
POPUP_OPTIONS_ITEMS_TERMINATOR
}
},
POPUP_OPTIONS_TERMINATOR
};
} // End of namespace Darkseed

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 DARKSEED_DIALOGS_H
#define DARKSEED_DIALOGS_H
#include "gui/ThemeEval.h"
#include "gui/widget.h"
#include "gui/widgets/popup.h"
namespace Darkseed {
struct PopUpOptionsItem {
const char *label;
int configValue;
};
#define POPUP_OPTIONS_ITEMS_TERMINATOR {nullptr, 0}
struct PopUpOptionsMap {
const char *guioFlag;
const char *label;
const char *tooltip;
const char *configOption;
int defaultState;
PopUpOptionsItem items[10];
};
#define POPUP_OPTIONS_TERMINATOR \
{ \
nullptr, nullptr, nullptr, nullptr, 0, { POPUP_OPTIONS_ITEMS_TERMINATOR } \
}
class OptionsWidget : public GUI::OptionsContainerWidget {
public:
explicit OptionsWidget(GuiObject *boss, const Common::String &name, const Common::String &domain);
// OptionsContainerWidget API
void load() override;
bool save() override;
private:
// OptionsContainerWidget API
void defineLayout(GUI::ThemeEval &layouts, const Common::String &layoutName, const Common::String &overlayedLayout) const override;
Common::String _guiOptions;
Common::HashMap<Common::String, GUI::CheckboxWidget *> _checkboxes;
Common::HashMap<Common::String, GUI::PopUpWidget *> _popUps;
};
extern const ADExtraGuiOptionsMap optionsList[];
extern const PopUpOptionsMap popUpOptionsList[];
} // End of namespace Darkseed
#endif // DARKSEED_DIALOGS_H

View File

@@ -0,0 +1,118 @@
/* 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 "darkseed/darkseed.h"
#include "darkseed/gamefont.h"
namespace Darkseed {
extern DarkseedEngine *g_engine;
GameFont::GameFont() {
if (_letters.load("tosfont.nsp")) {
_maxWidth = _letters.getMaxSpriteWidth() + 1;
} else {
error("Error loading tosfont.nsp");
}
}
const Sprite *GameFont::getCharacterSprite(char c) const {
int letterIdx = 1000;
switch (c) {
case 0x20 :
case 0x2d :
letterIdx = 0x46;
break;
case 0x21 :
letterIdx = 0x36;
break;
case 0x22 :
case 0x5e :
letterIdx = 0x3a;
break;
case 0x27 :
letterIdx = 0x45;
break;
case 0x28 :
letterIdx = 0x37;
break;
case 0x29 :
letterIdx = 0x38;
break;
case 0x2b :
letterIdx = 0xa;
break;
case 0x2c :
letterIdx = 0x34;
break;
case 0x2e :
letterIdx = 0x35;
break;
case 0x3a :
letterIdx = 0x47;
break;
case 0x3f :
letterIdx = 0x39;
break;
default: {
if (c < 0x41 || c > 0x5a) {
if (c < 0x61 || c > 0x7a) {
if (c > 0x2f && c < 0x3a) {
letterIdx = c + 0xb;
}
} else {
letterIdx = c - 0x61;
}
} else {
letterIdx = c - 0x27;
}
break;
}
}
if (letterIdx != 1000) {
return &_letters.getSpriteAt(letterIdx);
}
return nullptr;
}
int GameFont::getFontHeight() const {
return 10;
}
int GameFont::getMaxCharWidth() const {
return _maxWidth;
}
int GameFont::getCharWidth(uint32 chr) const {
auto letter = getCharacterSprite((char)chr);
return letter ? letter->_width + 1 : 0;
}
void GameFont::drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const {
auto letter = getCharacterSprite((char)chr);
if (letter) {
letter->draw(dst, x, y);
}
}
} // namespace Darkseed

View File

@@ -0,0 +1,50 @@
/* 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 DARKSEED_GAMEFONT_H
#define DARKSEED_GAMEFONT_H
#include "graphics/font.h"
#include "darkseed/nsp.h"
#include "graphics/surface.h"
namespace Darkseed {
class GameFont : public Graphics::Font {
private:
Nsp _letters;
int _maxWidth = 0;
public:
GameFont();
int getFontHeight() const override;
int getMaxCharWidth() const override;
int getCharWidth(uint32 chr) const override;
void drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const override;
private:
const Sprite *getCharacterSprite(char c) const;
};
} // namespace Darkseed
#endif // DARKSEED_GAMEFONT_H

137
engines/darkseed/img.cpp Normal file
View File

@@ -0,0 +1,137 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/debug.h"
#include "common/file.h"
#include "darkseed/darkseed.h"
#include "darkseed/img.h"
namespace Darkseed {
bool Img::load(const Common::Path &filename) {
Common::File file;
if (!file.open(filename)) {
return false;
}
bool ret = load(file);
file.close();
if (ret) {
debug("Loaded %s (%d,%d) (%d,%d) %x", filename.toString().c_str(), _x, _y, _width, _height, _mode);
}
return ret;
}
bool Img::load(Common::SeekableReadStream &readStream) {
Common::Array<uint8> unpackedData;
unpackRLE(readStream, unpackedData);
_x = (uint16)READ_LE_INT16(&unpackedData.data()[0]);
_y = (uint16)READ_LE_INT16(&unpackedData.data()[2]);
unpackPlanarData(unpackedData, 4);
return true;
}
bool Img::loadWithoutPosition(Common::SeekableReadStream &readStream) {
Common::Array<uint8> unpackedData;
unpackRLE(readStream, unpackedData);
_x = 0;
_y = 0;
unpackPlanarData(unpackedData, 0);
return false;
}
bool Img::unpackRLE(Common::SeekableReadStream &readStream, Common::Array<byte> &buf) {
uint16 size = readStream.readUint16LE();
uint16 idx = 0;
buf.resize(size + 1);
while (idx <= size) {
uint8 byte = readStream.readByte();
assert(!readStream.err());
if (byte & 0x80) {
uint8 count = byte & 0x7f;
count++;
byte = readStream.readByte();
for (int i = 0; i < count && idx + i < size; i++) {
buf[idx + i] = byte;
}
idx += count;
} else {
uint8 count = byte + 1;
for (int i = 0; i < count && idx + i < size; i++) {
buf[idx + i] = readStream.readByte();
}
idx += count;
}
}
return true;
}
void Img::unpackPlanarData(Common::Array<uint8> &planarData, uint16 headerOffset) {
_height = (uint16)READ_LE_INT16(&planarData.data()[headerOffset]);
_width = (uint16)READ_LE_INT16(&planarData.data()[headerOffset + 2]) * 8;
_mode = planarData.data()[headerOffset + 4];
// assert(mode == 0xff);
_pixels.resize(_width * _height, 0);
for (int py = 0; py < _height; py++) {
for (int plane = 0; plane < 4; plane++) {
for (int px = 0; px < _width; px++) {
int bitPos = (7 - (px % 8));
int planeBit = (planarData[(headerOffset + 5) + (px / 8) + (_width / 8) * plane + py * (_width / 8) * 4] & (1 << bitPos)) >> bitPos;
_pixels[px + py * _width] |= planeBit << (3 - plane);
}
}
}
}
Common::Array<uint8> &Img::getPixels() {
return _pixels;
}
void Img::draw(int drawMode, int drawWidth) {
drawAt(_x, _y, drawMode, drawWidth);
}
void Img::drawAt(uint16 xPos, uint16 yPos, int drawMode, int drawWidth) {
int w = drawWidth != 0 ? drawWidth : _width;
if (drawMode != 0) {
uint8 *screen = (uint8 *)g_engine->_screen->getBasePtr(xPos, yPos);
uint8 *imgPixels = _pixels.data();
for (int sy = 0; sy < _height; sy++) {
for (int sx = 0; sx < w; sx++) {
if (drawMode == 1 && imgPixels[sx] != 0) {
screen[sx] ^= imgPixels[sx];
} else if (drawMode == 2 && imgPixels[sx] != 15) {
screen[sx] &= imgPixels[sx];
} else if (drawMode == 3 && imgPixels[sx] != 0) {
screen[sx] |= imgPixels[sx];
}
}
imgPixels += _width;
screen += g_engine->_screen->pitch;
}
} else {
g_engine->_screen->copyRectToSurface(_pixels.data(), _width, xPos, yPos, w, _height);
}
g_engine->_screen->addDirtyRect({{(int16)xPos, (int16)yPos}, (int16)w, (int16)_height});
}
} // namespace Darkseed

67
engines/darkseed/img.h Normal file
View File

@@ -0,0 +1,67 @@
/* 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 DARKSEED_IMG_H
#define DARKSEED_IMG_H
#include "common/array.h"
#include "common/scummsys.h"
#include "common/file.h"
namespace Darkseed {
class Img {
uint16 _x = 0;
uint16 _y = 0;
uint16 _width = 0;
uint16 _height = 0;
byte _mode = 0;
Common::Array<uint8> _pixels;
public:
bool load(const Common::Path &filename);
bool load(Common::SeekableReadStream &readStream);
bool loadWithoutPosition(Common::SeekableReadStream &readStream);
void draw(int drawMode = 0, int drawWidth = 0);
void drawAt(uint16 xPos, uint16 yPos, int drawMode = 0, int drawWidth = 0);
Common::Array<uint8> &getPixels();
uint16 getX() const {
return _x;
}
uint16 getY() const {
return _y;
}
uint16 getWidth() const {
return _width;
}
uint16 getHeight() const {
return _height;
}
private:
bool unpackRLE(Common::SeekableReadStream &readStream, Common::Array<uint8> &buf);
void unpackPlanarData(Common::Array<uint8> &planarData, uint16 headerOffset);
};
} // namespace Darkseed
#endif // DARKSEED_IMG_H

View File

@@ -0,0 +1,227 @@
/* 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 "darkseed/darkseed.h"
#include "darkseed/inventory.h"
#include "darkseed/menu.h"
#include "darkseed/kofont.h"
namespace Darkseed {
constexpr uint16 MAX_INVENTORY = 42;
constexpr uint16 MAX_ICONS = 9;
static constexpr Common::Rect drawArea = {{139, 0}, 334, 40};
Inventory::Inventory() {
_iconList.resize(MAX_ICONS);
_inventory.resize(MAX_INVENTORY);
reset();
}
void Inventory::reset() {
_viewOffset = 0;
_inventory[0] = 8;
_inventoryLength = 1;
update();
}
void Inventory::addItem(uint8 item) {
_inventory[_inventoryLength] = item;
_inventoryLength++;
g_engine->playSound(6, 5, -1);
update();
}
void Inventory::removeItem(uint8 item) {
for (int i = 0; i < _inventoryLength; i++) {
if (_inventory[i] == item) {
for (int j = i; j < _inventoryLength - 1; j++) {
_inventory[j] = _inventory[j + 1];
}
_inventoryLength--;
update();
break;
}
}
}
void Inventory::update() {
if (_viewOffset != 0) {
if (_inventoryLength <= _viewOffset + (MAX_ICONS - 1)) {
_viewOffset = _inventoryLength - (MAX_ICONS - 1);
}
if (_viewOffset > 50) {
_viewOffset = 0;
}
}
_iconList[0] = 4;
if (_inventoryLength + 1 < MAX_ICONS) {
_viewOffset = 0;
for (int i = 0; i < _inventoryLength; i++) {
_iconList[i + 1] = _inventory[i];
}
} else {
for (int i = 0; i < MAX_ICONS - 1; i++) {
_iconList[i + 1] = _inventory[_viewOffset + i];
}
if (_viewOffset + 8 < _inventoryLength) {
_iconList[8] = 43;
}
if (_viewOffset != 0) {
_iconList[1] = 42;
}
}
_numIcons = MIN(_inventoryLength + 1, 9);
_redraw = true;
}
void Inventory::restoreFrame() {
g_engine->_frame.drawRect(drawArea);
g_engine->_screen->addDirtyRect(drawArea);
}
void Inventory::draw() {
if ((g_engine->_actionMode <= 4 && g_engine->_cursor.getY() > 40) || g_engine->_animation->_isPlayingAnimation_maybe || (g_engine->_objectVar[141] >= 1 && g_engine->_objectVar[141] <= 3)) {
if (_isVisible) {
restoreFrame();
_isVisible = false;
}
return;
}
if (_redraw) {
restoreFrame();
_redraw = false;
}
_isVisible = true;
for (int i = 0; i < _numIcons; i++) {
int icon = _iconList[i];
if (icon != 42 && icon != 43) {
icon += g_engine->isDosDemo() ? 36 : 42;
}
if (g_engine->_actionMode == _iconList[i] && g_engine->_actionMode > 4) {
const Sprite &selectedSprite = g_engine->_baseSprites.getSpriteAt(95);
g_engine->_sprites.addSpriteToDrawList(139 + i * 37, 20 - selectedSprite._height / 2, &selectedSprite, 255, selectedSprite._width, selectedSprite._height, false);
}
const Sprite &iconSprite = g_engine->_baseSprites.getSpriteAt(icon);
g_engine->_sprites.addSpriteToDrawList(140 + i * 37, 20 - iconSprite._height / 2, &iconSprite, 255, iconSprite._width, iconSprite._height, false);
}
g_engine->_screen->addDirtyRect(drawArea);
}
void Inventory::handleClick() {
Common::Point clickPos = g_engine->_cursor.getPosition();
if (clickPos.x < 140 || clickPos.x > 140 + _numIcons * 37) {
return;
}
int iconIdx = (clickPos.x - 140) / 37;
int icon = _iconList[iconIdx];
g_engine->playSound(5, 4, -1);
if (icon == 42) {
leftArrowClicked();
} else if (icon == 43) {
rightArrowClicked();
} else if (icon == 4) {
if (g_engine->isDosDemo()) {
g_engine->_console->addTextLine(Common::U32String("You can't load or save games in demo mode, press 'q' to exit."));
} else {
g_engine->_menu->loadMenu();
}
} else if (icon == 21) {
g_engine->_console->printTosText(935);
g_engine->_objectVar[21] = 1;
g_engine->_room->_collisionType = 1;
g_engine->_room->removeObjectFromRoom(21);
removeItem(21);
} else if ((g_engine->_actionMode == 25 && icon == 20) ||
(g_engine->_actionMode == 20 && icon == 25)
) {
g_engine->handleObjCollision(icon);
} else if (g_engine->_actionMode == kHandAction && icon == 35) {
g_engine->_objectVar[35] = 28800; // wind watch
g_engine->_console->printTosText(669);
} else if (g_engine->_actionMode == kLookAction) {
g_engine->lookCode(icon);
} else {
g_engine->_actionMode = icon;
if (g_engine->getLanguage() == Common::KO_KOR) {
g_engine->_console->addTextLine(KoFont::getObjectString(g_engine->_objectVar.getObjectName(icon)));
g_engine->_console->printTosText(972, true);
} else {
g_engine->_console->printTosText(972);
g_engine->_console->addToCurrentLine(formatInjectStrings(Common::U32String("%s.").c_str(), g_engine->_objectVar.getObjectName(icon).c_str()));
}
}
}
void Inventory::leftArrowClicked() {
if (_viewOffset > 0) {
_viewOffset--;
update();
}
}
void Inventory::rightArrowClicked() {
_viewOffset++;
update();
}
Common::Error Inventory::sync(Common::Serializer &s) {
s.syncAsSint16LE(_inventoryLength);
for (int i = 0; i < _inventoryLength; i++) {
s.syncAsByte(_inventory[i]);
}
_viewOffset = 0;
update();
return Common::kNoError;
}
void Inventory::endOfDayOutsideLogic() {
for (int i = 0; i < _inventoryLength; i++) {
g_engine->_objectVar.setMoveObjectRoom(_inventory[i], _inventory[i] == 28 ? 255 : 252);
}
_inventoryLength = 0;
_viewOffset = 0;
g_engine->_objectVar[53] = 2;
}
void Inventory::gotoJailLogic() {
for (int i = 0; i < _inventoryLength; i++) {
g_engine->_objectVar.setMoveObjectRoom(_inventory[i], 100);
}
g_engine->_objectVar.setMoveObjectRoom(28, 255);
_inventoryLength = 0;
_viewOffset = 0;
update();
}
bool Inventory::hasObject(uint8 objNum) {
for (int i = 0; i < _inventoryLength; i++) {
if (_inventory[i] == objNum) {
return true;
}
}
return false;
}
} // End of namespace Darkseed

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 DARKSEED_INVENTORY_H
#define DARKSEED_INVENTORY_H
namespace Darkseed {
class Inventory {
Common::Array<uint8> _inventory;
int16 _inventoryLength = 0;
Common::Array<uint8> _iconList;
int _viewOffset = 0;
int _numIcons = 0;
bool _isVisible = false;
bool _redraw = false;
public:
Inventory();
void reset();
void addItem(uint8 item);
void removeItem(uint8 item);
void draw();
void handleClick();
void endOfDayOutsideLogic();
void gotoJailLogic();
bool hasObject(uint8 objNum);
Common::Error sync(Common::Serializer &s);
private:
void update();
void leftArrowClicked();
void rightArrowClicked();
void restoreFrame();
};
} // namespace Darkseed
#endif // DARKSEED_INVENTORY_H

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/>.
*
*/
#include "common/debug.h"
#include "common/file.h"
#include "darkseed/darkseed.h"
#include "darkseed/kidpic.h"
namespace Darkseed {
static constexpr int BYTES_PER_LINE = 320;
static constexpr int BYTES_PER_PLANE = 80;
static constexpr int KID_WIDTH = 640;
static constexpr int KID_HEIGHT = 350;
KidPic::KidPic() {
_pixels.resize(KID_WIDTH * KID_HEIGHT);
Common::File file;
if (!file.open("kid.pic")) {
error("Failed to open kid.pic");
}
file.seek(0x10);
Pal pal;
pal.loadFromStream(file, false);
pal.swapEntries(14, 4); // not sure why we need to swap these palette entries. All the other entries line up correctly.
pal.installPalette();
unpackRLE(file);
}
bool KidPic::unpackRLE(Common::SeekableReadStream &readStream) {
uint idx = 0;
uint unpackedSize = _pixels.size() / 2;
readStream.seek(0x80);
while (idx < unpackedSize && !readStream.eos()) {
uint8 byte = readStream.readByte();
assert(!readStream.err());
uint repeat = 1;
if ((byte & 192) == 192) {
repeat = (byte & 63);
byte = readStream.readByte();
assert(!readStream.err());
}
for (uint j = 0; j < repeat; j++) {
unpackByte(byte);
}
idx += repeat;
}
return true;
}
// image is stored as 4 x 1-bit planes per line.
void KidPic::unpackByte(uint8 byte) {
int planeOffset = _lineByteIdx / BYTES_PER_PLANE;
int x = _lineByteIdx % BYTES_PER_PLANE;
for (int i = 0; i < 8; i++) {
if (byte & 1 << (7 - i)) {
_pixels[_lineNum * KID_WIDTH + (x * 8) + i] |= 1 << planeOffset;
}
}
_lineByteIdx++;
if (_lineByteIdx == BYTES_PER_LINE) {
_lineByteIdx = 0;
_lineNum++;
}
}
void KidPic::draw() {
g_engine->_screen->copyRectToSurface(_pixels.data(), KID_WIDTH, 0, 0, KID_WIDTH, KID_HEIGHT);
}
} // namespace Darkseed

47
engines/darkseed/kidpic.h Normal file
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 DARKSEED_KIDPIC_H
#define DARKSEED_KIDPIC_H
#include "common/array.h"
#include "common/scummsys.h"
#include "common/file.h"
namespace Darkseed {
class KidPic {
Common::Array<uint8> _pixels;
int _lineNum = 0;
int _lineByteIdx = 0;
public:
KidPic();
void draw();
private:
bool unpackRLE(Common::SeekableReadStream &readStream);
void unpackByte(uint8 byte);
};
} // namespace Darkseed
#endif // DARKSEED_KIDPIC_H

311
engines/darkseed/kofont.cpp Normal file
View File

@@ -0,0 +1,311 @@
/* 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 "darkseed/darkseed.h"
#include "darkseed/kofont.h"
namespace Darkseed {
KoFont::KoFont() {
Common::File fontData;
if (!fontData.open("k16.bin")) {
error("Error: failed to open k16.bin");
}
loadFontDataSet(_fontDataSet1, 191, 64, fontData);
loadFontDataSet(_fontDataSet2, 85, 64, fontData);
loadFontDataSet(_fontDataSet3, 109, 64, fontData);
Common::File fontOthersData;
if (!fontOthersData.open("others.bin")) {
error("Error: failed to open others.bin");
}
loadFontDataSet(_fontDataSetOther, 20, 32, fontOthersData);
fontOthersData.close();
fontData.close();
_gameFont = new GameFont();
}
KoFont::~KoFont() {
delete _gameFont;
}
void KoFont::loadFontDataSet(Common::Array<Common::Array<uint8> > &dataSet, int size, int packedGlyphSize, Common::File &file) {
dataSet.resize(size);
for (int i = 0; i < size; i++) {
dataSet[i].resize(packedGlyphSize * 4, 0);
loadFontGlyph(dataSet[i], packedGlyphSize, file);
}
}
static constexpr uint8 kFontPal[4] = {0, 2, 4, 11};
void KoFont::loadFontGlyph(Common::Array<uint8> &pixels, int packedGlyphSize, Common::File &file) {
// Unpack 2bpp font data into 8bpp
for (int i = 0; i < packedGlyphSize; i++) {
uint8 byte = file.readByte();
for (int j = 0; j < 4; j++) {
pixels[i * 4 + j] = kFontPal[(byte >> (3 - j) * 2) & 3];
}
}
}
int KoFont::getFontHeight() const {
return 16;
}
int KoFont::getMaxCharWidth() const {
return 16;
}
int KoFont::getCharWidth(uint32 chr) const {
if (chr < 128) {
if (getOtherCharIdx(chr) != -1) {
return 10;
}
return _gameFont->getCharWidth(chr);
}
return getMaxCharWidth();
}
void KoFont::createGlyph(uint8 *pixels, uint32 chr) const {
uint16 param1, param2, param3;
extractKoIndexComponents(chr, &param1, &param2, &param3);
if (param1 < 191) {
addToGlyph(pixels, param1);
}
if (param2 < 85) {
addToGlyph(pixels, param2 + 191);
}
if (param3 < 109) {
addToGlyph(pixels, param3 + 276);
}
}
void KoFont::addToGlyph(uint8 *destPixels, int16 index) const {
if (index < 192) {
addPixels(destPixels, _fontDataSet1[index]);
} else if (index < 277) {
addPixels(destPixels, _fontDataSet2[index - 191]);
} else {
addPixels(destPixels, _fontDataSet3[index - 276]);
}
}
void KoFont::addPixels(uint8 *destPixels, const Common::Array<uint8> &pixels) const {
for (uint i = 0; i < pixels.size(); i++) {
if (pixels[i] != 0) {
destPixels[i] = pixels[i];
}
}
}
int16 SHORT_ARRAY_1000_01ca[32] = {
-1, 0, 32, 352,
672, 992, 1312, 1632,
1952, 2272, 2592, 2912,
3232, 3552, 3872, 4192,
4512, 4832, 5152, 5472,
5792, -1, -1, -1,
-1, -1, -1, -1,
-1, -1, -1, -1};
int16 SHORT_ARRAY_1000_020a[32] = {
-1, -1, 0, 128,
256, 384, 512, 640,
-1, -1, 768, 896,
1024, 1152, 1280, 1408,
-1, -1, 1536, 1664,
1792, 1920, 2048, 2176,
-1, -1, 2304, 2432,
2560, 2688, -1, -1};
int16 SHORT_ARRAY_1000_024a[32] = {
-1, 0, 128, 256,
384, 512, 640, 768,
896, 1024, 1152, 1280,
1408, 1536, 1664, 1792,
1920, 2048, -1, 2176,
2304, 2432, 2560, 2688,
2816, 2944, 3072, 3200,
3328, 3456, -1, -1};
int16 SHORT_ARRAY_1000_028a[32] = {
-1, 0, 0, 64,
64, 64, 64, 64,
64, 64, 64, 64,
64, 64, 64, 64,
64, 0, 64, 64,
64, -1, -1, -1,
-1, -1, -1, -1,
-1, -1, -1, -1};
int16 SHORT_ARRAY_1000_02ca[32] = {
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 64, 192, 192,
0, 0, 192, 64,
128, 256, 256, 256,
0, 0, 128, 64,
192, 0, 0, 0};
int16 SHORT_ARRAY_1000_030a[32] = {
0, 0, 0, 0,
64, 0, 64, 32,
0, 0, 64, 32,
64, 96, 0, 64,
0, 0, 32, 96,
96, 32, 64, 32,
0, 0, 96, 96,
32, 32, 0, 0};
int16 SHORT_ARRAY_1000_034a[32] = {
-1, 0, 32, 32,
32, 32, 32, 32,
32, 32, 32, 32,
32, 32, 32, 32,
32, 32, -1, 32,
32, 32, 32, 32,
32, 32, 32, 32,
32, 32, -1, -1};
void KoFont::extractKoIndexComponents(uint32 charIdx, uint16 *param_2, uint16 *param_3, uint16 *param_4) {
int uVar1;
int iVar2;
int uVar3;
int uVar4;
int uVar5;
iVar2 = (charIdx & 31) * 2;
uVar5 = (charIdx << 1) >> 5 & 62;
uVar4 = (charIdx << 1) >> 10 & 62;
uVar1 = SHORT_ARRAY_1000_020a[uVar5 / 2];
if (uVar1 > 0) {
uVar1 += SHORT_ARRAY_1000_028a[uVar4 / 2] + SHORT_ARRAY_1000_034a[iVar2 / 2] - 3;
}
uVar4 = SHORT_ARRAY_1000_01ca[uVar4 / 2];
if (uVar4 > 0) {
uVar4 += SHORT_ARRAY_1000_02ca[uVar5 / 2] + SHORT_ARRAY_1000_034a[iVar2 / 2];
}
uVar3 = SHORT_ARRAY_1000_024a[iVar2 / 2];
if (uVar3 > 0) {
uVar3 += SHORT_ARRAY_1000_030a[uVar5 / 2] - 3;
}
*param_2 = uVar4 >> 5;
*param_3 = (uVar1 >> 5) - 2;
*param_4 = (uVar3 >> 5) - 2;
}
bool KoFont::isConsonant(uint32 charIdx) {
uint16 param1, param2, param3;
extractKoIndexComponents(charIdx, &param1, &param2, &param3);
return param3 < 109;
}
Common::U32String KoFont::getObjectString(const Common::U32String &object) {
if (object.size() == 0) {
return Common::U32String("");
}
if (isConsonant(object[object.size() - 1])) {
return object + convertToU32String("\xb7\x69", Common::KO_KOR); // -eul
}
return object + convertToU32String("\x9f\x69", Common::KO_KOR); // -reul
}
Common::U32String KoFont::getTopicString(const Common::U32String &object) {
if (object.size() == 0) {
return Common::U32String("");
}
if (isConsonant(object[object.size() - 1])) {
return object + convertToU32String("\xb7\x65", Common::KO_KOR); // -eun
}
return object + convertToU32String("\x93\x65", Common::KO_KOR); // -neun
}
Common::U32String KoFont::getLinkingString(const Common::U32String &object) {
if (object.size() == 0) {
return Common::U32String("");
}
if (isConsonant(object[object.size() - 1])) {
return object + convertToU32String("\x89\xc1", Common::KO_KOR); // -gwa
}
return object + convertToU32String("\xb5\xc1", Common::KO_KOR); // -wa
}
Common::U32String KoFont::getLocationString(const Common::U32String &object) {
if (object.size() == 0) {
return Common::U32String("");
}
if (isConsonant(object[object.size() - 1])) {
return object + convertToU32String("\xb7\x61", Common::KO_KOR); // -eu
}
return object;
}
int KoFont::getOtherCharIdx(uint32 chr) const {
switch (chr) {
case '!':
return 2;
case '"':
return 6;
case '(':
return 3;
case ')':
return 4;
case ',':
return 0;
case '.':
return 1;
case '?':
return 5;
default:
break;
}
return -1;
}
void KoFont::drawOtherGlyph(Graphics::Surface *dst, uint8 chr, int x, int y) const {
g_engine->_screen->copyRectToSurfaceWithKey(_fontDataSetOther[chr].data(), 8, x, y, 8, 16,0xf);
}
void KoFont::drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const {
if (chr < 128) {
int otherCharIdx = getOtherCharIdx(chr);
if (otherCharIdx != -1) {
drawOtherGlyph(dst, (uint8)otherCharIdx, x, y);
} else {
_gameFont->drawChar(dst, chr, x, y, color);
}
return;
}
uint8 pixels[256];
memset(pixels, 0, 256);
createGlyph(pixels, chr);
g_engine->_screen->copyRectToSurfaceWithKey(pixels, 16, x, y - 2, 16, 16,0xf);
}
} // namespace Darkseed

68
engines/darkseed/kofont.h Normal file
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/>.
*
*/
#ifndef DARKSEED_KOFONT_H
#define DARKSEED_KOFONT_H
#include "graphics/font.h"
#include "common/file.h"
#include "darkseed/gamefont.h"
namespace Darkseed {
class KoFont : public Graphics::Font {
private:
Common::Array<Common::Array<uint8>> _fontDataSet1;
Common::Array<Common::Array<uint8>> _fontDataSet2;
Common::Array<Common::Array<uint8>> _fontDataSet3;
Common::Array<Common::Array<uint8>> _fontDataSetOther;
GameFont *_gameFont;
public:
KoFont();
~KoFont();
int getFontHeight() const override;
int getMaxCharWidth() const override;
int getCharWidth(uint32 chr) const override;
void drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const override;
static void extractKoIndexComponents(uint32 charIdx, uint16 *param_2, uint16 *param_3, uint16 *param_4);
static bool isConsonant(uint32 charIdx);
static Common::U32String getObjectString(const Common::U32String &object);
static Common::U32String getTopicString(const Common::U32String &object);
static Common::U32String getLinkingString(const Common::U32String &object);
static Common::U32String getLocationString(const Common::U32String &object);
private:
void loadFontDataSet(Common::Array<Common::Array<uint8>> &dataSet, int size, int packedGlyphSize, Common::File &file);
void loadFontGlyph(Common::Array<uint8> &pixels, int packedGlyphSize, Common::File &file);
void createGlyph(uint8 *pixels, uint32 chr) const;
void addToGlyph(uint8 *destPixels, int16 index) const;
void addPixels(uint8 *destPixels, const Common::Array<uint8> &pixels) const;
int getOtherCharIdx(uint32 chr) const;
void drawOtherGlyph(Graphics::Surface *dst, uint8 chr, int x, int y) const;
};
} // namespace Darkseed
#endif // DARKSEED_KOFONT_H

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 "darkseed/darkseed.h"
#include "darkseed/langtext.h"
namespace Darkseed {
Common::U32String getI18NText(const I18nText &text) {
switch (g_engine->getLanguage()) {
case Common::ES_ESP : return Common::U32String(text.es);
case Common::FR_FRA : return Common::U32String(text.fr);
case Common::DE_DEU : return Common::U32String(text.de);
case Common::KO_KOR : return text.ko ? convertToU32String(text.ko, Common::KO_KOR) : Common::U32String(text.en);
case Common::ZH_ANY : return text.zh ? convertToU32String(text.zh, Common::ZH_ANY) : Common::U32String(text.en);
default : return Common::U32String(text.en);
}
}
const TextWithPosition &getI18NTextWithPosition(const I18NTextWithPosition &i18nTextWithPosition) {
switch (g_engine->getLanguage()) {
case Common::ES_ESP : return i18nTextWithPosition.es;
case Common::FR_FRA : return i18nTextWithPosition.fr;
case Common::DE_DEU : return i18nTextWithPosition.de;
case Common::KO_KOR : return i18nTextWithPosition.ko;
case Common::ZH_ANY : return i18nTextWithPosition.zh;
default : return i18nTextWithPosition.en;
}
}
Common::U32String convertToU32String(const char *text, Common::Language language) {
size_t len = strlen(text);
switch (language) {
case Common::ZH_ANY:
case Common::KO_KOR: {
Common::U32String str;
for (uint i = 0; i < len; i++) {
uint8 byte = text[i];
if (byte & 0x80) {
if (i < len - 1) {
uint8 byte2 = text[i + 1];
str += (int)byte << 8 | byte2;
i++;
}
} else {
str += byte;
}
}
return str;
}
default : break;
}
return Common::U32String(text);
}
Common::U32String formatInjectStrings(const Common::u32char_type_t *format, ...) {
Common::U32String outString;
va_list args;
va_start(args, format);
for (const Common::u32char_type_t *itr = format; *itr; itr++) {
if (*itr == '%') {
itr++;
if (!*itr) {
outString += '%';
break;
}
if (*itr == 's') {
auto text = va_arg(args, char32_t *);
outString += text;
} else {
outString += *itr;
}
} else {
outString += *itr;
}
}
va_end(args);
return outString;
}
} // End of namespace Darkseed

447
engines/darkseed/langtext.h Normal file
View File

@@ -0,0 +1,447 @@
/* 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 DARKSEED_LANGTEXT_H
#define DARKSEED_LANGTEXT_H
#include "common/language.h"
namespace Darkseed {
enum class KoreanObjectSuffixType {
None,
Object,
Topic,
Linking,
Location
};
struct I18nText {
const char *en;
const char *es;
const char *fr;
const char *de;
const char *ko;
const char *zh;
};
struct TextWithPosition {
int x;
int y;
const char *text;
};
struct I18NTextWithPosition {
const TextWithPosition en;
const TextWithPosition es;
const TextWithPosition fr;
const TextWithPosition de;
const TextWithPosition ko;
const TextWithPosition zh;
};
Common::U32String getI18NText(const I18nText &text);
const TextWithPosition &getI18NTextWithPosition(const I18NTextWithPosition &i18nTextWithPosition);
Common::U32String convertToU32String(const char *text, Common::Language language);
Common::U32String formatInjectStrings(const Common::u32char_type_t *format, ...);
constexpr I18nText kI18N_CarKeysIgnitionText = {
"You see the car keys in the ignition.",
"VES LAS LLAVES DEL COCHE EN EL CONTACTO.",
"VOUS VOYEZ LES CLEFS DE LA VOTTURE DAUS LE STARTER.",
"DU SIEHSTDIE AUTOSCHLUSSEL IM ANLASSER.",
"\xb8\x61\x95\xb7\xc0\x61\x20\xb5\x69\xae\x41\x88\x61\x20\xb7\xa1\x8b\x61\x93\xa1\xad\x65\xb5\x41\x20\x8d\xb9\xd1\x61\xb7\xb6\x93\x65\x88\xf5\xb7\xa1\x20\xa5\xa1\xb7\xb3\x93\xa1\x94\x61\x2e",
"\xa7\x41\xac\xdd\xa8\xec\xa4\x46\xa8\xae\xc6\x5f\xb0\xcd\xa6\x62\xc2\x49\xa4\xf5\xb6\x7d\xc3\xf6\xa4\x57\xa1\x43"
};
constexpr I18nText kI18N_YouSeeIronBarsText = {
"You see the iron bars of your cell.",
"VES LAS BARRAS DE HIERRO DE TU CELDA.",
"VOUS VOYEZ LES BARREAUX DE FER DE VOTRE CELLULE.",
"DU SIEHST DIE EISENSTANGEN IN DER ZELLE.",
"\x88\x71\xa4\x77\xb7\x81\x20\xb8\x61\xa2\x89\xae\x41\xc9\xb7\x20\xb7\xb3\x93\xa1\x94\x61\x2e",
"\xa7\x41\xac\xdd\xa8\xec\xa4\x46\xba\xbb\xc5\xa2\xb8\xcc\xaa\xba\xc5\x4b\xb1\xf8\xa1\x43"
};
constexpr I18nText kI18N_YouSeeDelbertText = {
"You see Delbert, not much to look at.",
"VES A DELBERT, NO HAY MUCHO QUE VER.",
"VOUS VOYEZ DELBERT, PAS GRAND CHOSE A VOIR.",
"DU SIEHST DELBERT, NICHT VIEL ZU SEHEN",
"\xd0\x65\x88\x61\x9d\xb3\x89\x41\x20\xb4\x81\x89\x65\x89\xc1\xb7\x81\x20\x91\xa9\xb7\xa1\xb5\x41\x20\xb5\x69\xba\x97\xd0\x61\x89\xa1\xb7\xb6\xaf\x73\x93\xa1\x94\x61\x2e",
"\xa7\x41\xac\xdd\xa8\xec\xa4\x46\xc0\xb9\xba\xb8\xa7\x42\xa1\x41\xa5\x4c\xa6\xfc\xa5\x47\xa6\xa3\xb5\xdb\xb3\x72\xaa\xaf\xaa\xb1\xa1\x43"
};
constexpr I18nText kI18N_YouSeeTheClerkText = {
"You see the clerk.",
"VES AL EMPLEADO.",
"VOUS VOYEZ L'HOMME.",
"DU SIEHST DEN MANN.",
"\xb7\xa1\x20\x88\x61\x89\x41\xb7\x81\x20\xb8\xf1\xb6\xa5\xb7\xb3\x93\xa1\x94\x61\x2e",
"\xa7\x41\xac\xdd\xa8\xec\xa4\x46\xa9\xb1\xad\xfb\xa1\x43"
};
constexpr I18nText kI18N_YouSeeTheOpenGloveBoxText = {
"You see the open glove box.",
"VES LA CAJA DE LOS GUANTES ABIERTA.",
"VOUS VOYEZ LA BOITE DE GANT OUVERTE.",
"DU SIEHST DAS OFFENE HANDSCHUHFACH.",
"\xb8\x77\x88\x73\xc9\xb7\xb7\xa1\x20\xb5\x69\x9d\x61\xb7\xb6\x93\x65\x88\xf5\xb7\xa1\x20\xa5\xa1\xb7\xb3\x93\xa1\x94\x61\x2e",
"\xa7\x41\xac\xdd\xa8\xec\xa5\xb4\xb6\x7d\xa4\x46\xaa\xba\xa4\xe2\xae\x4d\xb2\xb0\xa1\x43"
};
constexpr I18nText kI18N_youSeeTheText = {
"You see the %s.",
"VES %s.",
"VOUS VOYEZ %s.",
"DU SIEHST %s.",
"%s \xac\x69\xcd\x61\xa5\xb3\x93\xa1\x94\x61",
"\xa7\x41\xac\xdd\xa8\xec\xa4\x46%s\xa1\x43"
};
constexpr I18nText kI18N_TheCopsIgnoreYourDemandsText = {
"The cops ignore your demands for attention.",
"LOS POLICIAS IGNORAN TUS LLAMADAS DE ATENCION.",
"LES FLICS IGNORENT VOTRE DEMANDE D'ATTENTION.",
"DIE POLIZISTEN BEACHTEN NICHT DEINE FORDERUNGEN.",
"\xb4\x61\xa2\x81\x9f\xa1\x20\xaf\xa5\xd1\xa1\x9f\x69\x20\xa5\xa1\x90\x81\x95\xa1\x20\x89\x77\xc0\x69\x97\x69\xb7\x65\x20\xc1\x61\x94\x61\xa5\xa9\x20\xac\x97\x88\x62\x95\xa1\x20\xd0\x61\xbb\xa1\x20\xb4\x67\x93\x65\x8a\x85\xb6\x61\x2e",
nullptr // Untranslated in this version
};
constexpr I18nText kI18N_ThePhoneIsRingingText = {
"The phone is ringing.",
"EL TELEFONO ESTA SONANDO.",
"LE TELEPHONE SONNE.",
"DAS TELEFON KLINGELT.",
"\xb8\xe5\xd1\xc1\xa5\x49\xb7\xa1\x20\xb6\x89\x9f\xb3\x93\xa1\x94\x61\x2e",
"\xb9\x71\xb8\xdc\xc1\x6e\xc5\x54\xb0\x5f\x2e\x0a"
};
constexpr I18nText kI18N_TheDoorbellIsRingingText = {
"The doorbell is ringing.",
"EL TIMBRE DE LA PUERTA ESTA SONANDO.",
"LA SONETTE DE LA PORTE SONNE.",
"DIE TUERKLINGEL LAEUTET.",
"\xd1\x65\x89\xc5\x20\xc1\xa1\xb7\xa5\xb9\xb7\xb7\xa1\x20\xb6\x89\x9f\xb3\x93\xa1\x94\x61\x2e",
"\xaa\xf9\xb9\x61\xc1\x6e\xc5\x54\xb0\x5f\x2e\x0a"
};
constexpr I18nText kI18N_ChooseAnItemBeforeText = {
"Choose an item before giving clerk more money.",
"ELIGE UN OBJETO ANTES DE DARLE AL EMPLEADO MAS DINERO.",
"CHOISISSEZ QUELQUE CHOSE AVANT DE REMETTRE L'ARGENT A VENDEUR.",
"SUCHE ETWAS AUS BEVOR DU DEN MANN BEZAHLST.",
"\xb8\xf1\xb6\xa5\xb5\x41\x89\x41\x20\x95\xa5\xb7\x69\x20\x94\xe1\x20\xba\x81\x8b\xa1\xb8\xe5\xb5\x41\x20\xa0\xe5\xb8\xe1\x20\x8a\x81\xb7\xb3\xd0\x69\x20\xa2\x89\x88\xe5\xb7\x69\x20\x89\xa1\x9f\x61\xad\x41\xb6\x61\x2e",
"\xad\x6e\xae\xb3\xb3\x66\xab\x7e\xab\x65\xa1\x41\xb0\x4f\xb1\x6f\xbd\xd0\xa5\xfd\xa5\x49\xbf\xfa\xb5\xb9\xa9\xb1\xad\xfb\xa1\x43"
};
constexpr I18nText kI18N_YouTouchDelbertText = {
"You touch Delbert...",
"TOCAS A DELBERT...",
"VOUS TOUCHEZ DELBERT.",
"GREIFE DELBERT AN...",
"\xae\x89\xb7\x69\x20\xd0\x65\xa4\xe5\x20\x8a\xa5\xd0\x81\x20\xa5\xa1\x93\x65\x88\xf5\xb7\xa1\x20\xb4\xe1\x98\xe9\x8c\x61\xb6\x61\x3f",
"\xa7\x41\xb8\x49\xc4\xb2\xc0\xb9\xba\xb8\xa7\x42\xa4\x40\xa4\x55\xa1\x44\xa1\x44"
};
constexpr I18nText kI18N_YouTouchTheColdIronBarsText = {
"You touch the cold iron bars.",
"TOCAS LAS FRIAS BARRAS DE HIERRO.",
"VOUS TOUCHEZ LES BARREAUX DE FER.",
"GREIFE DIE KALTEN EISEN STANGEN AN.",
"\xc0\x77\xac\x69\xb7\x81\x20\x88\x71\xc1\xa2\xb7\x65\x20\xb5\x62\xaf\xa1\x20\xc0\x61\x88\x73\x8a\x85\xb6\x61\x21",
"\xa7\x41\xba\x4e\xb5\xdb\xa6\x42\xa7\x4e\xaa\xba\xc5\x4b\xb1\xf8\xa1\x43"
};
constexpr I18nText kI18N_TheSergeantSaysNiceGunText = {
"The sergeant says 'Nice gun eh? It's a Browning'",
"EL SARGENTO DICE: 'BUENA PISTOLA, EH? ES UNA BROWNING.'",
"LE SERGENT DIT: BEAU REVOLVER HEIN, C'EST UN BROWNING.",
"DER SEARGENT SAGT 'SCHOENE PISTOLE, EH? ES IST EIN BROWNING.'",
"\xc1\xb7\x20\x8b\x65\xc0\xe1\xb5\x41\xac\xe1\x20\x98\xe9\xb4\xe1\xbb\xa1\xaf\xa1\xb6\x61\x21",
"\xc4\xb5\xa9\x78\xbb\xa1\xa1\x47\xac\x4f\xa7\xe2\xa6\x6e\xba\x6a\xa7\x63\xa1\x48\xb3\x6f\xac\x4f\xa5\xd5\xae\xd4\xb9\xe7\xa4\xe2\xba\x6a\xa9\x4f\xa1\x49"
};
constexpr I18nText kI18N_YouTurnOnTheMusicText = {
"You turn on the music.",
"PONES MUSICA.",
"VOUS METTEZ LA MUSIQUE.",
"SCHALTE DIE MUSIK AN.",
"\xc0\x61\x20\x20\x9c\x61\x97\xa1\xb5\xa1\x9f\x69\x20\xc5\x73\x93\xa1\x94\x61\x2e",
"\xa7\x41\xa5\xb4\xb6\x7d\xad\xb5\xbc\xd6\xa1\x43"
};
constexpr I18nText kI18N_YouTurnOffTheMusicText = {
"You turn off the music.",
"QUITAS LA MUSICA.",
"VOUS ARRETEZ LA MUSIQUE.",
"SCHALTE DIE MUSIK AB.",
"\xc0\x61\x20\x9c\x61\x97\xa1\xb5\xa1\x9f\x69\x20\x8f\x73\x93\xa1\x94\x61\x2e",
"\xa7\x41\xc3\xf6\xb1\xbc\xad\xb5\xbc\xd6\xa1\x43"
};
constexpr I18nText kI18N_YouTouchTheOrnateSignalText = {
"You touch the surface of the ornate sigil.",
"TOCAS LA SUPERFICIE DE LA FIGURA ADORNADA.",
"VOUS TOUCHEZ LA SURFACE DE LA PIERRE MAGIQUE.",
"GREIFE DIE VERZAUBERTEN STEINE AN.",
"\xac\xe2\xa2\x61\xb7\x81\x20\xa5\xb7\xb7\xa5\xb7\x69\x20\xa0\x65\xb9\x61\xa5\xb3\x93\xa1\x94\x61\x2e",
"\xa7\x41\xc4\xb2\xba\x4e\xb5\xdb\xc0\x4a\xb9\xa2\xba\xeb\xac\xfc\xaa\xba\xab\xca\xa6\x4c\xaa\xed\xad\xb1\xa1\x43"
};
constexpr I18nText kI18N_ThisSentryCannotBeStoppedWithText = {
"This sentry cannot be stopped with a %s.",
"NO PUEDES PARAR A ESTE CENTINELA CON %s.",
"VOUS N'ARRETEREZ PAS LA SENTINELLE AVEC %s.",
"DIESE WACHE KANN NICHT AUFGEHALTEN WERDEN MIT %s.",
"%s\x9d\xa1\x93\x65\x20\x88\x65\xae\x81\x9f\x69\x20\xb8\xe1\xbb\xa1\xd0\x69\x20\xae\x81\x20\xb4\xf4\xaf\x73\x93\xa1\x94\x61\x2e",
nullptr // not translated
};
constexpr I18nText kI18N_HasNoEffectOnTheAlienTubesText = {
"The %s has no effect on the alien tubes.",
"%s NO TIENE EFECTO SOBRE LOS TUBOS ALIENIGENAS.",
"%s RESTE SANS EFFET SUR LES TUBES EXTRA-TERRESTRES.",
"%s BEWIRKT NICHTS AN AUSSERIRDISCHEN TUNNELS.",
"%s \x95\xb7\x9d\x62\xac\xe5\xb5\x41\x20\xb4\x61\xa2\x81\x9c\xe5\x20\xb5\x77\xd0\xb7\xb7\x69\x20\xa3\xa1\xc3\xa1\xbb\xa1\x20\xa1\xb5\xd0\x73\x93\xa1\x94\x61\x2e",
"\xa5\xce\x25\x73\xa6\x62\xb3\x6f\xa8\xc7\xb2\xa7\xa7\xce\xb8\xd5\xba\xde\xa4\x57\xa1\x41\xa4\x40\xc2\x49\xae\xc4\xaa\x47\xa4\x5d\xa8\x53\xa6\xb3\xa1\x43"
};
constexpr I18nText kI18N_YouIncinerateTheText = {
"You incinerate the %s inside the power nexus!.",
"INCINERAS %s DENTRO DE LA FUENTA DE ENERGIA.",
"VOUS INCINEREZ %s A L'INTERIEUR DU LIEN DE PUISSANCE.",
"DU VERNICHTEST %s MIT DER STROMVERSORGUNG!",
"%s \xb5\xc5\xb8\xe5\xd3\xa1\x20\xcc\x61\x8a\x41\xaf\xa1\xc5\x76\xaf\x73\x93\xa1\x94\x61\x2e",
"\xa7\x41\xb1\x4e\x25\x73\xa1\x41\xa9\xf1\xa6\x62\xaf\xe0\xb6\x71\xc1\x70\xb5\xb2\xa8\x74\xb2\xce\xa4\xa4\xa4\xdf\xa4\xba\xbf\x4e\xa6\xa8\xa6\xc7\xc2\x75\xa1\x49"
};
constexpr I18nText kI18N_HasNoEffectOnTheAlienMonstrosityText = {
"The %s has no effect on this alien monstrosity.",
"%s NO TIENE EFECTO SOBRE ESTA MONSTRUOSIDAD ALIENIGENA.",
"%s RESTE SANS EFFET SUR CETTE MONSTRUOSITE EXTRA-TERRESTRE.",
"%s BEWIRKT NICHTS AM AUSSERIRDISCHEN MONSTRUM.",
"%s \xb7\x41\x8b\xa1\xc1\x41\x20\xc4\xf1\xcf\x41\xc8\xe1\xb5\x41\x20\xb5\x77\xd0\xb7\xb7\x69\x20\xa3\xa1\xc3\xa9\xae\x85\x20\xb4\xf4\xaf\x73\x93\xa1\x94\x61\x2e",
"\xa5\xce%s\xb9\xef\xa5\x49\xb3\x6f\xa8\xc7\xb2\xa7\xa7\xce\xa9\xc7\xaa\xab\xa1\x41\xae\xda\xa5\xbb\xa8\x53\xa6\xb3\xa5\xce\xa1\x43"
};
constexpr I18nText kI18N_cutTheWiresText = {
"The %s is not strong enough to cut the cables.\nPerhaps if you had some wire cutters?",
"%s NO ES LO BASTANTE RESISTENTE COMO PARA CORTAR LOS CABLES.\nY SI TUVIERAS UN CORTACABLES?",
"%s NE POURRA PAS COUPER LES CABLES.\nET SI VOUS AVIEZ DES PINCES COUPANTES?",
"%s IST NICHT STARK GENUG, UM DIE KABEL ABZUSCHNEIDEN.\nHAST DU VIELLEICHT EINEN KABELSCHNEIDER?",
"%s \xc5\x41\xb7\xa1\xa7\x69\xb7\x69\x20\xb8\xe9\x94\x65\xd0\x69\x20\xae\x81\x20\xb7\xb6\xb7\x69\x20\xb8\xf7\x95\xa1\x9d\xa1\x20\x88\x77\xd0\x61\xbb\xa5\x20\xa1\xb5\xd0\x73\x93\xa1\x94\x61\x2e\x20\x89\xc1\xb5\x65\x20\xc5\x41\xb7\xa1\xa7\x69\xb7\x69\x20\xb8\xe9\x94\x65\xd0\x69\x20\xae\x81\x20\xb7\xb6\xb7\x69\x8c\x61\xb6\x61\x3f",
"%s\xae\xda\xa5\xbb\xa8\x53\xbf\xec\xaa\x6b\xa4\xc1\xc2\x5f\xb3\x6f\xa8\xc7\xb9\x71\xc6\x6c\xbd\x75\xa1\x43 \xa9\xce\xb3\xa7\x41\xa6\xb3\xaa\xf7\xc4\xdd\xa4\xc1\xb3\xce\xbe\xb9\xb6\xdc\xa1\x48"
};
constexpr I18nText kI18N_NoEffectOnTheProtectedAncientText = {
"The %s has no effect on the protected ancient.",
"%s NO TIENE EFECTO SOBRE EL ANTEPASADO PROTEGIDO.",
"%s N'A AUCUN EFFET SUR L'ANCIEN PROTEGE.",
"%s BEWIRKT NICHTS BEI DEM GESCHUETZTEN ALTEN.",
"%s \xc4\x93\xaf\x49\xad\xa2\xb7\x81\x20\xa3\xa1\xb7\xa1\x9c\x61\xb5\x41\x89\x41\x20\xb5\x77\xd0\xb7\xb7\x69\x20\xa3\xa1\xc3\xa1\xbb\xa1\x20\xa1\xb5\xd0\x73\x93\xa1\x94\x61\x2e",
"\x25\x73\xb9\xef\xb3\x6f\xa8\xc7\xa8\xfc\xa8\xec\xab\x4f\xc5\x40\xaa\xba\xa5\x6a\xa5\x4e\xa4\x48\xa1\x41\xa8\xc3\xa8\x53\xa6\xb3\xa5\xf4\xa6\xf3\xae\xc4\xa5\xce\xa1\x43"
};
constexpr I18nText kI18N_YouHideTheObjectUnderThePillowText = {
"You hide the %s under the pillow.",
"OCULTAS %s BAJO LA ALMOHADA.",
"VOUS CACHEZ %s SOUS L'OREILLER.",
"DU VERSTECKST %s UNTER DEM KISSEN.",
"%s \xa5\x41\x88\x81\xa3\xbb\xb5\x41\x20\xae\x91\x8b\xb3\x93\xa1\x94\x61\x2e",
"\xa7\x41\xa7\xe2%s\xc2\xc3\xa6\x62\xaa\x45\xc0\x59\xa9\xb3\xa4\x55\xa1\x43"
};
constexpr I18nText kI18N_DontPutTheObjectInTheTrunkText = {
"Don't put the %s in the trunk, you'll forget it.",
"NO PONGAS %s EN EL BAUL, TE OLVIDARAS.",
"NE METTEZ PAS %s DANS LE COFFRE, VOUS L'OUBLIEREZ.",
"WENN DU %s IN DEN TRUHE LEGST, VERGISST DU ES.", // TODO check this is correct.
"%s \xcb\x61\x9c\xf7\xc7\x61\xb5\x41\x20\x90\xfd\xbb\xa1\x20\xa0\x61\xaf\xb3\xaf\xa1\xb6\x61\x2e",
"\xa4\xa3\xad\x6e\xb1\x4e\x25\x73\xa9\xf1\xa6\x62\xa8\x54\xa8\xae\xa6\xe6\xa7\xf5\xbd\x63\xa4\xa4\xa1\x41\xa7\x41\xb1\x4e\xab\xdc\xae\x65\xa9\xf6\xa7\xd1\xb0\x4f\xa5\xa6\xb4\xbf\xb8\x67\xa9\xf1\xa6\x62\xa6\xf3\xb3\x42\xa1\x43"
};
constexpr I18nText kI18N_TheCarWontStartWithTheText = {
"The car won't start with the %s.",
"EL COCHE NO SE PONDRA EN MARCHA CON %s.",
"LA VOITURE NE DEMARRERA PAS AVEC %s.",
"DAS AUTO SPRINGT NICHT AN MIT %s.",
"%s\x9d\xa1\x93\x65\x20\xb8\x61\x95\xb7\xc0\x61\xb7\x81\x20\xaf\xa1\x95\xb7\xb7\x69\x20\x88\xe9\x20\xae\x81\x20\xb4\xf4\xaf\x73\x93\xa1\x94\x61\x2e",
"\xa8\xcf\xa5\xce%s\xa8\xc3\xa4\xa3\xaf\xe0\xc5\xfd\xa8\x54\xa8\xae\xb5\x6f\xb0\xca\xa1\x43"
};
constexpr I18nText kI18N_IfYouPutTheObjectInTheTrunkText = {
"If you put the %s in the trunk, you'll forget it.",
"SI PONES %s EN EL BAUL, TE OLVIDARAS.",
"SI VOUS METTEZ %s DANS LE COFFRE, VOUS L'OUBLIEREZ.",
"WENN DU %s IN DEN TRUHE LEGST, VERGISST DU ES.",
"%s \xb7\xb0\xb4\xe1\xa4\xe1\x9f\xa1\x89\xa1\x20\xaf\xbc\xb7\x61\xaf\xb3\x93\xa1\x8c\x61\x3f",
"\xad\x59\xac\x4f\xa7\x41\xb1\x4e\x25\x73\xa9\xf1\xa6\x62\xa8\x54\xa8\xae\xa6\xe6\xa7\xf5\xbd\x63\xa4\xa4\xa1\x41\xa7\x41\xb1\x4e\xab\xdc\xae\x65\xa9\xf6\xa7\xd1\xb0\x4f\xa5\xa6\xb4\xbf\xb8\x67\xa9\xf1\xa6\x62\xa6\xf3\xb3\x42\xa1\x43"
};
constexpr I18nText kI18N_TheObjectIsYoursYouHaventLostItText = {
"The %s is yours, you haven't lost it.",
"%s ES TUYA, NO LA HASA PERDIDO.",
"%s EST A VOUS, VOUS NE L'AVEZ PAS PERDUE.",
"%s GEHOERT DIR, DU HAST ES NICHT VERLOREN.",
"%s \xb7\xa1\xa3\xa1\x20\x94\x77\xaf\xa5\xb7\x81\x20\x88\xf5\xb7\xb3\x93\xa1\x94\x61\x2e",
"%s\xa4\x77\xb8\x67\xac\x4f\xa7\x41\xaa\xba\xa4\x46\xa1\x41\xa7\x41\xa8\xc3\xa8\x53\xa6\xb3\xbf\xf2\xa5\xa2\xb1\xbc\xb0\xda\xa1\x48"
};
constexpr I18nText kI18N_notAGoodPlaceToHideTheText = {
"Not a good place to hide the %s.",
"NO ES UN BUEN SITIO PARA OCULTAR %S.",
"VOUS NE POURREZ PAS CACHER %S.",
"KEIN GUTES VERSTECK FUER %S.",
"%s \x91\xbd\xb4\x61\x96\x89\xa0\x65\xd0\x65\x20\xb8\x77\xad\xa1\x93\x65\x20\xb4\x61\x93\xb3\x93\xa1\x94\x61\x2e",
"\xb3\x6f\xa4\xa3\xac\x4f\xa4\x40\xad\xd3\xc2\xc3%s\xaa\xba\xa6\x6e\xa6\x61\xa4\xe8\xa1\x43"
};
constexpr I18nText kI18N_youTryToPutTheObjectInsideButTheDoorWontOpenText = {
"You try to put the %s inside, but the door won't open",
"INTENTAS PONER %s DENTRO, PERO LA PUERTA NO SE ABRIRA.",
"VOUS ESSAYEZ DE METTRE %s A L'INTERIEUR, MAIS LA PORTE NE S'OUVRIRA PAS.",
"DU VERSUCHST, %s HINEINZUSTELLEN, ABER DIE TUER OEFFNET SICH NICHT.",
"%s \x8b\x61\x89\xb5\xb5\x41\x20\xac\x61\xb6\x77\xd0\x69\x20\xcf\xa9\xb6\x61\x88\x61\x20\xb7\xb6\xb7\x69\x8c\x61\xb6\x61\x3f",
"\xa7\x41\xb9\xc1\xb8\xd5\xb1\x4e\x25\x73\xa9\xf1\xb8\x6d\xa6\x62\xb8\xcc\xad\xb1\xa1\x41\xa6\xfd\xac\x4f\xaa\xf9\xa8\xc3\xa8\x53\xa6\xb3\xa5\xb4\xb6\x7d\xa1\x43"
};
constexpr I18nText kI18N_theKitchenIsNoPlaceToKeepTheText = {
"The kitchen is no place to keep the %s.",
"LA COCINA NO ES LUGAR PARA GUARDAR %s.",
"LA CUISINE NE CONVIENT PAS A %s.",
"DIE KUECHE IST KEIN PLATZ FUER %s.",
"\xa6\x81\xb4\xfa\xb5\x41\x93\x65 %s \x91\xbd\xb4\x61\x96\x89\xa0\x65\xd0\x65\x20\xb8\x77\xad\xa1\x88\x61\x20\xb4\x61\x93\xb3\x93\xa1\x94\x61\x2e",
"\xbc\x70\xa9\xd0\xa8\xc3\xa8\x53\xa6\xb3\xa6\x68\xa4\xd6\xaa\xc5\xb6\xa1\xa8\xd3\xa6\x73\xa9\xf1%s"
};
constexpr I18nText kI18N_youllForgetTheObjectHereText = {
"You'll forget the %s here.",
"OLIVIDARAS %s AQUI.",
"VOUS OUBLIEREZ %s ICI.",
"DU WIRST NOCH %s HIER VERGESSEN.",
"\xb5\x61\x8b\xa1\x94\x61 %s \x91\xbd\xb4\x61\x96\x81\xa1\x65\x20\xb7\xb0\xb4\xe1\xa4\xe1\x9f\xa9\x20\x88\xf5\xb7\xb3\x93\xa1\x94\x61\x2e",
"\xa7\x41\xb1\x4e\xb7\x7c\xa7\xd1\xa4\x46\xa7\xe2\x25\x73\xa9\xf1\xa6\x62\xb3\x6f\xb8\xcc\xa1\x43"
};
constexpr I18nText kI18N_youdRatherHaveTheObjectWithYouText = {
"You'd rather have the %s with you.",
"SERIA MEJOR TENER %s CONTIGO.",
"IL VAUDRAIT MIEUX AVOIR %s SUR VOUS.",
"DU HAETTEST LIEBER %s BEI DIR.",
"\xc0\x61\x9c\x61\x9f\xa1 %s \x88\x61\xbb\xa1\x93\x65\x89\x41\x20\xb9\xbd\xb7\x69\x88\xf5\x20\xb7\xb3\x93\xa1\x94\x61\x2e",
"\xa7\x41\xb9\xe7\xc4\x40\xb1\x4e\x25\x73\xaf\x64\xa6\x62\xa8\xad\xa4\x57\xa1\x43"
};
constexpr I18nText kI18N_theObjectHasNoEffectText = {
"The %s has no effect.",
"%s NO TIENE EFECTO.",
"%s N'A AUCUN EFFET.",
"%s BEWIRKT NICHTS.",
"%s \xb5\x77\xd0\xb7\xb7\x69\x20\xa3\xa1\xc3\xa1\xbb\xa1\x20\xa1\xb5\xd0\x73\x93\xa1\x94\x61\x2e",
"%s\xa8\x53\xa6\xb3\xae\xc4\xaa\x47\xa1\x43"
};
constexpr I18nText kI18N_thisIsNotAGoodPlaceForTheText = {
"This is not a good place for the %s.",
"NO ES UN BUEN SITIO PARA %s.",
"CE N'EST PAS UN BON ENDROIT POUR %s.",
"DAS IST KEIN GUTER PLATZ FUER %s.",
"%s \x91\xbd\xb4\x61\x96\x89\x20\xb8\x77\xad\xa1\x9d\xa1\xac\xe1\x93\x65\x20\xb8\xe2\xd0\x73\xd0\x61\xbb\xa1\x20\xb4\x67\xb7\x65\x88\xf5\x20\x88\x7b\x8a\x85\xb6\x61\x2e",
"\xb3\x6f\xb8\xcc\xa4\xa3\xac\x4f\xa9\xf1\xb8\x6d\x25\x73\xaa\xba\xa6\x6e\xa6\x61\xa4\xe8\xa1\x43"
};
constexpr I18nText kI18N_youSeeAReflectionOfTheText = {
"You see a reflection of the %s.",
"VES UN REFLEJO DE %s.",
"VOUS VOYEZ UN REFLET REPRESENTANT %s.",
"DU SIEHST EIN SPIEGELBILD VON %s.",
"%s \x88\xe1\xb6\x89\xb9\xa1\x88\x62\xb5\x41\x20\xa7\xa1\xc2\xa1\x95\xa1\x20\xb4\x61\xa2\x81\x9c\xe5\x20\xa4\x65\xb7\x77\xb7\xa1\x20\xb4\xf4\xaf\x73\x93\xa1\x94\x61\x2e",
"\xa7\x41\xac\xdd\xa8\xec\xa4\x46\x25\x73\xaa\xba\xa4\xcf\xae\x67\xac\x4d\xb9\xb3\xa1\x43"
};
constexpr I18nText kI18N_youDontWantToLeaveTheObjectUnderTheBedText = {
"You don't want to leave the %s under the bed.",
"NO QUIERES DEJAR %s DEBAJO DE LA CAMA.",
"POURQUOI LAISSERIEZ-VOUS %s SOUS LE LIT?",
"DU WILLST %s NICHT UNTER DEM BETT LASSEN.",
"%s \xc3\xb1\x94\x81\xa3\xbb\xb5\x41\x20\x90\xfd\x93\x65\x88\xf5\xb7\x65\x20\xa5\x69\x9d\xa1\x20\xb9\xbd\xb7\x65\x20\xac\x97\x88\x62\xb7\xa1\x20\xb4\x61\x93\xa5\x88\xf5\x20\x88\x7b\xaf\x73\x93\xa1\x94\x61\x2e",
"\xa7\x41\xa4\xa3\xad\x6e\xb1\x4e\x25\x73\xaf\x64\xa6\x62\xa7\xc9\xa9\xb3\xa4\x55\xa1\x43"
};
constexpr I18nText kI18N_genResponse0_usingTheObjectOnTheObjectItMustBeYourHeadachesText = {
"Using the %s on the %s doesn't make any sense, it must be your headaches!",
"UTILIZAR %s EN %s NO TIENE SENTIDO, TE PRODUCIRA DOLOR DE CABEZA",
"POURQUOI UTILISER %s SUR %s PRENEZ DONC DE L'ASPIRINE!",
"GEBRAUCHEN VON %s AUF %s MACHT KEINEN SINN, DAS MUSS AN DEINEN KOPFSCHMERZEN LIEGEN!",
"%s %s\xb5\x41\x20\xac\x61\xb6\x77\xd0\x61\x93\x65\x88\xf5\xb7\x65\x20\xa0\x69\x95\xa1\x96\x41\xbb\xa1\x20\xb4\x67\xaf\x73\x93\xa1\x94\x61\x2e\x20\x8b\x61\x20\xaf\xb1\xd0\x65\x20\x96\x81\xc9\xb7\x98\x81\xa2\x85\xb5\x41\x20\x8b\x61\x9c\xe1\x93\x65\x88\xf5\xb7\xa1\x20\xb4\x61\x93\xa9\x8c\x61\xb6\x61\x3f",
"\xa8\xcf\xa5\xce\x25\x73\xa9\xf3\x25\x73\xb2\x40\xb5\x4c\xb7\x4e\xb8\x71\xa1\x41\xa8\xba\xa4\x40\xa9\x77\xa5\xd1\xa9\xf3\xa7\x41\xaa\xba\xc0\x59\xb5\x68\xa1\x49"
};
constexpr I18nText kI18N_genResponse1_theObjectWillDoNothingToTheText = {
"The %s will do nothing to the %s.",
"%s NO TIENE NADA QUE VER CON %s.",
"%s NE FERONT RIEN A %s.",
"%s HAT NICHTS ZU TUN MIT %s",
"%s %s\xb5\x41\x20\xac\x61\xb6\x77\xd0\x61\x93\x65\x88\xf5\xb7\x65\x20\xb4\x61\xa2\x81\x9c\xe5\x20\xad\xa1\xb6\x77\xb7\xa1\x20\xb4\xf4\xaf\x73\x93\xa1\x94\x61\x2e",
"\xa7\xe2\x25\x73\xa5\xce\xa6\x62\x25\x73\xa4\x57\xa8\x53\xa6\xb3\xa5\xce\xa1\x43"
};
constexpr I18nText kI18N_genResponse2_theObjectDoesntHaveAnyEffectOnTheText = {
"The %s doesn't have any effect on the %s.",
"%s NO LE CAUSARA EFECTO A %s.",
"%s N'AURA AUCUN EFFET SUR %s.",
"%s BEWIRKT NICHTS MIT %s.",
"%s %s\xb5\x41\x20\xb4\x61\xa2\x81\xb5\x77\xd0\xb7\x95\xa1\x20\xa3\xa1\xc3\xa1\xbb\xa1\xa1\xb5\xd0\x73\x93\xa1\x94\x61\x2e",
"\xa7\xe2\x25\x73\xa5\xce\xa6\x62\x25\x73\xa4\x57\xb2\x40\xb5\x4c\xae\xc4\xaa\x47\xa1\x43"
};
constexpr I18nText kI18N_genResponse3_theObjectHasNothingToDoWithTheText = {
"The %s has nothing to do with %s.",
"%s NO TIENE NADA QUE VER CON %s.",
"%s N'A AUCUN RAPPORT AVEC %s.",
"%s HAT NICHTS ZU TUN MIT %s.",
"%s %s \xb8\xe5\xd1\x61\x20\x89\xc5\x89\x81\x88\x61\x20\xb4\xf4\xaf\x73\x93\xa1\x94\x61\x2e",
"\x25\x73\xb9\xef\x25\x73\xb0\x5f\xa4\xa3\xa4\x46\xa4\xb0\xbb\xf2\xa7\x40\xa5\xce\xa1\x43"
};
constexpr I18nText kI18N_genResponse4_areYouFeelingAlrightText = {
"Are you feeling alright?",
"TE ENCUENTRAS BIEN?",
"VOUS ETES SUR QUE CA VA BIEN?",
"FUEHLST DU DICH GUT?",
"\xbb\xa1\x8b\x71\x20\xb8\xf7\xaf\xa5\xb7\xa1\x20\xb5\xa5\xb8\xe5\xd0\x73\x93\xa1\x8c\x61\x3f\x0a",
"\xa7\x41\xc1\xd9\xa6\x6e\xa7\x61\xa1\x48"
};
constexpr I18nText kI18N_blankText = {
"",
"",
"",
"",
"",
""
};
} // namespace Darkseed
#endif //DARKSEED_LANGTEXT_H

245
engines/darkseed/menu.cpp Normal file
View File

@@ -0,0 +1,245 @@
/* 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/keyboard.h"
#include "common/config-manager.h"
#include "darkseed/menu.h"
#include "darkseed/darkseed.h"
#include "darkseed/langtext.h"
namespace Darkseed {
constexpr I18NTextWithPosition kMenu_load = {
{127, 62, "Load"},
{119, 62, "CARGAR"},
{118, 62, "CHARGER"},
{121, 62, "LADEN"},
{120, 62, "\xA6\x89\x9C\xE1\x90\x91"},
{118, 55, "\xa8\xfa\x20\xc0\xc9"}
};
constexpr I18NTextWithPosition kMenu_silent = {
{123, 136, "Silent"},
{115, 136, "SIN SON"},
{118, 136, "SILENCE"},
{129, 136, "RUHE"},
{121, 136, "\xB7q\xAD\xA1\x88\xE1"},
{118, 129, "\xc0\x52\x20\xad\xb5"}
};
constexpr I18NTextWithPosition kMenu_sounds = {
{117, 136, "Sounds"},
{121, 136, "SONIDO"},
{126, 136, "SONS"},
{123, 136, "SOUND"},
{128, 136, "\xB7q\xD0\xB7"},
{118, 129, "\xad\xb5\x20\xae\xc4"}
};
constexpr I18NTextWithPosition kMenu_save = {
{127, 99, "Save"},
{119, 99, "GRABAR"},
{121, 99, "SAUVER"},
{115, 99, "SICHERN"},
{128, 99, "\xB8\xE1\xB8w"},
{118, 92, "\xa6\x73\x20\xc0\xc9"}
};
constexpr I18NTextWithPosition kMenu_resume = {
{118, 173, "Resume"},
{122, 173, "SEQUIR"},
{124, 173, "JOUER"},
{118, 173, "WEITER"},
{128, 173, "\xA5\xA2\x8A\xE1"},
{118, 166, "\xc4\x7e\x20\xc4\xf2"}
};
constexpr I18NTextWithPosition kMenu_quit = {
{129, 210, "Quit"},
{125, 210, "SALIR"},
{117, 210, "QUITTER"},
{129, 210, "ENDE"},
{119, 210, "\x8F{\xA0""a\xC3\xB1"},
{118, 203, "\xb5\xb2\x20\xa7\xf4"}
};
void Menu::drawMenuItem(const I18NTextWithPosition &menuText) {
const TextWithPosition &textWithPosition = getI18NTextWithPosition(menuText);
if (_zhFont) {
_zhFont->drawString(g_engine->_screen, convertToU32String(textWithPosition.text, g_engine->getLanguage()), textWithPosition.x, textWithPosition.y, 640, 14);
} else {
g_engine->_console->drawStringAt(textWithPosition.x, textWithPosition.y, convertToU32String(textWithPosition.text, g_engine->getLanguage()));
}
}
void Menu::drawSoundMenuItem() {
if (g_engine->getLanguage() == Common::ZH_ANY) {
g_engine->_screen->fillRect({{115, 129}, 62, 20}, 0);
} else {
g_engine->_screen->fillRect({{115, 136}, 62, 13}, 0);
}
if (g_engine->_sound->isMuted()) {
drawMenuItem(kMenu_silent);
} else {
drawMenuItem(kMenu_sounds);
}
}
Common::KeyCode Menu::getLocalisedConfirmToQuitKeycode() {
switch (g_engine->getLanguage()) {
case Common::ES_ESP : return Common::KeyCode::KEYCODE_s;
case Common::FR_FRA : return Common::KeyCode::KEYCODE_o;
case Common::DE_DEU : return Common::KeyCode::KEYCODE_j;
default : return Common::KeyCode::KEYCODE_y;
}
}
Menu::Menu() {
if (g_engine->getLanguage() == Common::ZH_ANY) {
_zhFont = new ZhMenuFont("zhmenufont_game.dat", ZhLargeFontType::InGame);
}
}
Menu::~Menu() {
delete _zhFont;
}
void Menu::loadMenu() {
g_engine->_sound->stopSpeech();
if (g_engine->_sound->isPlayingMusic()) {
g_engine->_sound->startFadeOut();
while (g_engine->_sound->isFading()) {
g_engine->wait();
}
g_engine->_sound->stopMusic();
}
g_engine->_sound->stopSfx();
_open = true;
Graphics::Surface screenCopy;
screenCopy.copyFrom(*g_engine->_screen);
g_engine->showFullscreenPic(g_engine->_room->isGiger() ? "glady.pic" : "clady.pic");
g_engine->_frame.draw();
g_engine->drawFullscreenPic();
g_engine->_console->draw(true);
drawMenuItem(kMenu_load);
drawMenuItem(kMenu_save);
drawSoundMenuItem();
drawMenuItem(kMenu_resume);
drawMenuItem(kMenu_quit);
g_engine->_screen->makeAllDirty();
g_engine->_screen->update();
g_engine->zeroMouseButtons();
g_engine->_lastKeyPressed = Common::KeyCode::KEYCODE_INVALID;
while (!g_engine->shouldQuit()) {
g_engine->updateEvents();
int menuItemIdx = -1;
if (g_engine->_isLeftMouseClicked) {
g_engine->zeroMouseButtons();
Common::Point cursorPos = g_engine->_cursor.getPosition();
if (cursorPos.x > 111 && cursorPos.x < 178 && cursorPos.y > 50 && cursorPos.y < 235) {
menuItemIdx = (cursorPos.y - 50) / 37;
}
}
if (g_engine->_lastKeyPressed != Common::KeyCode::KEYCODE_INVALID) {
switch (g_engine->_lastKeyPressed) {
case Common::KeyCode::KEYCODE_l: menuItemIdx = 0; break;
case Common::KeyCode::KEYCODE_s: menuItemIdx = 1; break;
case Common::KeyCode::KEYCODE_r: menuItemIdx = 3; break;
case Common::KeyCode::KEYCODE_q: menuItemIdx = 4; break;
default: break;
}
g_engine->_lastKeyPressed = Common::KeyCode::KEYCODE_INVALID;
}
if (menuItemIdx > -1) {
g_engine->_sound->playSfx(5, 0, -1);
g_engine->waitForSpeechOrSfx();
}
if (menuItemIdx == 0) {
g_engine->loadGameDialog();
break;
}
if (menuItemIdx == 1) {
// restore game screen back for the save game thumbnail
g_engine->_screen->copyRectToSurface(screenCopy, 0, 0, {screenCopy.w, screenCopy.h});
g_engine->_room->installPalette(); // restore room working palette
g_engine->_screen->update();
_open = false; // mark menu as closed to allow saving.
g_engine->saveGameDialog();
break;
}
if (menuItemIdx == 2) { // sound settings
g_engine->flipMute();
ConfMan.flushToDisk();
drawSoundMenuItem();
}
if (menuItemIdx == 3) { // Resume
break;
}
if (menuItemIdx == 4) {
g_engine->_console->printTosText(16);
g_engine->_console->draw();
g_engine->_screen->update();
g_engine->_lastKeyPressed = Common::KeyCode::KEYCODE_INVALID;
while (!g_engine->shouldQuit()) {
g_engine->updateEvents();
if (g_engine->_lastKeyPressed == getLocalisedConfirmToQuitKeycode() || g_engine->_isLeftMouseClicked) {
g_engine->quitGame();
break;
}
if (g_engine->_lastKeyPressed == Common::KeyCode::KEYCODE_n ||
g_engine->_lastKeyPressed == Common::KeyCode::KEYCODE_ESCAPE ||
g_engine->_isRightMouseClicked) {
g_engine->_console->printTosText(17);
g_engine->_console->draw();
g_engine->_screen->update();
break;
}
g_engine->_screen->update();
g_engine->wait();
}
}
g_engine->_screen->update();
g_engine->wait();
}
g_engine->_room->loadRoomMusic();
g_engine->removeFullscreenPic();
screenCopy.free();
_open = false;
}
} // End of namespace Darkseed

49
engines/darkseed/menu.h Normal file
View File

@@ -0,0 +1,49 @@
/* 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 DARKSEED_MENU_H
#define DARKSEED_MENU_H
#include "darkseed/zhmenufont.h"
#include "darkseed/langtext.h"
namespace Darkseed {
class Menu {
private:
bool _open = false;
ZhMenuFont *_zhFont = nullptr;
public:
Menu();
~Menu();
bool isOpen() const { return _open; }
void loadMenu();
private:
void drawSoundMenuItem();
Common::KeyCode getLocalisedConfirmToQuitKeycode();
void drawMenuItem(const I18NTextWithPosition &menuText);
};
} // namespace Darkseed
#endif //DARKSEED_MENU_H

View File

@@ -0,0 +1,99 @@
/* 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/translation.h"
#include "backends/keymapper/action.h"
#include "backends/keymapper/standard-actions.h"
#include "darkseed/metaengine.h"
#include "darkseed/detection.h"
#include "darkseed/darkseed.h"
#include "darkseed/dialogs.h"
const char *DarkseedMetaEngine::getName() const {
return "darkseed";
}
Common::KeymapArray DarkseedMetaEngine::initKeymaps(const char *target) const {
using namespace Common;
Keymap *engineKeyMap = new Keymap(Keymap::kKeymapTypeGame, "darkseed", "Darkseed");
Action *act;
act = new Action(kStandardActionLeftClick, _("Action"));
act->setCustomEngineActionEvent(Darkseed::kDarkseedActionSelect);
act->addDefaultInputMapping("MOUSE_LEFT");
act->addDefaultInputMapping("JOY_A");
engineKeyMap->addAction(act);
act = new Action("CHANGECOMMAND", _("Change command"));
act->setCustomEngineActionEvent(Darkseed::kDarkseedActionChangeCommand);
act->addDefaultInputMapping("MOUSE_RIGHT");
act->addDefaultInputMapping("JOY_B");
engineKeyMap->addAction(act);
act = new Action("TIMEADVANCE", _("Time advance"));
act->setCustomEngineActionEvent(Darkseed::kDarkseedActionTimeAdvance);
act->addDefaultInputMapping("t");
act->addDefaultInputMapping("JOY_Y");
engineKeyMap->addAction(act);
act = new Action("QUIT", _("Quit game"));
act->setCustomEngineActionEvent(Darkseed::kDarkseedActionQuit);
act->addDefaultInputMapping("C+q");
engineKeyMap->addAction(act);
act = new Action("SKIPCUTSCENE", _("Skip cutscene"));
act->setCustomEngineActionEvent(Darkseed::kDarkseedActionSkipCutscene);
act->addDefaultInputMapping("SPACE");
engineKeyMap->addAction(act);
return Keymap::arrayOf(engineKeyMap);
}
Common::Error DarkseedMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
*engine = new Darkseed::DarkseedEngine(syst, desc);
return Common::kNoError;
}
bool DarkseedMetaEngine::hasFeature(MetaEngineFeature f) const {
return checkExtendedSaves(f) ||
(f == kSupportsLoadingDuringStartup);
}
void DarkseedMetaEngine::registerDefaultSettings(const Common::String &target) const {
for (const ADExtraGuiOptionsMap *entry = Darkseed::optionsList; entry->guioFlag; ++entry)
ConfMan.registerDefault(entry->option.configOption, entry->option.defaultState);
for (const Darkseed::PopUpOptionsMap *entry = Darkseed::popUpOptionsList; entry->guioFlag; ++entry)
ConfMan.registerDefault(entry->configOption, entry->defaultState);
}
GUI::OptionsContainerWidget *DarkseedMetaEngine::buildEngineOptionsWidget(GUI::GuiObject *boss, const Common::String &name, const Common::String &target) const {
return new Darkseed::OptionsWidget(boss, name, target);
}
#if PLUGIN_ENABLED_DYNAMIC(DARKSEED)
REGISTER_PLUGIN_DYNAMIC(DARKSEED, PLUGIN_TYPE_ENGINE, DarkseedMetaEngine);
#else
REGISTER_PLUGIN_STATIC(DARKSEED, PLUGIN_TYPE_ENGINE, DarkseedMetaEngine);
#endif

View File

@@ -0,0 +1,46 @@
/* 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 DARKSEED_METAENGINE_H
#define DARKSEED_METAENGINE_H
#include "backends/keymapper/keymap.h"
#include "engines/advancedDetector.h"
class DarkseedMetaEngine : public AdvancedMetaEngine<ADGameDescription> {
public:
const char *getName() const override;
Common::Error createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const override;
/**
* Determine whether the engine supports the specified MetaEngine feature.
*
* Used by e.g. the launcher to determine whether to enable the Load button.
*/
bool hasFeature(MetaEngineFeature f) const override;
Common::KeymapArray initKeymaps(const char *target) const override;
void registerDefaultSettings(const Common::String &target) const override;
GUI::OptionsContainerWidget *buildEngineOptionsWidget(GUI::GuiObject *boss, const Common::String &name, const Common::String &target) const override;
};
#endif // DARKSEED_METAENGINE_H

View File

@@ -0,0 +1,303 @@
/* 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 "darkseed/midiparser_sbr.h"
#include "audio/mididrv.h"
namespace Darkseed {
MidiParser_SBR::MidiParser_SBR(int8 source, bool sfx) : MidiParser_SMF(source), _sfx(sfx) {
Common::fill(_trackDeltas, _trackDeltas + ARRAYSIZE(_trackDeltas), 0);
Common::fill(_trackInstruments, _trackInstruments + ARRAYSIZE(_trackInstruments), 0xFF);
Common::fill(_trackNoteActive, _trackNoteActive + ARRAYSIZE(_trackNoteActive), 0xFF);
Common::fill(_trackLoopCounter, _trackLoopCounter + ARRAYSIZE(_trackLoopCounter), 0);
// SBR uses a fixed tempo.
_ppqn = 96;
setTempo(1318214);
}
void MidiParser_SBR::parseNextEvent(EventInfo &info) {
uint8 subtrack = info.subtrack;
const byte *parsePos = _position._subtracks[subtrack]._playPos;
const uint8 *start = parsePos;
/**
* SBR uses 6 byte structures to represent events:
* - Event type / note
* - Note velocity
* - (unused)
* - Delta (word, little-endian)
* - Instrument
* Delta is ticks to the next event, not preceding this event like SMF.
*
* The following event types are used:
* - 0x00: End of track. This is the only byte in this event.
* - 0x01: Subtrack list. See loadMusic.
* - 0x02: Rest. Effectively a note off and a delta to the next event.
* - 0x03: Sample. Indicates the sample filename shoud be read from the
* corresponding DIG file entry. See Sound class.
* - 0x05: Restart playback from the beginning of the subtrack.
* - 0x06: Loop. Velocity specifies the number of events to jump back.
* Delta specifies the number of times to repeat this section.
* - 0x24+: Note on.
*/
info.start = start;
info.length = 0;
info.noop = false;
info.loop = false;
info.delta = _trackDeltas[subtrack];
_trackDeltas[subtrack] = 0;
// Any event will turn off the active note on this subtrack.
if (_trackNoteActive[subtrack] != 0xFF) {
info.event = 0x80 | subtrack;
info.basic.param1 = _trackNoteActive[subtrack];
info.basic.param2 = 0x00;
_trackNoteActive[subtrack] = 0xFF;
return;
}
if (parsePos == nullptr || parsePos[0] == 0) {
// Reached the end of the track. Generate an end-of-track event.
info.event = 0xFF;
info.ext.type = MidiDriver::MIDI_META_END_OF_TRACK;
info.ext.data = parsePos;
return;
}
// There are more MIDI events in this track.
uint8 noteType = parsePos[0];
if (noteType == 5) {
// Subtrack needs to be restarted. Generate a custom meta event.
info.event = 0xFF;
info.ext.type = 5;
info.loop = true;
return;
}
if (noteType == 6) {
// Subtrack needs to be looped. Generate a custom meta event.
info.event = 0xFF;
info.ext.type = 6;
info.ext.data = parsePos;
info.loop = true;
// Delta needs to be one more than specified due to differences in the
// way this event is processed compared to the original code.
info.delta++;
return;
}
// Check if the instrument specified in this event is different from the
// current instrument for this track. Typically, all events in a subtrack
// have the same instrument.
uint8 instrument = parsePos[5];
if (_trackInstruments[subtrack] != instrument) {
if (_trackInstruments[subtrack] <= 0xF && (_trackInstruments[subtrack] / 3) != (instrument / 3)) {
// WORKAROUND The current instrument for this subtrack is a rhythm
// instrument, and the instrument specified in this event is for a
// different rhythm instrument type. This occurs a few times in
// the Dark Seed SBR files. The instrument for the offending events
// is 0, probably by mistake. The original code will allocate a
// subtrack to an OPL rhythm instrument based on the first event
// and then never change it, even when an event with an instrument
// with a different rhythm type is encountered. Instead, it will
// write the new instrument definition to the originally allocated
// OPL rhythm instrument, even if it has the wrong rhythm type.
// The ScummVM code will change the allocation to the rhythm
// instrument indicated by the instrument on the new event, which
// causes incorrect playback, because typically there already is
// a subtrack allocated to this instrument.
// To fix this, the incorrect instrument on this event is simply
// ignored.
}
else {
// The instrument on this event is different from the current
// instrument. Generate a program change event.
_trackInstruments[subtrack] = instrument;
info.event = 0xC0 | subtrack;
info.basic.param1 = instrument;
info.basic.param2 = 0;
return;
}
}
if (noteType >= 24) {
// Note on.
info.event = 0x90 | subtrack;
info.basic.param1 = noteType;
info.basic.param2 = parsePos[1];
_trackNoteActive[subtrack] = noteType;
}
else {
// For rest events, nothing needs to be done other than turning off
// the current note (already done above) and process the delta.
// Subtrack list and sample events are not processed here.
info.noop = true;
}
if (noteType == 2 || noteType >= 24) {
// Delta to the next event is only processed for
// note on and rest events.
_trackDeltas[subtrack] = READ_LE_UINT16(parsePos + 3);
}
// Set play position to the start of the next event.
_position._subtracks[subtrack]._playPos += 6;
}
bool MidiParser_SBR::processEvent(const EventInfo &info, bool fireEvents) {
// Handle custom meta events here.
if (info.event == 0xFF) {
uint8 subtrack = info.subtrack;
if (info.ext.type == 5) {
// Restart. Set play position to the beginning of the subtrack.
_position._subtracks[subtrack]._playPos = _tracks[_activeTrack][subtrack];
return true;
}
else if (info.ext.type == 6) {
// Loop.
bool loop = false;
if (_trackLoopCounter[subtrack] > 0) {
// A loop iteration has completed.
_trackLoopCounter[subtrack]--;
if (_trackLoopCounter[subtrack] > 0) {
// There are more iterations remaining.
loop = true;
}
else {
// Loop has finished. Playback will resume at the event
// after the loop event.
_position._subtracks[subtrack]._playPos += 6;
}
}
else {
// Initialize the loop. Read number of iterations from the
// event delta.
_trackLoopCounter[subtrack] = READ_LE_UINT16(info.ext.data + 3);
loop = true;
}
if (loop) {
// Set the play position back by the number of events indicated
// by the event velocity.
_position._subtracks[subtrack]._playPos -= ((info.ext.data[1]) * 6);
}
return true;
}
}
// All other events are handled like SMF events.
return MidiParser::processEvent(info, fireEvents);
}
void MidiParser_SBR::onTrackStart(uint8 track) {
Common::fill(_trackDeltas, _trackDeltas + ARRAYSIZE(_trackDeltas), 0);
Common::fill(_trackInstruments, _trackInstruments + ARRAYSIZE(_trackInstruments), 0xFF);
Common::fill(_trackNoteActive, _trackNoteActive + ARRAYSIZE(_trackNoteActive), 0xFF);
Common::fill(_trackLoopCounter, _trackLoopCounter + ARRAYSIZE(_trackLoopCounter), 0);
}
bool MidiParser_SBR::loadMusic(const byte *data, uint32 size) {
assert(size > 0);
unloadMusic();
// SBR files typically contain 120 tracks; some have 100.
// Music files only use the first 10. SFX files use the remaining 110.
uint8 startTrack = _sfx ? 10 : 0;
uint8 endTrack = _sfx ? 120 : 10;
// Read all the tracks. These consist of 0 or more 6 byte events,
// terminated by a 00 byte.
uint16 bytesRead = 0;
for (int i = 0; i < endTrack; i++) {
const byte *startOfTrack = data;
uint16 trackSize = 0;
bool foundEndOfTrack = false;
while (bytesRead < size) {
uint8 eventType = data[0];
if (eventType == 0) {
foundEndOfTrack = true;
data++;
trackSize++;
bytesRead++;
break;
}
else {
data += 6;
trackSize += 6;
bytesRead += 6;
}
}
if (!foundEndOfTrack) {
// Some files have less than 120 tracks
endTrack = i;
break;
}
else if (i < startTrack) {
_tracks[i][0] = nullptr;
}
else {
_tracks[i][0] = startOfTrack;
}
}
_numTracks = endTrack;
// Look for tracks starting with a subtrack list event (type 01).
// These are tracks consisting of multiple parallel subtracks.
// Music files typically have a subtrack list in track 0. Some SFX use
// multiple subtracks as well.
for (int i = 0; i < _numTracks; i++) {
if (_tracks[i][0] != nullptr && _tracks[i][0][0] == 0x01) {
// Read the subtrack list. This is a variable-length list of
// subtrack indices, terminated by a 00 byte.
// (The track is padded with garbage and terminated by another
// 00 byte to match the x * 6 byte event format used by all tracks.)
const uint8 *tracklist = _tracks[i][0] + 1;
uint8 subtrackIndex = 0;
while (*tracklist != 0) {
_tracks[i][subtrackIndex++] = _tracks[*tracklist][0];
tracklist++;
}
_numSubtracks[i] = subtrackIndex;
}
else {
// This is a track containing just a single subtrack.
_numSubtracks[i] = 1;
}
}
_disableAutoStartPlayback = true;
resetTracking();
setTrack(0);
return true;
}
bool MidiParser_SBR::isSampleSfx(uint8 sfxId) {
if (!_sfx || sfxId >= _numTracks)
return false;
return _tracks[sfxId][0] != nullptr && _tracks[sfxId][0][0] == 0x03;
}
} // End of namespace Darkseed

View File

@@ -0,0 +1,53 @@
/* 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 DARKSEED_MIDIPARSER_SBR_H
#define DARKSEED_MIDIPARSER_SBR_H
#include "audio/midiparser_smf.h"
namespace Darkseed {
/**
* MIDI parser for the SBR format used by Dark Seed floppy version.
*/
class MidiParser_SBR : public MidiParser_SMF {
public:
MidiParser_SBR(int8 source = -1, bool sfx = false);
bool loadMusic(const byte *data, uint32 size) override;
bool isSampleSfx(uint8 sfxId);
protected:
void parseNextEvent(EventInfo &info) override;
bool processEvent(const EventInfo &info, bool fireEvents = true) override;
void onTrackStart(uint8 track) override;
bool _sfx = false;
uint8 _trackInstruments[10];
uint16 _trackDeltas[10];
uint8 _trackNoteActive[10];
uint16 _trackLoopCounter[10];
};
} // End of namespace Darkseed
#endif

View File

@@ -0,0 +1,48 @@
MODULE := engines/darkseed
MODULE_OBJS = \
adlib_dsf.o \
adlib_worx.o \
animation.o \
anm.o \
big5font.o \
console.o \
cursor.o \
cutscene.o \
darkseed.o \
debugconsole.o \
dialogs.o \
gamefont.o \
img.o \
inventory.o \
kidpic.o \
kofont.o \
langtext.o \
menu.o \
metaengine.o \
midiparser_sbr.o \
morph.o \
music.o \
nsp.o \
objects.o \
pal.o \
pic.o \
player.o \
room.o \
sound.o \
sprites.o \
titlefont.o \
tostext.o \
usecode.o \
zhmenufont.o
# This module can be built as a plugin
ifeq ($(ENABLE_DARKSEED), DYNAMIC_PLUGIN)
PLUGIN := 1
endif
# Include common rules
include $(srcdir)/rules.mk
# Detection objects
DETECT_OBJS += $(MODULE)/detection.o

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 "darkseed/morph.h"
#include "darkseed/darkseed.h"
namespace Darkseed {
Morph::Morph(const Common::Rect &area) : _area(area) {}
void Morph::loadSrcFromScreen() {
_src.copyFrom(*(Graphics::ManagedSurface *)g_engine->_screen);
}
void Morph::loadDestFromScreen() {
_dest.copyFrom(*(Graphics::ManagedSurface *)g_engine->_screen);
}
void Morph::start(MorphDirection direction) {
_stepCount = 0;
_direction = direction;
}
bool Morph::morphStep() {
if (_stepCount > 16) {
return false;
}
draw(_direction == MorphDirection::Forward ? _stepCount : 16 - _stepCount);
_stepCount += 1;
return _stepCount < 17;
}
void Morph::draw(int16 drawIdx) {
uint8 *screen = (uint8 *)g_engine->_screen->getBasePtr(_area.left, _area.top);
uint8 *src = (uint8 *)_src.getBasePtr(_area.left, _area.top);
uint8 *dest = (uint8 *)_dest.getBasePtr(_area.left, _area.top);
for (int y = 0; y < _area.height(); y++) {
for (int x = 0; x < _area.width(); x++) {
if (src[x] != dest[x]) {
screen[x] = (src[x] * (16 - drawIdx) + dest[x] * drawIdx) >> 4;
}
}
screen += g_engine->_screen->pitch;
src += _src.pitch;
dest += _dest.pitch;
}
g_engine->_screen->addDirtyRect(_area);
}
} // End of namespace Darkseed

57
engines/darkseed/morph.h Normal file
View File

@@ -0,0 +1,57 @@
/* 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 DARKSEED_MORPH_H
#define DARKSEED_MORPH_H
#include "common/rect.h"
#include "graphics/managed_surface.h"
namespace Darkseed {
enum class MorphDirection : uint8 {
Forward,
Backward
};
class Morph {
Common::Rect _area;
Graphics::ManagedSurface _src;
Graphics::ManagedSurface _dest;
MorphDirection _direction = MorphDirection::Forward;
int16 _stepCount = 0;
public:
explicit Morph(const Common::Rect &area);
void loadSrcFromScreen();
void loadDestFromScreen();
void start(MorphDirection direction);
bool morphStep();
private:
void draw(int16 drawIdx);
};
} // End of namespace Darkseed
#endif // DARKSEED_MORPH_H

449
engines/darkseed/music.cpp Normal file
View File

@@ -0,0 +1,449 @@
/* 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 "darkseed/music.h"
#include "darkseed/darkseed.h"
namespace Darkseed {
MusicPlayer::MusicPlayer(DarkseedEngine* vm, bool useFloppyMusic, bool useFloppySfx) :
_vm(vm),
_driver(nullptr),
_floppyAdLibDriver(nullptr),
_paused(false),
_deviceType(MT_NULL),
_musicParser(nullptr),
_musicData(nullptr),
_sfxParserSbr(nullptr),
_sfxData(nullptr),
_tosInstrumentBankData(nullptr),
_tosInstrumentBankLoaded(false),
_useFloppyMusic(useFloppyMusic),
_useFloppySfx(useFloppySfx) {
for (int i = 0; i < NUM_SFX_PARSERS; i++) {
_sfxParsers[i] = nullptr;
}
}
MusicPlayer::~MusicPlayer() {
stopMusic();
stopAllSfx();
if (_driver != nullptr) {
_driver->setTimerCallback(nullptr, nullptr);
_driver->close();
}
Common::StackLock lock(_mutex);
if (_musicParser != nullptr)
delete _musicParser;
if (_musicData != nullptr)
delete[] _musicData;
for (int i = 0; i < NUM_SFX_PARSERS; i++) {
if (_sfxParsers[i] != nullptr)
delete _sfxParsers[i];
}
if (_sfxData != nullptr)
delete _sfxData;
if (_tosInstrumentBankData != nullptr)
delete[] _tosInstrumentBankData;
if (_driver != nullptr) {
delete _driver;
_driver = nullptr;
}
}
int MusicPlayer::open() {
assert(!_driver);
int devFlags = MDT_ADLIB; // | MDT_PCSPK;
MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(devFlags);
_deviceType = MidiDriver::getMusicType(dev);
if (_useFloppyMusic) {
switch (_deviceType) {
case MT_ADLIB:
_floppyAdLibDriver = new MidiDriver_DarkSeedFloppy_AdLib(OPL::Config::kOpl2);
_driver = _floppyAdLibDriver;
break;
case MT_PCSPK:
// TODO Implement PC speaker driver
default:
_driver = new MidiDriver_NULL_Multisource();
break;
}
_musicParser = new MidiParser_SBR(0);
if (_useFloppySfx) {
for (int i = 0; i < NUM_SFX_PARSERS; i++) {
if (_sfxParserSbr == nullptr) {
_sfxParserSbr = new MidiParser_SBR(i + 1, true);
_sfxParsers[i] = _sfxParserSbr;
}
else {
_sfxParsers[i] = new MidiParser_SBR(i + 1, true);
}
}
}
} else {
switch (_deviceType) {
case MT_ADLIB:
_driver = new MidiDriver_Worx_AdLib(OPL::Config::kOpl2);
// Some tracks do not set instruments and expect instrument 0
// to be set on each channel. Make sure this is done every time
// a track starts.
_driver->setControllerDefault(MidiDriver_Multisource::CONTROLLER_DEFAULT_PROGRAM);
break;
case MT_PCSPK:
// TODO Implement PC speaker driver
default:
_driver = new MidiDriver_NULL_Multisource();
break;
}
// CD version uses SMF data
_musicParser = MidiParser::createParser_SMF(0);
}
_driver->property(MidiDriver::PROP_USER_VOLUME_SCALING, true);
if (_musicParser != nullptr)
_musicParser->property(MidiParser::mpDisableAutoStartPlayback, true);
for (int i = 0; i < NUM_SFX_PARSERS; i++) {
if (_sfxParsers[i] != nullptr) {
_sfxParsers[i]->property(MidiParser::mpDisableAutoStartPlayback, true);
}
}
int returnCode = _driver->open();
if (returnCode != 0) {
error("MusicPlayer::open - Failed to open MIDI driver - error code %d.", returnCode);
return 1;
}
syncSoundSettings();
if (_musicParser != nullptr) {
_musicParser->setMidiDriver(_driver);
_musicParser->setTimerRate(_driver->getBaseTempo());
}
for (int i = 0; i < NUM_SFX_PARSERS; i++) {
if (_sfxParsers[i] != nullptr) {
_sfxParsers[i]->setMidiDriver(_driver);
_sfxParsers[i]->setTimerRate(_driver->getBaseTempo());
}
}
_driver->setTimerCallback(this, &onTimer);
return 0;
}
void MusicPlayer::onTimer(void *data) {
MusicPlayer *p = (MusicPlayer *)data;
Common::StackLock lock(p->_mutex);
if (p->_musicParser != nullptr)
p->_musicParser->onTimer();
for (int i = 0; i < NUM_SFX_PARSERS; i++) {
if (p->_sfxParsers[i] != nullptr) {
p->_sfxParsers[i]->onTimer();
}
}
}
bool MusicPlayer::isPlayingMusic() {
Common::StackLock lock(_mutex);
return _musicParser != nullptr && _musicParser->isPlaying();
}
void MusicPlayer::stopMusic() {
Common::StackLock lock(_mutex);
if (_musicParser != nullptr) {
_musicParser->stopPlaying();
if (_driver != nullptr) {
_driver->deinitSource(0);
}
}
}
void MusicPlayer::pauseMusic(bool pause) {
Common::StackLock lock(_mutex);
if (_paused == pause || _musicParser == nullptr)
return;
_paused = pause;
if (_paused) {
_musicParser->pausePlaying();
} else {
_musicParser->resumePlaying();
}
}
bool MusicPlayer::isPlayingSfx() {
Common::StackLock lock(_mutex);
for (int i = 0; i < NUM_SFX_PARSERS; i++) {
if (_sfxParsers[i] != nullptr && _sfxParsers[i]->isPlaying()) {
return true;
}
}
return false;
}
bool MusicPlayer::isPlayingSfx(uint8 sfxId) {
Common::StackLock lock(_mutex);
for (int i = 0; i < NUM_SFX_PARSERS; i++) {
if (_sfxParsers[i] != nullptr && _sfxParsers[i]->isPlaying() && _sfxParsers[i]->getActiveTrack() == sfxId) {
return true;
}
}
return false;
}
void MusicPlayer::playSfx(uint8 sfxId, uint8 priority) {
Common::StackLock lock(_mutex);
if (_sfxParsers[0] == nullptr)
return;
if (!_tosInstrumentBankLoaded) {
warning("Attempt to play floppy sound effect %i while TOS instrument bank is not loaded", sfxId);
return;
}
uint8 sfxParserIdx = assignSfxParser();
if (sfxParserIdx == 0xFF) {
warning("All SFX parsers in use when trying to play SFX %i", sfxId);
return;
}
if (_driver != nullptr) {
_driver->resetSourceVolume(sfxParserIdx + 1);
}
if (_floppyAdLibDriver != nullptr)
_floppyAdLibDriver->setSourcePriority(sfxParserIdx + 1, priority);
_sfxParsers[sfxParserIdx]->setTrack(sfxId);
_sfxParsers[sfxParserIdx]->startPlaying();
}
uint8 MusicPlayer::assignSfxParser() {
Common::StackLock lock(_mutex);
int parserIdx = 0xFF;
for (int i = 0; i < NUM_SFX_PARSERS; i++) {
if (_sfxParsers[i] != nullptr && !_sfxParsers[i]->isPlaying()) {
// Make sure all inactive SFX parsers have released their resources
if (_driver != nullptr)
_driver->deinitSource(i + 1);
if (parserIdx == 0xFF)
parserIdx = i;
}
}
// If all SFX parsers are already playing, parserIdx is still 0xFF
return parserIdx;
}
void MusicPlayer::stopAllSfx() {
Common::StackLock lock(_mutex);
for (int i = 0; i < NUM_SFX_PARSERS; i++) {
if (_sfxParsers[i] != nullptr) {
_sfxParsers[i]->stopPlaying();
if (_driver != nullptr) {
_driver->deinitSource(i + 1);
}
}
}
}
bool MusicPlayer::stopSfx(uint8 sfxId) {
Common::StackLock lock(_mutex);
bool stoppedSfx = false;
for (int i = 0; i < NUM_SFX_PARSERS; i++) {
if (_sfxParsers[i] != nullptr && _sfxParsers[i]->getActiveTrack() == sfxId) {
_sfxParsers[i]->stopPlaying();
if (_driver != nullptr) {
_driver->deinitSource(i + 1);
}
stoppedSfx = true;
}
}
return stoppedSfx;
}
void MusicPlayer::syncSoundSettings() {
if (_driver)
_driver->syncSoundSettings();
}
void MusicPlayer::setLoopMusic(bool loop) {
Common::StackLock lock(_mutex);
if (_musicParser)
_musicParser->property(MidiParser::mpAutoLoop, loop);
}
void MusicPlayer::load(Common::SeekableReadStream *in, int32 size, bool sfx) {
Common::StackLock lock(_mutex);
MidiParser *parser = sfx ? _sfxParsers[0] : _musicParser;
byte **dataPtr = sfx ? &_sfxData : &_musicData;
if (parser == nullptr)
return;
if (size < 0) {
// Use the parser to determine the size of the MIDI data.
int64 startPos = in->pos();
size = parser->determineDataSize(in);
if (size < 0) {
warning("MusicPlayer::load - Could not determine size of music data");
return;
}
// determineDataSize might move the stream position, so return it to
// the original position.
in->seek(startPos);
}
if (!sfx && isPlayingMusic()) {
stopMusic();
_musicParser->unloadMusic();
}
if (sfx && isPlayingSfx()) {
stopAllSfx();
for (int i = 0; i < NUM_SFX_PARSERS; i++) {
_sfxParsers[i]->unloadMusic();
}
}
if (*dataPtr != nullptr) {
delete[] *dataPtr;
}
*dataPtr = new byte[size];
in->read(*dataPtr, size);
if (!sfx) {
_musicParser->loadMusic(*dataPtr, size);
}
else {
for (int i = 0; i < NUM_SFX_PARSERS; i++) {
_sfxParsers[i]->loadMusic(*dataPtr, size);
}
}
}
void MusicPlayer::loadTosInstrumentBankData(Common::SeekableReadStream* in, int32 size) {
if (size != 4096) {
warning("MusicPlayer::loadTosInstrumentBankData - Specified instrument bank has unexpected size %d", size);
return;
}
if (_tosInstrumentBankData != nullptr)
delete[] _tosInstrumentBankData;
_tosInstrumentBankData = new byte[size];
in->read(_tosInstrumentBankData, size);
}
void MusicPlayer::loadTosInstrumentBank() {
if (_floppyAdLibDriver == nullptr) {
warning("MusicPlayer::loadTosInstrumentBank - Driver does not support instrument banks");
return;
}
if (_tosInstrumentBankData == nullptr) {
warning("MusicPlayer::loadTosInstrumentBank - TOS instrument bank data has not been loaded");
return;
}
if (!_tosInstrumentBankLoaded) {
if (isPlayingMusic())
stopMusic();
_floppyAdLibDriver->loadInstrumentBank(_tosInstrumentBankData);
_tosInstrumentBankLoaded = true;
}
}
void MusicPlayer::loadInstrumentBank(Common::SeekableReadStream *in, int32 size) {
if (_floppyAdLibDriver == nullptr) {
warning("MusicPlayer::loadInstrumentBank - Driver does not support instrument banks");
return;
}
if (size != 4096) {
warning("MusicPlayer::loadInstrumentBank - Specified instrument bank has unexpected size %d", size);
return;
}
byte *instrumentBankData = new byte[size];
in->read(instrumentBankData, size);
if (isPlayingMusic())
stopMusic();
_floppyAdLibDriver->loadInstrumentBank(instrumentBankData);
_tosInstrumentBankLoaded = false;
}
bool MusicPlayer::isSampleSfx(uint8 sfxId) {
if (_sfxParserSbr == nullptr)
return false;
return _sfxParserSbr->isSampleSfx(sfxId);
}
void MusicPlayer::playMusic(uint8 priority, bool loop) {
Common::StackLock lock(_mutex);
if (_musicParser == nullptr || _driver == nullptr)
return;
if (!_useFloppyMusic)
_musicParser->property(MidiParser::mpAutoLoop, loop);
if (_floppyAdLibDriver != nullptr)
_floppyAdLibDriver->setSourcePriority(0, priority);
if (_driver->isFading())
_driver->abortFade();
_driver->resetSourceVolume(0);
_musicParser->startPlaying();
}
void MusicPlayer::startFadeOutMusic() {
_driver->startFade(0, 1100, 0);
}
bool MusicPlayer::isFadingMusic() {
return _driver->isFading();
}
} // namespace Darkseed

97
engines/darkseed/music.h Normal file
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 DARKSEED_MUSIC_H
#define DARKSEED_MUSIC_H
#include "darkseed/adlib_dsf.h"
#include "darkseed/adlib_worx.h"
#include "darkseed/midiparser_sbr.h"
#include "audio/mididrv_ms.h"
#include "audio/midiparser.h"
#include "common/mutex.h"
namespace Darkseed {
class DarkseedEngine;
class MusicPlayer {
protected:
static const uint8 NUM_SFX_PARSERS = 5;
DarkseedEngine *_vm;
Common::Mutex _mutex;
MidiDriver_Multisource *_driver;
MidiDriver_DarkSeedFloppy_AdLib *_floppyAdLibDriver;
MidiParser *_musicParser;
MidiParser *_sfxParsers[NUM_SFX_PARSERS];
MidiParser_SBR *_sfxParserSbr;
// The type of the music device selected for playback.
MusicType _deviceType;
byte *_musicData;
byte *_sfxData;
byte *_tosInstrumentBankData;
bool _tosInstrumentBankLoaded;
bool _useFloppyMusic;
bool _useFloppySfx;
bool _paused;
static void onTimer(void *data);
public:
MusicPlayer(DarkseedEngine *vm, bool useFloppyMusic, bool useFloppySfx);
~MusicPlayer();
int open();
void load(Common::SeekableReadStream *in, int32 size = -1, bool sfx = false);
void loadTosInstrumentBankData(Common::SeekableReadStream *in, int32 size = -1);
void loadTosInstrumentBank();
void loadInstrumentBank(Common::SeekableReadStream *in, int32 size = -1);
bool isSampleSfx(uint8 sfxId);
void playMusic(uint8 priority = 0xFF, bool loop = false);
void setLoopMusic(bool loop);
bool isPlayingMusic();
void stopMusic();
void pauseMusic(bool pause);
void playSfx(uint8 sfxId, uint8 priority = 0xFF);
bool isPlayingSfx();
bool isPlayingSfx(uint8 sfxId);
void stopAllSfx();
bool stopSfx(uint8 sfxId);
void startFadeOutMusic();
bool isFadingMusic();
void syncSoundSettings();
protected:
uint8 assignSfxParser();
};
} // namespace Darkseed
#endif // DARKSEED_MUSIC_H

272
engines/darkseed/nsp.cpp Normal file
View File

@@ -0,0 +1,272 @@
/* 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 "darkseed/darkseed.h"
#include "darkseed/nsp.h"
#include "common/debug.h"
namespace Darkseed {
Sprite::Sprite(uint16 width, uint16 height, uint16 pitch) : _width(width), _height(height), _pitch(pitch) {
_pixels.resize(pitch * height, 0);
}
bool Sprite::loadData(Common::SeekableReadStream &readStream) {
g_engine->waitForSpeech();
if (_width == 1 && _height == 1) {
byte b = readStream.readByte();
_pixels[0] = b >> 4;
} else {
bool hasReadByte = false;
int currentDataByte = 0;
for (int i = 0; i < _pitch * _height; i++) {
if (!hasReadByte) {
currentDataByte = readStream.readByte();
if (readStream.eos()) {
debug("Argh!");
return false;
}
hasReadByte = true;
_pixels[i] = currentDataByte >> 4;
} else {
hasReadByte = false;
_pixels[i] = currentDataByte & 0xf;
}
}
}
return true;
}
void Sprite::clipToScreen(int x, int y, uint16 frameBottom, uint16 *clippedWidth, uint16 *clippedHeight) const {
*clippedWidth = _width;
*clippedHeight = _height;
if (x + _width > g_engine->_screen->w) {
*clippedWidth = g_engine->_screen->w - x;
}
if (x + _width > 570) {
*clippedWidth = 570 - x;
}
if (frameBottom != 0 && y + _height > g_engine->_frameBottom) {
if (y >= frameBottom) {
return;
}
*clippedHeight = frameBottom - y;
}
}
void Sprite::draw(int x, int y, uint16 frameBottom) const {
uint16 clippedWidth = _width;
uint16 clippedHeight = _height;
clipToScreen(x, y, frameBottom, &clippedWidth, &clippedHeight);
// clip to left side of frame.
if (x + _width <= 70) {
return;
}
int dX = 0;
if (x < 70) {
dX = 70 - x;
x = 70;
clippedWidth -= dX;
}
const uint8 *pixels = _pixels.data() + dX;
g_engine->_screen->copyRectToSurfaceWithKey(pixels, _pitch, x, y, clippedWidth, clippedHeight, 0xf);
}
void Sprite::draw(Graphics::Surface *dst, int x, int y, uint16 frameBottom) const {
uint16 clippedWidth = _width;
uint16 clippedHeight = _height;
clipToScreen(x, y, frameBottom, &clippedWidth, &clippedHeight);
dst->copyRectToSurfaceWithKey(_pixels.data(), _pitch, x, y, clippedWidth, clippedHeight, 0xf);
}
void Sprite::drawScaled(int destX, int destY, int destWidth, int destHeight, bool flipX) const {
//TODO image isn't exactly the same when not scaling. It seems larger by about a pixel.
//TODO this logic is pretty messy. It should probably be re-written. It is trying to scale, clip and flip at once.
Graphics::ManagedSurface * destSurface = g_engine->_screen;
// Based on the GNAP engine scaling code
if (destWidth == 0 || destHeight == 0) {
return;
}
const byte *source = _pixels.data();
const int xs = ((_width - 1) << 16) / destWidth;
const int ys = ((_height - 1) << 16) / destHeight;
int clipX = 0, clipY = 0;
const int destPitch = destSurface->pitch;
if (destX < 0) {
clipX = -destX;
destX = 0;
destWidth -= clipX;
}
if (destY < 0) {
clipY = -destY;
destY = 0;
destHeight -= clipY;
}
if (destY + destHeight >= destSurface->h) {
destHeight = destSurface->h - destY;
}
if (destWidth < 0 || destHeight < 0)
return;
byte *dst = (byte *)destSurface->getBasePtr(destX, destY);
int yi = ys * clipY;
const byte *hsrc = source + _pitch * ((yi + 0x8000) >> 16);
int16 currY = destY;
for (int yc = 0; yc < destHeight && currY < g_engine->_frameBottom; ++yc) {
byte *wdst = flipX ? dst + (destWidth - 1) : dst;
int16 currX = flipX ? destX + (destWidth - 1) : destX;
int xi = flipX ? xs : xs * clipX;
const byte *wsrc = hsrc + ((xi + 0x8000) >> 16);
for (int xc = 0; xc < destWidth; ++xc) {
if (currX > 69 && currX < 570 && currX < destSurface->w) { // clip to game window. TODO pass clip rect into method.
byte colorIndex = *wsrc;
// uint16 c = READ_LE_UINT16(&palette[colorIndex * 2]);
if (colorIndex != 0xf) {
*wdst = colorIndex;
// if (!(c & 0x8000u) || alpha == NONE) {
// // only copy opaque pixels
// WRITE_SCREEN(wdst, c & ~0x8000);
// } else {
// WRITE_SCREEN(wdst, alphaBlendRGB555(c & 0x7fffu, READ_SCREEN(wdst) & 0x7fffu, 128));
// // semi-transparent pixels.
// }
}
}
currX += (flipX ? -1 : 1);
wdst += (flipX ? -1 : 1);
xi += xs;
wsrc = hsrc + ((xi + 0x8000) >> 16);
}
dst += destPitch;
yi += ys;
hsrc = source + _pitch * ((yi + 0x8000) >> 16);
currY++;
}
}
bool Nsp::load(const Common::Path &filename) {
Common::File file;
Common::Path filePath = g_engine->getRoomFilePath(filename);
if (!file.open(filePath)) {
return false;
}
bool ret = load(file);
file.close();
if (ret) {
Common::String filePathStr = filePath.toString();
debug("Loaded %s", filePathStr.c_str());
Common::Path obtFilename = Common::Path(filePathStr.substr(0, filePathStr.size() - 4) + ".obt");
if (!loadObt(obtFilename)) {
debug("failed to load %s", obtFilename.toString().c_str());
}
}
return ret;
}
bool Nsp::load(Common::SeekableReadStream &readStream) {
_frames.clear();
for (int i = 0; i < 96; i++) {
int w = readStream.readByte();
int h = readStream.readByte();
int p = w + (w & 1);
_frames.push_back(Sprite(w, h, p));
}
for (int i = 0; i < 96; i++) {
if (!_frames[i].loadData(readStream)) {
return false;
}
}
return true;
}
const Sprite &Nsp::getSpriteAt(int index) const {
if (index >= (int)_frames.size()) {
error("getSpriteAt: Invalid sprite index. %d", index);
}
return _frames[index];
}
bool Nsp::loadObt(const Common::Path &filename) {
Common::File file;
if (!file.open(filename)) {
return false;
}
_animations.clear();
_animations.resize(20);
for (int i = 0; i < 20; i++) {
_animations[i]._numFrames = file.readByte();
for (int j = 0; j < 20; j++) {
if (file.readByte()) {
_animations[i]._deltaX.push_back(-(file.readUint16LE() / 100));
} else {
_animations[i]._deltaX.push_back(file.readUint16LE() / 100);
}
if (file.readByte()) {
_animations[i]._deltaY.push_back(-(file.readUint16LE() / 100));
} else {
_animations[i]._deltaY.push_back(file.readUint16LE() / 100);
}
_animations[i]._frameNo.push_back(file.readByte());
_animations[i]._frameDuration.push_back(file.readByte());
}
}
file.close();
debug("Loaded %s", filename.toString().c_str());
return true;
}
const Obt &Nsp::getAnimAt(int index) {
return _animations[index];
}
int16 Nsp::getMaxSpriteWidth() {
int maxWidth = 0;
for (auto &frame : _frames) {
if (frame._width > maxWidth) {
maxWidth = frame._width;
}
}
return maxWidth;
}
Obt::Obt() {
_numFrames = 0;
_deltaX.reserve(20);
_deltaY.reserve(20);
_frameNo.reserve(20);
_frameDuration.reserve(20);
}
Obt::~Obt() {
_deltaX.clear();
_deltaY.clear();
_frameNo.clear();
_frameDuration.clear();
}
} // End of namespace Darkseed

84
engines/darkseed/nsp.h Normal file
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 DARKSEED_NSP_H
#define DARKSEED_NSP_H
#include "common/array.h"
#include "common/path.h"
#include "common/stream.h"
#include "common/scummsys.h"
#include "graphics/surface.h"
namespace Darkseed {
class Sprite {
public:
uint16 _width;
uint16 _height;
uint16 _pitch;
Common::Array<uint8> _pixels;
Sprite(uint16 width, uint16 height, uint16 pitch);
bool loadData(Common::SeekableReadStream &readStream);
void draw(int x, int y, uint16 frameBottom = 0) const;
void draw(Graphics::Surface *dst, int x, int y, uint16 frameBottom = 0) const;
void drawScaled(int x, int y, int destWidth, int destHeight, bool flipX) const;
private:
void clipToScreen(int x, int y, uint16 frameBottom, uint16 *clippedWidth, uint16 *clippedHeight) const;
};
class Obt {
public:
uint8 _numFrames;
Common::Array<int32> _deltaX;
Common::Array<int32> _deltaY;
Common::Array<uint8> _frameNo;
Common::Array<uint8> _frameDuration;
Obt();
virtual ~Obt();
};
class Nsp {
Common::Array<Sprite> _frames;
Common::Array<Obt> _animations;
public:
bool load(const Common::Path &filename);
bool containsSpriteAt(int index) const {
return (int)_frames.size() > index;
}
const Sprite &getSpriteAt(int index) const;
const Obt &getAnimAt(int index);
int16 getTotalAnim() const {
return (int16)_animations.size();
}
int16 getMaxSpriteWidth();
private:
bool load(Common::SeekableReadStream &readStream);
bool loadObt(const Common::Path &filename);
};
} // namespace Darkseed
#endif // DARKSEED_NSP_H

View File

@@ -0,0 +1,836 @@
/* 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 DARKSEED_OBJECT_NAME_TABLES_H
#define DARKSEED_OBJECT_NAME_TABLES_H
namespace Darkseed {
static constexpr char objectNameTbl_en[199][21] = {
"Nothing.",
"Box",
"hand",
"eye",
"Ctrl",
"crowbar",
"journal",
"scotch",
"money",
"newspaper",
"library card",
"bobby pin",
"journal scrap",
"clock key",
"gloves",
"mirror shard",
"binoculars",
"shovel",
"business card",
"stick",
"axe handle",
"head band",
"trunk",
"rope",
"microfiche card",
"loose rock",
"car keys",
"hammer",
"gun",
"grave journal scrap",
"note from movers",
"package",
"package",
"package",
"blue print",
"watch",
"crackers",
"sardines",
"insecticide",
"soysauce",
"olives",
"tin cup",
"trunk lock",
"book 'Alien'",
"gas cap",
"Fido",
"librarian",
"telephone",
"bone bars",
"stairs",
"brass urn",
"viewscreen",
"shower",
"sergeant",
"guard",
"evil plans",
"evil sergeant",
"starship lever",
"Dreketh guards",
"secret door",
"turbo door",
"post",
"radio dial",
"exit in car",
"guard",
"prisoner lock",
"tomb left",
"tomb up",
"tomb right",
"front door",
"top of stairs",
"car ignition",
"police at door",
"evil mike",
"Gcell lock",
"tombstone",
"ladder down",
"ladder",
"secret door 2",
"evil fido",
"microfiche",
"box behind desk",
"button",
"library floor",
"book",
"store",
"glove compartment",
"tombstone",
"tombstone",
"tombstone",
"tombstone",
"tombstone",
"tombstone",
"tombstone",
"tombstone",
"tombstone",
"tombstone",
"tombstone",
"tombstone",
"tomb door",
"coat",
"car trunk",
"rope tied to house",
"clock",
"cupboard",
"cupboard",
"cupboard",
"cupboard",
"stove",
"drawer",
"kitchen tap",
"bathroom tap",
"medicine chest",
"pillow",
"Ancient",
"power nexus",
"abyss",
"observatory lever",
"horizon",
"force field",
"leech victims",
"shrubs",
"railing",
"bed",
"artwork",
"carpet",
"chair",
"spittoon",
"bed",
"bed bottom",
"fridge",
"books",
"globe",
"mug shots",
"map",
"desk",
"xxx",
"mirror",
"clerk",
"ladder",
"postman",
"Delbert",
"cement rock",
"towels",
"water heater",
"bathtub",
"xxx",
"couch",
"window",
"art",
"art",
"clock",
"counter",
"books",
"desk",
"xxx",
"desk",
"books",
"arch",
"ground",
"pillars",
"caskets",
"horizon",
"urn",
"urn",
"urn",
"urn",
"urn",
"urn",
"xxx",
"soccer ball",
"skis",
"trunk",
"bottles",
"barrels",
"wheel",
"car",
"furniture",
"xxx",
"cocoons",
"wall of skulls",
"creature",
"glass case",
"power cables",
"tubes",
"brain nexus",
"xxx",
"control panel",
"desk",
"cell bars",
"cell bars",
"console",
"viewer",
"building",
"box",
"tools",
"newspaper",
"table",
"bed bottom",
};
static constexpr char objectNameTbl_fr[199][22] = {
"RIEN",
"LA BOITE",
"LA MAIN",
"LA QUESTION",
"CTRL",
"LE MANIVELLE",
"LE JOURNAL",
"LE WHISKY",
"DE L'ARGENT",
"LE JOURNAL",
"LA CARTE DE PRET",
"LA PINCE",
"LE BOUT DE JOURNAL",
"LA CLEF DE L'HORLOGE",
"LES GANTS",
"UN FRAGMENT DU MIROIR",
"LES JUMELLES",
"LA PELLE",
"CARTE PROFESSIONNELLE",
"LE BATON",
"LA COGNEE",
"LE BANDEAU",
"LE COFFRE",
"LA CORDE",
"CARTE A MICROFICHES",
"PIERRE DESCELLEE",
"CLEFS DE LA VOITURE",
"MARTEAU",
"L'ARME",
"JOURNAL DU CIMETIERE",
"NOTE DES DEMENAGEURS",
"LE PAQUET",
"LE PAQUET",
"LE PAQUET",
"LE PLAN",
"LA MONTRE",
"DES CRACKERS",
"DES SARDINES",
"UN INSECTICIDE",
"UNE SAUCE AU SOJA",
"LES OLIVES",
"TIMBALE EN ETAIN",
"SERRURE DU COFFRE",
"LIVRE 'ALIEN'",
"LE BOUCHON",
"FIDO",
"LE BIBLIOTHECAIRE",
"LE TELEPHONE",
"UNE BARRE EN OS",
"LES ESCALIERS",
"L'URNE EN CUIVRE",
"L'ECRAN",
"LA DOUCHE",
"LE SERGENT",
"XXX",
"DES PLANS MAUDITS",
"LE SERGENT MALEFIQUE",
"MANETTE DU VAISSEAU",
"LES GARDES DE DREKETH",
"LA PORTE DEROBEE",
"LA PORTE TURBO",
"POSTE",
"SELECTEUR RADIO",
"SORTIE EN VOITURE",
"DELBERT",
"LE VERROU",
"LA TOMBE A GAUCHE",
"LA TOMBE EN HAUT",
"LA TOMBE A DROITE",
"LA PORTE DE DEVANT",
"LE HAUT DES ESCALIERS",
"LE CONTACT",
"POLICE CHEZ VOUS",
"MALFAISANT MIKE",
"VERROU CELL. G",
"LA PIERRE TOMBALE",
"L'ECHELLE",
"L'ECHELLE",
"LA PORTE DEROBEE",
"MAUVAIS FIDO",
"LA MICROFICHE",
"BOITE APRES LE BUREAU",
"LE BOUTON",
"ETAGE BIBLIOTHEQUE",
"LE LIVRE",
"LE MAGASIN",
"LE LIVRE",
"LA PIERRE TOMBALE",
"LA PIERRE TOMBALE",
"LA PIERRE TOMBALE",
"LA PIERRE TOMBALE",
"LA PIERRE TOMBALE",
"LA PIERRE TOMBALE",
"LA PIERRE TOMBALE",
"LA PIERRE TOMBALE",
"LA PIERRE TOMBALE",
"LA PIERRE TOMBALE",
"LA PIERRE TOMBALE",
"LA PIERRE TOMBALE",
"LA PORTE DU CAVEAU",
"LE MANTEAU",
"COFFRE DE L'AUTO",
"CORDE LIEE AU LOGIS",
"L'HORLOGE",
"LE PLACARD",
"LE PLACARD",
"LE PLACARD",
"LE PLACARD",
"LE FOUR",
"LE TIROIR",
"ROBINET DE LA CUISINE",
"ROBINET",
"BOITE A PHARMACIE",
"L'OREILLER",
"L'ANCIEN",
"LIEN DE PUISSANCE",
"L'ABYSSE",
"POIGNER",
"L'HORIZON",
"LE CHAMPS DE FORCE",
"VICTIME DE SANGSUES",
"DES ARBUSTES",
"LES RAMPES",
"LE LIT",
"LE DECORUM",
"LE TAPIS",
"LA CHAISE",
"LE CRACHOIR",
"LE LIT",
"LE PIED DU LIT",
"LE REFRIGERATEUR",
"LES LIVRES",
"LE GLOBE",
"PHOTOS D'IDENTITE",
"LA CARTE",
"LE BUREAU",
"XXX",
"LE MIROIR",
"L'EMPLOYE",
"LE CORDON",
"LE POSTIER",
"DELBERT",
"LE BLOC DE CIMENT",
"LES SERVIETTES",
"LE CHAUFFE-EAU",
"LA BAIGNOIRE",
"XXX",
"LE DIVAN",
"LA FENETRE",
"L'ART",
"L'ART",
"L'HORLOGE",
"LE COMPTOIR",
"LES LIVRES",
"LE BUREAU",
"XXX",
"LE BUREAU",
"LES LIVRES",
"L'ARCHE",
"LE SOL",
"LES COLONNES",
"LES BOITES",
"L'HORIZON",
"L'URNE",
"L'URNE",
"L'URNE",
"L'URNE",
"L'URNE",
"L'URNE",
"XXX",
"BALLON DE FOOT",
"LES SKIS",
"LE COFFRE",
"LES BOUTEILLES",
"LES TONNEAUX",
"LE VOLANT",
"LA VOITURE",
"LES MEUBLES",
"XXX",
"LES COCONS",
"LE MUR DE CRANES",
"LA CREATURE",
"LE GLOBE",
"CABLES D'ALIMENTATION",
"LES TUBES",
"LIEN DU CERVEAU",
"XXX",
"LE TABLEAU DE BORD",
"LE BUREAU",
"LE BARREAU",
"LE BARREAU",
"LA CONSOLE",
"LA VISIONNEUSE",
"LE BATIMENT",
"LA BOITE",
"LES OUTILS",
"LE JOURNAL",
"LA TABLE",
"PIED DU LIT"
};
static constexpr char objectNameTbl_de[199][27] = {
"NICHTS",
"DER KASTEN",
"DIE HAND",
"DIE FRAGE",
"CTRL",
"DAS BRECHEISEN",
"DAS TAGEBUCH",
"DIE FLASCHE SCOTCH",
"DAS GELD",
"DIE ZEITUNG",
"DIE BUECHEREIKARTE",
"DIE HAARNADEL",
"DIE TAGEBUCHSCHNIPSEL",
"DER UHRENSCHLUESSEL",
"DIE HANDSCHUHE",
"DIE SPIEGELSCHERBE",
"DAS FERNGLAS",
"DIE SCHAUFEL",
"DIE VISITENKARTE",
"DER STOCK",
"DER AXTGRIFF",
"DAS STIRNBAND",
"DIE TRUHE",
"DAS SEIL",
"DIE MIKROFILMKARTE",
"DER LOSER STEIN",
"DIE AUTOSCHLUESSEL",
"DER HAMMER",
"DIE PISTOLE",
"DIE TAGEBUCHSEITEN",
"DIE NOTIZ DER MOEBELPACKER",
"DAS PAKET",
"DAS PAKET",
"DAS PAKET",
"DIE BLAUPAUSE",
"DIE UHR",
"DIE KEKSE",
"DIE SARDINEN",
"DAS INSEKTENSPRAY",
"DIE SOJASAUCE",
"DIE OLIVEN",
"DER ZINNBECHER",
"DAS TRUHENSCHLOSS",
"DAS BUCH 'AUSSERIRDISCHER'",
"DER GASHAHN",
"DER FIDO",
"DIE BIBLIOTHEKARIN",
"DAS TELEFON",
"DIE KNOCHENGITTER",
"DIE STUFEN",
"DIE MESSING-URNE",
"DER BILDSCHIRM",
"DIE DUSCHE",
"DER POLIZIST",
"xxx",
"DIE PLAENE DER BOESEN",
"DER BOESER POLIZIST",
"DER HEBEL FUER RAUMSCHIFF",
"DIE DREKETH-WACHEN",
"DIE GEHEIMTUER",
"DIE AUFZUGTUER",
"DIE POST",
"DIE FREQUENZANZEIGE",
"DER AUSGANG IM AUTO",
"DER DELBERT",
"DAS GEFAENGNISSCHLOSS",
"DIE LINKE STEINPLATTE",
"DIE OBERE STEINPLATTE",
"DIE RECHTE STEINPLATTE",
"DIE HAUSTUER",
"DIE OBERSTE STUFE",
"DAS ZUENDSCHLOSS",
"DIE POLIZEI AN DER TUER",
"DER BOESER MIKE",
"DAS ZELLENSCHLOSS",
"DER GRABSTEIN",
"DIE LEITER",
"DIE LEITER",
"DIE GEHEIMTUER",
"DER BOESER FIDO",
"DER MIKROFILM",
"DER KASTEN HINTER PULT",
"DER KNOPF",
"DER BUECHERGANG",
"DAS BUCH",
"DER LADEN",
"DAS BUCH",
"DER GRABSTEIN",
"DER GRABSTEIN",
"DER GRABSTEIN",
"DER GRABSTEIN",
"DER GRABSTEIN",
"DER GRABSTEIN",
"DER GRABSTEIN",
"DER GRABSTEIN",
"DER GRABSTEIN",
"DER GRABSTEIN",
"DER GRABSTEIN",
"DER GRABSTEIN",
"DIE TUER DER GRUFT",
"DER MANTEL",
"DER KOFFERRAUM",
"DAS ANGEBUNDENES SEIL",
"DIE UHR",
"DER SCHRANK",
"DER SCHRANK",
"DER SCHRANK",
"DER SCHRANK",
"DER HERD",
"DIE SCHUBLADE",
"DER KUECHEN-WASSERHAHN",
"DER BAD-WASSERHAHN",
"DER MEDIZINSCHRANK",
"DIE KISSEN",
"DIE ALTER",
"DIE STROMVERSORGUNG",
"DER ABGRUND",
"DER OBSERVATORIUMSHEBEL",
"DER HORIZONT",
"DAS KRAFTFELD",
"DER OPFER DES BLUTSAUGERS",
"DIE BUESCHE",
"DAS GELAENDER",
"DAS BETT",
"DAS KUNSTWERK",
"DER TEPPICH",
"DER STUHL",
"DER SPUCKNAPF",
"DAS BETT",
"DER FUSS DES BETTES",
"DER KUEHLSCHRANK",
"DIE BUECHER",
"DER GLOBUS",
"DIE PHANTOMBILDER",
"DIE KARTE",
"DER SCHREIBTISCH",
"xxx",
"DER SPIEGEL",
"DER ANGESTELLTER",
"DAS SEIL",
"DER POSTBOTE",
"DER DELBERT",
"DAS ZEMENTGESTEIN",
"DIE HANDTUECHER",
"DER BOILER",
"DIE BADEWANNE",
"xxx",
"DIE COUCH",
"DIE FENSTER",
"DIE KUNST",
"DIE KUNST",
"DIE UHR",
"DIE TRESEN",
"DIE BUECHER",
"DER SCHREIBTISCH",
"xxx",
"DER SCHREIBTISCH",
"DIE BUECHER",
"DER BOGEN",
"DER BODEN",
"DIE SAEULEN",
"DIE URNEN",
"DER HORIZONT",
"DIE URNE",
"DIE URNE",
"DIE URNE",
"DIE URNE",
"DIE URNE",
"DIE URNE",
"xxx",
"DER FUSSBALL",
"DIE SKIER",
"DIE TRUHE",
"DIE FLASCHEN",
"DIE FAESSER",
"DAS RAD",
"DAS AUTO",
"DIE MOEBEL",
"xxx",
"DIE KOKONS",
"DIE WAND VOLLER SCHAEDELN",
"DIE KREATUR",
"DER GLASKAEFIG",
"DIE STROMKABEL",
"DIE ROHRE",
"DIE GEHIRNVERSORGUNG",
"xxx",
"DER KONTROLLPULT",
"DER SCHREIBTISCH",
"DIE ZELLENGITTER",
"DIE ZELLENGITTER",
"DIE KONSOLE",
"DIE SUCHER",
"DIE GEBAEUDE",
"DIE KASTEN",
"DAS WERKZEUG",
"DIE ZEITUNG",
"DER TISCH",
"DAS FUSSENDE DES BETTES"
};
static constexpr char objectNameTbl_es[199][35] = {
"NADA",
"LA CAJA",
"LA MANO",
"LA PREGUNTA",
"CTRL",
"LA BALLESTA",
"EL DIARIO",
"EL WHISKY",
"EL DINERO",
"EL PERIODICO",
"LA FICHA DE LIBRERIA",
"LA HORQUILLA",
"EL PAPEL DE PERIODICO",
"LA LLAVE DEL RELOJ",
"LOS GUANTES",
"EL TROZO DE ESPEJO",
"LOS PRISMATICOS",
"LA PALA",
"LA TARJETA DE VISITA",
"LA VARA",
"EL MANGO DEL HACHA",
"LA CINTA DE LA CABEZA",
"EL BAUL",
"LA CUERDA",
"LA TARJETA MICROFICHA",
"LA PIEDRA SUELTA",
"LAS LLAVES DEL COCHE",
"EL MARTILLO",
"LA PISTOLA",
"UNA NECROLOGIA",
"LA NOTA DE LA MUDANZA",
"EL PAQUETE",
"EL PAQUETE",
"EL PAQUETE",
"EL PLANO",
"EL RELOJ",
"LAS GALLETAS",
"LAS SARDINAS",
"EL INSECTICIDA",
"LA SALSA DE SOJA",
"LAS ACEITUNAS",
"LA TAZA DE HOJALATA",
"LA CERRADURA DEL BAUL",
"EL LIBRO 'ALIEN'",
"EL TAPON DE GASOLINA",
"FIDO",
"LA BIBLIOTECARIA",
"EL TELEFONO",
"LAS BARRAS DE HUESO",
"LAS ESCALERAS",
"LA URNA DE BRONCE",
"LA PANTALLA DE VISION",
"LA DUCHA",
"EL SARGENTO",
"xxx",
"LOS PLANOS DIABOLICOS",
"EL SARGENTO DIABOLICO",
"LA PALANCA",
"LOS GUARDIANES DE DREKETH",
"LA PUERTA SECRETA",
"LA TURBO PUERTA",
"EL CORREO",
"EL DIAL DE LA RADIO",
"LA SALIDA EN COCHE",
"DELBERT",
"LA CERRADURA DEL PRISIONERO",
"LA TUMBA DE LA IZQUIERDA",
"LA TUMBA DE ARRIBA",
"LA TUMBA DE LA DERECHA",
"LA PUERTA DELANTERA",
"LA PARTA SUPERIOR DE LAS ESCALERAS",
"EL ARRANQUE DEL COCHE",
"EL POLICIA EN LA PUERTA",
"MIKE DIABOLICO",
"LA CERRADURA DE LA CELDA",
"LA LAPIDA",
"LA ESCALERA",
"LA ESCALERA",
"LA PUERTA SECRETA",
"FIDO DIABOLICO",
"LA MICROFICHA",
"LA CAJA DETRAS DEL ESCRITORIO",
"EL BOTON",
"EL SUELO DE LA BIBLIOTECA",
"EL LIBRO",
"LA TIENDA",
"EL LIBRO",
"LA LAPIDA",
"LA LAPIDA",
"LA LAPIDA",
"LA LAPIDA",
"LA LAPIDA",
"LA LAPIDA",
"LA LAPIDA",
"LA LAPIDA",
"LA LAPIDA",
"LA LAPIDA",
"LA LAPIDA",
"LA LAPIDA",
"LA PUERTA DE LA TUMBA",
"EL ABRIGO",
"EL PORTAEQUIPAJES",
"LA CUERDA ATADA A LA CASA",
"EL RELOJ",
"EL ARMARIO",
"EL ARMARIO",
"EL ARMARIO",
"EL ARMARIO",
"LA ESTUFA",
"EL COJON",
"LA LLAVE DE LA COCINA",
"LA LLAVE DEL CUARTO DE ASEO",
"EL BOTIQUIN",
"LA ALMOHADA",
"EL ANTEPASADO",
"LA FUENTA DE ENERGIA",
"EL ABISMO",
"LA PALANCA DEL OBSERVATORIO",
"EL HORIZONTE",
"EL CAMPO DE FUERZA",
"LAS VICTIMAS DE LA SANGUIJUELA",
"LOS ARBUSTOS",
"LA BARANDILLA",
"LA CAMA",
"LA OBRA DE ARTE",
"LA ALFOMBRA",
"LA SILLA",
"LA ESCUPIDERA",
"LA CAMA",
"EL FONDO DE LA CAMA",
"EL FRIGORIFICO",
"LOS LIBROS",
"EL GLOBO TERRAQUEO",
"LAS FOTOGRAFIAS DE RUFIANES",
"EL MAPA",
"EL ESCRITORIO",
"xxx",
"EL ESPEJO",
"EL EMPLEADO",
"EL CORDON",
"EL CARTERO",
"DELBERT",
"LA PIEDRA DE CEMENTO",
"LAS TOALLAS",
"EL CALENTADOR DE AGUA",
"LA BANERA",
"xxx",
"EL SOFA",
"LA VENTANA",
"EL ARTE",
"EL ARTE",
"EL RELOJ",
"EL MOSTRADOR",
"LOS LIBROS",
"EL ESCRITORIO",
"xxx",
"EL ESCRITORIO",
"LOS LIBROS",
"EL ARCO",
"EL SUELO",
"LOS PILARES",
"LOS ATAUDES",
"EL HORIZONTE",
"LA URNA",
"LA URNA",
"LA URNA",
"LA URNA",
"LA URNA",
"LA URNA",
"xxx",
"EL BALON DE FUTBOL",
"LOS ESQUIS",
"EL BAUL",
"LAS BOTELLAS",
"LOS BARRILES",
"LA RUEDA",
"EL COCHE",
"LOS MUEBLES",
"xxx",
"LOS CAPULLOS",
"LA PARED DE CALAVERAS",
"LA CRIATURA",
"LA CAJA DE CRISTAL",
"LOS CABLES DE ENERGIA",
"LOS TUBOS",
"EL NEXO CEREBRAL",
"xxx",
"EL PANEL DE CONTROL",
"EL ESCRITORIO",
"LAS BARRAS DE LA CELDA",
"LAS BARRAS DE LA CELDA",
"LA CONSOLA",
"EL VISOR",
"EL EDIFICIO",
"LA CAJA",
"LAS HERRAMIENTAS",
"EL PERIODICO",
"LA MESA",
"EL FONDO DE LA CAMA"
};
} // namespace Darkseed
#endif //DARKSEED_OBJECT_NAME_TABLES_H

View File

@@ -0,0 +1,276 @@
/* 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 "darkseed/darkseed.h"
#include "darkseed/objects.h"
#include "darkseed/object_name_tables.h"
namespace Darkseed {
Objects::Objects() {
_objectVar.resize(MAX_OBJECTS);
_objectRunningCode.resize(MAX_OBJECTS);
_moveObjectXY.resize(MAX_OBJECTS);
_moveObjectRoom.resize(MAX_OBJECTS); // The original only allocates 42 entries here but writes 199 in the save file!
_objectNames.resize(MAX_OBJECTS);
reset();
}
void Objects::reset() {
for (int i = 0; i < MAX_OBJECTS; i++) {
_objectVar[i] = 0;
_objectRunningCode[i] = 0;
_moveObjectXY[i].x = 0; // TODO verify this is the correct reset state for these XY vars.
_moveObjectXY[i].y = 0;
_moveObjectRoom[i] = i < 42 ? 0xff : 0; // Hack for weird behaviour in original engine.
}
// Initial object state.
setVar(52, 1);
setVar(112, 0);
setVar(62, 0);
}
void Objects::setVar(uint16 varIdx, int16 newValue) {
if (varIdx >= MAX_OBJECTS) {
error("setVar: Object Index out of range! %d", varIdx);
}
_objectVar[varIdx] = newValue;
}
int16 Objects::getVar(uint16 varIdx) {
if (varIdx >= MAX_OBJECTS) {
error("getVar: Object Index out of range! %d", varIdx);
}
return _objectVar[varIdx];
}
Common::Point Objects::getMoveObjectPosition(uint8 objIdx) {
if (objIdx >= MAX_OBJECTS) {
error("getMoveObjectPosition: Object Index out of range! %d", objIdx);
}
return _moveObjectXY[objIdx];
}
void Objects::setMoveObjectPosition(uint8 objIdx, const Common::Point &newPoint) {
if (objIdx >= MAX_OBJECTS) {
error("setMoveObjectPosition: Object Index out of range! %d", objIdx);
}
_moveObjectXY[objIdx] = newPoint;
}
void Objects::setMoveObjectX(uint8 objIdx, int16 xPos) {
if (objIdx >= MAX_OBJECTS) {
error("setMoveObjectX: Object Index out of range! %d", objIdx);
}
_moveObjectXY[objIdx].x = xPos;
}
int16 &Objects::operator[](uint16 varIdx) {
if (varIdx >= MAX_OBJECTS) {
error("getVar: Object Index out of range! %d", varIdx);
}
return _objectVar[varIdx];
}
const int16 &Objects::operator[](uint16 varIdx) const {
if (varIdx >= MAX_OBJECTS) {
error("getVar: Object Index out of range! %d", varIdx);
}
return _objectVar[varIdx];
}
void Objects::loadObjectNames() {
auto lang = g_engine->getLanguage();
if (lang == Common::KO_KOR) {
loadKoreanObjectNames();
} else if (lang == Common::ZH_ANY) {
loadChineseObjectNames();
} else {
for (int i = 0; i < MAX_OBJECTS; i++) {
switch (lang) {
case Common::FR_FRA: _objectNames[i] = Common::U32String(objectNameTbl_fr[i]); break;
case Common::DE_DEU: _objectNames[i] = Common::U32String(objectNameTbl_de[i]); break;
case Common::ES_ESP: _objectNames[i] = Common::U32String(objectNameTbl_es[i]); break;
default: _objectNames[i] = Common::U32String(objectNameTbl_en[i]); break;
}
}
}
}
void Objects::loadKoreanObjectNames() {
Common::File file;
if (!file.open("tos.exe")) {
error("Failed to open TOS.EXE");
}
for (int i = 0; i < MAX_OBJECTS; i++) {
file.seek(0x22f62 + i * 4);
uint16 offset = file.readUint16LE();
file.seek(0x20990 + offset);
_objectNames[i] = readU32String(file);
}
file.close();
}
void Objects::loadChineseObjectNames() {
Common::File file;
if (!file.open("zh_objectnames.dat")) {
error("Failed to open zh_objectnames.dat");
}
for (int i = 0; i < MAX_OBJECTS; i++) {
file.seek(i * 21);
_objectNames[i] = readU32String(file);
}
file.close();
}
Common::U32String Objects::readU32String(Common::SeekableReadStream &readStream) {
Common::U32String str;
uint8 byte = readStream.readByte();
while (byte != 0) {
if (byte & 0x80) {
uint8 byte2 = readStream.readByte();
if (readStream.err()) {
error("Failed to read byte from stream!");
}
str += (int)byte << 8 | byte2;
} else {
str += byte;
}
byte = readStream.readByte();
}
return str;
}
static constexpr uint16 eyeDescriptionsTbl[] = {
0, 0, 0, 0,
0, 0, 0, 513,
0, 421, 0, 521,
0, 0, 713, 0,
791, 812, 0, 743,
0, 0, 661, 166,
0, 0, 0, 0,
500, 0, 0, 423,
425, 427, 418, 667,
503, 505, 507, 509,
511, 755, 652, 0,
728, 0, 0, 43,
0, 0, 0, 0,
192, 0, 0, 0,
0, 893, 845, 0,
0, 452, 721, 0,
483, 0, 466, 466,
466, 0, 0, 705,
0, 0, 0, 0,
0, 0, 0, 829,
552, 0, 0, 0,
0, 0, 711, 608,
610, 606, 604, 602,
600, 598, 596, 594,
592, 590, 588, 0,
0, 732, 166, 0,
0, 0, 0, 0,
0, 0, 0, 241,
231, 750, 815, 826,
838, 794, 797, 806,
802, 440, 448, 117,
259, 271, 305, 90,
161, 136, 376, 398,
414, 474, 477, 480,
0, 0, 999, 252,
0, 0, 170, 182,
212, 219, 284, 315,
328, 337, 346, 356,
515, 526, 533, 0,
547, 561, 570, 575,
613, 615, 620, 624,
636, 638, 641, 643,
645, 0, 673, 677,
680, 683, 688, 717,
726, 746, 0, 759,
765, 780, 787, 818,
822, 824, 0, 855,
862, 0, 880, 887,
891, 900, 0, 724,
671, 321, 163
};
int Objects::getEyeDescriptionTosIdx(uint16 objNum) {
if (objNum >= MAX_OBJECTS) {
error("getEyeDescriptionTosIdx: Object Index out of range! %d", objNum);
}
return eyeDescriptionsTbl[objNum];
}
int Objects::getMoveObjectRoom(uint16 idx) {
if (idx >= MAX_OBJECTS) {
error("getMoveObjectRoom: index out of range.");
}
return _moveObjectRoom[idx];
}
void Objects::setMoveObjectRoom(uint16 idx, uint8 value) {
if (idx >= MAX_OBJECTS) {
error("setMoveObjectRoom: index out of range.");
}
_moveObjectRoom[idx] = value;
}
int16 Objects::getObjectRunningCode(int idx) {
if (idx >= MAX_OBJECTS) {
error("getObjectRunningCode: index out of range.");
}
return _objectRunningCode[idx];
}
void Objects::setObjectRunningCode(int idx, int16 value) {
if (idx >= MAX_OBJECTS) {
error("setObjectRunningCode: index out of range.");
}
_objectRunningCode[idx] = value;
}
const Common::U32String &Objects::getObjectName(int idx) {
if (idx < 0 || idx >= MAX_OBJECTS) {
error("getObjectName: index out of range.");
}
return _objectNames[idx];
}
static inline void syncPoint(Common::Serializer &s, Common::Point &value) {
s.syncAsSint16LE(value.x);
s.syncAsSint16LE(value.y);
}
Common::Error Objects::sync(Common::Serializer &s) {
s.syncArray(_objectVar.data(), _objectVar.size(), Common::Serializer::Sint16LE);
s.syncArray(_objectRunningCode.data(), _objectRunningCode.size(), Common::Serializer::Sint16LE);
s.syncArray(_objectRunningCode.data(), _objectRunningCode.size(), Common::Serializer::Sint16LE);
s.syncArray(_moveObjectXY.data(), _moveObjectXY.size(), syncPoint);
s.syncArray(_moveObjectRoom.data(), _moveObjectRoom.size(), Common::Serializer::Byte);
return Common::kNoError;
}
} // End of namespace Darkseed

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 DARKSEED_OBJECTS_H
#define DARKSEED_OBJECTS_H
#include "common/array.h"
#include "common/error.h"
#include "common/rect.h"
#include "common/serializer.h"
namespace Darkseed {
enum ObjType {
OBJ_21_HEADBAND = 21
};
class Objects {
Common::Array<int16> _objectVar;
Common::Array<Common::Point> _moveObjectXY;
Common::Array<uint8> _moveObjectRoom;
Common::U32StringArray _objectNames;
public:
Common::Array<int16> _objectRunningCode;
static constexpr int MAX_MOVED_OBJECTS = 42;
static constexpr int MAX_OBJECTS = 199;
Objects();
void reset();
void loadObjectNames();
Common::Error sync(Common::Serializer &s);
void setVar(uint16 varIdx, int16 newValue);
int16 getVar(uint16 varIdx);
int16 getObjectRunningCode(int idx);
void setObjectRunningCode(int idx, int16 value);
Common::Point getMoveObjectPosition(uint8 objIdx);
void setMoveObjectPosition(uint8 objIdx, const Common::Point &newPoint);
void setMoveObjectX(uint8 objIdx, int16 xPos);
int getEyeDescriptionTosIdx(uint16 objNum);
int getMoveObjectRoom(uint16 idx);
void setMoveObjectRoom(uint16 idx, uint8 value);
const Common::U32String &getObjectName(int idx);
int16 &operator[](uint16 varIdx);
const int16 &operator[](uint16 varIdx) const;
private:
void loadKoreanObjectNames();
void loadChineseObjectNames();
Common::U32String readU32String(Common::SeekableReadStream &readStream);
};
} // namespace Darkseed
#endif // DARKSEED_OBJECTS_H

112
engines/darkseed/pal.cpp Normal file
View File

@@ -0,0 +1,112 @@
/* 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/paletteman.h"
#include "darkseed/pal.h"
#include "darkseed/darkseed.h"
namespace Darkseed {
#define DARKSEED_NUM_PAL_ENTRIES 16
#define DARKSEED_PAL_SIZE (DARKSEED_NUM_PAL_ENTRIES * 3)
Pal::Pal(const Pal &pal) {
load(pal);
}
Pal &Pal::operator=(const Pal &pal) {
if (this != &pal) {
load(pal);
}
return *this;
}
void Pal::load(const Pal &pal) {
memcpy(_palData, pal._palData, DARKSEED_PAL_SIZE);
}
bool Pal::load(const Common::Path &filename, bool shouldInstallPalette) {
Common::File file;
if (!file.open(filename)) {
loadFromScreen();
return false;
}
return loadFromStream(file, shouldInstallPalette);
}
bool Pal::loadFromStream(Common::SeekableReadStream &readStream, bool shouldInstallPalette) {
uint32 bytesRead = readStream.read(_palData, DARKSEED_PAL_SIZE);
assert(bytesRead == DARKSEED_PAL_SIZE);
for (int i = 0; i < DARKSEED_PAL_SIZE; i++) {
_palData[i] = _palData[i] << 2;
}
if (shouldInstallPalette) {
installPalette();
}
return true;
}
void Pal::loadFromScreen() {
g_system->getPaletteManager()->grabPalette(_palData, 0, DARKSEED_NUM_PAL_ENTRIES);
}
void Pal::clear() {
memset(_palData, 0, DARKSEED_PAL_SIZE);
}
void Pal::swapEntries(int idx1, int idx2) {
uint8 tmpEntry[3];
tmpEntry[0] = _palData[idx1 * 3];
tmpEntry[1] = _palData[idx1 * 3 + 1];
tmpEntry[2] = _palData[idx1 * 3 + 2];
_palData[idx1 * 3] = _palData[idx2 * 3];
_palData[idx1 * 3 + 1] = _palData[idx2 * 3 + 1];
_palData[idx1 * 3 + 2] = _palData[idx2 * 3 + 2];
_palData[idx2 * 3] = tmpEntry[0];
_palData[idx2 * 3 + 1] = tmpEntry[1];
_palData[idx2 * 3 + 2] = tmpEntry[2];
}
void Pal::updatePalette(int delta, const Pal &targetPal, bool shouldInstallPalette) {
for (int i = 0; i < DARKSEED_PAL_SIZE; i++) {
int c = _palData[i] + delta;
if (c < 0) {
c = 0;
} else if (delta > 0 && c > targetPal._palData[i]) {
c = targetPal._palData[i];
}
_palData[i] = (uint8)c;
}
if (shouldInstallPalette) {
installPalette();
}
}
void Pal::installPalette() const {
g_system->getPaletteManager()->setPalette(_palData, 0, DARKSEED_NUM_PAL_ENTRIES);
}
} // namespace Darkseed

53
engines/darkseed/pal.h Normal file
View File

@@ -0,0 +1,53 @@
/* 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 DARKSEED_PAL_H
#define DARKSEED_PAL_H
#include "common/path.h"
#include "common/stream.h"
namespace Darkseed {
#define DARKSEED_NUM_PAL_ENTRIES 16
#define DARKSEED_PAL_SIZE (DARKSEED_NUM_PAL_ENTRIES * 3)
class Pal {
public:
byte _palData[DARKSEED_PAL_SIZE];
Pal() {};
Pal(const Pal &pal);
Pal & operator=(const Pal &pal);
void loadFromScreen();
void load(const Pal &pal);
bool load(const Common::Path &filename, bool shouldInstallPalette = true);
bool loadFromStream(Common::SeekableReadStream &readStream, bool shouldInstallPalette = true);
void clear();
void swapEntries(int idx1, int idx2);
void updatePalette(int delta, const Pal &targetPal, bool shouldInstallPalette = true);
void installPalette() const;
};
} // namespace Darkseed
#endif // DARKSEED_PAL_H

108
engines/darkseed/pic.cpp Normal file
View File

@@ -0,0 +1,108 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "darkseed/pic.h"
#include "darkseed/darkseed.h"
#include "common/debug.h"
namespace Darkseed {
bool Pic::load(const Common::Path &filename) {
g_engine->waitForSpeech();
Common::File file;
Common::Path fullPath = g_engine->getPictureFilePath(filename);
if (!file.open(fullPath)) {
debug("Failed to load %s", fullPath.toString().c_str());
return false;
}
bool ret = load(file);
file.close();
if (ret) {
debug("Loaded %s (%d,%d)", fullPath.toString().c_str(), _width, _height);
}
return ret;
}
bool Pic::load(Common::SeekableReadStream &readStream) {
_width = readStream.readUint16BE();
_height = readStream.readUint16BE();
_pixels.resize(_width * (_height + 1), 0);
int curX = 0;
int curY = 0;
while (curY < _height) {
int rleCommand = readNextNibble(readStream);
if (rleCommand < 8) {
// read nibble count of nibbles pixels
for (int i = 0; i < rleCommand + 1; i++) {
byte pixel = readNextNibble(readStream);
_pixels[curX + curY * _width] = pixel;
curX++;
if (curX == _width) {
curX = 0;
curY++;
}
}
} else {
// fetch next nibble and repeat if n times.
byte pixel = readNextNibble(readStream);
for (int i = 16; i >= rleCommand; i--) {
_pixels[curX + curY * _width] = pixel;
curX++;
if (curX == _width) {
curX = 0;
curY++;
}
}
}
}
return true;
}
byte Pic::readNextNibble(Common::SeekableReadStream &readStream) {
if (!_hasReadByte) {
_currentDataByte = readStream.readByte();
if (readStream.eos()) {
debug("Argh!");
}
_hasReadByte = true;
return _currentDataByte >> 4;
} else {
_hasReadByte = false;
return _currentDataByte & 0xf;
}
}
void Pic::draw() {
draw(0, 0);
}
void Pic::draw(int xOffset, int yOffset) {
g_engine->_screen->copyRectToSurface(getPixels().data(), getWidth(), xOffset, yOffset, getWidth(), getHeight());
}
void Pic::drawRect(const Common::Rect &rect) {
void *ptr = getPixels().data() + rect.left + (rect.top * getWidth());
g_engine->_screen->copyRectToSurface(ptr, getWidth(), rect.left, rect.top, rect.width(), rect.height());
}
} // End of namespace Darkseed

65
engines/darkseed/pic.h Normal file
View File

@@ -0,0 +1,65 @@
/* 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 DARKSEED_PIC_H
#define DARKSEED_PIC_H
#include "common/array.h"
#include "common/file.h"
#include "common/rect.h"
namespace Darkseed {
class Pic {
uint16 _width = 0;
uint16 _height = 0;
Common::Array<uint8> _pixels;
bool _hasReadByte = false;
byte _currentDataByte = 0;
public:
Pic() {}
bool load(const Common::Path &filename);
Common::Array<uint8> &getPixels() {
return _pixels;
}
uint16 getWidth() const {
return _width;
}
uint16 getHeight() const {
return _height;
}
void draw();
void draw(int xOffset, int yOffset);
void drawRect(const Common::Rect &rect);
private:
bool load(Common::SeekableReadStream &readStream);
byte readNextNibble(Common::SeekableReadStream &readStream);
};
} // namespace Darkseed
#endif // DARKSEED_PIC_H

470
engines/darkseed/player.cpp Normal file
View File

@@ -0,0 +1,470 @@
/* 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 "darkseed/player.h"
#include "darkseed/darkseed.h"
namespace Darkseed {
Player::Player() {
_cPlayerSprites.load("cplayer.nsp");
_gPlayerSprites.load("gplayer.nsp");
_connectorList.resize(Room::MAX_CONNECTORS);
}
const Sprite &Player::getSprite(int frameNo) const {
if (g_engine->_room->isGiger()) {
return _gPlayerSprites.getSpriteAt(frameNo);
} else {
return _cPlayerSprites.getSpriteAt(frameNo);
}
}
bool Player::loadAnimations(const Common::Path &filename) {
return _animations.load(filename);
}
uint8 playerSpriteIndexDirectionTbl[] = { 24, 26, 28, 26 };
static constexpr bool DIRECTION_FLIP_SPRITE_TBL[] = { false, false, false, true };
uint16 walkFrameOffsetTbl[] = { 0, 8, 16, 8 };
void Player::updateSprite() {
if (!_playerIsChangingDirection) {
if ((_direction == 3) || (_direction == 1)) {
_flipSprite = DIRECTION_FLIP_SPRITE_TBL[_direction];
}
if (_position.x == _walkTarget.x && _position.y == _walkTarget.y && !_heroMoving) {
_frameIdx = playerSpriteIndexDirectionTbl[_direction];
} else {
_frameIdx = _playerWalkFrameIdx + walkFrameOffsetTbl[_direction];
}
if (_direction == 2) {
if (_position.x < _walkTarget.x) {
_flipSprite = true;
} else if (_walkTarget.x < _position.x) {
_flipSprite = false;
}
}
if (_direction == 0) {
if (_walkTarget.x < _position.x) {
_flipSprite = true;
} else if (_position.x < _walkTarget.x) {
_flipSprite = false;
}
}
} else {
_flipSprite = (4 < _playerSpriteWalkIndex_maybe);
if (_flipSprite) {
_frameIdx = 0x20 - _playerSpriteWalkIndex_maybe;
} else {
_frameIdx = _playerSpriteWalkIndex_maybe + 0x18;
}
}
}
bool Player::isAtPosition(int x, int y) const {
return _position.x == x && _position.y == y;
}
bool Player::isAtWalkTarget() const {
return _position == _walkTarget;
}
void Player::changeDirection(int16 oldDir, int16 newDir) {
if (oldDir != newDir) {
_playerIsChangingDirection = true;
_playerSpriteWalkIndex_maybe = (int16)(oldDir * 2);
_playerNewFacingDirection_maybe = (int16)(newDir * 2);
_playerWalkFrameDeltaOffset = 1;
if (oldDir < 4) {
switch (oldDir) {
case 0:
if (newDir == 3) {
_playerWalkFrameDeltaOffset = -1;
}
break;
case 1:
if (newDir == 0) {
_playerWalkFrameDeltaOffset = -1;
}
break;
case 2:
if (newDir == 1) {
_playerWalkFrameDeltaOffset = -1;
}
break;
case 3:
if (newDir == 2) {
_playerWalkFrameDeltaOffset = -1;
}
break;
default:
break;
}
}
}
}
void Player::playerFaceWalkTarget() {
int previousDirection;
int xDelta;
int yDelta;
previousDirection = _direction;
if (_position.x < _walkTarget.x) {
xDelta = _walkTarget.x - _position.x;
} else {
xDelta = _position.x - _walkTarget.x;
}
if (_position.y < _walkTarget.y) {
yDelta = _walkTarget.y - _position.y;
} else {
yDelta = _position.y - _walkTarget.y;
}
if (yDelta * 2 <= xDelta) {
if (_position.x < _walkTarget.x) {
_direction = 1;
} else if (_walkTarget.x < _position.x) {
_direction = 3;
}
} else if (_position.y < _walkTarget.y) {
_direction = 2;
} else {
_direction = 0;
}
changeDirection(previousDirection, _direction);
updateSprite();
_positionLong = _position;
}
void Player::calculateWalkTarget() {
_heroMoving = true;
_playerWalkFrameIdx = 0;
_walkPathIndex = -1;
_numConnectorsInWalkPath = 0;
int selectedObjNum = 0;
if (g_engine->_actionMode == kPointerAction) {
selectedObjNum = g_engine->_room->getRoomExitAtCursor();
}
if (selectedObjNum == 0) {
_walkTarget.x = g_engine->_cursor.getX();
_walkTarget.y = g_engine->_cursor.getY();
} else {
int currentRoomNumber = g_engine->_room->_roomNumber;
if (currentRoomNumber == 34 || (currentRoomNumber > 18 && currentRoomNumber < 24)) {
g_engine->_previousRoomNumber = currentRoomNumber;
if (currentRoomNumber == 34) {
g_engine->changeToRoom(33);
} else {
g_engine->changeToRoom(28);
}
return;
}
g_engine->_room->getWalkTargetForObjectType_maybe(selectedObjNum);
}
if (_walkTarget.y > 237) {
_walkTarget.y = 238;
}
if (!g_engine->_room->canWalkAtLocation(_walkTarget.x, _walkTarget.y)) {
int ty = _walkTarget.y;
while (!g_engine->_room->canWalkAtLocation(_walkTarget.x, ty) && ty <= 233) {
ty += 4;
}
if (ty < 235) {
_walkTarget.y = ty;
}
}
if (g_engine->_room->canWalkInLineToTarget(_position.x, _position.y, _walkTarget.x, _walkTarget.y)) {
return;
}
if (!g_engine->_room->canWalkAtLocation(_walkTarget.x, _walkTarget.y)) {
Common::Point connector = getClosestUnusedConnector(_walkTarget.x, _walkTarget.y);
if (connector.x == -1 && connector.y == -1) {
return;
}
int connectorToTargetDist = Math::hypotenuse(connector.x - _walkTarget.x, connector.y - _walkTarget.y);
int playerToTargetDist = Math::hypotenuse(_position.x - _walkTarget.x, _position.y - _walkTarget.y);
if (connectorToTargetDist < playerToTargetDist) {
if (g_engine->_room->canWalkInLineToTarget(_position.x, _position.y, connector.x, connector.y)) {
_finalTarget = _walkTarget;
_walkTarget = connector;
} else {
Common::Point tmpDest = _walkTarget;
_walkTarget = connector;
if (_numConnectorsInWalkPath > 0 && _numConnectorsInWalkPath < Room::MAX_CONNECTORS - 1 && _connectorList[_numConnectorsInWalkPath - 1] != connector) {
_connectorList[_numConnectorsInWalkPath] = connector;
_numConnectorsInWalkPath++;
}
_finalTarget = tmpDest;
}
}
} else {
createConnectorPathToDest();
}
}
int Player::getWidth() {
return getSprite(_frameIdx)._width;
}
int Player::getHeight() {
return getSprite(_frameIdx)._height;
}
void Player::updatePlayerPositionAfterRoomChange() {
int currentRoomNumber = g_engine->_room->_roomNumber;
g_engine->_room->calculateScaledSpriteDimensions(getWidth(), getHeight(), _position.y);
if (currentRoomNumber == 0x29 && g_engine->_previousRoomNumber == 0x2c) {
_position = Common::Point(0x13d, 0xa9);
} else if (currentRoomNumber == 0x2c && g_engine->_previousRoomNumber == 0x29) {
_position = Common::Point(0x16e, 0xb8);
} else if (_direction == 0 || ((currentRoomNumber == 0x29 || currentRoomNumber == 0x2c) && _direction == 2)) {
_position.y = 0xec;
g_engine->_room->calculateScaledSpriteDimensions(getWidth(), getHeight(), _position.y);
while (!g_engine->_room->canWalkAtLocation(_position.x, _position.y + 3) && _position.y > 100) {
_position.y--;
}
} else if (_direction == 2) {
while (!g_engine->_room->canWalkAtLocation(_position.x, _position.y - 5) && _position.y < 0xee && currentRoomNumber != 0x29 && currentRoomNumber != 0x2c) {
_position.y++;
}
} else if (_direction == 3) {
if (currentRoomNumber == 0x20 || currentRoomNumber == 0x1a) {
g_engine->_scaledSpriteHeight = 5;
} else {
g_engine->_room->calculateScaledSpriteDimensions(getWidth(), getHeight(), _position.y + g_engine->_scaledSpriteHeight);
}
_position.y += g_engine->_scaledSpriteHeight;
if (_position.y > 0xee) {
_position.y = 0xee;
}
if (_position.x > 0x27b) {
_position.x = 0x27b;
}
int yUp = _position.y;
int yDown = _position.y;
while (!g_engine->_room->canWalkAtLocation(_position.x, yUp) && yUp < 0xee) {
yUp++;
}
while (!g_engine->_room->canWalkAtLocation(_position.x, yDown) && yDown > 0x28) {
yDown--;
}
if (yUp - _position.y < _position.y - yDown) {
_position.y = yUp;
} else {
_position.y = yDown;
}
} else {
g_engine->_room->calculateScaledSpriteDimensions(getWidth(), getHeight(), _position.y + g_engine->_scaledSpriteHeight);
_position.y += g_engine->_scaledSpriteHeight;
if (_position.y > 0xee) {
_position.y = 0xee;
}
int yUp = _position.y;
int yDown = _position.y;
while (!g_engine->_room->canWalkAtLocation(_position.x, yUp) && yUp < 0xee) {
yUp++;
}
while (!g_engine->_room->canWalkAtLocation(_position.x, yDown) && yDown > 0x28) {
yDown--;
}
if (yUp - _position.y < _position.y - yDown) {
_position.y = yUp;
} else {
_position.y = yDown;
}
}
}
void Player::createConnectorPathToDest() {
constexpr Common::Point noConnectorFound(-1, -1);
Common::Point origWalkTarget = _walkTarget;
Common::Point startPoint = _position;
if (g_engine->_room->_roomNumber != 5 || _position.x > 320) {
startPoint = _walkTarget;
_walkTarget = _position;
}
_numConnectorsInWalkPath = 0;
Common::Point connector;
if (!g_engine->_room->canWalkAtLocation(startPoint.x, startPoint.y)) {
connector = getClosestUnusedConnector(startPoint.x, startPoint.y);
} else {
connector = getClosestUnusedConnector(startPoint.x, startPoint.y, true);
}
if (connector == noConnectorFound) {
if (g_engine->_room->_roomNumber != 5 || _position.x > 320) {
_walkTarget = origWalkTarget;
}
return;
}
_walkPathIndex = 0;
_connectorList[_numConnectorsInWalkPath] = connector;
_numConnectorsInWalkPath++;
while (_numConnectorsInWalkPath < Room::MAX_CONNECTORS && connector != noConnectorFound) {
if (g_engine->_room->canWalkInLineToTarget(connector.x, connector.y, _walkTarget.x, _walkTarget.y)) {
break;
}
connector = getClosestUnusedConnector(connector.x, connector.y, true);
if (connector == _walkTarget) {
break;
}
if (connector != noConnectorFound) {
_connectorList[_numConnectorsInWalkPath] = connector;
_numConnectorsInWalkPath++;
}
}
if (g_engine->_room->_roomNumber != 5 || _position.x > 320) {
reverseConnectorList();
_walkTarget = origWalkTarget;
}
optimisePath();
if (g_engine->_room->_roomNumber == 5 && _position.x < 321) {
_finalTarget = _walkTarget;
} else {
_finalTarget = origWalkTarget;
}
_walkTarget = _connectorList[0];
}
Common::Point Player::getClosestUnusedConnector(int16 x, int16 y, bool mustHaveCleanLine) {
Common::Point closestPoint = {-1, -1};
int closestDist = 5000;
for (auto &roomConnector : g_engine->_room->_connectors) {
bool containsPoint = false;
for (int i = 0; i < _numConnectorsInWalkPath; i++) {
if (_connectorList[i] == roomConnector) {
containsPoint = true;
}
}
if (!containsPoint) {
int dist = Math::hypotenuse((roomConnector.x - x), (roomConnector.y - y));
if (dist < closestDist) {
if (!mustHaveCleanLine || g_engine->_room->canWalkInLineToTarget(x, y, roomConnector.x, roomConnector.y)) {
closestPoint = roomConnector;
closestDist = dist;
}
}
}
}
return closestPoint;
}
void Player::walkToNextConnector() {
if (_walkPathIndex == -1) {
return;
}
if (_walkPathIndex + 1 < _numConnectorsInWalkPath) {
_walkPathIndex++;
_walkTarget = _connectorList[_walkPathIndex];
} else {
_walkTarget = _finalTarget;
_walkPathIndex = -1;
}
playerFaceWalkTarget();
}
void Player::draw() {
if (g_engine->_debugShowWalkPath) {
if (_walkPathIndex != -1) {
for (int i = _walkPathIndex; i < _numConnectorsInWalkPath; i++) {
if (i == _walkPathIndex) {
g_engine->_screen->drawLine(_position.x, _position.y, _connectorList[i].x, _connectorList[i].y, 2);
} else {
g_engine->_screen->drawLine(_connectorList[i].x, _connectorList[i].y, _connectorList[i - 1].x, _connectorList[i - 1].y, 2);
}
}
g_engine->_screen->drawLine(_connectorList[_numConnectorsInWalkPath - 1].x, _connectorList[_numConnectorsInWalkPath - 1].y, _finalTarget.x, _finalTarget.y, 2);
}
}
}
void Player::reverseConnectorList() {
for (int i = 0; i < _numConnectorsInWalkPath / 2; i++) {
SWAP(_connectorList[i], _connectorList[_numConnectorsInWalkPath - 1 - i]);
}
}
void Player::optimisePath() {
if (g_engine->_room->_roomNumber != 7 && g_engine->_room->_roomNumber != 32) {
while (_numConnectorsInWalkPath > 1) {
if (g_engine->_room->canWalkInLineToTarget(_connectorList[_numConnectorsInWalkPath - 2].x, _connectorList[_numConnectorsInWalkPath - 2].y, _walkTarget.x, _walkTarget.y)) {
_numConnectorsInWalkPath--;
} else {
break;
}
}
}
}
static constexpr uint8 _closerroom[10] = {
0, 5, 0, 9,
0, 0, 5, 6,
7, 6
};
void Player::setPlayerTowardsBedroom() {
if (g_engine->_animation->_isPlayingAnimation_maybe) {
return;
}
Common::Point currentCursor = g_engine->_cursor.getPosition();
uint8 currentRoomNumber = g_engine->_room->_roomNumber;
if (currentRoomNumber == 0) {
Common::Point target = {223, 190};
g_engine->_cursor.setPosition(target);
} else {
uint8 local_a = 0;
if (currentRoomNumber < 10) {
local_a = _closerroom[currentRoomNumber];
} else if (currentRoomNumber == 13) {
local_a = 61;
} else if (currentRoomNumber == 61) {
local_a = 5;
} else if (currentRoomNumber == 62) {
local_a = 8;
}
if (currentRoomNumber == 6 && g_engine->_objectVar[137] == 2) {
local_a = 10;
}
Common::Point exitPosition = g_engine->_room->getExitPointForRoom(local_a);
g_engine->_cursor.setPosition(exitPosition);
uint16 exitObjNum = g_engine->_room->getRoomExitAtCursor();
g_engine->_room->getWalkTargetForObjectType_maybe(exitObjNum);
g_engine->_cursor.setPosition(_walkTarget);
}
calculateWalkTarget();
playerFaceWalkTarget();
g_engine->_cursor.setPosition(currentCursor);
}
} // End of namespace Darkseed

88
engines/darkseed/player.h Normal file
View File

@@ -0,0 +1,88 @@
/* 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 DARKSEED_PLAYER_H
#define DARKSEED_PLAYER_H
#include "common/rect.h"
#include "common/path.h"
#include "darkseed/nsp.h"
namespace Darkseed {
class Player {
Nsp _cPlayerSprites;
Nsp _gPlayerSprites;
public:
Nsp _animations;
int _frameIdx = 0;
int _direction = 0;
Common::Point _position;
Common::Point _positionLong; // the original sometimes seems to use a long (4 byte) version of the location
Common::Point _walkTarget;
Common::Point _finalTarget;
int16 _playerSpriteWalkIndex_maybe = 0;
int16 _playerWalkFrameDeltaOffset = 0;
int16 _playerNewFacingDirection_maybe = 0;
uint16 _playerWalkFrameIdx = 0;
bool _actionToPerform = false; // player is pathfinding to some destination?
bool _playerIsChangingDirection = false; // AKA _Rotating
bool _isAutoWalkingToBed = false;
bool _heroMoving = false; // maybe set to true while player is walking around the room.
bool _heroWaiting = false;
int _walkPathIndex = -1;
uint16 _numConnectorsInWalkPath = 0;
Common::Array<Common::Point> _connectorList;
int16 _sequenceRotation = -1;
bool _walkToSequence = false;
Common::Point _walkToSequencePoint;
bool _flipSprite = false;
Player();
bool loadAnimations(const Common::Path &filename);
const Sprite &getSprite(int frameNo) const;
void updateSprite();
void draw();
bool isAtPosition(int x, int y) const;
bool isAtWalkTarget() const;
void calculateWalkTarget();
void changeDirection(int16 oldDir, int16 newDir);
void playerFaceWalkTarget();
int getWidth();
int getHeight();
void updatePlayerPositionAfterRoomChange();
void setPlayerTowardsBedroom();
void walkToNextConnector();
private:
void createConnectorPathToDest();
Common::Point getClosestUnusedConnector(int16 x, int16 y, bool mustHaveCleanLine = false);
void reverseConnectorList();
void optimisePath();
};
} // namespace Darkseed
#endif // DARKSEED_PLAYER_H

1514
engines/darkseed/room.cpp Normal file

File diff suppressed because it is too large Load Diff

125
engines/darkseed/room.h Normal file
View File

@@ -0,0 +1,125 @@
/* 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 DARKSEED_ROOM_H
#define DARKSEED_ROOM_H
#include "common/rect.h"
#include "darkseed/nsp.h"
#include "darkseed/pal.h"
#include "darkseed/pic.h"
#include "sound.h"
namespace Darkseed {
struct RoomExit {
uint16 x = 0;
uint16 y = 0;
uint16 width = 0;
uint16 height = 0;
uint16 roomNumber = 0;
uint8 direction = 0;
};
struct RoomStruct2 {
uint8 strip[40];
};
struct RoomObjElement {
uint16 type = 0;
uint16 objNum = 0;
uint16 xOffset = 0;
uint16 yOffset = 0;
uint16 width = 0;
uint16 height = 0;
uint8 depth = 0;
uint8 spriteNum = 0;
};
class Room {
bool _palLoaded = false;
public:
static constexpr int MAX_CONNECTORS = 12;
uint8 _roomNumber;
Pic _pic;
Pal _pal;
Pal _workPal; // used to darken the sky.
Nsp _locationSprites;
Common::Array<int16> _locObjFrame;
Common::Array<int16> _locObjFrameTimer;
Common::Array<RoomExit> _room1;
Common::Array<RoomStruct2> _walkableLocationsMap;
Common::Array<RoomObjElement> _roomObj;
Common::Array<Common::Point> _connectors;
uint16 _selectedObjIndex = 0;
int16 _collisionType = 0;
explicit Room(int roomNumber);
void initRoom();
void draw();
void update();
int checkCursorAndMoveableObjects();
int checkCursorAndStaticObjects(int x, int y);
int CheckCursorAndMovedObjects();
int getRoomExitAtCursor();
void getWalkTargetForObjectType_maybe(int objId);
int getObjectUnderCursor();
uint16 getDoorTargetRoom(int objId);
int getExitRoomNumberAtPoint(int x, int y);
bool exitRoom();
Common::String getRoomFilenameBase(int roomNumber);
bool canWalkAtLocation(int x, int y);
bool canWalkInLineToTarget(int x, int y, int targetX, int targetY);
void printRoomDescriptionText() const;
void calculateScaledSpriteDimensions(int width, int height, int curYPosition);
bool isOutside() const;
bool isGiger();
void runRoomObjects();
void removeObjectFromRoom(int16 objNum);
void updateRoomObj(int16 objNum, int16 x, int16 width, int16 y, int16 height);
bool advanceFrame(int animIdx);
void mikeStickThrowAnim();
void loadRoom61AWalkableLocations();
void restorePalette();
void installPalette();
void darkenSky();
void loadLocationSprites(const Common::Path &path);
Common::Point getExitPointForRoom(uint8 roomNumber);
static MusicId getMusicIdForRoom(uint8 roomNumber);
void loadRoomMusic();
private:
bool load();
static Common::String stripSpaces(const Common::String &source);
void drawTrunk();
void advanceLocAnimFrame(int roomObjIdx);
void dosDemoFixupRoomObjects();
};
} // namespace Darkseed
#endif // DARKSEED_ROOM_H

587
engines/darkseed/sound.cpp Normal file
View File

@@ -0,0 +1,587 @@
/* 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/raw.h"
#include "audio/decoders/voc.h"
#include "common/config-manager.h"
#include "common/util.h"
#include "darkseed/sound.h"
#include "darkseed/darkseed.h"
namespace Darkseed {
static constexpr char musicDosFloppyFilenameTbl[][14] = {
"lab1",
"victory1",
"cemetry1",
"leech1",
"ext1",
"quiet",
"package",
"dth",
"library",
"radio",
"outdoor",
"town"
};
static constexpr char musicDosCDFilenameTbl[][14] = {
"lab.mid",
"victory.mid",
"cemetery.mid",
"leech.mid",
"exterior.mid",
"passtime.mid",
"mirrorst.mid",
"dth.mid",
"lib_moz.mid",
"carradio.mid",
"greenwal.mid",
"walktown.mid"
};
static constexpr char startMusicDosFloppyFilenameTbl[][14] = {
"credits",
"alien",
"implant",
"launch",
"night2",
"night3",
"book",
"doll"
};
static constexpr char startMusicDosCDFilenameTbl[][14] = {
"openingt.mid",
"alienmou.mid",
"mindfuck.mid",
"spaceshi.mid",
"mindfuck.mid",
"zombie.mid",
"booktran.mid",
"babydoll.mid"
};
static constexpr char sfxCDFilenameTbl[][14] = {
"",
"opendoor.sfx",
"showers2.sfx",
"razzsysb.sfx",
"medicine.sfx",
"pop.sfx",
"pickupit.sfx",
"rockener.sfx",
"pullleve.sfx",
"starship.sfx",
"podwrith.sfx",
"starterc.sfx",
"sigils.sfx",
"tombdoor.sfx",
"digging.sfx",
"opendoor.sfx",
"carstart.sfx",
"makehamm.sfx",
"picklock.sfx",
"impaled.sfx",
"evilbeas.sfx",
"laser.sfx",
"knock.sfx",
"bubblesi.sfx",
"phone.sfx",
"softphon.sfx",
"pulsar.sfx",
"doorbell.sfx",
"mirrorsm.sfx",
"softdoor.sfx",
"electroc.sfx",
"medicine.sfx",
"pourings.sfx",
"tuneinra.sfx",
"opendoor.sfx",
"showers1.sfx",
"yo.sfx",
"showers2.sfx",
"popii.sfx",
"carhorn.sfx",
"yo.sfx",
"secretdo.sfx",
"opendoor.sfx",
"tick.sfx",
"tock.sfx",
"chime.sfx",
"softchim.sfx",
"shakeurn.sfx",
"beaming.sfx"
};
// Maps CD SFX IDs to floppy SFX IDs
static constexpr int sfxCdFloppyMapping[60] = {
// 0
-1, // Unused
10, // House front door
11, // Shower
-1, // Delbert's dog (not present in floppy version)
13, // Stairs
14, // Press button
15, // Pick up item
16, // Unused (energizing hammer head?)
17, // Lever
18, // Spaceship
// 10
19, // Leech
20, // Car engine start
21, // Mausoleum sigils (only used by floppy version)
22, // Mausoleum door (only used by CD version)
23, // Shovel
24, // Car door
25, // Car engine start failure
26, // Assembling hammer
27, // Lock picking
28, // Guardian
// 20
29, // Alien dog
30, // Alien cop gun
12, // Cup
32, // Cocoon
90, // Phone (loud)
91, // Phone (quiet)
118, // Fixing mirror
93, // Doorbell (loud)
94, // Destroying mirror
95, // Doorbell (quiet)
// 30
96, // Electricity
97, // Car trunk / trunk (attic)
98, // Drinking / pouring
99, // Unused (tuning radio?)
100, // Bathroom cabinet
101, // Bathroom faucet
-1, // Unused
-1, // Unused (running water?)
-1, // Unused (press button alternate?)
111, // Car horn
// 40
-1, // Unused
116, // Secret door
115, // Kitchen doors
107, // Clock tick (only used by floppy version)
108, // Clock tock (only used by floppy version)
104, // Clock chime (loud)
106, // Clock chime (quiet)
113, // Urn
114, // Teleporter
-1, // Unused
// 50 Floppy-only sound effects
35, // Recruitment center
51, // Car engine running (original code uses 50 here, which sounds wrong)
92, // Footstep
105, // Mosquito (upstairs hallway)
109, // Book stamp
110, // Kitchen faucet / fountain
112, // Phone dial tone
-1,
-1,
-1
};
// Maps DOS CD speech IDs to DOS floppy SFX IDs
static Common::Pair<int, int> speechCdFloppyMapping[] = {
// 59: ralph1 (unused & invalid)
{ 904, 60 }, // m1-1 Librarian (phone): "Hello?" "Hello Mike. This is Sue at the library. ..."
{ 927, 61 }, // cl1 Store clerk: "Serve yourself, Mr. Dawson."
{ 905, 62 }, // cl2 Store clerk: "That's the last bottle of Scotch. ..."
{ 907, 63 }, // d4a-1 Delbert: "Hi you must be Mike. ..."
{ 908, 64 }, // d6c-2 Delbert: "You're a writer, huh? ..."
{ 909, 65 }, // d5a-1 Delbert: "Good to see you, Dawson. ..."
{ 910, 66 }, // d6a-2 Delbert: "Boy, that's smooth. ..."
{ 906, 67 }, // cl3 Store clerk: "I'm sorry, Mr. Dawson, ..."
// CD ID 925 includes both this line and the next. These are 2 separate IDs in the floppy version. CD ID 926 is unused.
{ 925, 68 }, // gl0a Librarian: "I'm not really sure why I'm here, ..."
{ 926, 69 }, // gl1b Librarian: "I know it sounds strange, ..."
{ 924, 70 }, // gl2a Librarian: "This card really should be kept with the book. ..."
// 71: s7a-1 (invalid)
{ 912, 72 }, // s8a-2 Cop: "You're under arrest, Dawson. ..."
{ 913, 73 }, // k9a-3 Keeper: "Greetings Michael. ..."
{ 914, 74 }, // k9c-3 Keeper: "If born, this creature will destroy you..."
{ 915, 75 }, // k9e-3 Keeper: "Also, the Police in your world..."
{ 928, 76 }, // gl3a Librarian: "Hi Mike. Here's the book that was put on hold for you."
// 77: k9e-3 (duplicate of 75)
// 78: k9f-3 (invalid)
{ 916, 79 }, // g10a-1 Sargo: "Greetings, human. ..."
{ 917, 80 }, // g10b-1 Sargo: "I am prepared to give you the gift..."
{ 918, 81 }, // m11a-1 Mike: "I'm just beginning to understand."
{ 919, 82 }, // o12a-1 Keeper (radio): "Steal from those who protect you..."
{ 920, 83 }, // o13a-1 Keeper (radio): "What you do in the light..."
// 84: o13b-1 (invalid)
{ 921, 85 }, // o14a-1 Keeper (radio): "Turn yourself in and leave behind the key..."
{ 922, 86 }, // k15a-1 Keeper (phone): "Remember, anything seen in the mirror..."
{ 923, 87 }, // s16a-1 Alien cop: "So that's where my gun went! ..."
// 88: l17a-1 (invalid)
// 89: l18a-1 (invalid)
{ -1, -1 }
};
Sound::Sound(Audio::Mixer *mixer) : _mixer(mixer), _lastPlayedDigitalSfx(0) {
memset(_sfxFloppyDigFilenameTbl, 0, 120 * 14);
bool floppyMusicSetting = ConfMan.hasKey("use_floppy_music") ? ConfMan.getBool("use_floppy_music") : false;
_useFloppyMusic = g_engine->isDosFloppy() || floppyMusicSetting;
// SFX mode 0: CD SFX only
// SFX mode 1: CD SFX + additional floppy SFX
// SFX mode 2: floppy SFX only
// CD SFX are only available when using the CD version
// Floppy SFX are only available when using floppy music
int sfxMode = ConfMan.hasKey("sfx_mode") ? ConfMan.getInt("sfx_mode") : -1;
_useCdSfx = g_engine->isCdVersion() && (!_useFloppyMusic || sfxMode != 2);
_useFloppySfx = _useFloppyMusic && (g_engine->isDosFloppy() || sfxMode == 1 || sfxMode == 2);
_musicPlayer = new MusicPlayer(g_engine, _useFloppyMusic, _useFloppySfx);
_didSpeech.resize(978);
resetSpeech();
}
Sound::~Sound() {
delete _musicPlayer;
}
int Sound::init() {
int returnCode = _musicPlayer->open();
if (returnCode != 0)
return returnCode;
if (_useFloppyMusic || _useFloppySfx) {
Common::File file;
Common::Path path = g_engine->isCdVersion() ? Common::Path("sound").join("tos1.sit") : Common::Path("tos1.sit");
if (file.open(path)) {
_musicPlayer->loadTosInstrumentBankData(&file, (int32)file.size());
} else {
warning("Failed to load TOS floppy instrument bank data %s", path.toString().c_str());
}
file.close();
}
if (_useFloppySfx) {
Common::File file;
Common::Path path = g_engine->isCdVersion() ? Common::Path("sound").join("tos1.sbr") : Common::Path("tos1.sbr");
if (file.open(path)) {
_musicPlayer->load(&file, (int32)file.size(), true);
} else {
warning("Failed to load TOS floppy sound effects data %s", path.toString().c_str());
}
file.close();
}
if (g_engine->isDosFloppy()) {
Common::File file;
Common::Path path = Common::Path("tos1.dig");
if (file.open(path)) {
loadTosDigData(&file, (int32)file.size());
} else {
warning("Failed to load TOS floppy speech data %s", path.toString().c_str());
}
file.close();
}
return 0;
}
void Sound::loadTosDigData(Common::SeekableReadStream* in, int32 size) {
int32 bytesRead = 0;
int entriesRead = 0;
while (bytesRead < size && entriesRead < 120) {
byte type = in->readByte();
if (type == 3) {
// VOC filename entry
uint32 entryBytesRead = in->read(_sfxFloppyDigFilenameTbl[entriesRead], 14);
if (entryBytesRead != 14) {
warning("Failed to read all bytes from DIG filename entry %i", entriesRead);
return;
}
bytesRead += 15;
}
else if (type == 0) {
// Unknown what this entry type contains. It is ignored by the original code.
uint16 entrySize = in->readUint16LE();
if (!in->skip(entrySize - 3)) {
warning("Failed to read all bytes from DIG type 0 entry %i", entriesRead);
return;
}
bytesRead += entrySize;
}
else {
// Unknown entry type.
warning("Unknown DIG entry type %X in entry %i", type, entriesRead);
return;
}
entriesRead++;
}
if (entriesRead < 100) {
// DIG files typically contain at least 100 entries
warning("DIG file only contained %i entries", entriesRead);
}
}
void Sound::playTosSpeech(int tosIdx) {
if (g_engine->isDosFloppy()) {
int floppySfxId = convertCdSpeechToFloppySfxId(tosIdx);
if (floppySfxId == -1)
return;
playDosFloppySfx(floppySfxId, 5);
return;
}
if (!g_engine->isCdVersion() || _didSpeech[tosIdx] == 1) {
return;
}
Common::String filename = Common::String::format("%d.voc", tosIdx + 1);
Common::Path path = Common::Path("speech").join(filename);
Common::File f;
if (!f.open(path)) {
return;
}
Common::SeekableReadStream *srcStream = f.readStream((uint32)f.size());
Audio::SeekableAudioStream *stream = Audio::makeVOCStream(srcStream,
Audio::FLAG_UNSIGNED, DisposeAfterUse::YES);
_mixer->playStream(Audio::Mixer::kSpeechSoundType, &_speechHandle, stream);
_didSpeech[tosIdx] = 1;
}
void Sound::stopSpeech() {
_mixer->stopHandle(_speechHandle);
}
bool Sound::isPlayingSpeech() const {
return _mixer->isSoundHandleActive(_speechHandle);
}
bool Sound::isPlayingSfx() const {
return _mixer->isSoundHandleActive(_sfxHandle) || _musicPlayer->isPlayingSfx();
}
bool Sound::isPlayingSfx(uint8 sfxId) const {
if (_useFloppySfx) {
int floppySfxId = sfxCdFloppyMapping[sfxId];
if (floppySfxId == -1)
return false;
return _musicPlayer->isPlayingSfx(floppySfxId);
}
return _lastPlayedDigitalSfx == sfxId && _mixer->isSoundHandleActive(_sfxHandle);
}
bool Sound::isPlayingMusic() {
return _musicPlayer->isPlayingMusic();
}
void Sound::resetSpeech() {
for (int i = 0; i < (int)_didSpeech.size(); i++) {
_didSpeech[i] = 0;
}
}
void Sound::playMusic(MusicId musicId, bool loop) {
if (musicId == MusicId::kNone) {
return;
}
int filenameIdx = static_cast<uint8>(musicId) - 1;
playMusic(_useFloppyMusic ?
Common::String(musicDosFloppyFilenameTbl[filenameIdx]) + ".sbr" : musicDosCDFilenameTbl[filenameIdx],
nullptr, 6, loop);
}
void Sound::playMusic(StartMusicId musicId) {
int filenameIdx = static_cast<uint8>(musicId);
if (_useFloppyMusic) {
Common::String const &filenameBase = startMusicDosFloppyFilenameTbl[filenameIdx];
Common::String const &filenameSbr = filenameBase + ".sbr";
Common::String const &filenameSit = filenameBase + ".sit";
playMusic(filenameSbr, &filenameSit, 5);
}
else {
playMusic(startMusicDosCDFilenameTbl[filenameIdx]);
}
}
void Sound::playMusic(Common::String const &musicFilename, Common::String const *instrBankFilename, uint8 priority, bool loop) {
Common::File file;
Common::Path path;
if (_useFloppyMusic) {
if (instrBankFilename != nullptr) {
path = g_engine->isCdVersion() ? Common::Path("sound").join(instrBankFilename->c_str()) : Common::Path(instrBankFilename->c_str());
debug("Loading instrument bank: %s", path.toString().c_str());
if (!file.open(path)) {
debug("Failed to load %s", path.toString().c_str());
return;
}
_musicPlayer->loadInstrumentBank(&file, (int32)file.size());
file.close();
}
else {
debug("Loading TOS instrument bank");
_musicPlayer->loadTosInstrumentBank();
}
}
path = g_engine->isCdVersion() ? Common::Path("sound").join(musicFilename) : Common::Path(musicFilename);
debug("Loading music: %s", path.toString().c_str());
if (!file.open(path)) {
debug("Failed to load %s", path.toString().c_str());
return;
}
_musicPlayer->load(&file, (int32)file.size());
file.close();
_musicPlayer->playMusic(priority, loop);
}
void Sound::stopMusic() {
_musicPlayer->stopMusic();
}
void Sound::pauseMusic(bool pause) {
_musicPlayer->pauseMusic(pause);
}
void Sound::killAllSound() {
stopMusic();
stopSfx();
stopSpeech();
}
void Sound::syncSoundSettings() {
_musicPlayer->syncSoundSettings();
}
Common::Error Sound::sync(Common::Serializer &s) {
s.syncArray(_didSpeech.data(), _didSpeech.size(), Common::Serializer::Byte);
return Common::kNoError;
}
bool Sound::isMuted() const {
bool soundIsMuted = false;
if (ConfMan.hasKey("mute")) {
soundIsMuted = ConfMan.getBool("mute");
}
return soundIsMuted;
}
void Sound::playSfx(uint8 sfxId, uint8 priority, int unk2) {
// Do not play floppy-only SFX using the CD SFX playing code
if (_useCdSfx && sfxId <= 48 && sfxId != 43 && sfxId != 44) {
playDosCDSfx(sfxId);
}
else if (_useFloppySfx && sfxId < 60) {
int floppySfxId = sfxCdFloppyMapping[sfxId];
if (floppySfxId == -1)
return;
playDosFloppySfx(floppySfxId, priority);
}
}
void Sound::stopSfx() {
_mixer->stopHandle(_sfxHandle);
_musicPlayer->stopAllSfx();
}
void Sound::playDosCDSfx(int sfxId) {
if (sfxId == 0 || sfxId > 48) {
return;
}
if (isPlayingSfx()) {
return;
}
Common::Path path = Common::Path("sound").join(sfxCDFilenameTbl[sfxId]);
Common::File f;
if (!f.open(path)) {
debug("Failed to load sfx. %s", path.toString().c_str());
return;
}
Common::SeekableReadStream *srcStream = f.readStream((uint32)f.size());
Audio::SeekableAudioStream *stream = Audio::makeVOCStream(srcStream,
Audio::FLAG_UNSIGNED, DisposeAfterUse::YES);
_mixer->playStream(Audio::Mixer::kSFXSoundType, &_sfxHandle, stream);
_lastPlayedDigitalSfx = sfxId;
}
void Sound::playDosFloppySfx(byte floppySfxId, uint8 priority) {
if (_musicPlayer->isSampleSfx(floppySfxId)) {
playFloppySpeech(floppySfxId);
} else if (floppySfxId >= 10 && floppySfxId < 120) {
_musicPlayer->playSfx(floppySfxId, priority);
}
}
void Sound::playFloppySpeech(int floppySfxId) {
Common::String filename = _sfxFloppyDigFilenameTbl[floppySfxId];
if (filename.size() == 0)
return;
Common::Path path = Common::Path(filename + ".voc");
Common::File f;
if (!f.open(path)) {
warning("Failed to load speech file %s", path.toString().c_str());
return;
}
Common::SeekableReadStream *srcStream = f.readStream((uint32)f.size());
Audio::SeekableAudioStream *stream = Audio::makeVOCStream(srcStream,
Audio::FLAG_UNSIGNED, DisposeAfterUse::YES);
_mixer->playStream(Audio::Mixer::kSpeechSoundType, &_speechHandle, stream);
}
void Sound::startFadeOut() {
_musicPlayer->startFadeOutMusic();
}
bool Sound::isFading() {
return _musicPlayer->isFadingMusic();
}
int Sound::convertCdSpeechToFloppySfxId(int cdSfxId) {
int i = 0;
while (true) {
int entryCdSfxId = speechCdFloppyMapping[i].first;
if (entryCdSfxId == -1)
return -1;
if (entryCdSfxId == cdSfxId)
return speechCdFloppyMapping[i].second;
i++;
}
}
bool Sound::isUsingCdSfx() const {
return _useCdSfx;
}
bool Sound::isUsingFloppySfx() const {
return _useFloppySfx;
}
void Sound::resetIndividualSpeech(int tosIdx) {
_didSpeech[tosIdx] = 0;
}
} // End of namespace Darkseed

115
engines/darkseed/sound.h Normal file
View File

@@ -0,0 +1,115 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef DARKSEED_SOUND_H
#define DARKSEED_SOUND_H
#include "darkseed/music.h"
#include "audio/mixer.h"
#include "common/array.h"
#include "common/error.h"
#include "common/serializer.h"
namespace Darkseed {
enum class MusicId : uint8 {
kNone = 0,
kLab,
kVictory,
kCemetry,
kLeech,
kExt,
kQuiet,
kPackage,
kDth,
kLibrary,
kRadio,
kOutdoor,
kTown,
};
enum class StartMusicId : uint8 {
kCredits = 0,
kAlien,
kImplant,
kLaunch,
kNight2,
kNight3,
kBook,
kDoll
};
class Sound {
// Floppy speech filenames from TOS1.DIG
char _sfxFloppyDigFilenameTbl[120][14];
Audio::Mixer *_mixer;
Audio::SoundHandle _speechHandle;
Audio::SoundHandle _sfxHandle;
MusicPlayer *_musicPlayer;
Common::Array<uint8> _didSpeech;
bool _useFloppyMusic;
bool _useCdSfx;
bool _useFloppySfx;
uint8 _lastPlayedDigitalSfx;
public:
explicit Sound(Audio::Mixer *mixer);
~Sound();
int init();
Common::Error sync(Common::Serializer &s);
bool isMuted() const;
void playTosSpeech(int tosIdx);
void stopSpeech();
bool isPlayingSpeech() const;
bool isPlayingSfx() const;
bool isPlayingSfx(uint8 sfxId) const;
bool isPlayingMusic();
void resetSpeech();
void resetIndividualSpeech(int tosIdx);
void playMusic(MusicId musicId, bool loop = true);
void playMusic(StartMusicId musicId);
void playMusic(Common::String const &filename, Common::String const *instrBankFilename = nullptr, uint8 priority = 0xFF, bool loop = false);
void stopMusic();
void pauseMusic(bool pause);
void playSfx(uint8 sfxId, uint8 priority, int unk2);
void playDosFloppySfx(byte sfxId, uint8 priority);
void stopSfx();
bool isUsingCdSfx() const;
bool isUsingFloppySfx() const;
void syncSoundSettings();
void killAllSound();
void startFadeOut();
bool isFading();
private:
void loadTosDigData(Common::SeekableReadStream *in, int32 size);
void playDosCDSfx(int sfxId);
void playFloppySpeech(int tosIdx);
int convertCdSpeechToFloppySfxId(int cdSfxId);
};
} // namespace Darkseed
#endif // DARKSEED_SOUND_H

View File

@@ -0,0 +1,78 @@
/* 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 "darkseed/sprites.h"
#include "darkseed/darkseed.h"
namespace Darkseed {
#define DARKSEED_MAX_SPRITES_ON_SCREEN 30
Sprites::Sprites() {
_spriteDrawList.reserve(DARKSEED_MAX_SPRITES_ON_SCREEN);
}
void Sprites::addSpriteToDrawList(uint16 destX, uint16 destY, const Sprite *sprite, uint8 order, uint16 destW, uint16 destH, bool flip) {
if (_spriteDrawList.size() == DARKSEED_MAX_SPRITES_ON_SCREEN || destX >= 570) {
return;
}
SpriteDrawInstruction drawInstruction;
drawInstruction.destX = destX;
drawInstruction.destY = destY;
drawInstruction.sprite = sprite;
drawInstruction.order = order;
drawInstruction.destW = destW;
drawInstruction.destH = destH;
drawInstruction.flip = flip;
if (!_spriteDrawList.empty()) {
uint insertLocation = 0;
for (; insertLocation < _spriteDrawList.size(); insertLocation++) {
if (order < _spriteDrawList[insertLocation].order) {
break;
}
}
_spriteDrawList.insert_at(insertLocation, drawInstruction);
} else {
_spriteDrawList.push_back(drawInstruction);
}
}
void Sprites::clearSpriteDrawList() {
// not using clear() here to avoid freeing array storage memory.
while (!_spriteDrawList.empty()) {
_spriteDrawList.pop_back();
}
}
void Sprites::drawSprites() {
for (int i = _spriteDrawList.size() - 1; i >= 0; i--) {
SpriteDrawInstruction &drawInstruction = _spriteDrawList[i];
if (drawInstruction.sprite->_width == drawInstruction.destW && drawInstruction.sprite->_height == drawInstruction.destH && !drawInstruction.flip) {
drawInstruction.sprite->draw(drawInstruction.destX, drawInstruction.destY, g_engine->_frameBottom); // TODO add support for flipping sprite.
} else {
drawInstruction.sprite->drawScaled(drawInstruction.destX, drawInstruction.destY, drawInstruction.destW, drawInstruction.destH, drawInstruction.flip);
}
}
}
} // End of namespace Darkseed

View File

@@ -0,0 +1,54 @@
/* 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 DARKSEED_SPRITES_H
#define DARKSEED_SPRITES_H
#include "common/scummsys.h"
#include "darkseed/nsp.h"
namespace Darkseed {
struct SpriteDrawInstruction {
uint16 destX = 0;
uint16 destY = 0;
uint16 srcW = 0;
uint16 srcH = 0;
const Sprite *sprite = nullptr;
uint8 order = 0;
uint16 destW = 0;
uint16 destH = 0;
bool flip = false;
};
class Sprites {
Common::Array<SpriteDrawInstruction> _spriteDrawList;
public:
Sprites();
void addSpriteToDrawList(uint16 destX, uint16 destY, const Sprite *sprite, uint8 order, uint16 destW, uint16 destH, bool flip);
void clearSpriteDrawList();
void drawSprites();
};
} // namespace Darkseed
#endif // DARKSEED_SPRITES_H

View File

@@ -0,0 +1,78 @@
/* 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/screen.h"
#include "darkseed/titlefont.h"
#include "darkseed/darkseed.h"
namespace Darkseed {
extern DarkseedEngine *g_engine;
TitleFont::TitleFont() {
_letters.load("art/letters.anm");
}
uint8 letterIndexLookupTbl[] = {
50, 52, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 2, 4, 6,
8, 10, 12, 14,
16, 18, 20, 22,
24, 26, 28, 30,
0, 32, 34, 36,
38, 40, 42, 44,
46, 48
};
int16 letterWidthLookupTbl[] = {
18, 18, 18, 18,
18, 18, 18, 18,
10, 12, 16, 18,
20, 20, 18, 18,
18, 18, 18, 18,
18, 20, 20, 18,
20, 18, 10
};
void TitleFont::displayString(uint16 x, uint16 y, const Common::String &text) {
for (unsigned int i = 0; i < text.size(); i++) {
if (text[i] == ' ') {
x += 0x12;
continue;
}
Img letterShadow;
Img letter;
int letterId = letterIndexLookupTbl[text[i] - 45];
_letters.getImg(letterId, letterShadow, false);
_letters.getImg(letterId + 1, letter, false);
int w = letterWidthLookupTbl[letterId / 2];
letterShadow.drawAt(x, y, 2, w - 1); // TODO the original doesn't seem to need to override the width here.
letter.drawAt(x, y + 1, 3);
debug("%c %d %d %d", text[i], w, letter.getWidth(), letterShadow.getWidth());
x += w;
}
}
} // namespace Darkseed

View File

@@ -0,0 +1,39 @@
/* 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 DARKSEED_TITLEFONT_H
#define DARKSEED_TITLEFONT_H
#include "darkseed/anm.h"
namespace Darkseed {
class TitleFont {
Anm _letters;
public:
TitleFont();
void displayString(uint16 x, uint16 y, const Common::String &text);
};
} // namespace Darkseed
#endif // DARKSEED_TITLEFONT_H

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/file.h"
#include "darkseed/darkseed.h"
#include "darkseed/tostext.h"
namespace Darkseed {
bool TosText::load() {
if (g_engine->isDosDemo()) {
loadDemoTosEntries();
return true;
}
Common::File tostextFile;
if (!tostextFile.open("tostext.bin")) {
return false;
}
_numEntries = tostextFile.readUint16LE() / 2;
_textArray.resize(_numEntries);
for (int i = 0; i < _numEntries; i++) {
_textArray[i] = loadString(tostextFile, i);
}
return true;
}
void TosText::loadDemoTosEntries() {
_numEntries = 973;
_textArray.resize(_numEntries);
_textArray[10] = "My head is killing me.";
_textArray[11] = "My head feels like it is going to explode.";
_textArray[12] = "It seems like I've had this headache since I moved here.";
_textArray[13] = "I need an asprin or something.";
_textArray[798] = "You can't touch the horizon!";
_textArray[802] = "You see the unfortunate victims of the life leech.";
_textArray[972] = "Holding ";
}
const Common::U32String &TosText::getText(uint16 textIndex) {
assert(textIndex < _numEntries);
return _textArray[textIndex];
}
Common::U32String TosText::loadString(Common::File &file, uint16 index) const {
Common::U32String str;
file.seek(index * 2, SEEK_SET);
auto startOffset = file.readUint16LE();
uint16 strLen = index == _numEntries - 1
? (uint16)file.size() - startOffset
: file.readUint16LE() - startOffset;
file.seek(startOffset, SEEK_SET);
if (g_engine->getLanguage() == Common::KO_KOR || g_engine->getLanguage() == Common::ZH_ANY) {
// handle multi-byte languages
for (int i = 0; i < strLen; i++) {
uint8 byte = (char)file.readByte();
if (byte & 0x80) {
if (i < strLen - 1) {
uint8 byte2 = (char)file.readByte();
str += (int)byte << 8 | byte2;
i++;
}
} else {
str += byte;
}
}
} else {
for (int i = 0; i < strLen; i++) {
str += (char)file.readByte();
}
}
return str;
}
uint16 TosText::getNumEntries() const {
return _numEntries;
}
} // namespace Darkseed

View File

@@ -0,0 +1,48 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef DARKSEED_TOSTEXT_H
#define DARKSEED_TOSTEXT_H
#include "common/array.h"
#include "common/file.h"
#include "common/str.h"
namespace Darkseed {
class TosText {
Common::U32StringArray _textArray;
uint16 _numEntries = 0;
public:
bool load();
uint16 getNumEntries() const;
const Common::U32String &getText(uint16 textIndex);
private:
Common::U32String loadString(Common::File &file, uint16 index) const;
void loadDemoTosEntries();
};
} // namespace Darkseed
#endif // DARKSEED_TOSTEXT_H

1763
engines/darkseed/usecode.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,98 @@
/* 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 DARKSEED_USECODE_H
#define DARKSEED_USECODE_H
#include "common/scummsys.h"
#include "darkseed/console.h"
#include "darkseed/inventory.h"
#include "darkseed/objects.h"
#include "darkseed/player.h"
namespace Darkseed {
class UseCode {
Console *_console = nullptr;
Player *_player = nullptr;
Objects &_objectVar;
Inventory &_inventory;
uint8 _genericResponseCounter = 0;
public:
UseCode(Console *console, Player *player, Objects &objectVar, Inventory &inventory);
void useCode(int objNum);
void useCodeGloves(int16 targetObjNum);
void useCodeMoney(int16 targetObjNum);
void useCodeJournal(int16 actionObjNum, int16 targetObjNum);
void useCodeShopItems(int16 actionObjNum, int16 targetObjNum);
void useCrowBar(int16 targetObjNum);
void useCodeNewspaper(int16 targetObjNum);
void useCodeLibraryCard(int16 targetObjNum);
void useCodeBobbyPin(int16 targetObjNum);
void useCodeKeys(int16 actionObjNum, int16 targetObjNum);
void useCodeMirrorShard(int16 targetObjNum);
void useCodeBinoculars(int16 targetObjNum);
void useCodeShovel(int16 targetObjNum);
void useCodeDelbertsCard(int16 targetObjNum);
void useCodeStick(int16 targetObjNum);
void useCodeAxeHandle(int16 targetObjNum);
void useCodeRope(int16 targetObjNum);
void useCodeMicroFilm(int16 targetObjNum);
void useCodeSpecialHammer(int16 actionObjNum, int16 targetObjNum);
void useCodeGun(int16 targetObjNum);
void useCodeMoversNote(int16 targetObjNum);
void useCodeBluePrints(int16 targetObjNum);
void useCodeWatch(int16 targetObjNum);
void useCodeTinCup(int16 targetObjNum);
void useCodeEmptyUrn(int16 targetObjNum);
void genericResponse(int16 useObjNum, int16 targetObjNum, int16 tosIdx);
private:
int getHandDescriptionTosIdx(uint16 objNum);
int16 getUseGlovesTosIdx(uint16 objNum);
int16 getUseMoneyTosIdx(uint16 objNum);
int16 getUseCrowbarTosIdx(uint16 objNum);
int16 getUseJournalTosIdx(uint16 objNum);
int16 getUseLibraryCardTosIdx(uint16 objNum);
int16 getUseBobbyPinTosIdx(uint16 objNum);
int16 getUseKeysTosIdx(uint16 objNum);
int16 getUseBinocularsTosIdx(uint16 objNum);
int16 getUseShovelTosIdx(uint16 objNum);
int16 getUseDelbertsCardTosIdx(uint16 objNum);
int16 getUseStickTosIdx(uint16 objNum);
int16 getUseAxeHandleTosIdx(uint16 objNum);
int16 getUseRopeTosIdx(uint16 objNum);
int16 getUseMicroFilmTosIdx(uint16 objNum);
int16 getUseSpecialHammerTosIdx(uint16 objNum);
int16 getUseGunTosIdx(uint16 objNum);
int16 getUseWatchTosIdx(uint16 objNum);
void putObjUnderPillow(int objNum);
void startDigging(int16 targetObjNum);
void genericSingleObjectResponse(const I18nText &text, int16 useObjNum, KoreanObjectSuffixType krObjSuffixType=KoreanObjectSuffixType::None);
};
} // namespace Darkseed
#endif // DARKSEED_USECODE_H

View File

@@ -0,0 +1,982 @@
/* 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 DARKSEED_USECODE_TOS_TABLES_H
#define DARKSEED_USECODE_TOS_TABLES_H
namespace Darkseed {
static constexpr uint16 handDescriptionsTbl[] = {
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
791, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 652, 0,
729, 0, 0, 0,
0, 0, 0, 852,
0, 0, 0, 772,
0, 0, 846, 0,
0, 453, 0, 0,
484, 0, 0, 0,
0, 0, 0, 706,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 609,
611, 607, 605, 603,
601, 599, 597, 595,
593, 591, 589, 0,
0, 0, 0, 355,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 827,
839, 0, 798, 807,
803, 441, 449, 118,
260, 272, 306, 91,
162, 137, 377, 399,
415, 475, 478, 481,
0, 0, 999, 253,
0, 0, 171, 183,
213, 220, 285, 316,
329, 338, 338, 357,
516, 527, 534, 0,
548, 562, 571, 576,
614, 616, 621, 625,
637, 0, 642, 644,
646, 0, 674, 678,
681, 684, 689, 0,
726, 747, 0, 760,
766, 781, 788, 819,
823, 825, 0, 0,
863, 0, 881, 888,
892, 901, 0, 0,
672, 322, 164
};
static constexpr int16 glovesTextTbl[199] = {
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 525,
999, 999, 999, 999,
999, 84, 999, 745,
999, 999, 84, 999,
999, 84, 84, 999,
999, 999, 999, 999,
999, 999, 999, 84,
999, 999, 999, 999,
999, 999, 84, 999,
84, 999, 84, 49,
999, 84, 999, 999,
198, 487, 999, 773,
999, 0, 999, 84,
999, 84, 84, 999,
999, 999, 84, 84,
84, 999, 999, 84,
999, 999, 999, 999,
999, 999, 35, 999,
84, 999, 999, 999,
84, 84, 84, 84,
84, 84, 84, 84,
84, 84, 84, 84,
84, 84, 84, 999,
84, 84, 999, 999,
999, 999, 999, 999,
386, 84, 392, 84,
996, 84, 999, 981,
999, 796, 999, 808,
804, 84, 84, 84,
999, 84, 84, 84,
999, 998, 991, 84,
84, 999, 84, 84,
999, 84, 999, 84,
999, 999, 84, 185,
84, 84, 84, 84,
84, 84, 84, 999,
999, 84, 84, 999,
84, 999, 84, 84,
84, 84, 999, 999,
999, 999, 999, 999,
999, 999, 84, 84,
84, 84, 84, 84,
84, 84, 999, 999,
768, 999, 999, 820,
84, 84, 999, 999,
999, 999, 999, 84,
84, 999, 84, 84,
84, 84, 999
};
static constexpr int16 moneyTextTbl[199] = {
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 169,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
961, 961, 961, 961,
961, 999, 999, 999,
999, 999, 543, 58,
872, 999, 999, 999,
206, 999, 999, 999,
999, 999, 999, 999,
999, 461, 999, 999,
999, 999, 999, 999,
999, 999, 999, 987,
999, 999, 999, 999,
999, 999, 999, 999,
560, 999, 999, 999,
999, 436, 999, 586,
586, 586, 586, 586,
586, 586, 586, 586,
586, 586, 586, 999,
156, 986, 999, 999,
999, 999, 999, 999,
386, 992, 392, 999,
996, 0, 999, 999,
999, 999, 999, 999,
804, 990, 999, 132,
999, 281, 86, 111,
999, 998, 991, 402,
417, 999, 999, 999,
999, 303, 0, 999,
999, 999, 999, 190,
217, 227, 290, 999,
999, 999, 999, 999,
906, 531, 999, 999,
550, 999, 573, 577,
999, 999, 999, 633,
633, 633, 633, 633,
633, 999, 999, 999,
988, 687, 999, 999,
999, 999, 999, 999,
999, 785, 999, 999,
980, 982, 999, 999,
865, 999, 885, 999,
999, 999, 989, 999,
999, 326, 999
};
static constexpr int16 crowBarTextTbl[199] = {
999, 999, 999, 999,
999, 999, 999, 518,
999, 999, 999, 524,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
518, 518, 518, 518,
518, 757, 0, 999,
999, 999, 999, 51,
999, 249, 999, 999,
999, 485, 999, 999,
999, 999, 979, 411,
999, 456, 999, 999,
999, 999, 470, 470,
470, 999, 999, 987,
999, 999, 999, 999,
999, 999, 36, 832,
999, 999, 999, 999,
999, 433, 999, 583,
583, 583, 583, 583,
583, 583, 583, 583,
583, 583, 583, 999,
999, 986, 999, 360,
999, 999, 999, 999,
386, 992, 392, 999,
996, 753, 984, 981,
999, 999, 999, 808,
804, 990, 999, 999,
22, 67, 85, 100,
999, 998, 379, 999,
999, 999, 479, 479,
999, 995, 999, 999,
999, 999, 173, 999,
215, 224, 286, 999,
330, 342, 349, 360,
999, 999, 999, 999,
550, 999, 999, 579,
999, 999, 999, 628,
628, 628, 628, 628,
628, 999, 999, 999,
988, 686, 999, 999,
999, 999, 999, 762,
999, 782, 999, 983,
980, 982, 999, 999,
999, 999, 999, 999,
999, 999, 989, 999,
999, 324, 999
};
static constexpr int16 journalTextTbl[199] = {
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 486, 999,
868, 999, 999, 999,
196, 486, 999, 999,
999, 999, 999, 999,
999, 455, 999, 999,
999, 999, 999, 999,
999, 999, 999, 987,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 986, 999, 999,
999, 999, 999, 999,
386, 992, 392, 999,
996, 999, 999, 981,
999, 999, 999, 999,
804, 999, 999, 999,
999, 999, 82, 93,
999, 998, 991, 999,
999, 999, 999, 999,
999, 299, 999, 999,
999, 999, 999, 999,
999, 222, 146, 309,
999, 999, 999, 999,
486, 999, 999, 999,
550, 999, 999, 577,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
988, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
980, 982, 999, 864,
999, 999, 999, 999,
999, 999, 989, 999,
999, 994, 999
};
static constexpr int16 libraryCardTextTbl[199] = {
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 0, 56,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
559, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
154, 999, 999, 999,
999, 999, 999, 999,
386, 992, 392, 999,
996, 0, 999, 981,
999, 999, 999, 999,
804, 443, 999, 999,
999, 999, 999, 999,
999, 998, 991, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 177, 999,
999, 222, 288, 309,
999, 999, 999, 999,
519, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 499, 999,
999, 993, 999
};
static constexpr int16 bobbyPinTextTbl[199] = {
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 50,
0, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 457, 999, 999,
999, 999, 999, 999,
999, 999, 999, 987,
999, 999, 999, 999,
999, 999, 999, 999,
555, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 986, 999, 999,
999, 999, 999, 999,
386, 992, 392, 999,
996, 0, 999, 981,
999, 999, 999, 808,
804, 990, 999, 125,
999, 66, 999, 99,
999, 998, 991, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 223, 999, 999,
332, 999, 999, 999,
999, 999, 999, 999,
550, 999, 999, 577,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 676, 999,
988, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
980, 982, 999, 999,
999, 999, 0, 999,
999, 999, 989, 999,
999, 993, 999
};
static constexpr int16 keysTextTbl[199] = {
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
871, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 413,
999, 999, 999, 999,
999, 999, 472, 472,
472, 999, 999, 0,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 435, 716, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 986, 999, 365,
999, 999, 999, 999,
386, 992, 392, 999,
996, 999, 999, 981,
999, 999, 999, 808,
804, 990, 999, 999,
25, 999, 999, 108,
999, 998, 381, 999,
416, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
336, 999, 999, 365,
999, 999, 999, 999,
550, 999, 999, 577,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
988, 999, 999, 999,
999, 999, 999, 999,
999, 784, 784, 999,
980, 982, 999, 999,
999, 999, 884, 999,
999, 999, 989, 999,
999, 993, 999
};
static constexpr int16 binocularsTextTbl[199] = {
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 53,
999, 999, 999, 999,
200, 999, 999, 999,
999, 999, 999, 999,
999, 459, 999, 999,
999, 999, 999, 999,
999, 999, 999, 987,
999, 999, 999, 999,
999, 999, 38, 999,
556, 999, 999, 999,
999, 999, 999, 584,
584, 584, 584, 584,
584, 584, 584, 584,
584, 584, 584, 999,
150, 986, 999, 361,
999, 999, 999, 999,
386, 192, 392, 999,
996, 753, 999, 999,
999, 999, 799, 999,
804, 990, 999, 127,
23, 69, 999, 103,
127, 998, 991, 401,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 174, 187,
999, 225, 287, 999,
334, 343, 343, 999,
999, 530, 999, 999,
550, 999, 999, 577,
999, 999, 623, 630,
630, 630, 630, 630,
630, 999, 999, 999,
988, 999, 999, 999,
999, 999, 999, 763,
999, 999, 999, 999,
980, 982, 999, 999,
999, 999, 999, 999,
999, 999, 498, 999,
999, 993, 998
};
static constexpr int16 shovelTextTbl[199] = {
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 524,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 48,
999, 999, 627, 999,
999, 485, 999, 999,
999, 999, 979, 411,
999, 456, 999, 999,
999, 999, 470, 470,
470, 999, 999, 987,
999, 999, 999, 999,
999, 999, 34, 832,
999, 999, 999, 999,
999, 999, 999, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 999,
147, 986, 999, 359,
999, 999, 999, 999,
386, 992, 392, 999,
996, 753, 999, 981,
841, 999, 999, 808,
804, 990, 999, 123,
21, 65, 310, 97,
999, 998, 991, 999,
999, 999, 999, 999,
999, 995, 999, 999,
999, 999, 999, 999,
999, 999, 286, 999,
330, 341, 348, 359,
999, 999, 999, 999,
550, 999, 572, 578,
999, 999, 999, 627,
627, 627, 627, 627,
627, 999, 999, 999,
988, 686, 999, 999,
999, 999, 999, 762,
999, 999, 999, 983,
980, 982, 999, 999,
999, 999, 999, 999,
999, 999, 498, 999,
999, 323, 999
};
static constexpr int16 delbertsCardTextTbl[199] = {
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 168,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 541, 52,
999, 999, 999, 999,
999, 0, 999, 999,
999, 999, 999, 999,
999, 455, 999, 999,
0, 999, 999, 999,
999, 999, 999, 987,
999, 999, 999, 999,
999, 999, 37, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
149, 986, 999, 999,
999, 999, 999, 999,
386, 992, 392, 999,
996, 754, 999, 981,
999, 999, 999, 999,
805, 990, 999, 999,
999, 999, 999, 101,
999, 998, 991, 999,
999, 999, 999, 482,
999, 999, 999, 999,
999, 999, 999, 999,
999, 222, 999, 309,
999, 999, 999, 999,
999, 999, 999, 999,
550, 999, 999, 577,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
988, 999, 999, 999,
999, 999, 999, 999,
999, 783, 999, 999,
980, 982, 999, 999,
999, 999, 999, 999,
999, 999, 989, 999,
999, 993, 999
};
static constexpr int16 stickTextTbl[199] = {
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 524,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 0, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
201, 489, 999, 999,
999, 999, 979, 411,
999, 456, 999, 999,
999, 999, 470, 470,
470, 999, 999, 987,
999, 999, 999, 999,
999, 999, 39, 832,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
147, 986, 999, 999,
999, 999, 999, 999,
386, 992, 392, 999,
996, 753, 984, 981,
843, 999, 999, 808,
804, 990, 999, 128,
999, 70, 999, 104,
999, 998, 991, 999,
999, 999, 479, 479,
999, 999, 999, 995,
999, 999, 175, 188,
216, 999, 999, 999,
330, 999, 999, 999,
999, 999, 999, 999,
550, 999, 999, 577,
999, 999, 999, 631,
631, 631, 631, 631,
631, 999, 999, 999,
988, 686, 999, 999,
999, 999, 999, 999,
999, 999, 999, 983,
980, 982, 999, 999,
999, 999, 999, 999,
999, 999, 989, 999,
999, 325, 999
};
static constexpr int16 axeHandleTextTbl[199] = {
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 984, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
199, 489, 999, 999,
999, 999, 979, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 987,
999, 999, 999, 999,
999, 999, 999, 832,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 986, 999, 999,
999, 999, 999, 999,
386, 992, 392, 999,
996, 753, 999, 981,
832, 999, 999, 808,
804, 990, 999, 126,
999, 68, 999, 102,
999, 998, 380, 999,
999, 999, 479, 999,
999, 999, 999, 999,
999, 999, 999, 186,
999, 999, 999, 999,
333, 999, 999, 999,
999, 529, 999, 999,
550, 999, 999, 577,
999, 999, 999, 629,
629, 629, 629, 629,
629, 999, 999, 999,
988, 686, 999, 999,
999, 999, 999, 999,
999, 999, 999, 983,
980, 982, 999, 999,
999, 999, 999, 999,
999, 999, 989, 999,
999, 993, 999
};
static constexpr int16 ropeTextTbl[199] = {
0, 0, 0, 0,
0, 0, 0, 999,
0, 999, 0, 523,
0, 0, 999, 0,
793, 999, 0, 999,
0, 0, 999, 0,
0, 999, 699, 0,
0, 0, 0, 0,
0, 0, 999, 999,
999, 999, 999, 999,
999, 0, 0, 0,
999, 0, 999, 45,
0, 0, 0, 0,
195, 485, 0, 999,
0, 999, 847, 410,
0, 454, 999, 0,
0, 0, 469, 469,
469, 0, 0, 987,
0, 0, 0, 0,
0, 0, 999, 831,
999, 0, 0, 0,
0, 0, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 0,
144, 986, 0, 358,
999, 999, 999, 999,
386, 992, 392, 990,
996, 753, 999, 981,
840, 999, 0, 808,
804, 990, 450, 121,
261, 63, 81, 92,
121, 998, 378, 400,
999, 999, 999, 999,
0, 297, 0, 999,
999, 0, 999, 184,
214, 221, 999, 999,
999, 339, 339, 358,
999, 528, 999, 0,
549, 0, 999, 577,
999, 617, 999, 626,
0, 0, 0, 0,
0, 0, 675, 999,
988, 999, 999, 999,
999, 999, 0, 761,
767, 999, 999, 999,
980, 982, 0, 0,
0, 0, 0, 999,
999, 0, 989, 999,
999, 999, 0
};
static constexpr int16 microFilmTextTbl[199] = {
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
870, 999, 999, 999,
999, 999, 999, 774,
999, 999, 999, 999,
999, 455, 999, 999,
999, 999, 999, 999,
999, 999, 999, 987,
999, 999, 999, 999,
999, 999, 999, 999,
0, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
152, 986, 999, 999,
999, 999, 999, 999,
386, 992, 392, 999,
996, 0, 999, 981,
999, 999, 999, 999,
804, 990, 999, 999,
999, 71, 999, 999,
105, 998, 991, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
335, 999, 999, 999,
999, 999, 999, 999,
550, 999, 999, 577,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
988, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
980, 982, 999, 999,
864, 999, 999, 999,
999, 999, 989, 999,
999, 993, 999
};
static constexpr int16 specialHammerTextTbl[199] = {
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 757, 999, 999,
999, 999, 999, 55,
999, 999, 999, 999,
203, 489, 999, 999,
999, 999, 979, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 987,
999, 999, 999, 999,
999, 999, 999, 832,
558, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 986, 999, 363,
999, 999, 999, 999,
387, 992, 392, 999,
996, 753, 984, 904,
999, 999, 999, 808,
805, 990, 999, 130,
999, 72, 999, 107,
999, 998, 991, 999,
999, 999, 999, 999,
999, 0, 999, 999,
999, 999, 176, 999,
216, 999, 999, 999,
330, 344, 351, 999,
999, 999, 999, 999,
550, 999, 999, 577,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
988, 999, 999, 999,
999, 999, 999, 762,
999, 999, 999, 983,
980, 982, 999, 999,
999, 999, 999, 889,
999, 999, 989, 999,
999, 324, 999
};
static constexpr int16 gunTextTbl[199] = {
19, 19, 19, 19,
19, 19, 19, 19,
19, 19, 19, 19,
19, 19, 19, 19,
999, 999, 19, 999,
19, 19, 999, 19,
19, 999, 19, 19,
19, 19, 19, 19,
19, 19, 19, 999,
19, 19, 19, 19,
19, 757, 19, 19,
999, 19, 19, 19,
19, 19, 19, 19,
19, 485, 19, 999,
540, 999, 848, 19,
19, 19, 999, 19,
19, 19, 19, 19,
19, 19, 19, 987,
19, 19, 19, 19,
19, 19, 19, 19,
19, 19, 19, 19,
19, 19, 999, 19,
19, 19, 19, 19,
19, 19, 19, 19,
19, 19, 19, 19,
19, 986, 19, 19,
19, 19, 19, 19,
350, 992, 392, 999,
996, 753, 19, 19,
19, 999, 999, 19,
19, 990, 19, 19,
19, 19, 19, 19,
19, 998, 991, 19,
19, 999, 999, 19,
19, 19, 19, 999,
19, 19, 19, 19,
19, 999, 19, 19,
19, 19, 19, 19,
19, 19, 19, 19,
19, 19, 19, 577,
19, 618, 622, 19,
19, 19, 19, 19,
19, 19, 19, 999,
19, 685, 999, 999,
999, 999, 19, 19,
19, 19, 999, 19,
19, 19, 19, 19,
19, 19, 19, 999,
999, 19, 497, 999,
999, 19, 19
};
static constexpr int16 watchTextTbl[199] = {
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 59,
873, 999, 999, 999,
210, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
160, 986, 999, 999,
999, 999, 999, 999,
386, 992, 392, 999,
996, 999, 999, 981,
999, 999, 999, 999,
804, 990, 999, 134,
27, 75, 87, 115,
134, 998, 991, 999,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 191,
999, 228, 932, 318,
999, 999, 999, 368,
999, 999, 999, 999,
999, 999, 999, 577,
999, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
988, 999, 999, 999,
999, 999, 999, 999,
999, 999, 999, 999,
980, 982, 999, 999,
866, 999, 999, 999,
999, 999, 999, 999,
999, 993, 998
};
} // namespace Darkseed
#endif // DARKSEED_USECODE_TOS_TABLES_H

View File

@@ -0,0 +1,133 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "darkseed/darkseed.h"
#include "darkseed/zhmenufont.h"
#include "graphics/fonts/dosfont.h"
namespace Darkseed {
ZhMenuFont::ZhMenuFont(const Common::Path &filename, ZhLargeFontType type) : _type(type) {
load(filename);
}
void ZhMenuFont::load(const Common::Path &filename) {
Common::File fontData;
if (!fontData.open(filename)) {
error("Error: failed to open zhmenufont_game.dat");
}
int numGlyphs = fontData.size()/74;
_glyphs.resize(numGlyphs);
for (int i = 0; i < numGlyphs; i++) {
_glyphs[i].charIdx = fontData.readUint16BE();
fontData.read(_glyphs[i].pixels, 72);
}
fontData.close();
}
const ZhLargeFontGlyph *ZhMenuFont::getGlyph(uint32 chr) const {
for (auto &glyph : _glyphs) {
if (glyph.charIdx == chr) {
return &glyph;
}
}
return nullptr;
}
int ZhMenuFont::getFontHeight() const {
return 24;
}
int ZhMenuFont::getMaxCharWidth() const {
return 24;
}
int ZhMenuFont::getCharWidth(uint32 chr) const {
auto glyph = getGlyph(chr);
return glyph ? getMaxCharWidth() : 8;
}
void ZhMenuFont::drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const {
auto glyph = getGlyph(chr);
if (glyph) {
if (_type == ZhLargeFontType::InGame) {
drawGlyph(glyph, x, y, color);
} else {
drawGlyph(glyph, x-1, y, 0);
drawGlyph(glyph, x, y+1, 0);
drawGlyph(glyph, x-1, y+1, 0);
drawGlyph(glyph, x, y, 0xd);
}
} else if (chr < 128) {
drawBiosFontGlyph(chr, x, y, 0);
drawBiosFontGlyph(chr, x+1, y, 0xd);
}
}
void ZhMenuFont::drawBiosFontGlyph(uint8 chr, int x, int y, uint8 color) const {
byte *ptr = (byte *)g_engine->_screen->getBasePtr(x, y);
int srcPixel = chr * 8;
int colorOffset = 1;
for (int sy = 0; sy < 8; sy++) {
for (int sx = 0; sx < 8; sx++) {
if (Graphics::DosFont::fontData_PCBIOS[srcPixel] & 1 << (7 - sx)) {
*ptr = color;
ptr[g_engine->_screen->pitch] = color;
ptr[g_engine->_screen->pitch * 2] = color;
color += colorOffset;
colorOffset = -colorOffset;
}
ptr++;
}
srcPixel++;
ptr -= 8;
ptr += (g_engine->_screen->pitch * 3);
}
}
void ZhMenuFont::drawGlyph(const ZhLargeFontGlyph *glyph, int x, int y, uint8 color) const {
byte *ptr = (byte *)g_engine->_screen->getBasePtr(x, y);
int srcPixel = 0;
int sByteOffset = 0;
int colorOffset = _type == ZhLargeFontType::InGame ? 0 : 1;
for (int sy = 0; sy < getFontHeight(); sy++) {
for (int sx = 0; sx < getMaxCharWidth(); sx++) {
if (glyph->pixels[srcPixel] & 1 << (7 - sByteOffset)) {
*ptr = (uint8)color;
color += colorOffset;
colorOffset = -colorOffset;
}
sByteOffset++;
if (sByteOffset == 8) {
sByteOffset = 0;
srcPixel++;
}
ptr++;
}
ptr -= getMaxCharWidth();
ptr += g_engine->_screen->pitch;
}
}
} // namespace Darkseed

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 DARKSEED_ZHMENUFONT_H
#define DARKSEED_ZHMENUFONT_H
#include "common/array.h"
#include "graphics/font.h"
#include "graphics/surface.h"
namespace Darkseed {
struct ZhLargeFontGlyph {
uint16 charIdx;
uint8 pixels[72]; // 24x24 pixels @ 1bpp
};
enum class ZhLargeFontType {
InGame,
Titles
};
class ZhMenuFont : public Graphics::Font {
private:
ZhLargeFontType _type = ZhLargeFontType::InGame;
Common::Array<ZhLargeFontGlyph> _glyphs;
public:
ZhMenuFont(const Common::Path &filename, ZhLargeFontType type);
int getFontHeight() const override;
int getMaxCharWidth() const override;
int getCharWidth(uint32 chr) const override;
void drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const override;
private:
void load(const Common::Path &filename);
const ZhLargeFontGlyph *getGlyph(uint32 chr) const;
void drawBiosFontGlyph(uint8 chr, int x, int y, uint8 color) const;
void drawGlyph(const ZhLargeFontGlyph *glyph, int x, int y, uint8 color) const;
};
} // namespace Darkseed
#endif // DARKSEED_ZHMENUFONT_H