Initial commit
This commit is contained in:
2
engines/parallaction/POTFILES
Normal file
2
engines/parallaction/POTFILES
Normal file
@@ -0,0 +1,2 @@
|
||||
engines/parallaction/metaengine.cpp
|
||||
engines/parallaction/saveload.cpp
|
||||
830
engines/parallaction/adlib.cpp
Normal file
830
engines/parallaction/adlib.cpp
Normal file
@@ -0,0 +1,830 @@
|
||||
/* 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/system.h"
|
||||
|
||||
#include "audio/fmopl.h"
|
||||
#include "audio/mpu401.h"
|
||||
#include "audio/mididrv.h"
|
||||
|
||||
namespace Parallaction {
|
||||
|
||||
const uint kNumVoices = 9;
|
||||
// adlib FM voices 0-5
|
||||
const uint kNumMelodic = 6;
|
||||
// adlib FM voice 6 and 7-8
|
||||
const uint kNumPercussion = 5;
|
||||
|
||||
// mask for maximum volume level
|
||||
#define LEVEL_MASK 0x7f
|
||||
|
||||
struct OPLOperator {
|
||||
uint8 characteristic; // amplitude modulation, vibrato, envelope, keyboard scaling, modulator frequency
|
||||
uint8 levels;
|
||||
uint8 attackDecay;
|
||||
uint8 sustainRelease;
|
||||
uint8 waveform;
|
||||
};
|
||||
|
||||
struct MelodicProgram {
|
||||
OPLOperator op[2];
|
||||
uint8 feedbackAlgo;
|
||||
};
|
||||
|
||||
struct PercussionNote {
|
||||
OPLOperator op[2];
|
||||
uint8 feedbackAlgo;
|
||||
uint8 percussion;
|
||||
uint8 valid;
|
||||
uint16 frequency;
|
||||
uint8 octave;
|
||||
};
|
||||
|
||||
static const MelodicProgram melodicPrograms[128] = {
|
||||
{{{ 0x1, 0x51, 0xf2, 0xb2, 0x0 }, { 0x11, 0x0, 0xf2, 0xa2, 0x0 }}, 0x0 },
|
||||
{{{ 0xc2, 0x4b, 0xf1, 0x53, 0x0 }, { 0xd2, 0x0, 0xf2, 0x74, 0x0 }}, 0x4 },
|
||||
{{{ 0x81, 0x9d, 0xf2, 0x74, 0x0 }, { 0x13, 0x0, 0xf2, 0xf1, 0x0 }}, 0x6 },
|
||||
{{{ 0x3, 0x4f, 0xf1, 0x53, 0x0 }, { 0x17, 0x3, 0xf2, 0x74, 0x0 }}, 0x6 },
|
||||
{{{ 0xd1, 0x81, 0x81, 0x73, 0x2 }, { 0xd4, 0x0, 0xe1, 0x34, 0x0 }}, 0x3 },
|
||||
{{{ 0x1, 0x0, 0x94, 0xa6, 0x0 }, { 0x2, 0x0, 0x83, 0x26, 0x0 }}, 0x1 },
|
||||
{{{ 0xf3, 0x84, 0x81, 0x2, 0x1 }, { 0x55, 0x80, 0xdd, 0x3, 0x0 }}, 0x4 },
|
||||
{{{ 0x5, 0x8a, 0xf2, 0x26, 0x0 }, { 0x1, 0x80, 0xf3, 0x48, 0x0 }}, 0x0 },
|
||||
{{{ 0x32, 0x0, 0xb1, 0x14, 0x0 }, { 0x12, 0x0, 0xfd, 0x36, 0x0 }}, 0x3 },
|
||||
{{{ 0x1, 0x0, 0x82, 0xa, 0x2 }, { 0x2, 0x0, 0x85, 0x15, 0x0 }}, 0x3 },
|
||||
{{{ 0xd1, 0x1, 0x97, 0xaa, 0x0 }, { 0x4, 0xd, 0xf3, 0xa5, 0x1 }}, 0x9 },
|
||||
{{{ 0x17, 0x0, 0xf2, 0x62, 0x0 }, { 0x12, 0x0, 0xf2, 0x72, 0x0 }}, 0x8 },
|
||||
{{{ 0x6, 0x0, 0xff, 0xf4, 0x0 }, { 0xc4, 0x0, 0xf8, 0xb5, 0x0 }}, 0xe },
|
||||
{{{ 0xc0, 0x81, 0xf2, 0x13, 0x2 }, { 0xc0, 0xc1, 0xf3, 0x14, 0x2 }}, 0xb },
|
||||
{{{ 0x44, 0x53, 0xf5, 0x31, 0x0 }, { 0x60, 0x80, 0xfd, 0x22, 0x0 }}, 0x6 },
|
||||
{{{ 0xe0, 0x80, 0xf4, 0xf2, 0x0 }, { 0x61, 0x0, 0xf2, 0x6, 0x0 }}, 0x8 },
|
||||
{{{ 0xc1, 0x6, 0x83, 0x23, 0x0 }, { 0xc1, 0x4, 0xf0, 0x26, 0x0 }}, 0x1 },
|
||||
{{{ 0x26, 0x0, 0xf4, 0xb6, 0x0 }, { 0x21, 0x0, 0x81, 0x4b, 0x0 }}, 0x1 },
|
||||
{{{ 0x24, 0x80, 0xff, 0xf, 0x0 }, { 0x21, 0x80, 0xff, 0xf, 0x0 }}, 0x1 },
|
||||
{{{ 0x24, 0x4f, 0xf2, 0xb, 0x0 }, { 0x31, 0x0, 0x52, 0xb, 0x0 }}, 0xb },
|
||||
{{{ 0x31, 0x8, 0x81, 0xb, 0x0 }, { 0xa1, 0x80, 0x92, 0x3b, 0x0 }}, 0x0 },
|
||||
{{{ 0x70, 0xc5, 0x52, 0x11, 0x1 }, { 0x71, 0x80, 0x31, 0xfe, 0x1 }}, 0x0 },
|
||||
{{{ 0x51, 0x88, 0x10, 0xf0, 0x0 }, { 0x42, 0x83, 0x40, 0xfc, 0x0 }}, 0x8 },
|
||||
{{{ 0xf0, 0xd9, 0x81, 0x3, 0x0 }, { 0xb1, 0x80, 0xf1, 0x5, 0x0 }}, 0xa },
|
||||
{{{ 0x21, 0x4f, 0xf1, 0x31, 0x0 }, { 0x2, 0x80, 0xc3, 0x45, 0x0 }}, 0x0 },
|
||||
{{{ 0x7, 0x8f, 0x9c, 0x33, 0x1 }, { 0x1, 0x80, 0x8a, 0x13, 0x0 }}, 0x0 },
|
||||
{{{ 0x21, 0x40, 0xf1, 0x31, 0x0 }, { 0x6, 0x80, 0xf4, 0x44, 0x0 }}, 0x0 },
|
||||
{{{ 0x21, 0x40, 0xf1, 0x31, 0x3 }, { 0x81, 0x0, 0xf4, 0x44, 0x2 }}, 0x2 },
|
||||
{{{ 0x11, 0x8d, 0xfd, 0x11, 0x0 }, { 0x11, 0x80, 0xfd, 0x11, 0x0 }}, 0x8 },
|
||||
{{{ 0xf0, 0x1, 0x97, 0x17, 0x0 }, { 0x21, 0xd, 0xf1, 0x18, 0x0 }}, 0x8 },
|
||||
{{{ 0xf1, 0x1, 0x97, 0x17, 0x0 }, { 0x21, 0xd, 0xf1, 0x18, 0x0 }}, 0x8 },
|
||||
{{{ 0xcd, 0x9e, 0x55, 0xd1, 0x0 }, { 0xd1, 0x0, 0xf2, 0x71, 0x0 }}, 0xe },
|
||||
{{{ 0x1, 0x0, 0xf2, 0x88, 0x0 }, { 0x1, 0x0, 0xf5, 0x88, 0x0 }}, 0x1 },
|
||||
{{{ 0x30, 0xd, 0xf2, 0xef, 0x0 }, { 0x21, 0x0, 0xf5, 0x78, 0x0 }}, 0x6 },
|
||||
{{{ 0x0, 0x10, 0xf4, 0xd9, 0x0 }, { 0x0, 0x0, 0xf5, 0xd7, 0x0 }}, 0x4 },
|
||||
{{{ 0x1, 0x4c, 0xf2, 0x50, 0x0 }, { 0x1, 0x40, 0xd2, 0x59, 0x0 }}, 0x8 },
|
||||
{{{ 0x20, 0x11, 0xe2, 0x8a, 0x0 }, { 0x20, 0x0, 0xe4, 0xa8, 0x0 }}, 0xa },
|
||||
{{{ 0x21, 0x40, 0x7b, 0x4, 0x1 }, { 0x21, 0x0, 0x75, 0x72, 0x0 }}, 0x2 },
|
||||
{{{ 0x31, 0xd, 0xf2, 0xef, 0x0 }, { 0x21, 0x0, 0xf5, 0x78, 0x0 }}, 0xa },
|
||||
{{{ 0x1, 0xc, 0xf5, 0x2f, 0x1 }, { 0x0, 0x80, 0xf5, 0x5c, 0x0 }}, 0x0 },
|
||||
{{{ 0xb0, 0x1c, 0x81, 0x3, 0x2 }, { 0x20, 0x0, 0x54, 0x67, 0x2 }}, 0xe },
|
||||
{{{ 0x1, 0x0, 0xf1, 0x65, 0x0 }, { 0x1, 0x80, 0xa3, 0xa8, 0x2 }}, 0x1 },
|
||||
{{{ 0xe1, 0x4f, 0xc1, 0xd3, 0x2 }, { 0x21, 0x0, 0x32, 0x74, 0x1 }}, 0x0 },
|
||||
{{{ 0x2, 0x0, 0xf6, 0x16, 0x0 }, { 0x12, 0x0, 0xf2, 0xf8, 0x0 }}, 0x1 },
|
||||
{{{ 0xe0, 0x63, 0xf8, 0xf3, 0x0 }, { 0x70, 0x80, 0xf7, 0xf3, 0x0 }}, 0x4 },
|
||||
{{{ 0x1, 0x6, 0xf3, 0xff, 0x0 }, { 0x8, 0x0, 0xf7, 0xff, 0x0 }}, 0x4 },
|
||||
{{{ 0x21, 0x16, 0xb0, 0x81, 0x1 }, { 0x22, 0x0, 0xb3, 0x13, 0x1 }}, 0xc },
|
||||
{{{ 0x1, 0x4f, 0xf0, 0xff, 0x0 }, { 0x30, 0x0, 0x90, 0xf, 0x0 }}, 0x6 },
|
||||
{{{ 0x0, 0x10, 0xf1, 0xf2, 0x2 }, { 0x1, 0x0, 0xf1, 0xf2, 0x3 }}, 0x0 },
|
||||
{{{ 0x1, 0x4f, 0xf1, 0x50, 0x0 }, { 0x21, 0x80, 0xa3, 0x5, 0x3 }}, 0x6 },
|
||||
{{{ 0xb1, 0x3, 0x55, 0x3, 0x0 }, { 0xb1, 0x3, 0x8, 0xa, 0x0 }}, 0x9 },
|
||||
{{{ 0x22, 0x0, 0xa9, 0x34, 0x1 }, { 0x1, 0x0, 0xa2, 0x42, 0x2 }}, 0x2 },
|
||||
{{{ 0xa0, 0xdc, 0x81, 0x31, 0x3 }, { 0xb1, 0x80, 0xf1, 0x1, 0x3 }}, 0x0 },
|
||||
{{{ 0x1, 0x4f, 0xf1, 0x50, 0x0 }, { 0x21, 0x80, 0xa3, 0x5, 0x3 }}, 0x6 },
|
||||
{{{ 0xf1, 0x80, 0xa0, 0x72, 0x0 }, { 0x74, 0x0, 0x90, 0x22, 0x0 }}, 0x9 },
|
||||
{{{ 0xe1, 0x13, 0x71, 0xae, 0x0 }, { 0xe1, 0x0, 0xf0, 0xfc, 0x1 }}, 0xa },
|
||||
{{{ 0x31, 0x1c, 0x41, 0xb, 0x0 }, { 0xa1, 0x80, 0x92, 0x3b, 0x0 }}, 0xe },
|
||||
{{{ 0x71, 0x1c, 0x41, 0x1f, 0x0 }, { 0xa1, 0x80, 0x92, 0x3b, 0x0 }}, 0xe },
|
||||
{{{ 0x21, 0x1c, 0x53, 0x1d, 0x0 }, { 0xa1, 0x80, 0x52, 0x3b, 0x0 }}, 0xc },
|
||||
{{{ 0x21, 0x1d, 0xa4, 0xae, 0x1 }, { 0x21, 0x0, 0xb1, 0x9e, 0x0 }}, 0xc },
|
||||
{{{ 0xe1, 0x16, 0x71, 0xae, 0x0 }, { 0xe1, 0x0, 0x81, 0x9e, 0x0 }}, 0xa },
|
||||
{{{ 0xe1, 0x15, 0x71, 0xae, 0x0 }, { 0xe2, 0x0, 0x81, 0x9e, 0x0 }}, 0xe },
|
||||
{{{ 0x21, 0x16, 0x71, 0xae, 0x0 }, { 0x21, 0x0, 0x81, 0x9e, 0x0 }}, 0xe },
|
||||
{{{ 0x71, 0x1c, 0x41, 0x1f, 0x0 }, { 0xa1, 0x80, 0x92, 0x3b, 0x0 }}, 0xe },
|
||||
{{{ 0x21, 0x4f, 0x81, 0x53, 0x0 }, { 0x32, 0x0, 0x22, 0x2c, 0x0 }}, 0xa },
|
||||
{{{ 0x22, 0x4f, 0x81, 0x53, 0x0 }, { 0x32, 0x0, 0x22, 0x2c, 0x0 }}, 0xa },
|
||||
{{{ 0x23, 0x4f, 0x81, 0x53, 0x0 }, { 0x34, 0x0, 0x22, 0x2c, 0x0 }}, 0xa },
|
||||
{{{ 0xe1, 0x16, 0x71, 0xae, 0x0 }, { 0xe1, 0x0, 0x81, 0x9e, 0x0 }}, 0xa },
|
||||
{{{ 0x71, 0xc5, 0x6e, 0x17, 0x0 }, { 0x22, 0x5, 0x8b, 0xe, 0x0 }}, 0x2 },
|
||||
{{{ 0xe6, 0x27, 0x70, 0xf, 0x1 }, { 0xe3, 0x0, 0x60, 0x9f, 0x0 }}, 0xa },
|
||||
{{{ 0x30, 0xc8, 0xd5, 0x19, 0x0 }, { 0xb1, 0x80, 0x61, 0x1b, 0x0 }}, 0xc },
|
||||
{{{ 0x32, 0x9a, 0x51, 0x1b, 0x0 }, { 0xa1, 0x82, 0xa2, 0x3b, 0x0 }}, 0xc },
|
||||
{{{ 0xad, 0x3, 0x74, 0x29, 0x0 }, { 0xa2, 0x82, 0x73, 0x29, 0x0 }}, 0x7 },
|
||||
{{{ 0x21, 0x83, 0x74, 0x17, 0x0 }, { 0x62, 0x8d, 0x65, 0x17, 0x0 }}, 0x7 },
|
||||
{{{ 0x94, 0xb, 0x85, 0xff, 0x1 }, { 0x13, 0x0, 0x74, 0xff, 0x0 }}, 0xc },
|
||||
{{{ 0x74, 0x87, 0xa4, 0x2, 0x0 }, { 0xd6, 0x80, 0x45, 0x42, 0x0 }}, 0x2 },
|
||||
{{{ 0xb3, 0x85, 0x76, 0x21, 0x1 }, { 0x20, 0x0, 0x3d, 0xc1, 0x0 }}, 0x6 },
|
||||
{{{ 0x17, 0x4f, 0xf2, 0x61, 0x0 }, { 0x12, 0x8, 0xf1, 0xb4, 0x0 }}, 0x8 },
|
||||
{{{ 0x4f, 0x86, 0x65, 0x1, 0x0 }, { 0x1f, 0x0, 0x32, 0x74, 0x0 }}, 0x4 },
|
||||
{{{ 0xe1, 0x23, 0x71, 0xae, 0x0 }, { 0xe4, 0x0, 0x82, 0x9e, 0x0 }}, 0xa },
|
||||
{{{ 0x11, 0x86, 0xf2, 0xbd, 0x0 }, { 0x4, 0x80, 0xa0, 0x9b, 0x1 }}, 0x8 },
|
||||
{{{ 0x20, 0x90, 0xf5, 0x9e, 0x2 }, { 0x11, 0x0, 0xf4, 0x5b, 0x3 }}, 0xc },
|
||||
{{{ 0xf0, 0x80, 0x34, 0xe4, 0x0 }, { 0x7e, 0x0, 0xa2, 0x6, 0x0 }}, 0x8 },
|
||||
{{{ 0x90, 0xf, 0xff, 0x1, 0x3 }, { 0x0, 0x0, 0x1f, 0x1, 0x0 }}, 0xe },
|
||||
{{{ 0x1, 0x4f, 0xf0, 0xff, 0x0 }, { 0x33, 0x0, 0x90, 0xf, 0x0 }}, 0x6 },
|
||||
{{{ 0x1e, 0x0, 0x1f, 0xf, 0x0 }, { 0x10, 0x0, 0x1f, 0x7f, 0x0 }}, 0x0 },
|
||||
{{{ 0xbe, 0x0, 0xf1, 0x1, 0x3 }, { 0x31, 0x0, 0xf1, 0x1, 0x0 }}, 0x4 },
|
||||
{{{ 0xbe, 0x0, 0xf1, 0x1, 0x3 }, { 0x31, 0x0, 0xf1, 0x1, 0x0 }}, 0x4 },
|
||||
{{{ 0x93, 0x6, 0xc1, 0x4, 0x1 }, { 0x82, 0x0, 0x51, 0x9, 0x0 }}, 0x6 },
|
||||
{{{ 0xa0, 0x0, 0x96, 0x33, 0x0 }, { 0x20, 0x0, 0x55, 0x2b, 0x0 }}, 0x6 },
|
||||
{{{ 0x0, 0xc0, 0xff, 0x5, 0x0 }, { 0x0, 0x0, 0xff, 0x5, 0x3 }}, 0x0 },
|
||||
{{{ 0x4, 0x8, 0xf8, 0x7, 0x0 }, { 0x1, 0x0, 0x82, 0x74, 0x0 }}, 0x8 },
|
||||
{{{ 0x0, 0x0, 0x2f, 0x5, 0x0 }, { 0x20, 0x0, 0xff, 0x5, 0x3 }}, 0xa },
|
||||
{{{ 0x93, 0x0, 0xf7, 0x7, 0x2 }, { 0x0, 0x0, 0xf7, 0x7, 0x0 }}, 0xa },
|
||||
{{{ 0x0, 0x40, 0x80, 0x7a, 0x0 }, { 0xc4, 0x0, 0xc0, 0x7e, 0x0 }}, 0x8 },
|
||||
{{{ 0x90, 0x80, 0x55, 0xf5, 0x0 }, { 0x0, 0x0, 0x55, 0xf5, 0x0 }}, 0x8 },
|
||||
{{{ 0xe1, 0x80, 0x34, 0xe4, 0x0 }, { 0x69, 0x0, 0xf2, 0x6, 0x0 }}, 0x8 },
|
||||
{{{ 0x3, 0x2, 0xf0, 0xff, 0x3 }, { 0x11, 0x80, 0xf0, 0xff, 0x2 }}, 0x2 },
|
||||
{{{ 0x1e, 0x0, 0x1f, 0xf, 0x0 }, { 0x10, 0x0, 0x1f, 0x7f, 0x0 }}, 0x0 },
|
||||
{{{ 0x0, 0x0, 0x2f, 0x1, 0x0 }, { 0x0, 0x0, 0xff, 0x1, 0x0 }}, 0x4 },
|
||||
{{{ 0xbe, 0x0, 0xf1, 0x1, 0x3 }, { 0x31, 0x0, 0xf1, 0x1, 0x0 }}, 0x4 },
|
||||
{{{ 0x93, 0x85, 0x3f, 0x6, 0x1 }, { 0x0, 0x0, 0x5f, 0x7, 0x0 }}, 0x6 },
|
||||
{{{ 0x6, 0x0, 0xa0, 0xf0, 0x0 }, { 0x44, 0x0, 0xc5, 0x75, 0x0 }}, 0xe },
|
||||
{{{ 0x60, 0x0, 0x10, 0x81, 0x0 }, { 0x20, 0x8c, 0x12, 0x91, 0x0 }}, 0xe },
|
||||
{{{ 0x1, 0x40, 0xf1, 0x53, 0x0 }, { 0x8, 0x40, 0xf1, 0x53, 0x0 }}, 0x0 },
|
||||
{{{ 0x31, 0x0, 0x56, 0x31, 0x0 }, { 0x16, 0x0, 0x7d, 0x41, 0x0 }}, 0x0 },
|
||||
{{{ 0x0, 0x10, 0xf2, 0x72, 0x0 }, { 0x13, 0x0, 0xf2, 0x72, 0x0 }}, 0xc },
|
||||
{{{ 0x10, 0x0, 0x75, 0x93, 0x1 }, { 0x1, 0x0, 0xf5, 0x82, 0x1 }}, 0x0 },
|
||||
{{{ 0x0, 0x0, 0xf6, 0xff, 0x2 }, { 0x0, 0x0, 0xf6, 0xff, 0x0 }}, 0x8 },
|
||||
{{{ 0x30, 0x0, 0xff, 0xa0, 0x3 }, { 0x63, 0x0, 0x65, 0xb, 0x2 }}, 0x0 },
|
||||
{{{ 0x2a, 0x0, 0xf6, 0x87, 0x0 }, { 0x2b, 0x0, 0x76, 0x25, 0x0 }}, 0x0 },
|
||||
{{{ 0x85, 0x0, 0xb8, 0x84, 0x0 }, { 0x43, 0x0, 0xe5, 0x8f, 0x0 }}, 0x6 },
|
||||
{{{ 0x7, 0x4f, 0xf2, 0x60, 0x0 }, { 0x12, 0x0, 0xf2, 0x72, 0x0 }}, 0x8 },
|
||||
{{{ 0x5, 0x40, 0xb3, 0xd3, 0x0 }, { 0x86, 0x80, 0xf2, 0x24, 0x0 }}, 0x2 },
|
||||
{{{ 0xd0, 0x0, 0x11, 0xcf, 0x0 }, { 0xd1, 0x0, 0xf4, 0xe8, 0x3 }}, 0x0 },
|
||||
{{{ 0x5, 0x4e, 0xda, 0x25, 0x2 }, { 0x1, 0x0, 0xf9, 0x15, 0x0 }}, 0xa },
|
||||
{{{ 0x3, 0x0, 0x8f, 0x7, 0x2 }, { 0x2, 0x0, 0xff, 0x6, 0x0 }}, 0x0 },
|
||||
{{{ 0x13, 0x0, 0x8f, 0x7, 0x2 }, { 0x2, 0x0, 0xf9, 0x5, 0x0 }}, 0x0 },
|
||||
{{{ 0xf0, 0x1, 0x97, 0x17, 0x0 }, { 0x21, 0xd, 0xf1, 0x18, 0x0 }}, 0x8 },
|
||||
{{{ 0xf1, 0x41, 0x11, 0x11, 0x0 }, { 0xf1, 0x41, 0x11, 0x11, 0x0 }}, 0x2 },
|
||||
{{{ 0x13, 0x0, 0x8f, 0x7, 0x2 }, { 0x2, 0x0, 0xff, 0x6, 0x0 }}, 0x0 },
|
||||
{{{ 0x1, 0x0, 0x2f, 0x1, 0x0 }, { 0x1, 0x0, 0xaf, 0x1, 0x3 }}, 0xf },
|
||||
{{{ 0x1, 0x6, 0xf3, 0xff, 0x0 }, { 0x8, 0x0, 0xf7, 0xff, 0x0 }}, 0x4 },
|
||||
{{{ 0xc0, 0x4f, 0xf1, 0x3, 0x0 }, { 0xbe, 0xc, 0x10, 0x1, 0x0 }}, 0x2 },
|
||||
{{{ 0x0, 0x2, 0xf0, 0xff, 0x0 }, { 0x11, 0x80, 0xf0, 0xff, 0x0 }}, 0x6 },
|
||||
{{{ 0x81, 0x47, 0xf1, 0x83, 0x0 }, { 0xa2, 0x4, 0x91, 0x86, 0x0 }}, 0x6 },
|
||||
{{{ 0xf0, 0xc0, 0xff, 0xff, 0x3 }, { 0xe5, 0x0, 0xfb, 0xf0, 0x0 }}, 0xe },
|
||||
{{{ 0x0, 0x2, 0xf0, 0xff, 0x0 }, { 0x11, 0x80, 0xf0, 0xff, 0x0 }}, 0x6 }
|
||||
};
|
||||
|
||||
static const PercussionNote percussionNotes[47] = {
|
||||
{{{ 0x0, 0xb, 0xa8, 0x38, 0x0 }, { 0x0, 0x0, 0xd6, 0x49, 0x0 }}, 0x0, 0x4, 0x1, 0x97, 0x4 },
|
||||
{{{ 0xc0, 0xc0, 0xf8, 0x3f, 0x2 }, { 0xc0, 0x0, 0xf6, 0x8e, 0x0 }}, 0x0, 0x4, 0x1, 0xf7, 0x4 },
|
||||
{{{ 0xc0, 0x80, 0xc9, 0xab, 0x0 }, { 0xeb, 0x40, 0xb5, 0xf6, 0x0 }}, 0x1, 0x3, 0x1, 0x6a, 0x6 },
|
||||
{{{ 0xc, 0x0, 0xd8, 0xa6, 0x0 }, { 0x0, 0x0, 0xd6, 0x4f, 0x0 }}, 0x1, 0x3, 0x1, 0x6c, 0x5 },
|
||||
{{{ 0x1, 0x0, 0xe2, 0xd2, 0x0 }, { 0x3, 0x41, 0x8f, 0x48, 0x49 }}, 0xc, 0x4, 0x1, 0x2f, 0x5 },
|
||||
{{{ 0x0, 0x0, 0xc8, 0x58, 0x3 }, { 0x0, 0x0, 0xf6, 0x4f, 0x0 }}, 0x9, 0x3, 0x1, 0x108, 0x4 },
|
||||
{{{ 0x1, 0x0, 0xff, 0x5, 0x0 }, { 0xf2, 0xff, 0xe0, 0x50, 0x52 }}, 0x5d, 0x2, 0x1, 0x9f, 0x5 },
|
||||
{{{ 0xe, 0x9, 0xb9, 0x47, 0x0 }, { 0xeb, 0x40, 0xf5, 0xe6, 0x0 }}, 0x0, 0x0, 0x1, 0x82, 0x6 },
|
||||
{{{ 0x0, 0x0, 0xd6, 0x83, 0x0 }, { 0xd6, 0xd7, 0xe0, 0x41, 0x5e }}, 0x4a, 0x2, 0x1, 0xc7, 0x5 },
|
||||
{{{ 0x1, 0x9, 0x89, 0x67, 0x0 }, { 0xd6, 0xd7, 0xe0, 0x41, 0x5e }}, 0x4a, 0x0, 0x1, 0x80, 0x6 },
|
||||
{{{ 0x1, 0x0, 0xd6, 0x96, 0x0 }, { 0xd6, 0xd7, 0xe0, 0x41, 0x5e }}, 0x4a, 0x2, 0x1, 0xed, 0x5 },
|
||||
{{{ 0x0, 0x9, 0xa9, 0x55, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x1, 0x82, 0x6 },
|
||||
{{{ 0x2, 0x0, 0xc6, 0x96, 0x0 }, { 0xe0, 0x0, 0xe0, 0x40, 0x0 }}, 0x1, 0x2, 0x1, 0x123, 0x5 },
|
||||
{{{ 0x5, 0x0, 0xf6, 0x56, 0x0 }, { 0xf7, 0xff, 0xb3, 0x90, 0x4f }}, 0x1, 0x2, 0x1, 0x15b, 0x5 },
|
||||
{{{ 0x1, 0x0, 0xf7, 0x14, 0x0 }, { 0xf7, 0xff, 0x36, 0x90, 0x79 }}, 0xe7, 0x1, 0x1, 0x1ac, 0x5 },
|
||||
{{{ 0x0, 0x0, 0xf6, 0x56, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x1, 0x2, 0x1, 0x18b, 0x5 },
|
||||
{{{ 0x0, 0x83, 0xfb, 0x5, 0x0 }, { 0xf7, 0x41, 0x39, 0x90, 0x79 }}, 0x1, 0x1, 0x1, 0xc8, 0x5 },
|
||||
{{{ 0x0, 0x0, 0xff, 0x5, 0x0 }, { 0xf7, 0xff, 0x36, 0x90, 0x79 }}, 0xe7, 0x1, 0x1, 0xf9, 0x5 },
|
||||
{{{ 0x1, 0x0, 0xa0, 0x5, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x2, 0x1, 0x27a, 0x6 },
|
||||
{{{ 0x0, 0x5, 0xf3, 0x6, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x2, 0x1, 0x108, 0x7 },
|
||||
{{{ 0x1, 0x0, 0xf9, 0x34, 0x0 }, { 0xf7, 0xff, 0x36, 0x90, 0x79 }}, 0xe7, 0x1, 0x1, 0x147, 0x4 },
|
||||
{{{ 0x0, 0x0, 0xf7, 0x16, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x2, 0x1, 0x120, 0x6 },
|
||||
{{{ 0x1, 0x0, 0xff, 0x5, 0x0 }, { 0xf7, 0xff, 0x36, 0x90, 0x79 }}, 0xe7, 0x1, 0x1, 0x42, 0x6 },
|
||||
{{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 },
|
||||
{{{ 0x1, 0x0, 0xff, 0x5, 0x0 }, { 0xf7, 0xff, 0x36, 0x90, 0x79 }}, 0xe7, 0x1, 0x1, 0x6d, 0x5 },
|
||||
{{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 },
|
||||
{{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 },
|
||||
{{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 },
|
||||
{{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 },
|
||||
{{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 },
|
||||
{{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 },
|
||||
{{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 },
|
||||
{{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 },
|
||||
{{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 },
|
||||
{{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 },
|
||||
{{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 },
|
||||
{{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 },
|
||||
{{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 },
|
||||
{{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 },
|
||||
{{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 },
|
||||
{{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 },
|
||||
{{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 },
|
||||
{{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 },
|
||||
{{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 },
|
||||
{{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 },
|
||||
{{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 },
|
||||
{{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }
|
||||
};
|
||||
|
||||
const uint16 melodicFrequencies[36] = {
|
||||
0x55, 0x5a, 0x60, 0x66, 0x6c, 0x72, 0x79, 0x80, 0x88,
|
||||
0x90, 0x99, 0xa1, 0xab, 0xb5, 0xc0, 0xcc, 0xd8, 0xe5,
|
||||
0xf2, 0x101, 0x110, 0x120, 0x132, 0x143, 0x156, 0x16b, 0x181,
|
||||
0x198, 0x1b0, 0x1ca, 0x1e5, 0x202, 0x220, 0x241, 0x263, 0x286
|
||||
};
|
||||
|
||||
class AdLibDriver;
|
||||
|
||||
class AdLibChannel : public MidiChannel_MPU401 {
|
||||
public:
|
||||
void reset();
|
||||
|
||||
uint8 _program;
|
||||
uint8 _volume;
|
||||
uint8 _pedal;
|
||||
};
|
||||
|
||||
struct MelodicVoice {
|
||||
bool _used;
|
||||
uint8 _channel;
|
||||
uint8 _program;
|
||||
|
||||
uint8 _key;
|
||||
uint32 _timestamp;
|
||||
uint16 _frequency;
|
||||
int8 _octave;
|
||||
};
|
||||
|
||||
class AdLibDriver : public MidiDriver {
|
||||
public:
|
||||
AdLibDriver(Audio::Mixer *mixer) {
|
||||
for (uint i = 0; i < 16; ++i)
|
||||
_channels[i].init(this, i);
|
||||
|
||||
_isOpen = false;
|
||||
|
||||
_opl = nullptr;
|
||||
memset(_voices, 0, sizeof(_voices));
|
||||
|
||||
_lastVoice = 0;
|
||||
_percussionMask = 0;
|
||||
|
||||
_adlibTimerProc = nullptr;
|
||||
_adlibTimerParam = nullptr;
|
||||
}
|
||||
|
||||
int open() override;
|
||||
void close() override;
|
||||
void send(uint32 b) override;
|
||||
MidiChannel *allocateChannel() override;
|
||||
MidiChannel *getPercussionChannel() override { return &_channels[9]; }
|
||||
bool isOpen() const override { return _isOpen; }
|
||||
uint32 getBaseTempo() override { return 1000000 / OPL::OPL::kDefaultCallbackFrequency; }
|
||||
|
||||
void setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) override {
|
||||
_adlibTimerProc = timerProc;
|
||||
_adlibTimerParam = timerParam;
|
||||
}
|
||||
|
||||
protected:
|
||||
OPL::OPL *_opl;
|
||||
AdLibChannel _channels[16];
|
||||
MelodicVoice _voices[kNumMelodic];
|
||||
uint8 _notesPerPercussion[kNumPercussion];
|
||||
|
||||
uint _lastVoice;
|
||||
|
||||
uint8 _percussionMask;
|
||||
|
||||
void noteOff(uint8 channel, uint8 note);
|
||||
void noteOn(uint8 channel, uint8 note, uint8 velocity);
|
||||
void allNotesOff();
|
||||
void setModulationWheel(uint8 channel, uint8 value);
|
||||
void setFootController(uint8 channel, uint8 value);
|
||||
void setVolume(uint8 channel, uint8 value);
|
||||
void setPitchBend(uint8 channel, int16 value);
|
||||
|
||||
void playNote(uint8 voice, uint8 octave, uint16 frequency);
|
||||
|
||||
void programOperatorSimple(uint8 offset, const OPLOperator &op);
|
||||
void programOperator(uint8 offset, const OPLOperator &op);
|
||||
void setOperatorLevel(uint8 offset, const OPLOperator &op, uint8 velocity, uint8 channel, bool forceVolume);
|
||||
|
||||
void setupPercussion(const PercussionNote ¬e);
|
||||
void playPercussion(uint8 channel, const PercussionNote ¬e, uint8 velocity);
|
||||
|
||||
void programMelodicVoice(uint8 voice, uint8 program);
|
||||
void playMelodicNote(uint8 voice, uint8 channel, uint8 note, uint8 velocity);
|
||||
void muteMelodicVoice(uint8 voice);
|
||||
|
||||
void initVoices();
|
||||
|
||||
private:
|
||||
void onTimer();
|
||||
|
||||
Common::TimerManager::TimerProc _adlibTimerProc;
|
||||
void *_adlibTimerParam;
|
||||
bool _isOpen;
|
||||
};
|
||||
|
||||
MidiDriver *createAdLibDriver() {
|
||||
return new AdLibDriver(g_system->getMixer());
|
||||
}
|
||||
|
||||
void AdLibChannel::reset() {
|
||||
_program = 0;
|
||||
_volume = 127;
|
||||
_pedal = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
bit 7 - Clear: AM depth is 1 dB
|
||||
bit 6 - Clear: Vibrato depth is 7 cent
|
||||
bit 5 - Set: Rhythm enabled (6 melodic voices)
|
||||
bit 4 - Bass drum off
|
||||
bit 3 - Snare drum off
|
||||
bit 2 - Tom tom off
|
||||
bit 1 - Cymbal off
|
||||
bit 0 - Hi Hat off
|
||||
*/
|
||||
const uint8 kDefaultPercussionMask = 0x20;
|
||||
|
||||
int AdLibDriver::open() {
|
||||
if (_isOpen)
|
||||
return MERR_ALREADY_OPEN;
|
||||
|
||||
_isOpen = true;
|
||||
|
||||
_opl = OPL::Config::create();
|
||||
_opl->init();
|
||||
_opl->writeReg(0x1, 0x20); // set bit 5 (enable all waveforms)
|
||||
|
||||
// Reset the OPL registers.
|
||||
for (uint i = 0; i < kNumVoices; ++i) {
|
||||
_opl->writeReg(0xA0 + i, 0); // frequency
|
||||
_opl->writeReg(0xB0 + i, 0); // key on
|
||||
_opl->writeReg(0xC0 + i, 0); // feedback
|
||||
}
|
||||
_opl->writeReg(0xBD, kDefaultPercussionMask);
|
||||
|
||||
initVoices();
|
||||
|
||||
_opl->start(new Common::Functor0Mem<void, AdLibDriver>(this, &AdLibDriver::onTimer));
|
||||
return 0;
|
||||
}
|
||||
|
||||
void AdLibDriver::close() {
|
||||
if (!_isOpen)
|
||||
return;
|
||||
|
||||
_isOpen = false;
|
||||
|
||||
delete _opl;
|
||||
}
|
||||
|
||||
void AdLibDriver::send(uint32 b) {
|
||||
uint channel = b & 0xf;
|
||||
uint cmd = (b >> 4) & 0xf;
|
||||
uint param1 = (b >> 8) & 0xff;
|
||||
uint param2 = (b >> 16) & 0xff;
|
||||
|
||||
switch (cmd) {
|
||||
case 8:
|
||||
noteOff(channel, param1);
|
||||
break;
|
||||
case 9:
|
||||
// TODO: map volume?
|
||||
noteOn(channel, param1, param2);
|
||||
break;
|
||||
case 11:
|
||||
// controller change
|
||||
switch (param1) {
|
||||
case 1:
|
||||
setModulationWheel(channel, param2);
|
||||
break;
|
||||
case 4:
|
||||
setFootController(channel, param2);
|
||||
break;
|
||||
case 7:
|
||||
setVolume(channel, param2);
|
||||
break;
|
||||
case 123:
|
||||
// all notes off
|
||||
allNotesOff();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 12:
|
||||
// program change
|
||||
_channels[channel]._program = param1;
|
||||
break;
|
||||
case 14:
|
||||
setPitchBend(channel, (param1 | (param2 << 7)) - 0x2000);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void AdLibDriver::noteOff(uint8 channel, uint8 note) {
|
||||
if (channel == 9) {
|
||||
if (note < 35 || note > 81)
|
||||
return;
|
||||
|
||||
_percussionMask &= ~(1 << percussionNotes[note - 35].percussion);
|
||||
_opl->writeReg(0xBD, _percussionMask);
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = kNumMelodic - 1; i >= 0; --i) {
|
||||
if (_voices[i]._channel != channel)
|
||||
continue;
|
||||
if (_voices[i]._key != note)
|
||||
continue;
|
||||
muteMelodicVoice(i);
|
||||
_voices[i]._used = false;
|
||||
return;
|
||||
}
|
||||
|
||||
//debug(1, "failed to find voice off for channel %d, note %d", channel, note);
|
||||
}
|
||||
|
||||
void AdLibDriver::noteOn(uint8 channel, uint8 note, uint8 velocity) {
|
||||
if (channel == 9) {
|
||||
if (note < 35 || note > 81)
|
||||
return;
|
||||
|
||||
const PercussionNote &info = percussionNotes[note - 35];
|
||||
if (!info.valid)
|
||||
return;
|
||||
|
||||
if (note != _notesPerPercussion[info.percussion]) {
|
||||
setupPercussion(info);
|
||||
_notesPerPercussion[info.percussion] = note;
|
||||
}
|
||||
|
||||
playPercussion(channel, info, velocity);
|
||||
return;
|
||||
}
|
||||
|
||||
if (velocity == 0) {
|
||||
noteOff(channel, note);
|
||||
return;
|
||||
}
|
||||
|
||||
// We want to play a note on a melodic (voice) channel.
|
||||
|
||||
// First, look for a voice playing the same note with the same program.
|
||||
for (uint i = 0; i < kNumMelodic; ++i) {
|
||||
if (_voices[i]._channel != channel || _voices[i]._key != note)
|
||||
continue;
|
||||
if (_voices[i]._program != _channels[channel]._program)
|
||||
continue;
|
||||
muteMelodicVoice(i);
|
||||
playMelodicNote(i, channel, note, velocity);
|
||||
return;
|
||||
}
|
||||
|
||||
// The loops below try to start at _lastVoice and find a voice to use.
|
||||
// They ignore _lastVoice itself, and update _lastVoice if they succeed.
|
||||
|
||||
// Then, try finding a melodic voice with the same program.
|
||||
for (uint i = (_lastVoice + 1) % kNumMelodic; i != _lastVoice; i = (i + 1) % kNumMelodic) {
|
||||
if (_voices[i]._used)
|
||||
continue;
|
||||
if (_voices[i]._program != _channels[channel]._program)
|
||||
continue;
|
||||
playMelodicNote(i, channel, note, velocity);
|
||||
_lastVoice = i;
|
||||
return;
|
||||
}
|
||||
|
||||
// Then, try finding a free melodic voice of any kind.
|
||||
for (uint i = (_lastVoice + 1) % kNumMelodic; i != _lastVoice; i = (i + 1) % kNumMelodic) {
|
||||
if (_voices[i]._used)
|
||||
continue;
|
||||
programMelodicVoice(i, _channels[channel]._program);
|
||||
playMelodicNote(i, channel, note, velocity);
|
||||
_lastVoice = i;
|
||||
return;
|
||||
}
|
||||
|
||||
// Then just try finding a melodic voice with the same program,
|
||||
// and steal it.
|
||||
for (uint i = (_lastVoice + 1) % kNumMelodic; i != _lastVoice; i = (i + 1) % kNumMelodic) {
|
||||
if (_voices[i]._program != _channels[channel]._program)
|
||||
continue;
|
||||
muteMelodicVoice(i);
|
||||
playMelodicNote(i, channel, note, velocity);
|
||||
_lastVoice = i;
|
||||
return;
|
||||
}
|
||||
|
||||
// Finally, just take control of the channel used least recently.
|
||||
uint voiceId = 0;
|
||||
uint32 bestTimestamp = 0xffffffff;
|
||||
for (uint i = 0; i < kNumMelodic; ++i)
|
||||
if (bestTimestamp > _voices[i]._timestamp) {
|
||||
voiceId = i;
|
||||
bestTimestamp = _voices[i]._timestamp;
|
||||
}
|
||||
|
||||
//debug(1, "ran out of voices for channel %d, note %d, program %d: reused voice %d", channel, note, _channels[channel]._program, voiceId);
|
||||
programMelodicVoice(voiceId, _channels[channel]._program);
|
||||
playMelodicNote(voiceId, channel, note, velocity);
|
||||
_lastVoice = voiceId;
|
||||
}
|
||||
|
||||
// TODO: this doesn't match original
|
||||
void AdLibDriver::allNotesOff() {
|
||||
for (uint i = 0; i < kNumMelodic; ++i) {
|
||||
muteMelodicVoice(i);
|
||||
_voices[i]._used = false;
|
||||
}
|
||||
|
||||
_percussionMask = kDefaultPercussionMask;
|
||||
_opl->writeReg(0xBD, kDefaultPercussionMask);
|
||||
}
|
||||
|
||||
void AdLibDriver::setModulationWheel(uint8 channel, uint8 value) {
|
||||
if (value >= 64)
|
||||
_percussionMask |= 0x80;
|
||||
else
|
||||
_percussionMask &= 0x7f;
|
||||
|
||||
_opl->writeReg(0xBD, _percussionMask);
|
||||
}
|
||||
|
||||
void AdLibDriver::setFootController(uint8 channel, uint8 value) {
|
||||
_channels[channel]._pedal = (value >= 64);
|
||||
}
|
||||
|
||||
void AdLibDriver::setVolume(uint8 channel, uint8 value) {
|
||||
_channels[channel]._volume = value;
|
||||
}
|
||||
|
||||
void AdLibDriver::setPitchBend(uint8 channel, int16 value) {
|
||||
for (uint i = 0; i < kNumMelodic; ++i) {
|
||||
if (_voices[i]._channel != channel || !_voices[i]._used)
|
||||
continue;
|
||||
|
||||
// index into frequency table
|
||||
uint f = 12 + (_voices[i]._key % 12);
|
||||
|
||||
int16 bendAmount = value;
|
||||
if (bendAmount > 0) {
|
||||
// bend up two semitones
|
||||
bendAmount *= (melodicFrequencies[f + 2] - melodicFrequencies[f]);
|
||||
} else {
|
||||
// bend down two semitones
|
||||
bendAmount *= (melodicFrequencies[f] - melodicFrequencies[f - 2]);
|
||||
}
|
||||
bendAmount /= 0x2000;
|
||||
bendAmount += melodicFrequencies[f]; // add the base frequency
|
||||
playNote(i, _voices[i]._octave, bendAmount);
|
||||
_voices[i]._timestamp = g_system->getMillis();
|
||||
}
|
||||
}
|
||||
|
||||
void AdLibDriver::playNote(uint8 voice, uint8 octave, uint16 frequency) {
|
||||
/* Percussions are always fed keyOn = 0 even to set the note, as they are activated using the
|
||||
BD register instead. I wonder if they can just be fed the same value as melodic voice and
|
||||
be done with it. */
|
||||
uint8 keyOn = (voice < kNumMelodic) ? 0x20 : 0;
|
||||
|
||||
// key on, octave, high 2 bits of frequency
|
||||
_opl->writeReg(0xB0 + voice, keyOn | ((octave & 7) << 2) | ((frequency >> 8) & 3));
|
||||
// low 8 bits of frequency
|
||||
_opl->writeReg(0xA0 + voice, frequency & 0xff);
|
||||
}
|
||||
|
||||
void AdLibDriver::programOperatorSimple(uint8 offset, const OPLOperator &op) {
|
||||
_opl->writeReg(0x40 + offset, op.levels & LEVEL_MASK);
|
||||
_opl->writeReg(0x60 + offset, op.attackDecay);
|
||||
_opl->writeReg(0x80 + offset, op.sustainRelease);
|
||||
}
|
||||
|
||||
void AdLibDriver::programOperator(uint8 offset, const OPLOperator &op) {
|
||||
_opl->writeReg(0x20 + offset, op.characteristic);
|
||||
_opl->writeReg(0x60 + offset, op.attackDecay);
|
||||
_opl->writeReg(0x80 + offset, op.sustainRelease);
|
||||
_opl->writeReg(0xE0 + offset, op.waveform);
|
||||
_opl->writeReg(0x40 + offset, op.levels);
|
||||
}
|
||||
|
||||
const uint16 adlibLogVolume[] = {
|
||||
0, 37, 58, 73, 85, 95, 103, 110, 116, 121, 127, 131, 135, 139, 143, 146,
|
||||
149, 153, 155, 158, 161, 163, 165, 168, 170, 172, 174, 176, 178, 179, 181, 183,
|
||||
184, 186, 188, 189, 191, 192, 193, 195, 196, 197, 198, 200, 201, 202, 203, 204,
|
||||
205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 219,
|
||||
220, 221, 222, 223, 223, 224, 225, 226, 226, 227, 228, 228, 229, 230, 231, 231,
|
||||
232, 233, 233, 234, 234, 235, 236, 236, 237, 237, 238, 239, 239, 240, 240, 241,
|
||||
241, 242, 242, 243, 244, 244, 245, 245, 246, 246, 247, 247, 248, 248, 248, 249,
|
||||
249, 250, 250, 251, 251, 252, 252, 253, 253, 253, 254, 254, 255, 255, 256, 256,
|
||||
256
|
||||
};
|
||||
|
||||
void AdLibDriver::setOperatorLevel(uint8 offset, const OPLOperator &op, uint8 velocity, uint8 channel, bool forceVolume) {
|
||||
uint8 programLevel = LEVEL_MASK;
|
||||
if (!forceVolume)
|
||||
programLevel -= (op.levels & LEVEL_MASK);
|
||||
|
||||
uint32 noteLevel = adlibLogVolume[velocity];
|
||||
uint32 channelLevel = adlibLogVolume[_channels[channel]._volume];
|
||||
// programLevel comes from the static data and is probably already in the correct logarithmic scale
|
||||
uint32 finalLevel = LEVEL_MASK - ((noteLevel * channelLevel * programLevel) >> 16);
|
||||
|
||||
// high 2 bits are scaling level, the rest is (inversed) volume
|
||||
_opl->writeReg(0x40 + offset, (op.levels & 0xc0) | (finalLevel & 0x3f));
|
||||
}
|
||||
|
||||
const uint8 operatorOffsetsForPercussion[] = {
|
||||
0x11, // hi-hat
|
||||
0x15, // cymbal
|
||||
0x12, // tom tom
|
||||
0x14 // snare drum
|
||||
};
|
||||
|
||||
void AdLibDriver::setupPercussion(const PercussionNote ¬e) {
|
||||
if (note.percussion < 4) {
|
||||
// simple percussion (1 operator)
|
||||
|
||||
// turn off relevant percussion
|
||||
_percussionMask &= ~(1 << note.percussion);
|
||||
_opl->writeReg(0xBD, _percussionMask);
|
||||
|
||||
programOperatorSimple(operatorOffsetsForPercussion[note.percussion], note.op[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
// bass drum (2 operators)
|
||||
|
||||
// turn off bass drum
|
||||
_percussionMask &= ~(0x10);
|
||||
_opl->writeReg(0xBD, _percussionMask);
|
||||
|
||||
programOperator(0x10, note.op[0]);
|
||||
programOperator(0x13, note.op[1]);
|
||||
|
||||
_opl->writeReg(0xC0 + 6, note.feedbackAlgo);
|
||||
}
|
||||
|
||||
void AdLibDriver::playPercussion(uint8 channel, const PercussionNote ¬e, uint8 velocity) {
|
||||
if (note.percussion < 4) {
|
||||
// simple percussion (1 operator)
|
||||
|
||||
// turn off relevant percussion
|
||||
_percussionMask &= ~(1 << note.percussion);
|
||||
_opl->writeReg(0xBD, _percussionMask);
|
||||
|
||||
setOperatorLevel(operatorOffsetsForPercussion[note.percussion], note.op[0], velocity, channel, true);
|
||||
|
||||
if (note.percussion == 2) {
|
||||
// tom tom
|
||||
playNote(8, note.octave, note.frequency);
|
||||
} else if (note.percussion == 3) {
|
||||
// snare drum
|
||||
playNote(7, note.octave, note.frequency);
|
||||
}
|
||||
|
||||
// turn on relevant percussion
|
||||
_percussionMask |= (1 << note.percussion);
|
||||
_opl->writeReg(0xBD, _percussionMask);
|
||||
return;
|
||||
}
|
||||
|
||||
// turn off bass drum
|
||||
_percussionMask &= ~(0x10);
|
||||
_opl->writeReg(0xBD, _percussionMask);
|
||||
|
||||
if (note.feedbackAlgo & 1) {
|
||||
// operators 1 and 2 in additive synthesis
|
||||
setOperatorLevel(0x10, note.op[0], velocity, channel, true);
|
||||
setOperatorLevel(0x13, note.op[1], velocity, channel, true);
|
||||
} else {
|
||||
// operator 2 is modulating operator 1
|
||||
setOperatorLevel(0x13, note.op[1], velocity, channel, true);
|
||||
}
|
||||
|
||||
playNote(6, note.octave, note.frequency);
|
||||
|
||||
// turn on bass drum
|
||||
_percussionMask |= 0x10;
|
||||
_opl->writeReg(0xBD, _percussionMask);
|
||||
}
|
||||
|
||||
const uint8 offset1ForMelodic[kNumVoices] = { 0x0, 0x1, 0x2, 0x8, 0x9, 0xa, 0x10, 0x11, 0x12 };
|
||||
const uint8 offset2ForMelodic[kNumVoices] = { 0x3, 0x4, 0x5, 0xb, 0xc, 0xd, 0x13, 0x14, 0x15 };
|
||||
|
||||
void AdLibDriver::programMelodicVoice(uint8 voice, uint8 program) {
|
||||
assert(program < 128);
|
||||
assert(voice < kNumMelodic);
|
||||
|
||||
const MelodicProgram &info = melodicPrograms[program];
|
||||
uint8 offset1 = offset1ForMelodic[voice];
|
||||
uint8 offset2 = offset2ForMelodic[voice];
|
||||
|
||||
// Start at lowest volume.
|
||||
_opl->writeReg(0x40 + offset1, LEVEL_MASK);
|
||||
_opl->writeReg(0x40 + offset2, LEVEL_MASK);
|
||||
|
||||
muteMelodicVoice(voice);
|
||||
|
||||
programOperator(offset1, info.op[0]);
|
||||
programOperator(offset2, info.op[1]);
|
||||
|
||||
_opl->writeReg(0xC0 + voice, info.feedbackAlgo);
|
||||
}
|
||||
|
||||
void AdLibDriver::playMelodicNote(uint8 voice, uint8 channel, uint8 note, uint8 velocity) {
|
||||
assert(voice < kNumMelodic);
|
||||
|
||||
uint8 octave = note / 12;
|
||||
uint8 f = 12 + (note % 12);
|
||||
|
||||
if (octave > 7)
|
||||
octave = 7;
|
||||
|
||||
const MelodicProgram &info = melodicPrograms[_channels[channel]._program];
|
||||
uint8 offset1 = offset1ForMelodic[voice];
|
||||
uint8 offset2 = offset2ForMelodic[voice];
|
||||
|
||||
if (info.feedbackAlgo & 1) {
|
||||
setOperatorLevel(offset1, info.op[0], velocity, channel, false);
|
||||
setOperatorLevel(offset2, info.op[1], velocity, channel, false);
|
||||
} else {
|
||||
setOperatorLevel(offset2, info.op[1], velocity, channel, true);
|
||||
}
|
||||
|
||||
playNote(voice, octave, melodicFrequencies[f]);
|
||||
|
||||
_voices[voice]._program = _channels[channel]._program;
|
||||
_voices[voice]._key = note;
|
||||
_voices[voice]._channel = channel;
|
||||
_voices[voice]._timestamp = g_system->getMillis();
|
||||
_voices[voice]._frequency = melodicFrequencies[f];
|
||||
_voices[voice]._octave = octave;
|
||||
_voices[voice]._used = true;
|
||||
}
|
||||
|
||||
void AdLibDriver::muteMelodicVoice(uint8 voice) {
|
||||
_opl->writeReg(0xB0 + voice, 0 | ((_voices[voice]._octave & 7) << 2) | ((_voices[voice]._frequency >> 8) & 3));
|
||||
}
|
||||
|
||||
MidiChannel *AdLibDriver::allocateChannel() {
|
||||
for (uint i = 0; i < 16; ++i) {
|
||||
if (i == 9)
|
||||
continue;
|
||||
|
||||
if (_channels[i].allocate())
|
||||
return &_channels[i];
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void AdLibDriver::onTimer() {
|
||||
if (_adlibTimerProc)
|
||||
(*_adlibTimerProc)(_adlibTimerParam);
|
||||
}
|
||||
|
||||
void AdLibDriver::initVoices() {
|
||||
_percussionMask = kDefaultPercussionMask;
|
||||
_opl->writeReg(0xBD, _percussionMask);
|
||||
|
||||
for (uint i = 0; i < 16; ++i)
|
||||
_channels[i].reset();
|
||||
|
||||
for (uint i = 0; i < kNumMelodic; ++i) {
|
||||
_voices[i]._key = 0xff;
|
||||
_voices[i]._program = 0xff;
|
||||
_voices[i]._channel = 0xff;
|
||||
_voices[i]._timestamp = 0;
|
||||
_voices[i]._frequency = 0;
|
||||
_voices[i]._octave = 0;
|
||||
_voices[i]._used = false;
|
||||
}
|
||||
|
||||
for (uint i = 0; i < kNumPercussion; ++i)
|
||||
_notesPerPercussion[i] = 0xff;
|
||||
|
||||
_lastVoice = 0;
|
||||
}
|
||||
|
||||
} // namespace Parallaction
|
||||
775
engines/parallaction/balloons.cpp
Normal file
775
engines/parallaction/balloons.cpp
Normal file
@@ -0,0 +1,775 @@
|
||||
/* 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 "parallaction/graphics.h"
|
||||
#include "parallaction/parallaction.h"
|
||||
|
||||
#include "common/tokenizer.h"
|
||||
|
||||
namespace Parallaction {
|
||||
|
||||
class WrappedLineFormatter {
|
||||
|
||||
protected:
|
||||
Common::String _line;
|
||||
Font *_font;
|
||||
uint16 _lines, _lineWidth;
|
||||
|
||||
virtual void setup() = 0;
|
||||
virtual void action() = 0;
|
||||
virtual void end() = 0;
|
||||
virtual Common::String expand(const Common::String &token) { return token; }
|
||||
|
||||
void textAccum(const Common::String &token, uint16 width) {
|
||||
if (token.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_lineWidth += width;
|
||||
_line += token;
|
||||
}
|
||||
|
||||
void textNewLine() {
|
||||
_lines++;
|
||||
_lineWidth = 0;
|
||||
_line.clear();
|
||||
}
|
||||
|
||||
public:
|
||||
WrappedLineFormatter(Font *font) : _font(font), _lines(0), _lineWidth(0) { }
|
||||
virtual ~WrappedLineFormatter() { }
|
||||
|
||||
virtual void calc(const Common::String &text, uint16 maxwidth) {
|
||||
setup();
|
||||
|
||||
_lineWidth = 0;
|
||||
_line.clear();
|
||||
_lines = 0;
|
||||
|
||||
Common::StringTokenizer tokenizer(text, " ");
|
||||
Common::String token;
|
||||
Common::String blank(" ");
|
||||
|
||||
uint16 blankWidth = _font->getStringWidth(" ");
|
||||
uint16 tokenWidth = 0;
|
||||
|
||||
while (!tokenizer.empty()) {
|
||||
token = tokenizer.nextToken();
|
||||
token = expand(token);
|
||||
|
||||
if (token == "/") {
|
||||
tokenWidth = 0;
|
||||
action();
|
||||
textNewLine();
|
||||
} else {
|
||||
// todo: expand '%'
|
||||
tokenWidth = _font->getStringWidth(token.c_str());
|
||||
|
||||
if (_lineWidth == 0) {
|
||||
textAccum(token, tokenWidth);
|
||||
} else {
|
||||
if (_lineWidth + blankWidth + tokenWidth <= maxwidth) {
|
||||
textAccum(blank, blankWidth);
|
||||
textAccum(token, tokenWidth);
|
||||
} else {
|
||||
action();
|
||||
textNewLine();
|
||||
textAccum(token, tokenWidth);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
end();
|
||||
}
|
||||
};
|
||||
|
||||
class StringExtent_NS : public WrappedLineFormatter {
|
||||
|
||||
uint _width, _height;
|
||||
|
||||
protected:
|
||||
Common::String expand(const Common::String &token) override {
|
||||
if (token.compareToIgnoreCase("%p") == 0) {
|
||||
return Common::String("/");
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
void setup() override {
|
||||
_width = _height = 0;
|
||||
|
||||
_line.clear();
|
||||
_lines = 0;
|
||||
_width = 0;
|
||||
}
|
||||
|
||||
void action() override {
|
||||
if (_lineWidth > _width) {
|
||||
_width = _lineWidth;
|
||||
}
|
||||
_height = _lines * _font->height();
|
||||
}
|
||||
|
||||
void end() override {
|
||||
action();
|
||||
}
|
||||
|
||||
public:
|
||||
StringExtent_NS(Font *font) : WrappedLineFormatter(font), _width(0), _height(0) { }
|
||||
|
||||
uint width() const { return _width; }
|
||||
uint height() const { return _height; }
|
||||
};
|
||||
|
||||
|
||||
class StringWriter_NS : public WrappedLineFormatter {
|
||||
Parallaction_ns *_vm;
|
||||
|
||||
uint _width, _height;
|
||||
byte _color;
|
||||
|
||||
Graphics::Surface *_surf;
|
||||
|
||||
protected:
|
||||
Common::String expand(const Common::String& token) override {
|
||||
if (token.compareToIgnoreCase("%p") == 0) {
|
||||
Common::String t(_vm->_password);
|
||||
for (int i = t.size(); i < 7; i++) {
|
||||
t += '.';
|
||||
}
|
||||
return Common::String("> ") + t;
|
||||
} else
|
||||
if (token.compareToIgnoreCase("%s") == 0) {
|
||||
char buf[20];
|
||||
Common::sprintf_s(buf, "%i", _vm->_score);
|
||||
return Common::String(buf);
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
void setup() override {
|
||||
}
|
||||
|
||||
void action() override {
|
||||
if (_line.empty()) {
|
||||
return;
|
||||
}
|
||||
uint16 rx = 10;
|
||||
uint16 ry = 4 + _lines * _font->height(); // y
|
||||
|
||||
_font->setColor(_color);
|
||||
_font->drawString(_surf, rx, ry, _line.c_str());
|
||||
}
|
||||
|
||||
void end() override {
|
||||
action();
|
||||
}
|
||||
|
||||
public:
|
||||
StringWriter_NS(Parallaction_ns *vm, Font *font) : WrappedLineFormatter(font), _vm(vm),
|
||||
_width(0), _height(0), _color(0), _surf(nullptr) { }
|
||||
|
||||
void write(const Common::String &text, uint maxWidth, byte color, Graphics::Surface *surf) {
|
||||
StringExtent_NS se(_font);
|
||||
se.calc(text, maxWidth);
|
||||
_width = se.width() + 10;
|
||||
_height = se.height() + 20;
|
||||
_color = color;
|
||||
_surf = surf;
|
||||
|
||||
calc(text, maxWidth);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
#define BALLOON_TRANSPARENT_COLOR_NS 2
|
||||
#define BALLOON_TRANSPARENT_COLOR_BR 0
|
||||
|
||||
#define BALLOON_TAIL_WIDTH 12
|
||||
#define BALLOON_TAIL_HEIGHT 10
|
||||
|
||||
|
||||
byte _resBalloonTail[2][BALLOON_TAIL_WIDTH*BALLOON_TAIL_HEIGHT] = {
|
||||
{
|
||||
0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x02, 0x02,
|
||||
0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x02, 0x02,
|
||||
0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x02, 0x02, 0x02,
|
||||
0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x02, 0x02, 0x02,
|
||||
0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x00, 0x02, 0x02, 0x02,
|
||||
0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x00, 0x02, 0x02, 0x02, 0x02,
|
||||
0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02,
|
||||
0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
|
||||
0x00, 0x01, 0x01, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
|
||||
0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
|
||||
},
|
||||
{
|
||||
0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x02, 0x02,
|
||||
0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, 0x02, 0x02,
|
||||
0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02,
|
||||
0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02,
|
||||
0x02, 0x00, 0x01, 0x01, 0x01, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
|
||||
0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02,
|
||||
0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x00, 0x02, 0x02, 0x02, 0x02,
|
||||
0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x00, 0x02, 0x02, 0x02,
|
||||
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x00, 0x02, 0x02,
|
||||
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x02, 0x02
|
||||
}
|
||||
};
|
||||
|
||||
class BalloonManager_ns : public BalloonManager {
|
||||
|
||||
Parallaction_ns *_vm;
|
||||
static int16 _dialogueBalloonX[5];
|
||||
|
||||
byte _textColors[3];
|
||||
|
||||
struct Balloon {
|
||||
Common::Rect outerBox;
|
||||
Common::Rect innerBox;
|
||||
Graphics::Surface *surface;
|
||||
GfxObj *obj;
|
||||
} _intBalloons[5];
|
||||
|
||||
uint _numBalloons;
|
||||
|
||||
int createBalloon(int16 w, int16 h, int16 winding, uint16 borderThickness);
|
||||
Balloon *getBalloon(uint id);
|
||||
|
||||
StringWriter_NS _sw;
|
||||
StringExtent_NS _se;
|
||||
|
||||
public:
|
||||
BalloonManager_ns(Parallaction_ns *vm, Font *font);
|
||||
~BalloonManager_ns() override;
|
||||
|
||||
void reset() override;
|
||||
int setLocationBalloon(const Common::String &text, bool endGame) override;
|
||||
int setDialogueBalloon(const Common::String &text, uint16 winding, TextColor textColor) override;
|
||||
int setSingleBalloon(const Common::String &text, uint16 x, uint16 y, uint16 winding, TextColor textColor) override;
|
||||
void setBalloonText(uint id, const Common::String &text, TextColor textColor) override;
|
||||
int hitTestDialogueBalloon(int x, int y) override;
|
||||
};
|
||||
|
||||
int16 BalloonManager_ns::_dialogueBalloonX[5] = { 80, 120, 150, 150, 150 };
|
||||
|
||||
BalloonManager_ns::BalloonManager_ns(Parallaction_ns *vm, Font *font) : _vm(vm), _numBalloons(0), _sw(vm, font), _se(font) {
|
||||
_textColors[kSelectedColor] = 0;
|
||||
_textColors[kUnselectedColor] = 3;
|
||||
_textColors[kNormalColor] = 0;
|
||||
}
|
||||
|
||||
BalloonManager_ns::~BalloonManager_ns() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
BalloonManager_ns::Balloon* BalloonManager_ns::getBalloon(uint id) {
|
||||
assert(id < _numBalloons);
|
||||
return &_intBalloons[id];
|
||||
}
|
||||
|
||||
int BalloonManager_ns::createBalloon(int16 w, int16 h, int16 winding, uint16 borderThickness) {
|
||||
assert(_numBalloons < 5);
|
||||
|
||||
int id = _numBalloons;
|
||||
|
||||
Balloon *balloon = &_intBalloons[id];
|
||||
|
||||
int16 real_h = (winding == -1) ? h : h + 9;
|
||||
balloon->surface = new Graphics::Surface;
|
||||
balloon->surface->create(w, real_h, Graphics::PixelFormat::createFormatCLUT8());
|
||||
balloon->surface->fillRect(Common::Rect(w, real_h), BALLOON_TRANSPARENT_COLOR_NS);
|
||||
|
||||
Common::Rect r(w, h);
|
||||
balloon->surface->fillRect(r, 0);
|
||||
balloon->outerBox = r;
|
||||
|
||||
r.grow(-borderThickness);
|
||||
balloon->surface->fillRect(r, 1);
|
||||
balloon->innerBox = r;
|
||||
|
||||
if (winding != -1) {
|
||||
// draws tail
|
||||
// TODO: this bitmap tail should only be used for Dos games. Amiga should use a polygon fill.
|
||||
winding = (winding == 0 ? 1 : 0);
|
||||
Common::Rect s(BALLOON_TAIL_WIDTH, BALLOON_TAIL_HEIGHT);
|
||||
s.moveTo(r.width()/2 - 5, r.bottom - 1);
|
||||
_vm->_gfx->blt(s, _resBalloonTail[winding], balloon->surface, LAYER_FOREGROUND, 100, BALLOON_TRANSPARENT_COLOR_NS);
|
||||
}
|
||||
|
||||
_numBalloons++;
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
int BalloonManager_ns::setSingleBalloon(const Common::String &text, uint16 x, uint16 y, uint16 winding, TextColor textColor) {
|
||||
|
||||
int16 w, h;
|
||||
|
||||
_se.calc(text, MAX_BALLOON_WIDTH);
|
||||
w = _se.width() + 14;
|
||||
h = _se.height() + 20;
|
||||
|
||||
_vm->sayText(text, Common::TextToSpeechManager::INTERRUPT);
|
||||
|
||||
int id = createBalloon(w+5, h, winding, 1);
|
||||
Balloon *balloon = &_intBalloons[id];
|
||||
|
||||
_sw.write(text, MAX_BALLOON_WIDTH, _textColors[textColor], balloon->surface);
|
||||
|
||||
// TODO: extract some text to make a name for obj
|
||||
balloon->obj = _vm->_gfx->registerBalloon(new SurfaceToFrames(balloon->surface), nullptr);
|
||||
balloon->obj->x = x;
|
||||
balloon->obj->y = y;
|
||||
balloon->obj->transparentKey = BALLOON_TRANSPARENT_COLOR_NS;
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
int BalloonManager_ns::setDialogueBalloon(const Common::String &text, uint16 winding, TextColor textColor) {
|
||||
|
||||
int16 w, h;
|
||||
|
||||
_se.calc(text, MAX_BALLOON_WIDTH);
|
||||
w = _se.width() + 14;
|
||||
h = _se.height() + 20;
|
||||
|
||||
|
||||
int id = createBalloon(w+5, h, winding, 1);
|
||||
Balloon *balloon = &_intBalloons[id];
|
||||
|
||||
_sw.write(text, MAX_BALLOON_WIDTH, _textColors[textColor], balloon->surface);
|
||||
|
||||
// TODO: extract some text to make a name for obj
|
||||
balloon->obj = _vm->_gfx->registerBalloon(new SurfaceToFrames(balloon->surface), nullptr);
|
||||
balloon->obj->x = _dialogueBalloonX[id];
|
||||
balloon->obj->y = 10;
|
||||
balloon->obj->transparentKey = BALLOON_TRANSPARENT_COLOR_NS;
|
||||
|
||||
if (id > 0) {
|
||||
balloon->obj->y += _intBalloons[id - 1].obj->y + _intBalloons[id - 1].outerBox.height();
|
||||
}
|
||||
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void BalloonManager_ns::setBalloonText(uint id, const Common::String &text, TextColor textColor) {
|
||||
Balloon *balloon = getBalloon(id);
|
||||
balloon->surface->fillRect(balloon->innerBox, 1);
|
||||
|
||||
if (textColor != kUnselectedColor && !text.contains("%P")) {
|
||||
_vm->sayText(text, Common::TextToSpeechManager::INTERRUPT);
|
||||
}
|
||||
|
||||
_sw.write(text, MAX_BALLOON_WIDTH, _textColors[textColor], balloon->surface);
|
||||
}
|
||||
|
||||
|
||||
int BalloonManager_ns::setLocationBalloon(const Common::String &text, bool endGame) {
|
||||
|
||||
int16 w, h;
|
||||
|
||||
_se.calc(text, MAX_BALLOON_WIDTH);
|
||||
w = _se.width() + 14;
|
||||
h = _se.height() + 20;
|
||||
|
||||
int id = createBalloon(w+(endGame ? 5 : 10), h+5, -1, BALLOON_TRANSPARENT_COLOR_NS);
|
||||
Balloon *balloon = &_intBalloons[id];
|
||||
_sw.write(text, MAX_BALLOON_WIDTH, _textColors[kNormalColor], balloon->surface);
|
||||
|
||||
// TODO: extract some text to make a name for obj
|
||||
balloon->obj = _vm->_gfx->registerBalloon(new SurfaceToFrames(balloon->surface), nullptr);
|
||||
balloon->obj->x = 5;
|
||||
balloon->obj->y = 5;
|
||||
balloon->obj->transparentKey = BALLOON_TRANSPARENT_COLOR_NS;
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
int BalloonManager_ns::hitTestDialogueBalloon(int x, int y) {
|
||||
|
||||
Common::Point p;
|
||||
|
||||
for (uint i = 0; i < _numBalloons; i++) {
|
||||
p.x = x - _intBalloons[i].obj->x;
|
||||
p.y = y - _intBalloons[i].obj->y;
|
||||
|
||||
if (_intBalloons[i].innerBox.contains(p))
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void BalloonManager_ns::reset() {
|
||||
for (uint i = 0; i < _numBalloons; i++) {
|
||||
_intBalloons[i].obj = nullptr;
|
||||
_intBalloons[i].surface = nullptr; // no need to delete surface, since it is done by Gfx
|
||||
}
|
||||
_numBalloons = 0;
|
||||
|
||||
if (_vm->_password.size() == 0) {
|
||||
_vm->stopTextToSpeech();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class StringExtent_BR : public WrappedLineFormatter {
|
||||
|
||||
uint _width, _height;
|
||||
|
||||
protected:
|
||||
void setup() override {
|
||||
_width = _height = 0;
|
||||
|
||||
_line.clear();
|
||||
_lines = 0;
|
||||
_width = 0;
|
||||
}
|
||||
|
||||
void action() override {
|
||||
if (_lineWidth > _width) {
|
||||
_width = _lineWidth;
|
||||
}
|
||||
_height = _lines * _font->height();
|
||||
}
|
||||
|
||||
void end() override {
|
||||
action();
|
||||
}
|
||||
|
||||
public:
|
||||
StringExtent_BR(Font *font) : WrappedLineFormatter(font), _width(0), _height(0) { }
|
||||
|
||||
uint width() const { return _width; }
|
||||
uint height() const { return _height; }
|
||||
};
|
||||
|
||||
|
||||
class StringWriter_BR : public WrappedLineFormatter {
|
||||
|
||||
uint _width, _height;
|
||||
byte _color;
|
||||
uint _x, _y;
|
||||
|
||||
Graphics::Surface *_surf;
|
||||
|
||||
protected:
|
||||
StringWriter_BR(Font *font, byte color) : WrappedLineFormatter(font), _width(0), _height(0),
|
||||
_color(color), _x(0), _y(0), _surf(nullptr) {
|
||||
|
||||
}
|
||||
|
||||
void setup() override {
|
||||
}
|
||||
|
||||
void action() override {
|
||||
if (_line.empty()) {
|
||||
return;
|
||||
}
|
||||
uint16 rx = _x + (_surf->w - _lineWidth) / 2;
|
||||
uint16 ry = _y + _lines * _font->height(); // y
|
||||
|
||||
_font->setColor(_color);
|
||||
_font->drawString(_surf, rx, ry, _line.c_str());
|
||||
}
|
||||
|
||||
void end() override {
|
||||
action();
|
||||
}
|
||||
|
||||
public:
|
||||
StringWriter_BR(Font *font) : WrappedLineFormatter(font), _width(0), _height(0),
|
||||
_color(0), _x(0), _y(0), _surf(nullptr) { }
|
||||
|
||||
void write(const Common::String &text, uint maxWidth, byte color, Graphics::Surface *surf) {
|
||||
StringExtent_BR se(_font);
|
||||
se.calc(text, maxWidth);
|
||||
_width = se.width() + 10;
|
||||
_height = se.height() + 12;
|
||||
_color = color;
|
||||
_surf = surf;
|
||||
|
||||
_x = 0;
|
||||
_y = (_surf->h - _height) / 2;
|
||||
calc(text, maxWidth);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
class BalloonManager_br : public BalloonManager {
|
||||
|
||||
Parallaction_br *_vm;
|
||||
byte _textColors[3];
|
||||
|
||||
struct Balloon {
|
||||
Common::Rect box;
|
||||
Graphics::Surface *surface;
|
||||
GfxObj *obj;
|
||||
} _intBalloons[3];
|
||||
|
||||
uint _numBalloons;
|
||||
|
||||
Frames *_leftBalloon;
|
||||
Frames *_rightBalloon;
|
||||
|
||||
void cacheAnims();
|
||||
int createBalloon(int16 w, int16 h, uint16 borderThickness);
|
||||
Balloon *getBalloon(uint id);
|
||||
Graphics::Surface *expandBalloon(Frames *data, int frameNum);
|
||||
|
||||
StringWriter_BR _sw;
|
||||
StringExtent_BR _se;
|
||||
|
||||
public:
|
||||
BalloonManager_br(Parallaction_br *vm, Font *font);
|
||||
~BalloonManager_br() override;
|
||||
|
||||
void reset() override;
|
||||
int setLocationBalloon(const Common::String &text, bool endGame) override;
|
||||
int setDialogueBalloon(const Common::String &text, uint16 winding, TextColor textColor) override;
|
||||
int setSingleBalloon(const Common::String &text, uint16 x, uint16 y, uint16 winding, TextColor textColor) override;
|
||||
void setBalloonText(uint id, const Common::String &text, TextColor textColor) override;
|
||||
int hitTestDialogueBalloon(int x, int y) override;
|
||||
};
|
||||
|
||||
|
||||
|
||||
BalloonManager_br::Balloon* BalloonManager_br::getBalloon(uint id) {
|
||||
assert(id < _numBalloons);
|
||||
return &_intBalloons[id];
|
||||
}
|
||||
|
||||
Graphics::Surface *BalloonManager_br::expandBalloon(Frames *data, int frameNum) {
|
||||
|
||||
Common::Rect rect;
|
||||
data->getRect(frameNum, rect);
|
||||
|
||||
rect.translate(-rect.left, -rect.top);
|
||||
|
||||
Graphics::Surface *surf = new Graphics::Surface;
|
||||
surf->create(rect.width(), rect.height(), Graphics::PixelFormat::createFormatCLUT8());
|
||||
|
||||
_vm->_gfx->unpackBlt(rect, data->getData(frameNum), data->getRawSize(frameNum), surf, LAYER_FOREGROUND, 100, BALLOON_TRANSPARENT_COLOR_BR);
|
||||
|
||||
return surf;
|
||||
}
|
||||
|
||||
int BalloonManager_br::setSingleBalloon(const Common::String &text, uint16 x, uint16 y, uint16 winding, TextColor textColor) {
|
||||
cacheAnims();
|
||||
|
||||
_vm->sayText(text, Common::TextToSpeechManager::INTERRUPT);
|
||||
|
||||
int id = _numBalloons;
|
||||
Frames *src = nullptr;
|
||||
int srcFrame = 0;
|
||||
|
||||
Balloon *balloon = &_intBalloons[id];
|
||||
|
||||
if (winding == 0) {
|
||||
src = _rightBalloon;
|
||||
srcFrame = 0;
|
||||
} else
|
||||
if (winding == 1) {
|
||||
src = _leftBalloon;
|
||||
srcFrame = 0;
|
||||
}
|
||||
|
||||
assert(src);
|
||||
|
||||
balloon->surface = expandBalloon(src, srcFrame);
|
||||
src->getRect(srcFrame, balloon->box);
|
||||
|
||||
_sw.write(text, 216, _textColors[textColor], balloon->surface);
|
||||
|
||||
// TODO: extract some text to make a name for obj
|
||||
balloon->obj = _vm->_gfx->registerBalloon(new SurfaceToFrames(balloon->surface), nullptr);
|
||||
balloon->obj->x = x + balloon->box.left;
|
||||
balloon->obj->y = y + balloon->box.top;
|
||||
balloon->obj->transparentKey = BALLOON_TRANSPARENT_COLOR_BR;
|
||||
|
||||
_numBalloons++;
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
int BalloonManager_br::setDialogueBalloon(const Common::String &text, uint16 winding, TextColor textColor) {
|
||||
cacheAnims();
|
||||
|
||||
int id = _numBalloons;
|
||||
Frames *src = nullptr;
|
||||
int srcFrame = 0;
|
||||
|
||||
Balloon *balloon = &_intBalloons[id];
|
||||
|
||||
if (winding == 0) {
|
||||
src = _rightBalloon;
|
||||
srcFrame = 0;
|
||||
} else
|
||||
if (winding == 1) {
|
||||
src = _leftBalloon;
|
||||
srcFrame = id;
|
||||
}
|
||||
|
||||
assert(src);
|
||||
|
||||
balloon->surface = expandBalloon(src, srcFrame);
|
||||
src->getRect(srcFrame, balloon->box);
|
||||
|
||||
// TODO: fix text positioning in the Amiga version
|
||||
_sw.write(text, 216, _textColors[textColor], balloon->surface);
|
||||
|
||||
// TODO: extract some text to make a name for obj
|
||||
balloon->obj = _vm->_gfx->registerBalloon(new SurfaceToFrames(balloon->surface), nullptr);
|
||||
balloon->obj->x = balloon->box.left;
|
||||
balloon->obj->y = balloon->box.top;
|
||||
balloon->obj->transparentKey = BALLOON_TRANSPARENT_COLOR_BR;
|
||||
|
||||
_numBalloons++;
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void BalloonManager_br::setBalloonText(uint id, const Common::String &text, TextColor textColor) {
|
||||
Balloon *balloon = getBalloon(id);
|
||||
|
||||
if (textColor != kUnselectedColor) {
|
||||
_vm->sayText(text, Common::TextToSpeechManager::INTERRUPT);
|
||||
}
|
||||
|
||||
_sw.write(text, 216, _textColors[textColor], balloon->surface);
|
||||
}
|
||||
|
||||
int BalloonManager_br::createBalloon(int16 w, int16 h, uint16 borderThickness) {
|
||||
assert(_numBalloons < 5);
|
||||
|
||||
int id = _numBalloons;
|
||||
Balloon *balloon = &_intBalloons[id];
|
||||
|
||||
balloon->surface = new Graphics::Surface;
|
||||
balloon->surface->create(w, h, Graphics::PixelFormat::createFormatCLUT8());
|
||||
|
||||
Common::Rect rect(w, h);
|
||||
balloon->surface->fillRect(rect, 1);
|
||||
rect.grow(-borderThickness);
|
||||
balloon->surface->fillRect(rect, 15);
|
||||
|
||||
_numBalloons++;
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
int BalloonManager_br::setLocationBalloon(const Common::String &text, bool endGame) {
|
||||
_se.calc(text, 240);
|
||||
|
||||
int id = createBalloon(_se.width() + 20, _se.height() + 30, 2);
|
||||
Balloon *balloon = &_intBalloons[id];
|
||||
|
||||
_sw.write(text, 240, kNormalColor, balloon->surface);
|
||||
|
||||
balloon->obj = _vm->_gfx->registerBalloon(new SurfaceToFrames(balloon->surface), nullptr);
|
||||
balloon->obj->x = 5;
|
||||
balloon->obj->y = 5;
|
||||
balloon->obj->transparentKey = BALLOON_TRANSPARENT_COLOR_BR;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int BalloonManager_br::hitTestDialogueBalloon(int x, int y) {
|
||||
|
||||
for (uint i = 0; i < _numBalloons; i++) {
|
||||
if (_intBalloons[i].box.contains(x, y)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void BalloonManager_br::reset() {
|
||||
for (uint i = 0; i < _numBalloons; i++) {
|
||||
_intBalloons[i].obj = nullptr;
|
||||
_intBalloons[i].surface = nullptr; // no need to delete surface, since it is done by Gfx
|
||||
}
|
||||
|
||||
_numBalloons = 0;
|
||||
|
||||
_vm->stopTextToSpeech();
|
||||
}
|
||||
|
||||
void BalloonManager_br::cacheAnims() {
|
||||
if (!_leftBalloon) {
|
||||
_leftBalloon = _vm->_disk->loadFrames("fumetto.ani");
|
||||
_rightBalloon = _vm->_disk->loadFrames("fumdx.ani");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
BalloonManager_br::BalloonManager_br(Parallaction_br *vm, Font *font) : _vm(vm), _numBalloons(0),
|
||||
_leftBalloon(nullptr), _rightBalloon(nullptr), _sw(font), _se(font) {
|
||||
|
||||
if (_vm->getPlatform() == Common::kPlatformDOS) {
|
||||
_textColors[kSelectedColor] = 12;
|
||||
_textColors[kUnselectedColor] = 0;
|
||||
_textColors[kNormalColor] = 0;
|
||||
} else {
|
||||
_textColors[kSelectedColor] = 11;
|
||||
_textColors[kUnselectedColor] = 1;
|
||||
_textColors[kNormalColor] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
BalloonManager_br::~BalloonManager_br() {
|
||||
delete _leftBalloon;
|
||||
delete _rightBalloon;
|
||||
}
|
||||
|
||||
void Parallaction_ns::setupBalloonManager() {
|
||||
_balloonMan = new BalloonManager_ns(this, _dialogueFont);
|
||||
}
|
||||
|
||||
void Parallaction_br::setupBalloonManager() {
|
||||
_balloonMan = new BalloonManager_br(this, _dialogueFont);
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace Parallaction
|
||||
92
engines/parallaction/callables_br.cpp
Normal file
92
engines/parallaction/callables_br.cpp
Normal file
@@ -0,0 +1,92 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "parallaction/parallaction.h"
|
||||
|
||||
#include "common/textconsole.h"
|
||||
|
||||
namespace Parallaction {
|
||||
|
||||
void Parallaction_br::_c_null(void *) {
|
||||
// do nothing :)
|
||||
}
|
||||
|
||||
void Parallaction_br::_c_blufade(void *) {
|
||||
warning("Parallaction_br::_c_blufade() not yet implemented");
|
||||
}
|
||||
|
||||
void Parallaction_br::_c_resetpalette(void *) {
|
||||
warning("Parallaction_br::_c_resetpalette() not yet implemented");
|
||||
}
|
||||
|
||||
/*
|
||||
Used in part 3 by location ULTIMAVAG to
|
||||
simulate the movement of trees and railroad.
|
||||
*/
|
||||
void Parallaction_br::_c_ferrcycle(void *) {
|
||||
Palette pal = _gfx->_palette;
|
||||
|
||||
if (_ferrcycleMode == 1) {
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
pal.setEntry(4 * i + 192, 0, 0, 0);
|
||||
pal.setEntry(4 * i + 193, 30, 12, 12);
|
||||
pal.setEntry(4 * i + 194, 28, 47, 54);
|
||||
pal.setEntry(4 * i + 195, 63, 63, 63);
|
||||
}
|
||||
} else if (_ferrcycleMode == 2) {
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
pal.setEntry(j + 16 * i + 192, 0, 0, 0);
|
||||
pal.setEntry(j + 16 * i + 196, 30, 12, 12);
|
||||
pal.setEntry(j + 16 * i + 200, 28, 47, 54);
|
||||
pal.setEntry(j + 16 * i + 204, 63, 63, 63);
|
||||
}
|
||||
}
|
||||
} else if (_ferrcycleMode == 0) {
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
pal.setEntry(i + 192, 0, 0, 0);
|
||||
pal.setEntry(i + 208, 30, 12, 12);
|
||||
pal.setEntry(i + 224, 28, 47, 54);
|
||||
pal.setEntry(i + 240, 63, 63, 63);
|
||||
}
|
||||
}
|
||||
|
||||
_gfx->setPalette(pal);
|
||||
|
||||
_ferrcycleMode = (_ferrcycleMode + 1) % 3;
|
||||
}
|
||||
|
||||
void Parallaction_br::_c_lipsinc(void *) {
|
||||
warning("Unexpected lipsinc routine call! Please notify the team");
|
||||
}
|
||||
|
||||
void Parallaction_br::_c_albcycle(void *) {
|
||||
warning("Parallaction_br::_c_albcycle() not yet implemented");
|
||||
}
|
||||
|
||||
void Parallaction_br::_c_password(void *) {
|
||||
warning("Parallaction_br::_c_password() not yet implemented");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace Parallaction
|
||||
610
engines/parallaction/callables_ns.cpp
Normal file
610
engines/parallaction/callables_ns.cpp
Normal file
@@ -0,0 +1,610 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include "common/system.h"
|
||||
|
||||
#include "common/file.h"
|
||||
|
||||
#include "graphics/primitives.h" // for Graphics::drawLine
|
||||
|
||||
|
||||
#include "parallaction/exec.h"
|
||||
#include "parallaction/input.h"
|
||||
#include "parallaction/parallaction.h"
|
||||
#include "parallaction/saveload.h"
|
||||
#include "parallaction/sound.h"
|
||||
|
||||
|
||||
namespace Parallaction {
|
||||
|
||||
/*
|
||||
intro callables data members
|
||||
*/
|
||||
|
||||
|
||||
static uint16 _rightHandPositions[684] = {
|
||||
0x0064, 0x0046, 0x006c, 0x0046, 0x0074, 0x0046, 0x007c, 0x0046,
|
||||
0x0084, 0x0046, 0x008c, 0x0046, 0x0094, 0x0046, 0x009c, 0x0046,
|
||||
0x00a4, 0x0046, 0x00ac, 0x0046, 0x00b4, 0x0046, 0x00bc, 0x0046,
|
||||
0x00c4, 0x0046, 0x00cc, 0x0046, 0x00d4, 0x0046, 0x00dc, 0x0046,
|
||||
0x00e4, 0x0046, 0x00ec, 0x0046, 0x00f4, 0x0046, 0x00fc, 0x0046,
|
||||
0x0104, 0x0046, 0x00ff, 0x0042, 0x00ff, 0x004a, 0x00ff, 0x0052,
|
||||
0x00ff, 0x005a, 0x00ff, 0x0062, 0x00ff, 0x006a, 0x00ff, 0x0072,
|
||||
0x00ff, 0x007a, 0x00ff, 0x0082, 0x00ff, 0x008a, 0x00ff, 0x0092,
|
||||
0x00ff, 0x009a, 0x00ff, 0x00a2, 0x0104, 0x0097, 0x00fc, 0x0097,
|
||||
0x00f4, 0x0097, 0x00ec, 0x0097, 0x00e4, 0x0097, 0x00dc, 0x0097,
|
||||
0x00d4, 0x0097, 0x00cc, 0x0097, 0x00c4, 0x0097, 0x00bc, 0x0097,
|
||||
0x00b4, 0x0097, 0x00ac, 0x0097, 0x00a4, 0x0097, 0x009c, 0x0097,
|
||||
0x0094, 0x0097, 0x008c, 0x0097, 0x0084, 0x0097, 0x007c, 0x0097,
|
||||
0x0074, 0x0097, 0x006c, 0x0097, 0x0064, 0x0097, 0x0066, 0x0042,
|
||||
0x0066, 0x004a, 0x0066, 0x0052, 0x0066, 0x005a, 0x0066, 0x0062,
|
||||
0x0066, 0x006a, 0x0066, 0x0072, 0x0066, 0x007a, 0x0066, 0x0082,
|
||||
0x0066, 0x008a, 0x0066, 0x0092, 0x0066, 0x009a, 0x0066, 0x00a2,
|
||||
0x008c, 0x0091, 0x0099, 0x0042, 0x0099, 0x004a, 0x0099, 0x0052,
|
||||
0x0099, 0x005a, 0x0099, 0x0062, 0x0099, 0x006a, 0x0099, 0x0072,
|
||||
0x0099, 0x007a, 0x0099, 0x0082, 0x0099, 0x008a, 0x0099, 0x0092,
|
||||
0x0099, 0x009a, 0x0099, 0x00a2, 0x00a0, 0x004d, 0x00cc, 0x0042,
|
||||
0x00cc, 0x004a, 0x00cc, 0x0052, 0x00cc, 0x005a, 0x00cc, 0x0062,
|
||||
0x00cc, 0x006a, 0x00cc, 0x0072, 0x00cc, 0x007a, 0x00cc, 0x0082,
|
||||
0x00cc, 0x008a, 0x00cc, 0x0092, 0x00cc, 0x009a, 0x00cc, 0x00a2,
|
||||
0x00ca, 0x0050, 0x00b1, 0x0050, 0x0081, 0x0052, 0x007e, 0x0052,
|
||||
0x007c, 0x0055, 0x007c, 0x005c, 0x007e, 0x005e, 0x0080, 0x005e,
|
||||
0x0082, 0x005c, 0x0082, 0x0054, 0x0080, 0x0052, 0x0078, 0x0052,
|
||||
0x007c, 0x005e, 0x0077, 0x0061, 0x0074, 0x006e, 0x0074, 0x0078,
|
||||
0x0076, 0x007a, 0x0079, 0x0078, 0x0079, 0x0070, 0x0078, 0x0070,
|
||||
0x0078, 0x006b, 0x007b, 0x0066, 0x007a, 0x006f, 0x0084, 0x006f,
|
||||
0x0085, 0x0066, 0x0086, 0x0070, 0x0085, 0x0070, 0x0085, 0x0079,
|
||||
0x0088, 0x0079, 0x008a, 0x0078, 0x008a, 0x006c, 0x0087, 0x0061,
|
||||
0x0085, 0x005f, 0x0082, 0x005f, 0x0080, 0x0061, 0x007e, 0x0061,
|
||||
0x007b, 0x005f, 0x007c, 0x006f, 0x007c, 0x0071, 0x0079, 0x0074,
|
||||
0x0079, 0x0089, 0x0076, 0x008c, 0x0076, 0x008e, 0x007a, 0x008e,
|
||||
0x007f, 0x0089, 0x007f, 0x0083, 0x007e, 0x0083, 0x007e, 0x0077,
|
||||
0x0080, 0x0077, 0x0080, 0x0083, 0x0080, 0x008b, 0x0084, 0x0090,
|
||||
0x0088, 0x0090, 0x0088, 0x008e, 0x0085, 0x008b, 0x0085, 0x0074,
|
||||
0x0082, 0x0071, 0x00b2, 0x0052, 0x00b0, 0x0054, 0x00b0, 0x0056,
|
||||
0x00ae, 0x0058, 0x00af, 0x0059, 0x00af, 0x005e, 0x00b2, 0x0061,
|
||||
0x00b5, 0x0061, 0x00b8, 0x005e, 0x00b8, 0x005a, 0x00b9, 0x0059,
|
||||
0x00b9, 0x0058, 0x00b7, 0x0056, 0x00b7, 0x0054, 0x00b5, 0x0052,
|
||||
0x00b2, 0x0052, 0x00ae, 0x005a, 0x00ab, 0x005b, 0x00ab, 0x006d,
|
||||
0x00ae, 0x0072, 0x00b8, 0x0072, 0x00bc, 0x006d, 0x00bc, 0x005b,
|
||||
0x00b9, 0x005a, 0x00bc, 0x005c, 0x00be, 0x005c, 0x00c1, 0x005f,
|
||||
0x00c4, 0x0067, 0x00c4, 0x006d, 0x00c1, 0x0076, 0x00c0, 0x0077,
|
||||
0x00bd, 0x0077, 0x00bb, 0x0075, 0x00bd, 0x0073, 0x00bb, 0x0072,
|
||||
0x00be, 0x0070, 0x00be, 0x006a, 0x00a9, 0x006a, 0x00a9, 0x0070,
|
||||
0x00ac, 0x0072, 0x00aa, 0x0073, 0x00ac, 0x0075, 0x00aa, 0x0077,
|
||||
0x00a7, 0x0077, 0x00a3, 0x006d, 0x00a3, 0x0067, 0x00a6, 0x005f,
|
||||
0x00a9, 0x005c, 0x00ab, 0x005c, 0x00ac, 0x0077, 0x00ac, 0x007c,
|
||||
0x00ab, 0x007c, 0x00ab, 0x0084, 0x00ac, 0x0084, 0x00ac, 0x008b,
|
||||
0x00a9, 0x008e, 0x00a9, 0x0090, 0x00ae, 0x0090, 0x00ae, 0x008d,
|
||||
0x00b2, 0x008c, 0x00b2, 0x0087, 0x00b1, 0x0086, 0x00b1, 0x007b,
|
||||
0x00b2, 0x0079, 0x00b4, 0x0079, 0x00b4, 0x007d, 0x00b5, 0x007d,
|
||||
0x00b5, 0x0087, 0x00b4, 0x0087, 0x00b4, 0x008c, 0x00b6, 0x008c,
|
||||
0x00b9, 0x0091, 0x00b4, 0x0091, 0x00bd, 0x008f, 0x00ba, 0x008c,
|
||||
0x00ba, 0x0083, 0x00bb, 0x0082, 0x00bb, 0x0075, 0x00cc, 0x006e,
|
||||
0x00d4, 0x006c, 0x00db, 0x0069, 0x00d9, 0x0068, 0x00d9, 0x0064,
|
||||
0x00dc, 0x0064, 0x00dc, 0x0060, 0x00df, 0x0056, 0x00e5, 0x0052,
|
||||
0x00e7, 0x0052, 0x00ec, 0x0056, 0x00ef, 0x005d, 0x00f1, 0x0065,
|
||||
0x00f3, 0x0064, 0x00f3, 0x0069, 0x00f0, 0x0069, 0x00ec, 0x0065,
|
||||
0x00ec, 0x005e, 0x00e9, 0x005f, 0x00e9, 0x005a, 0x00e7, 0x0058,
|
||||
0x00e4, 0x0058, 0x00e3, 0x0054, 0x00e3, 0x0058, 0x00e1, 0x005c,
|
||||
0x00e4, 0x0061, 0x00e7, 0x0061, 0x00e9, 0x005f, 0x00eb, 0x005d,
|
||||
0x00e4, 0x0062, 0x00e0, 0x0064, 0x00e0, 0x0069, 0x00e2, 0x006b,
|
||||
0x00e0, 0x0072, 0x00e0, 0x0077, 0x00ec, 0x0077, 0x00ec, 0x0071,
|
||||
0x00ea, 0x006b, 0x00ec, 0x006a, 0x00ec, 0x0063, 0x00e7, 0x0063,
|
||||
0x00e7, 0x0065, 0x00e1, 0x0069, 0x00e3, 0x0068, 0x00e6, 0x0069,
|
||||
0x00ec, 0x005e, 0x00ea, 0x006b, 0x00e7, 0x006b, 0x00e7, 0x006a,
|
||||
0x00e5, 0x006a, 0x00e5, 0x006b, 0x00e2, 0x006b, 0x00df, 0x006c,
|
||||
0x00dc, 0x006f, 0x00dc, 0x0071, 0x00da, 0x0073, 0x00d8, 0x0073,
|
||||
0x00d8, 0x006f, 0x00dc, 0x006b, 0x00dc, 0x0069, 0x00dd, 0x0068,
|
||||
0x00ef, 0x0068, 0x00f0, 0x0069, 0x00f0, 0x006b, 0x00f4, 0x006f,
|
||||
0x00f4, 0x0072, 0x00f3, 0x0073, 0x00f2, 0x0073, 0x00f0, 0x0071,
|
||||
0x00f0, 0x006f, 0x00ec, 0x006b, 0x00ec, 0x007a, 0x00eb, 0x007b,
|
||||
0x00eb, 0x007f, 0x00ec, 0x0080, 0x00ec, 0x0084, 0x00eb, 0x0085,
|
||||
0x00eb, 0x008b, 0x00ec, 0x008c, 0x00ec, 0x008f, 0x00ed, 0x0091,
|
||||
0x00e9, 0x0091, 0x00e9, 0x008f, 0x00e7, 0x008d, 0x00e7, 0x0090,
|
||||
0x00e7, 0x0089, 0x00e8, 0x0088, 0x00e8, 0x0086, 0x00e7, 0x0085,
|
||||
0x00e7, 0x007d, 0x00e6, 0x007c, 0x00e6, 0x0078, 0x00e5, 0x007d,
|
||||
0x00e5, 0x0085, 0x00e4, 0x0086, 0x00e4, 0x0088, 0x00e5, 0x0089,
|
||||
0x00e5, 0x0090, 0x00e5, 0x008b, 0x00e3, 0x0091, 0x00df, 0x0091,
|
||||
0x00e0, 0x0090, 0x00e0, 0x008c, 0x00e2, 0x008b, 0x00e1, 0x0085,
|
||||
0x00e0, 0x0084, 0x00e0, 0x0080, 0x00e1, 0x007f, 0x00e1, 0x007c,
|
||||
0x00e0, 0x007b, 0x00e0, 0x0077
|
||||
};
|
||||
|
||||
/*
|
||||
game callables
|
||||
*/
|
||||
|
||||
void Parallaction_ns::_c_null(void *parm) {
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void Parallaction_ns::_c_play_boogie(void *parm) {
|
||||
|
||||
static uint16 flag = 1;
|
||||
|
||||
if (flag == 0)
|
||||
return;
|
||||
flag = 0;
|
||||
|
||||
_soundManI->setMusicFile("boogie2");
|
||||
_soundManI->playMusic();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
void Parallaction_ns::_c_score(void *parm) {
|
||||
_score += 5;
|
||||
return;
|
||||
}
|
||||
|
||||
void Parallaction_ns::_c_fade(void *parm) {
|
||||
|
||||
Palette pal;
|
||||
_gfx->setPalette(pal);
|
||||
|
||||
for (uint16 _di = 0; _di < 64; _di++) {
|
||||
pal.fadeTo(_gfx->_palette, 1);
|
||||
_gfx->setPalette(pal);
|
||||
|
||||
_gfx->updateScreen();
|
||||
_system->delayMillis(20);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
void Parallaction_ns::startMovingSarcophagus(ZonePtr sarc) {
|
||||
if (!_moveSarcGetZones[0]) {
|
||||
// bind sarcophagi zones
|
||||
_moveSarcGetZones[0] = _location.findZone("sarc1");
|
||||
_moveSarcGetZones[1] = _location.findZone("sarc2");
|
||||
_moveSarcGetZones[2] = _location.findZone("sarc3");
|
||||
_moveSarcGetZones[3] = _location.findZone("sarc4");
|
||||
_moveSarcGetZones[4] = _location.findZone("sarc5");
|
||||
|
||||
_moveSarcExaZones[0] = _location.findZone("sarc1exa");
|
||||
_moveSarcExaZones[1] = _location.findZone("sarc2exa");
|
||||
_moveSarcExaZones[2] = _location.findZone("sarc3exa");
|
||||
_moveSarcExaZones[3] = _location.findZone("sarc4exa");
|
||||
_moveSarcExaZones[4] = _location.findZone("sarc5exa");
|
||||
}
|
||||
/*
|
||||
Each sarcophagus is made of 2 visible zones: one responds to
|
||||
'get' actions, the other to 'examine'. We need to find out
|
||||
both so they can be moved.
|
||||
*/
|
||||
for (uint16 i = 0; i < 5; i++) {
|
||||
if (_moveSarcGetZones[i] == sarc) {
|
||||
_moveSarcExaZone = _moveSarcExaZones[i];
|
||||
_moveSarcGetZone = _moveSarcGetZones[i];
|
||||
}
|
||||
}
|
||||
|
||||
// calculate destination for the sarcophagus
|
||||
int16 destX = _freeSarcophagusSlotX;
|
||||
_sarcophagusDeltaX = destX - _moveSarcGetZone->getX(); // x movement delta
|
||||
int16 destY = _moveSarcGetZone->getY() - (_sarcophagusDeltaX / 20); // gently degrade y when moving sideways
|
||||
|
||||
// set the new empty position (maybe this should be done on stopMovingSarcophagus?)
|
||||
_freeSarcophagusSlotX = _moveSarcGetZone->getX();
|
||||
|
||||
// calculate which way and how many steps the character should move
|
||||
int16 numSteps = _sarcophagusDeltaX / 2; // default to right
|
||||
int16 delta = 2; // default to right
|
||||
if (_sarcophagusDeltaX < 0) {
|
||||
// move left
|
||||
numSteps = -numSteps; // keep numSteps positive
|
||||
delta = -delta; // make delta negative if moving to left
|
||||
}
|
||||
|
||||
// GROSS HACK: since there is no obvious way to provide additional parameters to a script,
|
||||
// the game packs the data needed to calculate the position of the 'sposta' animation in
|
||||
// the coordinate fields of the animation itself, which are accessible from the scripts.
|
||||
// In detail: the sarcophagus destination coords are stored into Z and F, while the number
|
||||
// of calculated steps and step length in X and Y. See any of the sarc#.script files in
|
||||
// disk2 for details about unpacking.
|
||||
AnimationPtr a = _location.findAnimation("sposta");
|
||||
a->forceXYZF(numSteps, delta, destX, destY);
|
||||
|
||||
// start moving
|
||||
_movingSarcophagus = true;
|
||||
}
|
||||
|
||||
void Parallaction_ns::stopMovingSarcophagus() {
|
||||
|
||||
// moves both sarcophagus zones at the destination, so that the user
|
||||
// can interact with them
|
||||
_moveSarcGetZone->translate(_sarcophagusDeltaX, -_sarcophagusDeltaX / 20);
|
||||
_moveSarcExaZone->translate(_sarcophagusDeltaX, -_sarcophagusDeltaX / 20);
|
||||
|
||||
// check if the puzzle has been completed, by verifying the position of
|
||||
// the sarcophagi
|
||||
if (_moveSarcGetZones[0]->getX() == 35 &&
|
||||
_moveSarcGetZones[1]->getX() == 68 &&
|
||||
_moveSarcGetZones[2]->getX() == 101 &&
|
||||
_moveSarcGetZones[3]->getX() == 134 &&
|
||||
_moveSarcGetZones[4]->getX() == 167) {
|
||||
|
||||
AnimationPtr a = _location.findAnimation("finito");
|
||||
a->_flags |= (kFlagsActive | kFlagsActing);
|
||||
setLocationFlags(0x20); // GROSS HACK: activates 'finito' flag in dinoit_museo.loc
|
||||
}
|
||||
|
||||
// stop moving
|
||||
_movingSarcophagus = false;
|
||||
}
|
||||
|
||||
void Parallaction_ns::_c_moveSarc(void *parm) {
|
||||
if (!_movingSarcophagus) {
|
||||
startMovingSarcophagus(*(ZonePtr *)parm);
|
||||
} else {
|
||||
stopMovingSarcophagus();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void Parallaction_ns::_c_contaFoglie(void *parm) {
|
||||
|
||||
num_foglie++;
|
||||
if (num_foglie != 6)
|
||||
return;
|
||||
|
||||
g_globalFlags |= 0x1000;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void Parallaction_ns::_c_zeroFoglie(void *parm) {
|
||||
num_foglie = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
void Parallaction_ns::_c_trasformata(void *parm) {
|
||||
g_engineFlags ^= kEngineTransformedDonna;
|
||||
// No need to invoke changeCharacter here, as
|
||||
// transformation happens on a location switch
|
||||
// and character change is automatically triggered.
|
||||
return;
|
||||
}
|
||||
|
||||
void Parallaction_ns::_c_offMouse(void *parm) {
|
||||
_input->setMouseState(MOUSE_DISABLED);
|
||||
g_engineFlags |= kEngineBlockInput;
|
||||
}
|
||||
|
||||
void Parallaction_ns::_c_onMouse(void *parm) {
|
||||
g_engineFlags &= ~kEngineBlockInput;
|
||||
_input->setMouseState(MOUSE_ENABLED_SHOW);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Parallaction_ns::_c_setMask(void *parm) {
|
||||
|
||||
if (!_gfx->_backgroundInfo->hasMask())
|
||||
return;
|
||||
|
||||
memset(_gfx->_backgroundInfo->_mask->data + 3600, 0, 3600);
|
||||
_gfx->_backgroundInfo->layers[1] = 500;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void Parallaction_ns::_c_endComment(void *param) {
|
||||
/*
|
||||
NOTE: this routine is only run when the full game
|
||||
is over. The following command in the scripts is
|
||||
QUIT, which causes the engine to exit and return
|
||||
to system.
|
||||
Since this routine is still *blocking*, QUIT is
|
||||
not executed until the user presses a mouse
|
||||
button. If the input is reconciled with the main
|
||||
loop then the command sequence must be suspended
|
||||
to avoid executing QUIT before this actual
|
||||
routine gets a chance to be run. See bug #4184
|
||||
for a similar situation.
|
||||
*/
|
||||
|
||||
showLocationComment(_location._endComment, true);
|
||||
|
||||
Palette pal(_gfx->_palette);
|
||||
pal.makeGrayscale();
|
||||
|
||||
for (uint di = 0; di < 64; di++) {
|
||||
_gfx->_palette.fadeTo(pal, 1);
|
||||
_gfx->setPalette(_gfx->_palette);
|
||||
|
||||
_gfx->updateScreen();
|
||||
_system->delayMillis(20);
|
||||
}
|
||||
|
||||
_input->waitForButtonEvent(kMouseLeftUp);
|
||||
_gfx->freeDialogueObjects();
|
||||
}
|
||||
|
||||
void Parallaction_ns::_c_frankenstein(void *parm) {
|
||||
|
||||
Palette pal0(_gfx->_palette);
|
||||
Palette pal1;
|
||||
|
||||
for (uint16 i = 0; i < 32; i++) {
|
||||
pal0.setEntry(i, -1, 0, 0); // leaves reds unchanged while zeroing other components
|
||||
}
|
||||
|
||||
for (uint16 _di = 0; _di < 30; _di++) {
|
||||
_system->delayMillis(20);
|
||||
_gfx->setPalette(pal0);
|
||||
_gfx->updateScreen();
|
||||
_system->delayMillis(20);
|
||||
_gfx->setPalette(pal1);
|
||||
_gfx->updateScreen();
|
||||
}
|
||||
|
||||
_gfx->setPalette(_gfx->_palette);
|
||||
_gfx->updateScreen();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
void Parallaction_ns::_c_finito(void *parm) {
|
||||
|
||||
_saveLoad->setPartComplete(_char.getBaseName());
|
||||
|
||||
cleanInventory(true);
|
||||
cleanupGame();
|
||||
|
||||
_gfx->setPalette(_gfx->_palette);
|
||||
|
||||
startEndPartSequence();
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void Parallaction_ns::_c_ridux(void *parm) {
|
||||
changeCharacter(g_minidinoName);
|
||||
return;
|
||||
}
|
||||
|
||||
void Parallaction_ns::_c_testResult(void *parm) {
|
||||
if (_inTestResult) { // NOTE: _inTestResult has been added because the scripts call _c_testResult multiple times to cope with
|
||||
// the multiple buffering that was used in the original engine. _inTestResult now prevents the engine
|
||||
// from crashing when the scripts are executed.
|
||||
return;
|
||||
}
|
||||
_inTestResult = true;
|
||||
|
||||
_gfx->freeLabels();
|
||||
_gfx->updateScreen();
|
||||
|
||||
parseLocation("common");
|
||||
|
||||
destroyTestResultLabels();
|
||||
|
||||
_testResultLabels[0] = _gfx->createLabel(_menuFont, _location._slideText[0].c_str(), 1);
|
||||
_testResultLabels[1] = _gfx->createLabel(_menuFont, _location._slideText[1].c_str(), 1);
|
||||
|
||||
_gfx->showLabel(_testResultLabels[0], CENTER_LABEL_HORIZONTAL, 38);
|
||||
_gfx->showLabel(_testResultLabels[1], CENTER_LABEL_HORIZONTAL, 58);
|
||||
|
||||
const char *charName = nullptr;
|
||||
switch (_characterVoiceID) {
|
||||
case kDoug:
|
||||
charName = "Doug Nuts";
|
||||
break;
|
||||
case kDonna:
|
||||
charName = "Donna Fatale";
|
||||
break;
|
||||
case kDino:
|
||||
charName = "Dino Fagioli";
|
||||
break;
|
||||
}
|
||||
|
||||
if (charName != nullptr) {
|
||||
sayText(charName, Common::TextToSpeechManager::QUEUE);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void Parallaction_ns::_c_offSound(void *) {
|
||||
_soundManI->stopSfx(0);
|
||||
_soundManI->stopSfx(1);
|
||||
_soundManI->stopSfx(2);
|
||||
_soundManI->stopSfx(3);
|
||||
}
|
||||
|
||||
void Parallaction_ns::_c_startMusic(void *) {
|
||||
_soundManI->playMusic();
|
||||
}
|
||||
|
||||
void Parallaction_ns::_c_closeMusic(void *) {
|
||||
_soundManI->stopMusic();
|
||||
}
|
||||
|
||||
/*
|
||||
intro callables
|
||||
*/
|
||||
|
||||
void Parallaction_ns::_c_startIntro(void *parm) {
|
||||
_rightHandAnim = _location.findAnimation("righthand");
|
||||
|
||||
if (getPlatform() == Common::kPlatformDOS) {
|
||||
_soundManI->setMusicFile("intro");
|
||||
_soundManI->playMusic();
|
||||
}
|
||||
|
||||
g_engineFlags |= kEngineBlockInput;
|
||||
_input->setMouseState(MOUSE_DISABLED);
|
||||
_intro = true;
|
||||
}
|
||||
|
||||
void Parallaction_ns::_c_endIntro(void *parm) {
|
||||
if (getFeatures() & GF_DEMO) {
|
||||
// NOTE: suspend command execution queue, to
|
||||
// avoid running the QUIT command before
|
||||
// credits are displayed. This solves bug
|
||||
// #4184.
|
||||
// Execution of the command list will resume
|
||||
// as soon as runGameFrame is run.
|
||||
_cmdExec->suspend();
|
||||
}
|
||||
startCreditSequence();
|
||||
_intro = false;
|
||||
}
|
||||
|
||||
void Parallaction_ns::_c_moveSheet(void *parm) {
|
||||
|
||||
static uint16 x = 319;
|
||||
|
||||
if (x > 66)
|
||||
x -= 16;
|
||||
|
||||
Common::Rect r;
|
||||
|
||||
r.left = x;
|
||||
r.top = 47;
|
||||
r.right = (x + 32 > 319) ? 319 : (x + 32);
|
||||
r.bottom = 199;
|
||||
_gfx->fillBackground(r, 1);
|
||||
|
||||
if (x >= 104) return;
|
||||
|
||||
r.left = x+215;
|
||||
r.top = 47;
|
||||
r.right = (x + 247 > 319) ? 319 : (x + 247);
|
||||
r.bottom = 199;
|
||||
_gfx->fillBackground(r, 12);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
class ZeroMaskPrimitives : public Graphics::Primitives {
|
||||
void drawPoint(int x, int y, uint32 color, void *data) override {
|
||||
BackgroundInfo *info = (BackgroundInfo *)data;
|
||||
|
||||
uint16 _ax = x + y * info->_mask->w;
|
||||
info->_mask->data[_ax >> 2] &= ~(3 << ((_ax & 3) << 1));
|
||||
}
|
||||
};
|
||||
|
||||
void Parallaction_ns::_c_sketch(void *parm) {
|
||||
|
||||
static uint16 index = 1;
|
||||
|
||||
uint16 newx;
|
||||
uint16 newy;
|
||||
|
||||
uint16 oldy = _rightHandPositions[2*(index-1)+1];
|
||||
uint16 oldx = _rightHandPositions[2*(index-1)];
|
||||
|
||||
// WORKAROUND: original code overflowed _rightHandPositions by trying
|
||||
// to access elements at positions 684 and 685. That used to happen
|
||||
// when index == 342. Code now checks for this possibility and assigns
|
||||
// the last valid value to the new coordinates for drawing without
|
||||
// accessing the array.
|
||||
if (index == 342) {
|
||||
newy = oldy;
|
||||
newx = oldx;
|
||||
} else {
|
||||
newy = _rightHandPositions[2*index+1];
|
||||
newx = _rightHandPositions[2*index];
|
||||
}
|
||||
|
||||
if (_gfx->_backgroundInfo->hasMask()) {
|
||||
ZeroMaskPrimitives().drawLine(oldx, oldy, newx, newy, 0, _gfx->_backgroundInfo);
|
||||
}
|
||||
|
||||
_rightHandAnim->setX(newx);
|
||||
_rightHandAnim->setY(newy - 20);
|
||||
|
||||
index++;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void Parallaction_ns::_c_shade(void *parm) {
|
||||
|
||||
Common::Rect r(
|
||||
_rightHandAnim->getX() - 36,
|
||||
_rightHandAnim->getY() - 36,
|
||||
_rightHandAnim->getX(),
|
||||
_rightHandAnim->getY()
|
||||
);
|
||||
|
||||
uint16 _di = r.left/4 + r.top * _gfx->_backgroundInfo->_mask->internalWidth;
|
||||
|
||||
for (uint16 _si = r.top; _si < r.bottom; _si++) {
|
||||
memset(_gfx->_backgroundInfo->_mask->data + _di, 0, r.width()/4+1);
|
||||
_di += _gfx->_backgroundInfo->_mask->internalWidth;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
int16 projectorProgram[] = {
|
||||
0, 50, 1, 50, 2, 49, 3, 49, 4, 48, 5, 48, 6, 47, 7, 47, 8, 46, 9, 46, 10, 45, 11, 45,
|
||||
12, 44, 13, 44, 14, 43, 15, 43, 16, 42, 17, 42, 18, 41, 19, 41, 20, 40, 21, 40, 22, 39,
|
||||
23, 39, 24, 38, 25, 38, 26, 37, 27, 37, 28, 36, 29, 36, 30, 35, 31, 35, 32, 34, 33, 34,
|
||||
34, 33, 35, 33, 36, 32, 37, 32, 38, 31, 39, 31, 40, 30, 41, 30, 42, 29, 43, 29, 44, 28,
|
||||
45, 28, 46, 27, 47, 27, 48, 26, 49, 26, 50, 25, 51, 25, 52, 24, 53, 24, 54, 23, 55, 23,
|
||||
56, 22, 57, 22, 58, 21, 59, 21, 60, 20, 61, 20, 62, 19, 63, 19, 64, 18, 65, 18, 66, 17,
|
||||
67, 17, 68, 16, 69, 16, 70, 15, 71, 15, 72, 14, 73, 14, 74, 13, 75, 13, 76, 12, 77, 12,
|
||||
78, 11, 79, 11, 80, 10, 81, 10, 82, 9, 83, 9, 84, 8, 85, 8, 86, 7, 87, 7, 88, 6, 89, 6,
|
||||
90, 5, 91, 5, 92, 4, 93, 4, 94, 3, 95, 3, 96, 2, 97, 2, 98, 1, 99, 1, 100, 0, 101, 0,
|
||||
102, 1, 103, 1, 104, 2, 105, 2, 106, 3, 107, 3, 108, 4, 109, 4, 110, 5, 111, 5, 112, 6,
|
||||
113, 6, 114, 7, 115, 7, 116, 8, 117, 8, 118, 9, 119, 9, 120, 10, 121, 10, 122, 11, 123,
|
||||
11, 124, 12, 125, 12, 126, 13, 127, 13, 128, 14, 129, 14, 130, 15, 131, 15, 132, 16, 133,
|
||||
16, 134, 17, 135, 17, 136, 18, 137, 18, 138, 19, 139, 19, 140, 20, 141, 20, 142, 21, 143,
|
||||
21, 144, 22, 145, 22, 146, 23, 147, 23, 148, 24, 149, 24, 150, 25, 149, 25, 148, 25, 147,
|
||||
25, 146, 25, 145, 25, 144, 25, 143, 25, 142, 25, 141, 25, 140, 25, 139, 25, 138, 25, 137,
|
||||
25, 136, 25, 135, 25, 134, 25, 133, 25, 132, 25, 131, 25, 130, 25, 129, 25, 128, 25, 127,
|
||||
25, 126, 25, 125, 25, 124, 25, 123, 25, 122, 25, 121, 25, 120, 25, 119, 25, 118, 25, 117,
|
||||
25, 116, 25, 115, 25, 114, 25, 113, 25, 112, 25, 111, 25, 110, 25, -1, -1
|
||||
};
|
||||
|
||||
void Parallaction_ns::_c_projector(void *) {
|
||||
_gfx->setHalfbriteMode(true);
|
||||
_gfx->setProjectorProgram(projectorProgram);
|
||||
}
|
||||
|
||||
void Parallaction_ns::_c_HBOff(void *) {
|
||||
_gfx->setHalfbriteMode(false);
|
||||
}
|
||||
|
||||
void Parallaction_ns::_c_HBOn(void *) {
|
||||
_gfx->setHalfbriteMode(true);
|
||||
}
|
||||
|
||||
|
||||
} // namespace Parallaction
|
||||
3
engines/parallaction/configure.engine
Normal file
3
engines/parallaction/configure.engine
Normal file
@@ -0,0 +1,3 @@
|
||||
# This file is included from the main "configure" script
|
||||
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] [components]
|
||||
add_engine parallaction "Parallaction" yes "" "" "" "midi"
|
||||
3
engines/parallaction/credits.pl
Normal file
3
engines/parallaction/credits.pl
Normal file
@@ -0,0 +1,3 @@
|
||||
begin_section("Parallaction");
|
||||
add_person("", "peres", "");
|
||||
end_section();
|
||||
330
engines/parallaction/debug.cpp
Normal file
330
engines/parallaction/debug.cpp
Normal file
@@ -0,0 +1,330 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include "common/system.h"
|
||||
|
||||
#include "parallaction/parallaction.h"
|
||||
#include "parallaction/debug.h"
|
||||
|
||||
namespace Parallaction {
|
||||
|
||||
|
||||
Debugger::Debugger(Parallaction *vm)
|
||||
: GUI::Debugger() {
|
||||
_vm = vm;
|
||||
_mouseState = MOUSE_ENABLED_SHOW;
|
||||
|
||||
registerCmd("continue", WRAP_METHOD(Debugger, cmdExit));
|
||||
registerCmd("location", WRAP_METHOD(Debugger, Cmd_Location));
|
||||
registerCmd("give", WRAP_METHOD(Debugger, Cmd_Give));
|
||||
registerCmd("zones", WRAP_METHOD(Debugger, Cmd_Zones));
|
||||
registerCmd("animations", WRAP_METHOD(Debugger, Cmd_Animations));
|
||||
registerCmd("globalflags",WRAP_METHOD(Debugger, Cmd_GlobalFlags));
|
||||
registerCmd("toggleglobalflag",WRAP_METHOD(Debugger, Cmd_ToggleGlobalFlag));
|
||||
registerCmd("localflags", WRAP_METHOD(Debugger, Cmd_LocalFlags));
|
||||
registerCmd("locations", WRAP_METHOD(Debugger, Cmd_Locations));
|
||||
registerCmd("gfxobjects", WRAP_METHOD(Debugger, Cmd_GfxObjects));
|
||||
registerCmd("programs", WRAP_METHOD(Debugger, Cmd_Programs));
|
||||
registerCmd("showmouse", WRAP_METHOD(Debugger, Cmd_ShowMouse));
|
||||
}
|
||||
|
||||
|
||||
void Debugger::preEnter() {
|
||||
_mouseState = _vm->_input->getMouseState();
|
||||
GUI::Debugger::preEnter();
|
||||
}
|
||||
|
||||
|
||||
void Debugger::postEnter() {
|
||||
GUI::Debugger::postEnter();
|
||||
_vm->_input->setMouseState(_mouseState);
|
||||
_vm->_input->setArrowCursor(); // unselects the active item, if any
|
||||
}
|
||||
|
||||
bool Debugger::Cmd_Location(int argc, const char **argv) {
|
||||
|
||||
const char *character; // = _vm->_char.getName();
|
||||
const char *location; // = _vm->_location._name;
|
||||
|
||||
char tmp[PATH_LEN];
|
||||
|
||||
switch (argc) {
|
||||
case 3:
|
||||
character = const_cast<char *>(argv[2]);
|
||||
location = const_cast<char *>(argv[1]);
|
||||
Common::sprintf_s(tmp, "%s.%s", location, character);
|
||||
_vm->scheduleLocationSwitch(tmp);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
location = const_cast<char *>(argv[1]);
|
||||
_vm->scheduleLocationSwitch(location);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
debugPrintf("location <location name> [character name]\n");
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Debugger::Cmd_Locations(int argc, const char **argv) {
|
||||
|
||||
debugPrintf("+------------------------------+---------+\n"
|
||||
"| location name | flags |\n"
|
||||
"+------------------------------+---------+\n");
|
||||
for (uint i = 0; i < _vm->_numLocations; i++) {
|
||||
debugPrintf("|%-30s| %08x|\n", _vm->_locationNames[i], _vm->_localFlags[i]);
|
||||
}
|
||||
debugPrintf("+------------------------------+---------+\n");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Debugger::Cmd_GlobalFlags(int argc, const char **argv) {
|
||||
|
||||
uint32 flags = g_globalFlags;
|
||||
|
||||
debugPrintf("+------------------------------+---------+\n"
|
||||
"| flag name | value |\n"
|
||||
"+------------------------------+---------+\n");
|
||||
for (uint i = 0; i < _vm->_globalFlagsNames->count(); i++) {
|
||||
const char *value = ((flags & (1 << i)) == 0) ? "OFF" : "ON";
|
||||
debugPrintf("|%-30s| %-6s|\n", _vm->_globalFlagsNames->item(i), value);
|
||||
}
|
||||
debugPrintf("+------------------------------+---------+\n");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Debugger::Cmd_ToggleGlobalFlag(int argc, const char **argv) {
|
||||
|
||||
int i;
|
||||
|
||||
switch (argc) {
|
||||
case 2:
|
||||
i = _vm->_globalFlagsNames->lookup(argv[1]);
|
||||
if (i == Table::notFound) {
|
||||
debugPrintf("invalid flag '%s'\n", argv[1]);
|
||||
} else {
|
||||
i--;
|
||||
if ((g_globalFlags & (1 << i)) == 0)
|
||||
g_globalFlags |= (1 << i);
|
||||
else
|
||||
g_globalFlags &= ~(1 << i);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
debugPrintf("toggleglobalflag <flag name>\n");
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Debugger::Cmd_LocalFlags(int argc, const char **argv) {
|
||||
|
||||
uint32 flags = _vm->getLocationFlags();
|
||||
|
||||
debugPrintf("+------------------------------+---------+\n"
|
||||
"| flag name | value |\n"
|
||||
"+------------------------------+---------+\n");
|
||||
for (uint i = 0; i < _vm->_localFlagNames->count(); i++) {
|
||||
const char *value = ((flags & (1 << i)) == 0) ? "OFF" : "ON";
|
||||
debugPrintf("|%-30s| %-6s|\n", _vm->_localFlagNames->item(i), value);
|
||||
}
|
||||
debugPrintf("+------------------------------+---------+\n");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Debugger::Cmd_Give(int argc, const char **argv) {
|
||||
|
||||
if (argc == 1) {
|
||||
debugPrintf("give <item name>\n");
|
||||
} else {
|
||||
int index = _vm->_objectsNames->lookup(argv[1]);
|
||||
if (index != Table::notFound)
|
||||
_vm->addInventoryItem(index + 4);
|
||||
else
|
||||
debugPrintf("invalid item name '%s'\n", argv[1]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool Debugger::Cmd_Zones(int argc, const char **argv) {
|
||||
|
||||
ZoneList::iterator b = _vm->_location._zones.begin();
|
||||
ZoneList::iterator e = _vm->_location._zones.end();
|
||||
Common::Rect r;
|
||||
|
||||
debugPrintf("+--------------------+---+---+---+---+--------+--------+\n"
|
||||
"| name | l | t | r | b | type | flag |\n"
|
||||
"+--------------------+---+---+---+---+--------+--------+\n");
|
||||
for ( ; b != e; ++b) {
|
||||
ZonePtr z = *b;
|
||||
z->getRect(r);
|
||||
debugPrintf("|%-20s|%3i|%3i|%3i|%3i|%8x|%8x|\n", z->_name, r.left, r.top, r.right, r.bottom, z->_type, z->_flags );
|
||||
}
|
||||
debugPrintf("+--------------------+---+---+---+---+--------+--------+\n");
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Common::String Debugger::decodeZoneFlags(uint32 flags) {
|
||||
const char *descs[33] = {
|
||||
"none", // 0
|
||||
"closed", // 1
|
||||
"active", // 2
|
||||
"remove", // 4
|
||||
"acting", // 8
|
||||
"locked", // 0x10
|
||||
"fixed", // 0x20
|
||||
"noname", // 0x40
|
||||
"nomasked", // 0x80
|
||||
"looping", // 0x100
|
||||
"added", // 0x200
|
||||
"character",// 0x400
|
||||
"nowalk", // 0x800
|
||||
"yourself", // 0x1000
|
||||
"scaled", // 0x2000
|
||||
"selfuse", // 0x4000
|
||||
"0x8000", // 0x8000
|
||||
"0x10000",
|
||||
"0x20000",
|
||||
"0x40000",
|
||||
"0x80000",
|
||||
"0x100000",
|
||||
"0x200000",
|
||||
"0x400000",
|
||||
"0x800000",
|
||||
"isanimation", // 0x1000000
|
||||
"animlinked", // 0x2000000
|
||||
"0x4000000",
|
||||
"0x8000000",
|
||||
"0x10000000",
|
||||
"0x20000000",
|
||||
"0x40000000",
|
||||
"0x80000000"
|
||||
};
|
||||
|
||||
uint32 mask = 1;
|
||||
const char *matches[32];
|
||||
uint numMatches = 0;
|
||||
for (uint32 i = 1; i < 32; i++, mask<<=1) {
|
||||
if (flags & mask) {
|
||||
matches[numMatches] = descs[i];
|
||||
numMatches++;
|
||||
}
|
||||
}
|
||||
if (numMatches == 0) {
|
||||
matches[0] = descs[0];
|
||||
numMatches = 1;
|
||||
}
|
||||
|
||||
Common::String s(matches[0]);
|
||||
for (uint32 j = 1; j < numMatches; j++) {
|
||||
s += '+';
|
||||
s += matches[j];
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
bool Debugger::Cmd_Animations(int argc, const char **argv) {
|
||||
|
||||
AnimationList::iterator b = _vm->_location._animations.begin();
|
||||
AnimationList::iterator e = _vm->_location._animations.end();
|
||||
Common::String flags;
|
||||
|
||||
debugPrintf("+--------------------+----+----+----+---+--------+----------------------------------------+\n"
|
||||
"| name | x | y | z | f | type | flags | \n"
|
||||
"+--------------------+----+----+----+---+--------+----------------------------------------+\n");
|
||||
for ( ; b != e; ++b) {
|
||||
AnimationPtr a = *b;
|
||||
flags = decodeZoneFlags(a->_flags);
|
||||
debugPrintf("|%-20s|%4i|%4i|%4i|%3i|%8x|%-40s|\n", a->_name, a->getX(), a->getY(), a->getZ(), a->getF(), a->_type, flags.c_str() );
|
||||
}
|
||||
debugPrintf("+--------------------+---+---+---+---+--------+----------------------------------------+\n");
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Debugger::Cmd_GfxObjects(int argc, const char **argv) {
|
||||
|
||||
const char *objType[] = { "DOOR", "GET", "ANIM" };
|
||||
|
||||
debugPrintf("+--------------------+-----+-----+-----+-----+-----+-------+-----+--------+\n"
|
||||
"| name | x | y | w | h | z | layer | f | type |\n"
|
||||
"+--------------------+-----+-----+-----+-----+-----+-------+-----+--------+\n");
|
||||
|
||||
GfxObjArray::iterator b = _vm->_gfx->_sceneObjects.begin();
|
||||
GfxObjArray::iterator e = _vm->_gfx->_sceneObjects.end();
|
||||
Common::Rect r;
|
||||
|
||||
for ( ; b != e; ++b) {
|
||||
GfxObj *obj = *b;
|
||||
obj->getRect(obj->frame, r);
|
||||
debugPrintf("|%-20s|%5i|%5i|%5i|%5i|%5i|%7i|%5i|%8s|\n", obj->getName(), r.left, r.top, r.width(), r.height(),
|
||||
obj->z, obj->layer, obj->frame, objType[obj->type]);
|
||||
}
|
||||
|
||||
debugPrintf("+--------------------+-----+-----+-----+-----+-----+-------+-----+--------+\n");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Debugger::Cmd_Programs(int argc, const char** argv) {
|
||||
|
||||
ProgramList::iterator b = _vm->_location._programs.begin();
|
||||
ProgramList::iterator e = _vm->_location._programs.end();
|
||||
|
||||
const char *status[] = { "idle", "running", "completed" };
|
||||
|
||||
int i = 1;
|
||||
|
||||
debugPrintf("+---+--------------------+--------+----------+\n"
|
||||
"| # | bound animation | size | status |\n"
|
||||
"+---+--------------------+--------+----------+\n");
|
||||
for ( ; b != e; b++, i++) {
|
||||
ProgramPtr p = *b;
|
||||
debugPrintf("|%3i|%-20s|%8i|%-10s|\n", i, p->_anim->_name, p->_instructions.size(), status[p->_status] );
|
||||
}
|
||||
debugPrintf("+---+--------------------+--------+----------+\n");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Debugger::Cmd_ShowMouse(int argc, const char** argv) {
|
||||
_mouseState = MOUSE_ENABLED_SHOW;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Parallaction
|
||||
41
engines/parallaction/debug.h
Normal file
41
engines/parallaction/debug.h
Normal file
@@ -0,0 +1,41 @@
|
||||
|
||||
#ifndef PARALLACTION_DEBUGGER_H
|
||||
#define PARALLACTION_DEBUGGER_H
|
||||
|
||||
#include "gui/debugger.h"
|
||||
#include "parallaction/input.h"
|
||||
|
||||
namespace Parallaction {
|
||||
|
||||
class Parallaction;
|
||||
|
||||
class Debugger : public GUI::Debugger {
|
||||
public:
|
||||
Debugger(Parallaction *vm);
|
||||
|
||||
private:
|
||||
void preEnter() override;
|
||||
void postEnter() override;
|
||||
|
||||
private:
|
||||
Parallaction *_vm;
|
||||
MouseTriState _mouseState;
|
||||
|
||||
bool Cmd_Location(int argc, const char **argv);
|
||||
bool Cmd_Give(int argc, const char **argv);
|
||||
bool Cmd_Zones(int argc, const char **argv);
|
||||
bool Cmd_Animations(int argc, const char **argv);
|
||||
bool Cmd_LocalFlags(int argc, const char **argv);
|
||||
bool Cmd_GlobalFlags(int argc, const char **argv);
|
||||
bool Cmd_ToggleGlobalFlag(int argc, const char **argv);
|
||||
bool Cmd_Locations(int argc, const char **argv);
|
||||
bool Cmd_GfxObjects(int argc, const char **argv);
|
||||
bool Cmd_Programs(int argc, const char** argv);
|
||||
bool Cmd_ShowMouse(int argc, const char** argv);
|
||||
|
||||
Common::String decodeZoneFlags(uint32 flags);
|
||||
};
|
||||
|
||||
} // End of namespace Parallaction
|
||||
|
||||
#endif
|
||||
252
engines/parallaction/detection.cpp
Normal file
252
engines/parallaction/detection.cpp
Normal file
@@ -0,0 +1,252 @@
|
||||
/* 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/config-manager.h"
|
||||
#include "engines/advancedDetector.h"
|
||||
#include "common/system.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
#include "parallaction/detection.h"
|
||||
#include "parallaction/parallaction.h"
|
||||
|
||||
static const PlainGameDescriptor parallactionGames[] = {
|
||||
{"nippon", "Nippon Safes, Inc."},
|
||||
{"bra", "The Big Red Adventure"},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
static const DebugChannelDef debugFlagList[] = {
|
||||
{Parallaction::kDebugDialogue, "dialogue", "Dialogues debug level"},
|
||||
{Parallaction::kDebugParser, "parser", "Parser debug level"},
|
||||
{Parallaction::kDebugDisk, "disk", "Disk debug level"},
|
||||
{Parallaction::kDebugWalk, "walk", "Walk debug level"},
|
||||
{Parallaction::kDebugGraphics, "gfx", "Gfx debug level"},
|
||||
{Parallaction::kDebugExec, "exec", "Execution debug level"},
|
||||
{Parallaction::kDebugInput, "input", "Input debug level"},
|
||||
{Parallaction::kDebugAudio, "audio", "Audio debug level"},
|
||||
{Parallaction::kDebugMenu, "menu", "Menu debug level"},
|
||||
{Parallaction::kDebugInventory, "inventory", "Inventory debug level"},
|
||||
DEBUG_CHANNEL_END
|
||||
};
|
||||
|
||||
namespace Parallaction {
|
||||
|
||||
static const PARALLACTIONGameDescription gameDescriptions[] = {
|
||||
{
|
||||
{
|
||||
"nippon",
|
||||
"Multi-lingual",
|
||||
{
|
||||
{ "disk1", 0, "610363308258e926dbabd5a9e7bb769f", 1060142},
|
||||
{ "disk2", 0, "bfdd7bcfbc226f4acf3f67fa9efa2826", 907205},
|
||||
{ "disk3", 0, "eec08180240888d76e3cfe3e183d5d5d", 1030721},
|
||||
{ "disk4", 0, "5bffddc7db226bdaa7dd3e10e5a15e68", 1151403},
|
||||
{ "en", 0, "65cbfa81eafe308621184796ed116700", 399360},
|
||||
{ "fr", 0, "ac20c743ea10f2cb4491f76c5644582c", 410624},
|
||||
{ "ge", 0, "50916bfa34aee1380e0e959b37eceb5a", 410624},
|
||||
{ "it", 0, "89964aef04d2c53a615ee8983caf2775", 410624},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::UNK_LANG,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO2(GUIO_NOSPEECH, GAMEOPTION_TTS)
|
||||
},
|
||||
GType_Nippon,
|
||||
GF_LANG_EN | GF_LANG_FR | GF_LANG_DE | GF_LANG_IT | GF_LANG_MULT,
|
||||
},
|
||||
|
||||
// Alternate version with patched 'FR' file.
|
||||
// Bugreport #13630
|
||||
{
|
||||
{
|
||||
"nippon",
|
||||
"Multi-lingual alt",
|
||||
{
|
||||
{ "disk1", 0, "610363308258e926dbabd5a9e7bb769f", 1060142},
|
||||
{ "disk2", 0, "bfdd7bcfbc226f4acf3f67fa9efa2826", 907205},
|
||||
{ "disk3", 0, "eec08180240888d76e3cfe3e183d5d5d", 1030721},
|
||||
{ "disk4", 0, "5bffddc7db226bdaa7dd3e10e5a15e68", 1151403},
|
||||
{ "en", 0, "65cbfa81eafe308621184796ed116700", 399360},
|
||||
{ "fr", 0, "fd368bab0a8854021870b2199255b7ec", 410624},
|
||||
{ "ge", 0, "50916bfa34aee1380e0e959b37eceb5a", 410624},
|
||||
{ "it", 0, "89964aef04d2c53a615ee8983caf2775", 410624},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::UNK_LANG,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO2(GUIO_NOSPEECH, GAMEOPTION_TTS)
|
||||
},
|
||||
GType_Nippon,
|
||||
GF_LANG_EN | GF_LANG_FR | GF_LANG_DE | GF_LANG_IT | GF_LANG_MULT,
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
"nippon",
|
||||
"Multi-lingual",
|
||||
{
|
||||
{ "disk0", 0, "16cca8724fdf4ec8234385497a0c728a", 208437},
|
||||
{ "disk1", 0, "6b29987cfe2298d3745b6d99a0080c44", 901120},
|
||||
{ "disk2", 0, "2db40bf8198a57d18e4471a6deaab970", 901120},
|
||||
{ "disk3", 0, "0486972962b2bfc230e789b9f88f9ec8", 901120},
|
||||
{ "disk4", 0, "6f625e7f05da4a2f57d6b62d57013614", 901120},
|
||||
{ "en", 0, "c9ec4f2267d736eef4877c5133e1c6e1", 174074},
|
||||
{ "ge", 0, "42d6f10a4ebdadb25a6161d53ea4f450", 182298},
|
||||
{ "fr", 0, "cf17defc24f143d1a9acb52eaa5c2406", 179958},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::UNK_LANG,
|
||||
Common::kPlatformAmiga,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO2(GUIO_NOSPEECH, GAMEOPTION_TTS)
|
||||
},
|
||||
GType_Nippon,
|
||||
GF_LANG_EN | GF_LANG_FR | GF_LANG_DE | GF_LANG_MULT,
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
"nippon",
|
||||
"Demo",
|
||||
AD_ENTRY2s("disk0", "6fed2e18a6bfe5e8bb49144fcc95fd11", 624640,
|
||||
"fr", "72f04be4320dfac719431419ec2b9a0d", 12778),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformAmiga,
|
||||
ADGF_DEMO,
|
||||
GUIO2(GUIO_NOSPEECH, GAMEOPTION_TTS)
|
||||
},
|
||||
GType_Nippon,
|
||||
GF_LANG_EN | GF_DEMO,
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
{
|
||||
"nippon",
|
||||
"",
|
||||
{
|
||||
{ "disk0", 0, "bfee75d8015f1fb97e75dbe08df4bef7", 354304},
|
||||
{ "disk1", 0, "f339dd108c1a1f5cd4853d9966e5d01f", 901120},
|
||||
{ "disk2", 0, "2db40bf8198a57d18e4471a6deaab970", 901120},
|
||||
{ "disk3", 0, "0486972962b2bfc230e789b9f88f9ec8", 901120},
|
||||
{ "disk4", 0, "6f625e7f05da4a2f57d6b62d57013614", 901120},
|
||||
{ "it", 0, "746088eb8de2b2713685d243a4e4678f", 185344},
|
||||
AD_LISTEND
|
||||
},
|
||||
Common::IT_ITA,
|
||||
Common::kPlatformAmiga,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO2(GUIO_NOSPEECH, GAMEOPTION_TTS)
|
||||
},
|
||||
GType_Nippon,
|
||||
GF_LANG_IT,
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
"bra",
|
||||
"Multi-lingual",
|
||||
AD_ENTRY2s("tbra.bmp", "3174c095a0e1a4eaf05c403445711e9b", 80972,
|
||||
"russia.fnt", "57f85ff62aeca6334fdcaf718e313b49", 18344),
|
||||
Common::UNK_LANG,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_UNSTABLE,
|
||||
GUIO2(GUIO_NOSPEECH, GAMEOPTION_TTS)
|
||||
},
|
||||
GType_BRA,
|
||||
GF_LANG_EN | GF_LANG_FR | GF_LANG_DE | GF_LANG_IT | GF_LANG_MULT,
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
"bra",
|
||||
"Demo",
|
||||
AD_ENTRY1s("russia.fnt", "0dd55251d2886d6783718df2b184bf97", 10649),
|
||||
Common::UNK_LANG,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_DEMO | ADGF_UNSTABLE,
|
||||
GUIO2(GUIO_NOSPEECH, GAMEOPTION_TTS)
|
||||
},
|
||||
GType_BRA,
|
||||
GF_LANG_EN | GF_DEMO,
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
"bra",
|
||||
"En/Fr/De",
|
||||
AD_ENTRY1s("request.win", "7a844b9518310e4cc72eabb9c0340314", 6497),
|
||||
Common::UNK_LANG,
|
||||
Common::kPlatformAmiga,
|
||||
ADGF_UNSTABLE,
|
||||
GUIO2(GUIO_NOSPEECH, GAMEOPTION_TTS)
|
||||
},
|
||||
GType_BRA,
|
||||
GF_LANG_EN | GF_LANG_FR | GF_LANG_DE | GF_LANG_MULT,
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
"bra",
|
||||
"Demo",
|
||||
AD_ENTRY1s("request.win", "3b6a99ffd626e324b663839bbad59cb3", 5326),
|
||||
Common::UNK_LANG,
|
||||
Common::kPlatformAmiga,
|
||||
ADGF_DEMO | ADGF_UNSTABLE,
|
||||
GUIO2(GUIO_NOSPEECH, GAMEOPTION_TTS)
|
||||
},
|
||||
GType_BRA,
|
||||
GF_LANG_EN | GF_DEMO,
|
||||
},
|
||||
|
||||
{ AD_TABLE_END_MARKER, 0, 0 }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
class ParallactionMetaEngineDetection : public AdvancedMetaEngineDetection<Parallaction::PARALLACTIONGameDescription> {
|
||||
public:
|
||||
ParallactionMetaEngineDetection() : AdvancedMetaEngineDetection(Parallaction::gameDescriptions, parallactionGames) {
|
||||
_guiOptions = GUIO1(GUIO_NOLAUNCHLOAD);
|
||||
}
|
||||
|
||||
const char *getName() const override {
|
||||
return "parallaction";
|
||||
}
|
||||
|
||||
const char *getEngineName() const override {
|
||||
return "Parallaction";
|
||||
}
|
||||
|
||||
const char *getOriginalCopyright() const override {
|
||||
return "Nippon Safes, Inc. (C) Dynabyte";
|
||||
}
|
||||
|
||||
const DebugChannelDef *getDebugChannels() const override {
|
||||
return debugFlagList;
|
||||
}
|
||||
};
|
||||
|
||||
REGISTER_PLUGIN_STATIC(PARALLACTION_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, ParallactionMetaEngineDetection);
|
||||
56
engines/parallaction/detection.h
Normal file
56
engines/parallaction/detection.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/* 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 PARALLACTION_DETECTION_H
|
||||
#define PARALLACTION_DETECTION_H
|
||||
|
||||
#include "engines/advancedDetector.h"
|
||||
|
||||
namespace Parallaction {
|
||||
|
||||
enum {
|
||||
GF_DEMO = 1 << 0,
|
||||
GF_LANG_EN = 1 << 1,
|
||||
GF_LANG_FR = 1 << 2,
|
||||
GF_LANG_DE = 1 << 3,
|
||||
GF_LANG_IT = 1 << 4,
|
||||
GF_LANG_MULT = 1 << 5
|
||||
};
|
||||
|
||||
enum ParallactionGameType {
|
||||
GType_Nippon = 1,
|
||||
GType_BRA
|
||||
};
|
||||
|
||||
struct PARALLACTIONGameDescription {
|
||||
AD_GAME_DESCRIPTION_HELPERS(desc);
|
||||
|
||||
ADGameDescription desc;
|
||||
|
||||
int gameType;
|
||||
uint32 features;
|
||||
};
|
||||
|
||||
#define GAMEOPTION_TTS GUIO_GAMEOPTIONS1
|
||||
|
||||
} // End of namespace Parallaction
|
||||
|
||||
#endif // PARALLACTION_DETECTION_H
|
||||
622
engines/parallaction/dialogue.cpp
Normal file
622
engines/parallaction/dialogue.cpp
Normal file
@@ -0,0 +1,622 @@
|
||||
/* 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/events.h"
|
||||
#include "common/debug-channels.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "parallaction/exec.h"
|
||||
#include "parallaction/input.h"
|
||||
#include "parallaction/parallaction.h"
|
||||
|
||||
|
||||
|
||||
namespace Parallaction {
|
||||
|
||||
#define MAX_PASSWORD_LENGTH 7
|
||||
/*
|
||||
#define QUESTION_BALLOON_X 140
|
||||
#define QUESTION_BALLOON_Y 10
|
||||
#define QUESTION_CHARACTER_X 190
|
||||
#define QUESTION_CHARACTER_Y 80
|
||||
|
||||
#define ANSWER_CHARACTER_X 10
|
||||
#define ANSWER_CHARACTER_Y 80
|
||||
*/
|
||||
struct BalloonPositions {
|
||||
Common::Point _questionBalloon;
|
||||
Common::Point _questionChar;
|
||||
|
||||
Common::Point _answerChar;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#ifdef USE_TTS
|
||||
static const int kNumberOfVoiceDatas = 65;
|
||||
#endif
|
||||
|
||||
class DialogueManager {
|
||||
|
||||
Parallaction *_vm;
|
||||
Dialogue *_dialogue;
|
||||
|
||||
bool isNpc;
|
||||
GfxObj *_questioner;
|
||||
GfxObj *_answerer;
|
||||
int _faceId;
|
||||
|
||||
int _questionerVoiceID;
|
||||
int _answererVoiceID;
|
||||
|
||||
Question *_q;
|
||||
|
||||
int _answerId;
|
||||
|
||||
int _selection, _oldSelection;
|
||||
|
||||
uint32 _mouseButtons;
|
||||
Common::Point _mousePos;
|
||||
|
||||
protected:
|
||||
BalloonPositions _ballonPos;
|
||||
struct VisibleAnswer {
|
||||
Answer *_a;
|
||||
int _index; // index into Question::_answers[]
|
||||
int _balloon;
|
||||
} _visAnswers[5];
|
||||
int _numVisAnswers;
|
||||
bool _isKeyDown;
|
||||
uint16 _downKey;
|
||||
|
||||
protected:
|
||||
Gfx *_gfx;
|
||||
BalloonManager *_balloonMan;
|
||||
|
||||
public:
|
||||
DialogueManager(Parallaction *vm, ZonePtr z);
|
||||
virtual ~DialogueManager();
|
||||
|
||||
void start();
|
||||
|
||||
bool isOver() {
|
||||
return _state == DIALOGUE_OVER;
|
||||
}
|
||||
void run();
|
||||
|
||||
ZonePtr _z;
|
||||
CommandList *_cmdList;
|
||||
|
||||
protected:
|
||||
enum DialogueState {
|
||||
DIALOGUE_START,
|
||||
RUN_QUESTION,
|
||||
RUN_ANSWER,
|
||||
NEXT_QUESTION,
|
||||
NEXT_ANSWER,
|
||||
DIALOGUE_OVER
|
||||
} _state;
|
||||
|
||||
static const int NO_ANSWER_SELECTED = -1;
|
||||
|
||||
void transitionToState(DialogueState newState);
|
||||
|
||||
bool displayQuestion();
|
||||
void displayAnswers();
|
||||
bool testAnswerFlags(Answer *a);
|
||||
virtual void addVisibleAnswers(Question *q) = 0;
|
||||
virtual int16 selectAnswer() = 0;
|
||||
|
||||
int16 selectAnswer1();
|
||||
int16 selectAnswerN();
|
||||
int16 getHoverAnswer(int16 x, int16 y);
|
||||
|
||||
void runQuestion();
|
||||
void runAnswer();
|
||||
void nextQuestion();
|
||||
void nextAnswer();
|
||||
};
|
||||
|
||||
DialogueManager::DialogueManager(Parallaction *vm, ZonePtr z) : _vm(vm), _z(z) {
|
||||
_gfx = _vm->_gfx;
|
||||
_balloonMan = _vm->_balloonMan;
|
||||
|
||||
_dialogue = _z->u._speakDialogue;
|
||||
isNpc = !_z->u._filename.empty() && _z->u._filename.compareToIgnoreCase("yourself");
|
||||
_questioner = isNpc ? _vm->_disk->loadTalk(_z->u._filename.c_str()) : _vm->_char._talk;
|
||||
_answerer = _vm->_char._talk;
|
||||
|
||||
#ifdef USE_TTS
|
||||
_answererVoiceID = _vm->_characterVoiceID;
|
||||
|
||||
if (!isNpc) {
|
||||
_questionerVoiceID = _answererVoiceID;
|
||||
} else {
|
||||
_questionerVoiceID = kNarratorVoiceID;
|
||||
|
||||
for (int i = 1; i < kNumberOfVoiceDatas; ++i) {
|
||||
if (characterVoiceDatas[i].characterName && !scumm_stricmp(characterVoiceDatas[i].characterName, _z->u._filename.c_str())) {
|
||||
_questionerVoiceID = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
_cmdList = nullptr;
|
||||
_answerId = 0;
|
||||
|
||||
_faceId = 0;
|
||||
|
||||
_q = nullptr;
|
||||
memset(_visAnswers, 0, sizeof(_visAnswers));
|
||||
_numVisAnswers = 0;
|
||||
|
||||
_selection = _oldSelection = 0;
|
||||
|
||||
_isKeyDown = false;
|
||||
_downKey = 0;
|
||||
|
||||
_mouseButtons = 0;
|
||||
|
||||
_state = DIALOGUE_START;
|
||||
}
|
||||
|
||||
void DialogueManager::start() {
|
||||
_vm->_gfx->hideFloatingLabel();
|
||||
assert(_dialogue);
|
||||
_q = _dialogue->_questions[0];
|
||||
_state = DIALOGUE_START;
|
||||
transitionToState(displayQuestion() ? RUN_QUESTION : NEXT_ANSWER);
|
||||
}
|
||||
|
||||
|
||||
DialogueManager::~DialogueManager() {
|
||||
if (isNpc) {
|
||||
delete _questioner;
|
||||
}
|
||||
_z.reset();
|
||||
}
|
||||
|
||||
void DialogueManager::transitionToState(DialogueState newState) {
|
||||
static const char *dialogueStates[] = {
|
||||
"start",
|
||||
"runquestion",
|
||||
"runanswer",
|
||||
"nextquestion",
|
||||
"nextanswer",
|
||||
"over"
|
||||
};
|
||||
|
||||
if (_state != newState) {
|
||||
debugC(3, kDebugDialogue, "DialogueManager moved to state '%s'", dialogueStates[newState]);
|
||||
|
||||
if (DebugMan.isDebugChannelEnabled(kDebugDialogue) && gDebugLevel == 9) {
|
||||
switch (newState) {
|
||||
case RUN_QUESTION:
|
||||
debug(" Q : %s", _q->_text.c_str());
|
||||
break;
|
||||
case RUN_ANSWER:
|
||||
for (int i = 0; i < _numVisAnswers; ++i) {
|
||||
debug(" A%02i: %s", i, _visAnswers[i]._a->_text.c_str());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_state = newState;
|
||||
}
|
||||
|
||||
bool DialogueManager::testAnswerFlags(Answer *a) {
|
||||
uint32 flags = _vm->getLocationFlags();
|
||||
if (a->_yesFlags & kFlagsGlobal)
|
||||
flags = g_globalFlags | kFlagsGlobal;
|
||||
return ((a->_yesFlags & flags) == a->_yesFlags) && ((a->_noFlags & ~flags) == a->_noFlags);
|
||||
}
|
||||
|
||||
void DialogueManager::displayAnswers() {
|
||||
|
||||
_vm->setTTSVoice(_answererVoiceID);
|
||||
|
||||
// create balloons
|
||||
int id;
|
||||
for (int i = 0; i < _numVisAnswers; ++i) {
|
||||
id = _balloonMan->setDialogueBalloon(_visAnswers[i]._a->_text, 1, BalloonManager::kUnselectedColor);
|
||||
assert(id >= 0);
|
||||
_visAnswers[i]._balloon = id;
|
||||
|
||||
}
|
||||
|
||||
int mood = 0;
|
||||
if (_numVisAnswers == 1) {
|
||||
mood = _visAnswers[0]._a->speakerMood();
|
||||
_balloonMan->setBalloonText(_visAnswers[0]._balloon, _visAnswers[0]._a->_text, BalloonManager::kNormalColor);
|
||||
} else
|
||||
if (_numVisAnswers > 1) {
|
||||
mood = _visAnswers[0]._a->speakerMood();
|
||||
_oldSelection = NO_ANSWER_SELECTED;
|
||||
_selection = 0;
|
||||
}
|
||||
|
||||
_faceId = _gfx->setItem(_answerer, _ballonPos._answerChar.x, _ballonPos._answerChar.y);
|
||||
_gfx->setItemFrame(_faceId, mood);
|
||||
}
|
||||
|
||||
int16 DialogueManager::selectAnswer1() {
|
||||
if (_visAnswers[0]._a->textIsNull()) {
|
||||
return _visAnswers[0]._index;
|
||||
}
|
||||
|
||||
if (_mouseButtons == kMouseLeftUp) {
|
||||
return _visAnswers[0]._index;
|
||||
}
|
||||
|
||||
return NO_ANSWER_SELECTED;
|
||||
}
|
||||
|
||||
int16 DialogueManager::selectAnswerN() {
|
||||
|
||||
_selection = _balloonMan->hitTestDialogueBalloon(_mousePos.x, _mousePos.y);
|
||||
|
||||
if (_selection != _oldSelection) {
|
||||
if (_oldSelection != NO_ANSWER_SELECTED) {
|
||||
VisibleAnswer *oldAnswer = &_visAnswers[_oldSelection];
|
||||
_balloonMan->setBalloonText(oldAnswer->_balloon, oldAnswer->_a->_text, BalloonManager::kUnselectedColor);
|
||||
}
|
||||
|
||||
if (_selection != NO_ANSWER_SELECTED) {
|
||||
VisibleAnswer *answer = &_visAnswers[_selection];
|
||||
_balloonMan->setBalloonText(answer->_balloon, answer->_a->_text, BalloonManager::kSelectedColor);
|
||||
_gfx->setItemFrame(_faceId, answer->_a->speakerMood());
|
||||
}
|
||||
}
|
||||
|
||||
_oldSelection = _selection;
|
||||
|
||||
if ((_mouseButtons == kMouseLeftUp) && (_selection != NO_ANSWER_SELECTED)) {
|
||||
return _visAnswers[_selection]._index;
|
||||
}
|
||||
|
||||
return NO_ANSWER_SELECTED;
|
||||
}
|
||||
|
||||
bool DialogueManager::displayQuestion() {
|
||||
if (_q->textIsNull()) return false;
|
||||
|
||||
#ifdef USE_TTS
|
||||
// Some dialogue exchanges involve more than 1 character, differentiated by the mood
|
||||
if (_questionerVoiceID < kNumberOfVoiceDatas - 1 && characterVoiceDatas[_questionerVoiceID + 1].characterName == nullptr) {
|
||||
_vm->setTTSVoice(_questionerVoiceID + _q->speakerMood());
|
||||
} else {
|
||||
_vm->setTTSVoice(_questionerVoiceID);
|
||||
}
|
||||
#endif
|
||||
|
||||
_balloonMan->setSingleBalloon(_q->_text, _ballonPos._questionBalloon.x, _ballonPos._questionBalloon.y, _q->balloonWinding(), BalloonManager::kNormalColor);
|
||||
_faceId = _gfx->setItem(_questioner, _ballonPos._questionChar.x, _ballonPos._questionChar.y);
|
||||
_gfx->setItemFrame(_faceId, _q->speakerMood());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DialogueManager::runQuestion() {
|
||||
if (_mouseButtons == kMouseLeftUp) {
|
||||
_gfx->freeDialogueObjects();
|
||||
transitionToState(NEXT_ANSWER);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void DialogueManager::nextAnswer() {
|
||||
if (_q->_answers[0] == nullptr) {
|
||||
transitionToState(DIALOGUE_OVER);
|
||||
return;
|
||||
}
|
||||
|
||||
// try and check if there are any suitable answers,
|
||||
// given the current game state.
|
||||
addVisibleAnswers(_q);
|
||||
if (!_numVisAnswers) {
|
||||
// if there are no answers, then chicken out
|
||||
transitionToState(DIALOGUE_OVER);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_visAnswers[0]._a->textIsNull()) {
|
||||
// if the first answer is null (it's implied that it's the
|
||||
// only one because we already called addVisibleAnswers),
|
||||
// then jump to the next question
|
||||
_answerId = _visAnswers[0]._index;
|
||||
transitionToState(NEXT_QUESTION);
|
||||
} else {
|
||||
// at this point we are sure there are non-null answers to show
|
||||
displayAnswers();
|
||||
transitionToState(RUN_ANSWER);
|
||||
}
|
||||
}
|
||||
|
||||
void DialogueManager::runAnswer() {
|
||||
_answerId = selectAnswer();
|
||||
if (_answerId != NO_ANSWER_SELECTED) {
|
||||
_cmdList = &_q->_answers[_answerId]->_commands;
|
||||
_gfx->freeDialogueObjects();
|
||||
transitionToState(NEXT_QUESTION);
|
||||
}
|
||||
}
|
||||
|
||||
void DialogueManager::nextQuestion() {
|
||||
_q = _dialogue->findQuestion(_q->_answers[_answerId]->_followingName);
|
||||
if (_q == nullptr) {
|
||||
transitionToState(DIALOGUE_OVER);
|
||||
} else {
|
||||
transitionToState(displayQuestion() ? RUN_QUESTION : NEXT_ANSWER);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DialogueManager::run() {
|
||||
|
||||
// cache event data
|
||||
_mouseButtons = _vm->_input->getLastButtonEvent();
|
||||
_vm->_input->getCursorPos(_mousePos);
|
||||
_isKeyDown = _vm->_input->getLastKeyDown(_downKey);
|
||||
|
||||
switch (_state) {
|
||||
case RUN_QUESTION:
|
||||
runQuestion();
|
||||
break;
|
||||
|
||||
case NEXT_ANSWER:
|
||||
nextAnswer();
|
||||
break;
|
||||
|
||||
case NEXT_QUESTION:
|
||||
nextQuestion();
|
||||
break;
|
||||
|
||||
case RUN_ANSWER:
|
||||
runAnswer();
|
||||
break;
|
||||
|
||||
case DIALOGUE_OVER:
|
||||
break;
|
||||
|
||||
default:
|
||||
error("unknown state in DialogueManager");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
class DialogueManager_ns : public DialogueManager {
|
||||
protected:
|
||||
Parallaction_ns *_vm;
|
||||
bool _passwordChanged;
|
||||
bool _askPassword;
|
||||
|
||||
bool checkPassword() {
|
||||
return ((!scumm_stricmp(_vm->_char.getBaseName(), g_doughName) && _vm->_password.hasPrefix("1732461")) ||
|
||||
(!scumm_stricmp(_vm->_char.getBaseName(), g_donnaName) && _vm->_password.hasPrefix("1622")) ||
|
||||
(!scumm_stricmp(_vm->_char.getBaseName(), g_dinoName) && _vm->_password.hasPrefix("179")));
|
||||
}
|
||||
|
||||
void resetPassword() {
|
||||
_vm->_password.clear();
|
||||
_passwordChanged = true;
|
||||
}
|
||||
|
||||
void accumPassword(uint16 ascii) {
|
||||
if (!Common::isDigit(ascii)) {
|
||||
return;
|
||||
}
|
||||
|
||||
_vm->_password += ascii;
|
||||
_passwordChanged = true;
|
||||
}
|
||||
|
||||
int16 askPassword() {
|
||||
|
||||
if (_isKeyDown) {
|
||||
accumPassword(_downKey);
|
||||
}
|
||||
|
||||
if (_passwordChanged) {
|
||||
_balloonMan->setBalloonText(_visAnswers[0]._balloon, _visAnswers[0]._a->_text, BalloonManager::kNormalColor);
|
||||
_passwordChanged = false;
|
||||
}
|
||||
|
||||
if ((_vm->_password.size() == MAX_PASSWORD_LENGTH) || ((_isKeyDown) && (_downKey == Common::KEYCODE_RETURN))) {
|
||||
_vm->sayText(_vm->_password, Common::TextToSpeechManager::INTERRUPT);
|
||||
if (checkPassword()) {
|
||||
return 0;
|
||||
} else {
|
||||
resetPassword();
|
||||
}
|
||||
}
|
||||
|
||||
return NO_ANSWER_SELECTED;
|
||||
}
|
||||
|
||||
public:
|
||||
DialogueManager_ns(Parallaction_ns *vm, ZonePtr z) : DialogueManager(vm, z), _vm(vm),
|
||||
_passwordChanged(false), _askPassword(false) {
|
||||
_ballonPos._questionBalloon = Common::Point(140, 10);
|
||||
_ballonPos._questionChar = Common::Point(190, 80);
|
||||
_ballonPos._answerChar = Common::Point(10, 80);
|
||||
}
|
||||
|
||||
bool canDisplayAnswer(Answer *a) {
|
||||
return testAnswerFlags(a);
|
||||
}
|
||||
|
||||
void addVisibleAnswers(Question *q) override {
|
||||
_askPassword = false;
|
||||
_numVisAnswers = 0;
|
||||
for (int i = 0; i < NUM_ANSWERS && q->_answers[i]; i++) {
|
||||
Answer *a = q->_answers[i];
|
||||
if (!canDisplayAnswer(a)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (a->_text.contains("%P")) {
|
||||
_askPassword = true;
|
||||
|
||||
_vm->sayText(a->_text.substr(0, a->_text.find('@')), Common::TextToSpeechManager::INTERRUPT);
|
||||
}
|
||||
|
||||
_visAnswers[_numVisAnswers]._a = a;
|
||||
_visAnswers[_numVisAnswers]._index = i;
|
||||
_numVisAnswers++;
|
||||
}
|
||||
|
||||
resetPassword();
|
||||
}
|
||||
|
||||
int16 selectAnswer() override {
|
||||
int ans = NO_ANSWER_SELECTED;
|
||||
if (_askPassword) {
|
||||
ans = askPassword();
|
||||
} else
|
||||
if (_numVisAnswers == 1) {
|
||||
ans = selectAnswer1();
|
||||
} else {
|
||||
ans = selectAnswerN();
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
};
|
||||
|
||||
class DialogueManager_br : public DialogueManager {
|
||||
Parallaction_br *_vm;
|
||||
|
||||
public:
|
||||
DialogueManager_br(Parallaction_br *vm, ZonePtr z) : DialogueManager(vm, z), _vm(vm) {
|
||||
_ballonPos._questionBalloon = Common::Point(0, 0);
|
||||
_ballonPos._questionChar = Common::Point(380, 80);
|
||||
_ballonPos._answerChar = Common::Point(10, 80);
|
||||
}
|
||||
|
||||
bool canDisplayAnswer(Answer *a) {
|
||||
if (!a)
|
||||
return false;
|
||||
|
||||
if (a->_hasCounterCondition) {
|
||||
_vm->testCounterCondition(a->_counterName, a->_counterOp, a->_counterValue);
|
||||
return (_vm->getLocationFlags() & kFlagsTestTrue) != 0;
|
||||
}
|
||||
|
||||
return testAnswerFlags(a);
|
||||
}
|
||||
|
||||
void addVisibleAnswers(Question *q) override {
|
||||
_numVisAnswers = 0;
|
||||
for (int i = 0; i < NUM_ANSWERS && q->_answers[i]; i++) {
|
||||
Answer *a = q->_answers[i];
|
||||
if (!canDisplayAnswer(a)) {
|
||||
continue;
|
||||
}
|
||||
_visAnswers[_numVisAnswers]._a = a;
|
||||
_visAnswers[_numVisAnswers]._index = i;
|
||||
_numVisAnswers++;
|
||||
}
|
||||
}
|
||||
|
||||
int16 selectAnswer() override {
|
||||
int16 ans = NO_ANSWER_SELECTED;
|
||||
if (_numVisAnswers == 1) {
|
||||
ans = selectAnswer1();
|
||||
} else {
|
||||
ans = selectAnswerN();
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void Parallaction::enterDialogueMode(ZonePtr z) {
|
||||
if (!z->u._speakDialogue) {
|
||||
return;
|
||||
}
|
||||
|
||||
debugC(1, kDebugDialogue, "Parallaction::enterDialogueMode(%s)", z->u._filename.c_str());
|
||||
_dialogueMan = createDialogueManager(z);
|
||||
assert(_dialogueMan);
|
||||
_dialogueMan->start();
|
||||
_input->_inputMode = Input::kInputModeDialogue;
|
||||
}
|
||||
|
||||
void Parallaction::exitDialogueMode() {
|
||||
debugC(1, kDebugDialogue, "Parallaction::exitDialogueMode()");
|
||||
_input->_inputMode = Input::kInputModeGame;
|
||||
|
||||
setTTSVoice(_characterVoiceID);
|
||||
|
||||
/* Since the current instance of _dialogueMan must be destroyed before the
|
||||
zone commands are executed, as they may create a new instance of _dialogueMan that
|
||||
would overwrite the current, we need to save the references to the command lists.
|
||||
*/
|
||||
CommandList *_cmdList = _dialogueMan->_cmdList;
|
||||
ZonePtr z = _dialogueMan->_z;
|
||||
|
||||
// destroy the _dialogueMan here
|
||||
destroyDialogueManager();
|
||||
|
||||
// run the lists saved
|
||||
if (_cmdList) {
|
||||
_cmdExec->run(*_cmdList);
|
||||
}
|
||||
_cmdExec->run(z->_commands, z);
|
||||
}
|
||||
|
||||
void Parallaction::destroyDialogueManager() {
|
||||
// destroy the _dialogueMan here
|
||||
delete _dialogueMan;
|
||||
_dialogueMan = nullptr;
|
||||
}
|
||||
|
||||
void Parallaction::runDialogueFrame() {
|
||||
if (_input->_inputMode != Input::kInputModeDialogue) {
|
||||
return;
|
||||
}
|
||||
|
||||
_dialogueMan->run();
|
||||
|
||||
if (_dialogueMan->isOver()) {
|
||||
exitDialogueMode();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
DialogueManager *Parallaction_ns::createDialogueManager(ZonePtr z) {
|
||||
return new DialogueManager_ns(this, z);
|
||||
}
|
||||
|
||||
DialogueManager *Parallaction_br::createDialogueManager(ZonePtr z) {
|
||||
return new DialogueManager_br(this, z);
|
||||
}
|
||||
|
||||
} // namespace Parallaction
|
||||
282
engines/parallaction/disk.h
Normal file
282
engines/parallaction/disk.h
Normal file
@@ -0,0 +1,282 @@
|
||||
/* 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 PARALLACTION_DISK_H
|
||||
#define PARALLACTION_DISK_H
|
||||
|
||||
#define PATH_LEN 200
|
||||
|
||||
#include "common/archive.h"
|
||||
#include "common/str.h"
|
||||
|
||||
namespace Common {
|
||||
class FSDirectory;
|
||||
class ReadStream;
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace Graphics {
|
||||
struct Surface;
|
||||
}
|
||||
|
||||
namespace Parallaction {
|
||||
|
||||
class Table;
|
||||
class Parallaction;
|
||||
class Gfx;
|
||||
class Script;
|
||||
class Font;
|
||||
|
||||
struct Frames;
|
||||
struct Cnv;
|
||||
struct Sprites;
|
||||
struct BackgroundInfo;
|
||||
class GfxObj;
|
||||
struct MaskBuffer;
|
||||
struct PathBuffer;
|
||||
|
||||
class Disk {
|
||||
|
||||
public:
|
||||
Disk() { }
|
||||
virtual ~Disk() { }
|
||||
|
||||
virtual void init() { }
|
||||
|
||||
virtual Common::String selectArchive(const Common::String &name) = 0;
|
||||
virtual void setLanguage(uint16 language) = 0;
|
||||
|
||||
virtual Script* loadLocation(const char *name) = 0;
|
||||
virtual Script* loadScript(const char* name) = 0;
|
||||
virtual GfxObj* loadTalk(const char *name) = 0;
|
||||
virtual GfxObj* loadObjects(const char *name, uint8 part = 0) = 0;
|
||||
virtual Frames* loadPointer(const char *name) = 0;
|
||||
virtual GfxObj* loadHead(const char* name) = 0;
|
||||
virtual Font* loadFont(const char* name) = 0;
|
||||
virtual GfxObj* loadStatic(const char* name) = 0;
|
||||
virtual Frames* loadFrames(const char* name) = 0;
|
||||
virtual void loadSlide(BackgroundInfo& info, const char *filename) = 0;
|
||||
virtual void loadScenery(BackgroundInfo& info, const char* background, const char* mask, const char* path) = 0;
|
||||
virtual Table* loadTable(const char* name) = 0;
|
||||
virtual Common::SeekableReadStream* loadMusic(const char* name) = 0;
|
||||
virtual Common::SeekableReadStream* loadSound(const char* name) = 0;
|
||||
virtual MaskBuffer *loadMask(const char *name, uint32 w, uint32 h) { return 0; }
|
||||
virtual PathBuffer *loadPath(const char *name, uint32 w, uint32 h) { return 0; }
|
||||
};
|
||||
|
||||
class Disk_ns : public Disk {
|
||||
|
||||
protected:
|
||||
Parallaction *_vm;
|
||||
|
||||
Common::SearchSet _sset;
|
||||
|
||||
Common::String _resArchiveName;
|
||||
Common::String _language;
|
||||
Common::SeekableReadStream *openFile(const char *filename);
|
||||
virtual Common::SeekableReadStream *tryOpenFile(const char *filename) { return 0; }
|
||||
void errorFileNotFound(const char *filename);
|
||||
|
||||
void addArchive(const Common::String& name, int priority);
|
||||
|
||||
virtual void decodeCnv(byte *data, uint16 numFrames, uint16 width, uint16 height, Common::SeekableReadStream *stream) = 0;
|
||||
Cnv *makeCnv(Common::SeekableReadStream *stream);
|
||||
|
||||
public:
|
||||
Disk_ns(Parallaction *vm);
|
||||
~Disk_ns() override;
|
||||
|
||||
Common::String selectArchive(const Common::String &name) override;
|
||||
void setLanguage(uint16 language) override;
|
||||
|
||||
Script* loadLocation(const char *name) override;
|
||||
Script* loadScript(const char* name) override;
|
||||
};
|
||||
|
||||
class DosDisk_ns : public Disk_ns {
|
||||
|
||||
private:
|
||||
void unpackBackground(Common::ReadStream *stream, byte *screen, byte *mask, byte *path);
|
||||
Cnv* loadCnv(const char *filename);
|
||||
void loadBackground(BackgroundInfo& info, const char *filename);
|
||||
void createMaskAndPathBuffers(BackgroundInfo &info);
|
||||
void parseDepths(BackgroundInfo &info, Common::SeekableReadStream &stream);
|
||||
Font *createFont(const char *name, Cnv* cnv);
|
||||
|
||||
protected:
|
||||
Gfx *_gfx;
|
||||
Common::SeekableReadStream *tryOpenFile(const char* name) override;
|
||||
void decodeCnv(byte *data, uint16 numFrames, uint16 width, uint16 height, Common::SeekableReadStream *stream) override;
|
||||
|
||||
public:
|
||||
DosDisk_ns(Parallaction *vm);
|
||||
~DosDisk_ns() override;
|
||||
|
||||
void init() override;
|
||||
|
||||
GfxObj* loadTalk(const char *name) override;
|
||||
GfxObj* loadObjects(const char *name, uint8 part = 0) override;
|
||||
Frames* loadPointer(const char *name) override;
|
||||
GfxObj* loadHead(const char* name) override;
|
||||
Font* loadFont(const char* name) override;
|
||||
GfxObj* loadStatic(const char* name) override;
|
||||
Frames* loadFrames(const char* name) override;
|
||||
void loadSlide(BackgroundInfo& info, const char *filename) override;
|
||||
void loadScenery(BackgroundInfo& info, const char* background, const char* mask, const char* path) override;
|
||||
Table* loadTable(const char* name) override;
|
||||
Common::SeekableReadStream* loadMusic(const char* name) override;
|
||||
Common::SeekableReadStream* loadSound(const char* name) override;
|
||||
};
|
||||
|
||||
class AmigaDisk_ns : public Disk_ns {
|
||||
|
||||
protected:
|
||||
void patchFrame(byte *dst, byte *dlta, uint16 bytesPerPlane, uint16 height);
|
||||
void unpackFrame(byte *dst, byte *src, uint16 planeSize);
|
||||
void unpackBitmap(byte *dst, byte *src, uint16 numFrames, uint16 bytesPerPlane, uint16 height);
|
||||
Common::SeekableReadStream *tryOpenFile(const char* name) override;
|
||||
Font *createFont(const char *name, Common::SeekableReadStream &stream);
|
||||
void loadMask_internal(BackgroundInfo& info, const char *name);
|
||||
void loadPath_internal(BackgroundInfo& info, const char *name);
|
||||
void loadBackground(BackgroundInfo& info, const char *name);
|
||||
void buildMask(byte* buf);
|
||||
|
||||
void decodeCnv(byte *data, uint16 numFrames, uint16 width, uint16 height, Common::SeekableReadStream *stream) override;
|
||||
|
||||
public:
|
||||
AmigaDisk_ns(Parallaction *vm);
|
||||
~AmigaDisk_ns() override;
|
||||
|
||||
void init() override;
|
||||
|
||||
GfxObj* loadTalk(const char *name) override;
|
||||
GfxObj* loadObjects(const char *name, uint8 part = 0) override;
|
||||
Frames* loadPointer(const char *name) override;
|
||||
GfxObj* loadHead(const char* name) override;
|
||||
Font* loadFont(const char* name) override;
|
||||
GfxObj* loadStatic(const char* name) override;
|
||||
Frames* loadFrames(const char* name) override;
|
||||
void loadSlide(BackgroundInfo& info, const char *filename) override;
|
||||
void loadScenery(BackgroundInfo& info, const char* background, const char* mask, const char* path) override;
|
||||
Table* loadTable(const char* name) override;
|
||||
Common::SeekableReadStream* loadMusic(const char* name) override;
|
||||
Common::SeekableReadStream* loadSound(const char* name) override;
|
||||
};
|
||||
|
||||
|
||||
class Disk_br : public Disk {
|
||||
|
||||
Common::SeekableReadStream *openFile_internal(bool errorOnNotFound, const Common::String &name, const Common::String &ext);
|
||||
|
||||
protected:
|
||||
Parallaction *_vm;
|
||||
Common::SearchSet _sset;
|
||||
Common::FSDirectory *_baseDir;
|
||||
|
||||
uint16 _language;
|
||||
Common::String _currentPart;
|
||||
|
||||
Common::SeekableReadStream *tryOpenFile(const Common::String &name, const Common::String &ext = Common::String());
|
||||
Common::SeekableReadStream *openFile(const Common::String &name, const Common::String &ext = Common::String());
|
||||
void errorFileNotFound(const Common::String &filename);
|
||||
|
||||
public:
|
||||
Disk_br(Parallaction *vm);
|
||||
~Disk_br() override;
|
||||
};
|
||||
|
||||
// for the moment DosDisk_br subclasses Disk. When Amiga support will
|
||||
// be taken into consideration, it might be useful to add another level
|
||||
// like we did for Nippon Safes.
|
||||
class DosDisk_br : public Disk_br {
|
||||
|
||||
protected:
|
||||
Font *createFont(const char *name, Common::ReadStream &stream);
|
||||
Sprites* createSprites(Common::ReadStream *stream);
|
||||
void loadBitmap(Common::SeekableReadStream &stream, Graphics::Surface &surf, byte *palette);
|
||||
GfxObj* createInventoryObjects(Common::SeekableReadStream &stream);
|
||||
|
||||
public:
|
||||
DosDisk_br(Parallaction *vm);
|
||||
|
||||
void init() override;
|
||||
|
||||
Common::String selectArchive(const Common::String &name) override;
|
||||
void setLanguage(uint16 language) override;
|
||||
Script* loadLocation(const char *name) override;
|
||||
Script* loadScript(const char* name) override;
|
||||
GfxObj* loadTalk(const char *name) override;
|
||||
GfxObj* loadObjects(const char *name, uint8 part = 0) override;
|
||||
Frames* loadPointer(const char *name) override;
|
||||
GfxObj* loadHead(const char* name) override;
|
||||
Font* loadFont(const char* name) override;
|
||||
GfxObj* loadStatic(const char* name) override;
|
||||
Frames* loadFrames(const char* name) override;
|
||||
void loadSlide(BackgroundInfo& info, const char *filename) override;
|
||||
void loadScenery(BackgroundInfo& info, const char* name, const char* mask, const char* path) override;
|
||||
Table* loadTable(const char* name) override;
|
||||
Common::SeekableReadStream* loadMusic(const char* name) override;
|
||||
Common::SeekableReadStream* loadSound(const char* name) override;
|
||||
MaskBuffer *loadMask(const char *name, uint32 w, uint32 h) override;
|
||||
PathBuffer *loadPath(const char *name, uint32 w, uint32 h) override;
|
||||
};
|
||||
|
||||
class DosDemoDisk_br : public DosDisk_br {
|
||||
|
||||
public:
|
||||
DosDemoDisk_br(Parallaction *vm);
|
||||
|
||||
void init() override;
|
||||
|
||||
Common::String selectArchive(const Common::String& name) override;
|
||||
};
|
||||
|
||||
class AmigaDisk_br : public DosDisk_br {
|
||||
|
||||
protected:
|
||||
Sprites* createSprites(Common::ReadStream *stream);
|
||||
Font *createFont(const char *name, Common::SeekableReadStream &stream);
|
||||
void loadBackground(BackgroundInfo& info, const char *filename);
|
||||
void adjustForPalette(Graphics::Surface &surf, int transparentColor = -1);
|
||||
|
||||
public:
|
||||
AmigaDisk_br(Parallaction *vm);
|
||||
|
||||
void init() override;
|
||||
|
||||
Common::String selectArchive(const Common::String& name) override;
|
||||
GfxObj* loadTalk(const char *name) override;
|
||||
Font* loadFont(const char* name) override;
|
||||
GfxObj* loadStatic(const char* name) override;
|
||||
Frames* loadFrames(const char* name) override;
|
||||
void loadSlide(BackgroundInfo& info, const char *filename) override;
|
||||
void loadScenery(BackgroundInfo& info, const char* name, const char* mask, const char* path) override;
|
||||
GfxObj* loadObjects(const char *name, uint8 part = 0) override;
|
||||
Common::SeekableReadStream* loadMusic(const char* name) override;
|
||||
Common::SeekableReadStream* loadSound(const char* name) override;
|
||||
MaskBuffer *loadMask(const char *name, uint32 w, uint32 h) override;
|
||||
};
|
||||
|
||||
} // namespace Parallaction
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
767
engines/parallaction/disk_br.cpp
Normal file
767
engines/parallaction/disk_br.cpp
Normal file
@@ -0,0 +1,767 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/config-manager.h"
|
||||
#include "common/fs.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "image/iff.h"
|
||||
#include "parallaction/parallaction.h"
|
||||
#include "parallaction/parser.h"
|
||||
|
||||
|
||||
namespace Parallaction {
|
||||
|
||||
extern byte braAmigaFramesDefaultPalette[];
|
||||
|
||||
struct Sprite {
|
||||
uint16 size;
|
||||
uint16 x;
|
||||
uint16 y;
|
||||
uint16 w;
|
||||
uint16 h;
|
||||
|
||||
byte *packedData;
|
||||
|
||||
Sprite() : size(0), x(0), y(0), w(0), h(0), packedData(nullptr) {
|
||||
}
|
||||
|
||||
~Sprite() {
|
||||
free(packedData);
|
||||
}
|
||||
};
|
||||
|
||||
struct Sprites : public Frames {
|
||||
uint16 _num;
|
||||
Sprite* _sprites;
|
||||
|
||||
Sprites(uint num) : _num(0), _sprites(nullptr) {
|
||||
_num = num;
|
||||
_sprites = new Sprite[_num];
|
||||
}
|
||||
|
||||
~Sprites() override {
|
||||
delete[] _sprites;
|
||||
}
|
||||
|
||||
uint16 getNum() override {
|
||||
return _num;
|
||||
}
|
||||
|
||||
byte* getData(uint16 index) override {
|
||||
assert(index < _num);
|
||||
return _sprites[index].packedData;
|
||||
}
|
||||
|
||||
void getRect(uint16 index, Common::Rect &r) override {
|
||||
assert(index < _num);
|
||||
r.setWidth(_sprites[index].w);
|
||||
r.setHeight(_sprites[index].h);
|
||||
r.moveTo(_sprites[index].x, _sprites[index].y);
|
||||
}
|
||||
uint getRawSize(uint16 index) override {
|
||||
assert(index < _num);
|
||||
return _sprites[index].size;
|
||||
}
|
||||
uint getSize(uint16 index) override {
|
||||
assert(index < _num);
|
||||
return _sprites[index].w * _sprites[index].h;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
Common::SeekableReadStream *Disk_br::openFile_internal(bool errorOnNotFound, const Common::String &name, const Common::String &ext) {
|
||||
assert(!name.empty());
|
||||
debugC(5, kDebugDisk, "Disk_br::openFile(%s, %s)", name.c_str(), ext.c_str());
|
||||
|
||||
Common::Path lookup(name);
|
||||
|
||||
if (!ext.empty() && !name.hasSuffix(ext.c_str())) {
|
||||
// make sure we are using the specified extension
|
||||
debugC(9, kDebugDisk, "Disk_br::openFile: appending explicit extension (%s) to (%s)", ext.c_str(), name.c_str());
|
||||
lookup.appendInPlace(ext);
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *stream = _sset.createReadStreamForMember(lookup);
|
||||
if (stream) {
|
||||
return stream;
|
||||
}
|
||||
|
||||
// as a very last resort, try trimming the file name to 8 chars
|
||||
Common::String filename = lookup.baseName();
|
||||
if (!ext.empty() && filename.hasSuffix(ext.c_str())) {
|
||||
int len = filename.size();
|
||||
if (len > 8) {
|
||||
debugC(9, kDebugDisk, "Disk_br::openFile: trimming filename (%s) to 8 characters", name.c_str());
|
||||
while (len-- > 8) {
|
||||
filename.deleteLastChar();
|
||||
}
|
||||
filename += ext;
|
||||
stream = _sset.createReadStreamForMember(lookup.getParent().appendComponent(filename));
|
||||
}
|
||||
}
|
||||
|
||||
if (!stream && errorOnNotFound) {
|
||||
errorFileNotFound(name);
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *Disk_br::openFile(const Common::String &name, const Common::String &ext) {
|
||||
return openFile_internal(true, name, ext);
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *Disk_br::tryOpenFile(const Common::String &name, const Common::String &ext) {
|
||||
return openFile_internal(false, name, ext);
|
||||
}
|
||||
|
||||
|
||||
void Disk_br::errorFileNotFound(const Common::String &filename) {
|
||||
error("File '%s' not found", filename.c_str());
|
||||
}
|
||||
|
||||
Common::String DosDisk_br::selectArchive(const Common::String& name) {
|
||||
debugC(5, kDebugDisk, "DosDisk_br::selectArchive");
|
||||
|
||||
Common::String oldPath = _currentPart;
|
||||
_currentPart = name;
|
||||
|
||||
debugC(5, kDebugDisk, "DosDisk_br::selectArchive: adding part directory to search set");
|
||||
_sset.remove("part");
|
||||
_sset.add("part", _baseDir->getSubDirectory(Common::Path(name), 3), 10);
|
||||
|
||||
return oldPath;
|
||||
}
|
||||
|
||||
void DosDisk_br::setLanguage(uint16 language) {
|
||||
debugC(5, kDebugDisk, "DosDisk_br::setLanguage");
|
||||
assert(language < 4);
|
||||
_language = language;
|
||||
}
|
||||
|
||||
DosDisk_br::DosDisk_br(Parallaction* vm) : Disk_br(vm) {
|
||||
}
|
||||
|
||||
void DosDisk_br::init() {
|
||||
// TODO: clarify whether the engine or OSystem should add the base game directory to the search manager.
|
||||
// Right now, I am keeping an internal search set to do the job.
|
||||
_baseDir = new Common::FSDirectory(ConfMan.getPath("path"));
|
||||
_sset.add("base", _baseDir, 5, true);
|
||||
}
|
||||
|
||||
|
||||
GfxObj* DosDisk_br::loadTalk(const char *name) {
|
||||
debugC(5, kDebugDisk, "DosDisk_br::loadTalk(%s)", name);
|
||||
|
||||
Common::SeekableReadStream *stream = openFile("tal/" + Common::String(name), ".tal");
|
||||
|
||||
// talk position is set to (0,0), because talks are always displayed at
|
||||
// absolute coordinates, set in the dialogue manager. The original used
|
||||
// to null out coordinates every time they were needed. We do it better!
|
||||
Sprites *spr = createSprites(stream);
|
||||
for (int i = 0; i < spr->getNum(); i++) {
|
||||
spr->_sprites[i].x = 0;
|
||||
spr->_sprites[i].y = 0;
|
||||
}
|
||||
return new GfxObj(0, spr, name);
|
||||
}
|
||||
|
||||
Script* DosDisk_br::loadLocation(const char *name) {
|
||||
debugC(5, kDebugDisk, "DosDisk_br::loadLocation");
|
||||
|
||||
static const char * const langs[4] = { "it/", "fr/", "en/", "ge/" };
|
||||
|
||||
Common::String fullName(name);
|
||||
if (!fullName.hasSuffix(".slf")) {
|
||||
fullName += ".loc";
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *stream = openFile(langs[_language] + fullName);
|
||||
return new Script(stream, true);
|
||||
}
|
||||
|
||||
Script* DosDisk_br::loadScript(const char* name) {
|
||||
debugC(5, kDebugDisk, "DosDisk_br::loadScript");
|
||||
Common::SeekableReadStream *stream = openFile("scripts/" + Common::String(name), ".scr");
|
||||
return new Script(stream, true);
|
||||
}
|
||||
|
||||
// there are no Head resources in Big Red Adventure
|
||||
GfxObj* DosDisk_br::loadHead(const char* name) {
|
||||
debugC(5, kDebugDisk, "DosDisk_br::loadHead");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void DosDisk_br::loadBitmap(Common::SeekableReadStream &stream, Graphics::Surface &surf, byte *palette) {
|
||||
stream.skip(4);
|
||||
uint width = stream.readUint32BE();
|
||||
if (width & 1) width++;
|
||||
uint height = stream.readUint32BE();
|
||||
stream.skip(20);
|
||||
|
||||
if (palette) {
|
||||
stream.read(palette, 768);
|
||||
} else {
|
||||
stream.skip(768);
|
||||
}
|
||||
|
||||
surf.create(width, height, Graphics::PixelFormat::createFormatCLUT8());
|
||||
stream.read(surf.getPixels(), width * height);
|
||||
}
|
||||
|
||||
Frames* DosDisk_br::loadPointer(const char *name) {
|
||||
debugC(5, kDebugDisk, "DosDisk_br::loadPointer");
|
||||
Common::SeekableReadStream *stream = openFile(Common::String(name), ".ras");
|
||||
Graphics::Surface *surf = new Graphics::Surface;
|
||||
loadBitmap(*stream, *surf, nullptr);
|
||||
delete stream;
|
||||
return new SurfaceToFrames(surf);
|
||||
}
|
||||
|
||||
|
||||
Font* DosDisk_br::loadFont(const char* name) {
|
||||
debugC(5, kDebugDisk, "DosDisk_br::loadFont");
|
||||
Common::SeekableReadStream *stream = openFile(name, ".fnt");
|
||||
Font *font = createFont(name, *stream);
|
||||
delete stream;
|
||||
return font;
|
||||
}
|
||||
|
||||
|
||||
GfxObj* DosDisk_br::loadObjects(const char *name, uint8 part) {
|
||||
debugC(5, kDebugDisk, "DosDisk_br::loadObjects");
|
||||
Common::SeekableReadStream *stream = openFile(name);
|
||||
GfxObj *obj = createInventoryObjects(*stream);
|
||||
delete stream;
|
||||
return obj;
|
||||
}
|
||||
|
||||
GfxObj* DosDisk_br::loadStatic(const char* name) {
|
||||
debugC(5, kDebugDisk, "DosDisk_br::loadStatic");
|
||||
Common::SeekableReadStream *stream = openFile("ras/" + Common::String(name), ".ras");
|
||||
Graphics::Surface *surf = new Graphics::Surface;
|
||||
loadBitmap(*stream, *surf, nullptr);
|
||||
delete stream;
|
||||
return new GfxObj(0, new SurfaceToFrames(surf), name);
|
||||
}
|
||||
|
||||
Sprites* DosDisk_br::createSprites(Common::ReadStream *stream) {
|
||||
|
||||
uint16 num = stream->readUint16LE();
|
||||
|
||||
Sprites *sprites = new Sprites(num);
|
||||
|
||||
for (uint i = 0; i < num; i++) {
|
||||
Sprite *spr = &sprites->_sprites[i];
|
||||
spr->size = stream->readUint16LE();
|
||||
spr->x = stream->readUint16LE();
|
||||
spr->y = stream->readUint16LE();
|
||||
spr->w = stream->readUint16LE();
|
||||
spr->h = stream->readUint16LE();
|
||||
|
||||
spr->packedData = (byte *)malloc(spr->size);
|
||||
stream->read(spr->packedData, spr->size);
|
||||
}
|
||||
delete stream;
|
||||
|
||||
return sprites;
|
||||
}
|
||||
|
||||
Frames* DosDisk_br::loadFrames(const char* name) {
|
||||
Common::SeekableReadStream *stream = nullptr;
|
||||
|
||||
debugC(5, kDebugDisk, "DosDisk_br::loadFrames");
|
||||
|
||||
Common::String path(name);
|
||||
if (path.hasSuffix(".win")) {
|
||||
stream = openFile(path);
|
||||
} else {
|
||||
stream = openFile("ani/" + Common::String(name), ".ani");
|
||||
}
|
||||
|
||||
return createSprites(stream);
|
||||
}
|
||||
|
||||
// Slides in Nippon Safes are basically screen-sized pictures with valid
|
||||
// palette data used for menu and for location switches. Big Red Adventure
|
||||
// doesn't need slides in that sense, but it still has some special
|
||||
// graphics resources with palette data, so those will be named slides.
|
||||
//
|
||||
void DosDisk_br::loadSlide(BackgroundInfo& info, const char *name) {
|
||||
debugC(5, kDebugDisk, "DosDisk_br::loadSlide");
|
||||
|
||||
Common::SeekableReadStream *stream = openFile(name, ".bmp");
|
||||
|
||||
byte rgb[768];
|
||||
|
||||
loadBitmap(*stream, info.bg, rgb);
|
||||
info.width = info.bg.w;
|
||||
info.height = info.bg.h;
|
||||
|
||||
delete stream;
|
||||
|
||||
for (uint i = 0; i < 256; i++) {
|
||||
info.palette.setEntry(i, rgb[i] >> 2, rgb[i+256] >> 2, rgb[i+512] >> 2);
|
||||
}
|
||||
}
|
||||
|
||||
MaskBuffer *DosDisk_br::loadMask(const char *name, uint32 w, uint32 h) {
|
||||
if (!name) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *stream = openFile("msk/" + Common::String(name), ".msk");
|
||||
|
||||
MaskBuffer *buffer = new MaskBuffer;
|
||||
assert(buffer);
|
||||
buffer->create(w, h);
|
||||
buffer->bigEndian = false;
|
||||
|
||||
stream->read(buffer->data, buffer->size);
|
||||
delete stream;
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
PathBuffer *DosDisk_br::loadPath(const char *name, uint32 w, uint32 h) {
|
||||
if (!name) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *stream = openFile("pth/" + Common::String(name), ".pth");
|
||||
|
||||
PathBuffer *buffer = new PathBuffer;
|
||||
assert(buffer);
|
||||
buffer->create(w, h);
|
||||
buffer->bigEndian = false;
|
||||
|
||||
stream->read(buffer->data, buffer->size);
|
||||
delete stream;
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void DosDisk_br::loadScenery(BackgroundInfo& info, const char *name, const char *mask, const char* path) {
|
||||
debugC(5, kDebugDisk, "DosDisk_br::loadScenery");
|
||||
|
||||
Common::SeekableReadStream *stream;
|
||||
|
||||
if (name) {
|
||||
stream = openFile("bkg/" + Common::String(name), ".bkg");
|
||||
byte rgb[768];
|
||||
|
||||
loadBitmap(*stream, info.bg, rgb);
|
||||
info.width = info.bg.w;
|
||||
info.height = info.bg.h;
|
||||
|
||||
for (uint i = 0; i < 256; i++) {
|
||||
info.palette.setEntry(i, rgb[i] >> 2, rgb[i+256] >> 2, rgb[i+512] >> 2);
|
||||
}
|
||||
|
||||
delete stream;
|
||||
}
|
||||
|
||||
if (mask) {
|
||||
info._mask = loadMask(mask, info.width, info.height);
|
||||
}
|
||||
|
||||
if (path) {
|
||||
info._path = loadPath(path, info.width, info.height);
|
||||
}
|
||||
}
|
||||
|
||||
Table* DosDisk_br::loadTable(const char* name) {
|
||||
debugC(5, kDebugDisk, "DosDisk_br::loadTable");
|
||||
return createTableFromStream(100, openFile(name, ".tab"));
|
||||
}
|
||||
|
||||
Common::SeekableReadStream* DosDisk_br::loadMusic(const char* name) {
|
||||
debugC(5, kDebugDisk, "DosDisk_br::loadMusic");
|
||||
return openFile("msc/" + Common::String(name), ".msc");
|
||||
}
|
||||
|
||||
|
||||
Common::SeekableReadStream* DosDisk_br::loadSound(const char* name) {
|
||||
debugC(5, kDebugDisk, "DosDisk_br::loadSound");
|
||||
return openFile("sfx/" + Common::String(name), ".sfx");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
DosDemoDisk_br::DosDemoDisk_br(Parallaction *vm) : DosDisk_br(vm) {
|
||||
|
||||
}
|
||||
|
||||
void DosDemoDisk_br::init() {
|
||||
// TODO: clarify whether the engine or OSystem should add the base game directory to the search manager.
|
||||
// Right now, I am keeping an internal search set to do the job.
|
||||
_baseDir = new Common::FSDirectory(ConfMan.getPath("path"), 2);
|
||||
_sset.add("base", _baseDir, 5, false);
|
||||
}
|
||||
|
||||
|
||||
Common::String DosDemoDisk_br::selectArchive(const Common::String& name) {
|
||||
debugC(5, kDebugDisk, "DosDemoDisk_br::selectArchive");
|
||||
Common::String oldPath = _currentPart;
|
||||
_currentPart = name;
|
||||
return oldPath;
|
||||
}
|
||||
|
||||
|
||||
AmigaDisk_br::AmigaDisk_br(Parallaction *vm) : DosDisk_br(vm) {
|
||||
}
|
||||
|
||||
void AmigaDisk_br::init() {
|
||||
_baseDir = new Common::FSDirectory(ConfMan.getPath("path"));
|
||||
_sset.add("base", _baseDir, 5, false);
|
||||
|
||||
const char *subDirNames[3] = { "fonts", "backs", "common" };
|
||||
const char *subDirPrefixes[3] = { "fonts", "backs", "" };
|
||||
// The common sub directory, doesn't exist in the Amiga demo
|
||||
uint numDir = (_vm->getFeatures() & GF_DEMO) ? 2 : 3;
|
||||
for (uint i = 0; i < numDir; i++)
|
||||
_sset.add(subDirNames[i], _baseDir->getSubDirectory(subDirPrefixes[i], subDirNames[i], 2), 6);
|
||||
}
|
||||
|
||||
void AmigaDisk_br::adjustForPalette(Graphics::Surface &surf, int transparentColor) {
|
||||
uint size = surf.w * surf.h;
|
||||
byte *data = (byte *)surf.getPixels();
|
||||
for (uint i = 0; i < size; i++, data++) {
|
||||
if (transparentColor == -1 || transparentColor != *data)
|
||||
*data += 16;
|
||||
}
|
||||
}
|
||||
|
||||
void AmigaDisk_br::loadBackground(BackgroundInfo& info, const char *filename) {
|
||||
byte r,g,b;
|
||||
const byte *p;
|
||||
Common::SeekableReadStream *stream;
|
||||
Image::IFFDecoder decoder;
|
||||
uint i;
|
||||
|
||||
stream = tryOpenFile("backs/" + Common::String(filename), ".ap");
|
||||
if (stream) {
|
||||
// NOTE: Additional palette (15-colors) is always loaded onto colors 1-15
|
||||
uint32 size = stream->size() / 3;
|
||||
for (i = 1; i < size; i++) {
|
||||
r = stream->readByte() >> 2;
|
||||
g = stream->readByte() >> 2;
|
||||
b = stream->readByte() >> 2;
|
||||
info.palette.setEntry(i, r, g, b);
|
||||
}
|
||||
delete stream;
|
||||
} else {
|
||||
p = braAmigaFramesDefaultPalette;
|
||||
for (i = 0; i < 16; i++) {
|
||||
r = *p >> 2;
|
||||
p++;
|
||||
g = *p >> 2;
|
||||
p++;
|
||||
b = *p >> 2;
|
||||
p++;
|
||||
info.palette.setEntry(i, r, g, b);
|
||||
}
|
||||
}
|
||||
|
||||
stream = openFile("backs/" + Common::String(filename), ".bkg");
|
||||
decoder.loadStream(*stream);
|
||||
|
||||
info.bg.copyFrom(*decoder.getSurface());
|
||||
info.width = info.bg.w;
|
||||
info.height = info.bg.h;
|
||||
|
||||
// Overwrite the first color (transparent key) in the palette
|
||||
p = decoder.getPalette().data();
|
||||
info.palette.setEntry(0, p[0] >> 2, p[1] >> 2, p[2] >> 0);
|
||||
|
||||
for (i = 16; i < 32; i++) {
|
||||
r = *p >> 2;
|
||||
p++;
|
||||
g = *p >> 2;
|
||||
p++;
|
||||
b = *p >> 2;
|
||||
p++;
|
||||
info.palette.setEntry(i, r, g, b);
|
||||
}
|
||||
|
||||
// background data is drawn used the upper portion of the palette
|
||||
adjustForPalette(info.bg);
|
||||
}
|
||||
|
||||
void finalpass(byte *buffer, uint32 size) {
|
||||
byte b = 0xC0;
|
||||
byte r1 = 0x40;
|
||||
byte r2 = 0x80;
|
||||
for (uint32 i = 0; i < size*4; i++) {
|
||||
byte s = buffer[i/4];
|
||||
s &= b;
|
||||
|
||||
if (s == r1) {
|
||||
buffer[i/4] |= b;
|
||||
} else
|
||||
if (s == b) {
|
||||
buffer[i/4] ^= r2;
|
||||
}
|
||||
|
||||
b >>= 2; if (b == 0) { b = 0xC0; }
|
||||
r1 >>= 2; if (r1 == 0) { r1 = 0x40; }
|
||||
r2 >>= 2; if (r2 == 0) { r2 = 0x80; }
|
||||
}
|
||||
}
|
||||
|
||||
MaskBuffer *AmigaDisk_br::loadMask(const char *name, uint32 w, uint32 h) {
|
||||
if (!name) {
|
||||
return nullptr;
|
||||
}
|
||||
debugC(1, kDebugDisk, "AmigaDisk_br::loadMask '%s'", name);
|
||||
|
||||
Common::SeekableReadStream *stream = tryOpenFile("msk/" + Common::String(name), ".msk");
|
||||
if (!stream) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Image::IFFDecoder decoder;
|
||||
decoder.setNumRelevantPlanes(2); // use only 2 first bits from each pixels
|
||||
decoder.setPixelPacking(true); // pack 4 2bit pixels into 1 byte
|
||||
decoder.loadStream(*stream);
|
||||
|
||||
MaskBuffer *buffer = new MaskBuffer;
|
||||
// surface width was shrunk to 1/4th of the bitmap width due to the pixel packing
|
||||
buffer->create(decoder.getSurface()->w * 4, decoder.getSurface()->h);
|
||||
memcpy(buffer->data, decoder.getSurface()->getPixels(), buffer->size);
|
||||
buffer->bigEndian = true;
|
||||
finalpass(buffer->data, buffer->size);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void AmigaDisk_br::loadScenery(BackgroundInfo& info, const char* name, const char* mask, const char* path) {
|
||||
debugC(1, kDebugDisk, "AmigaDisk_br::loadScenery '%s', '%s' '%s'", name, mask, path);
|
||||
|
||||
if (name) {
|
||||
loadBackground(info, name);
|
||||
}
|
||||
if (mask) {
|
||||
info._mask = loadMask(mask, info.width, info.height);
|
||||
}
|
||||
|
||||
if (path) {
|
||||
info._path = loadPath(path, info.width, info.height);
|
||||
}
|
||||
}
|
||||
|
||||
void AmigaDisk_br::loadSlide(BackgroundInfo& info, const char *name) {
|
||||
debugC(1, kDebugDisk, "AmigaDisk_br::loadSlide '%s'", name);
|
||||
loadBackground(info, name);
|
||||
}
|
||||
|
||||
GfxObj* AmigaDisk_br::loadStatic(const char* name) {
|
||||
debugC(1, kDebugDisk, "AmigaDisk_br::loadStatic '%s'", name);
|
||||
|
||||
Common::String sName = name;
|
||||
Common::SeekableReadStream *stream = openFile("ras/" + sName, ".ras");
|
||||
Image::IFFDecoder decoder;
|
||||
decoder.loadStream(*stream);
|
||||
|
||||
Graphics::Surface *surf = new Graphics::Surface;
|
||||
assert(surf);
|
||||
surf->copyFrom(*decoder.getSurface());
|
||||
|
||||
// Static pictures are drawn used the upper half of the palette: this must be
|
||||
// done before shadow mask is applied. This way, only really transparent pixels
|
||||
// will have zero as a color.
|
||||
adjustForPalette(*surf);
|
||||
|
||||
// NOTE: this assumes that the extension is always present in the file name
|
||||
sName.deleteLastChar();
|
||||
sName.deleteLastChar();
|
||||
sName.deleteLastChar();
|
||||
sName.deleteLastChar();
|
||||
stream = tryOpenFile("ras/" + sName + ".ras_shdw");
|
||||
|
||||
if (!stream) {
|
||||
debugC(9, kDebugDisk, "Cannot find shadow file for '%s'\n", name);
|
||||
} else {
|
||||
uint32 shadowWidth = ((surf->w + 15)/8) & ~1;
|
||||
uint32 shadowSize = shadowWidth * surf->h;
|
||||
byte *shadow = new byte[shadowSize];
|
||||
assert(shadow);
|
||||
stream->read(shadow, shadowSize);
|
||||
for (int32 i = 0; i < surf->h; ++i) {
|
||||
byte *src = shadow + shadowWidth * i;
|
||||
byte *dst = (byte *)surf->getPixels() + surf->pitch * i;
|
||||
|
||||
for (int32 j = 0; j < surf->w; ++j, ++dst) {
|
||||
byte bit = src[j/8] & (1 << (7 - (j & 7)));
|
||||
if (bit == 0) *dst = 0;
|
||||
}
|
||||
}
|
||||
|
||||
delete[] shadow;
|
||||
delete stream;
|
||||
}
|
||||
|
||||
return new GfxObj(0, new SurfaceToFrames(surf), name);
|
||||
}
|
||||
|
||||
Sprites* AmigaDisk_br::createSprites(Common::ReadStream *stream) {
|
||||
uint16 num = stream->readUint16BE();
|
||||
|
||||
Sprites *sprites = new Sprites(num);
|
||||
|
||||
for (uint i = 0; i < num; i++) {
|
||||
Sprite *spr = &sprites->_sprites[i];
|
||||
spr->size = stream->readUint16BE();
|
||||
spr->x = stream->readUint16BE();
|
||||
spr->y = stream->readUint16BE();
|
||||
spr->w = stream->readUint16BE();
|
||||
spr->h = stream->readUint16BE() - 1;
|
||||
|
||||
spr->packedData = (byte *)malloc(spr->size);
|
||||
stream->read(spr->packedData, spr->size);
|
||||
}
|
||||
|
||||
delete stream;
|
||||
|
||||
return sprites;
|
||||
}
|
||||
|
||||
Frames* AmigaDisk_br::loadFrames(const char* name) {
|
||||
Common::SeekableReadStream *stream = nullptr;
|
||||
|
||||
debugC(5, kDebugDisk, "AmigaDisk_br::loadFrames");
|
||||
|
||||
Common::String path(name);
|
||||
if (path.hasSuffix(".win")) {
|
||||
stream = openFile(path);
|
||||
} else {
|
||||
stream = openFile("anims/" + Common::String(name), ".ani");
|
||||
}
|
||||
|
||||
return createSprites(stream);
|
||||
}
|
||||
|
||||
GfxObj* AmigaDisk_br::loadTalk(const char *name) {
|
||||
debugC(1, kDebugDisk, "AmigaDisk_br::loadTalk '%s'", name);
|
||||
|
||||
Common::SeekableReadStream *stream = openFile("talks/" + Common::String(name), ".tal");
|
||||
|
||||
// talk position is set to (0,0), because talks are always displayed at
|
||||
// absolute coordinates, set in the dialogue manager. The original used
|
||||
// to null out coordinates every time they were needed. We do it better!
|
||||
Sprites *spr = createSprites(stream);
|
||||
for (int i = 0; i < spr->getNum(); i++) {
|
||||
spr->_sprites[i].x = 0;
|
||||
spr->_sprites[i].y = 0;
|
||||
}
|
||||
return new GfxObj(0, spr, name);
|
||||
}
|
||||
|
||||
Font* AmigaDisk_br::loadFont(const char* name) {
|
||||
debugC(1, kDebugDisk, "AmigaFullDisk::loadFont '%s'", name);
|
||||
|
||||
Common::SeekableReadStream *stream = openFile("fonts/" + Common::String(name), ".font");
|
||||
|
||||
Common::String fontDir;
|
||||
Common::String fontFile;
|
||||
byte ch;
|
||||
|
||||
stream->seek(4, SEEK_SET);
|
||||
while ((ch = stream->readByte()) != 0x2F) fontDir += ch;
|
||||
while ((ch = stream->readByte()) != 0) fontFile += ch;
|
||||
delete stream;
|
||||
|
||||
stream = openFile("fonts/" + fontDir + "/" + fontFile);
|
||||
Font *font = createFont(name, *stream);
|
||||
|
||||
delete stream;
|
||||
return font;
|
||||
}
|
||||
|
||||
Common::SeekableReadStream* AmigaDisk_br::loadMusic(const char* name) {
|
||||
debugC(5, kDebugDisk, "AmigaDisk_br::loadMusic");
|
||||
return tryOpenFile("msc/" + Common::String(name), ".msc");
|
||||
}
|
||||
|
||||
|
||||
Common::SeekableReadStream* AmigaDisk_br::loadSound(const char* name) {
|
||||
debugC(5, kDebugDisk, "AmigaDisk_br::loadSound");
|
||||
return openFile("sfx/" + Common::String(name), ".sfx");
|
||||
}
|
||||
|
||||
static const uint16 objectsMax[5] = {
|
||||
5, 73, 71, 19, 48
|
||||
};
|
||||
|
||||
GfxObj* AmigaDisk_br::loadObjects(const char *name, uint8 part) {
|
||||
debugC(5, kDebugDisk, "AmigaDisk_br::loadObjects");
|
||||
|
||||
Common::SeekableReadStream *stream = openFile(name);
|
||||
Image::IFFDecoder decoder;
|
||||
decoder.loadStream(*stream);
|
||||
|
||||
uint16 max = objectsMax[part];
|
||||
if (_vm->getFeatures() & GF_DEMO)
|
||||
max = 72;
|
||||
|
||||
byte *data = new byte[max * 2601];
|
||||
const byte *srcPtr = (const byte *)decoder.getSurface()->getBasePtr(0,0);
|
||||
int w = decoder.getSurface()->w;
|
||||
|
||||
// Convert to the expected display format
|
||||
for (int i = 0; i < max; i++) {
|
||||
uint16 x = (i % 8) * 51;
|
||||
uint16 y = (i / 8) * 51;
|
||||
|
||||
const byte *src = srcPtr + y * w + x;
|
||||
byte *dst = data + i * 2601;
|
||||
for (int h = 0; h < 51; h++) {
|
||||
memcpy(dst, src, 51);
|
||||
src += w;
|
||||
dst += 51;
|
||||
}
|
||||
}
|
||||
|
||||
return new GfxObj(0, new Cnv(max, 51, 51, data, true));
|
||||
}
|
||||
|
||||
Common::String AmigaDisk_br::selectArchive(const Common::String& name) {
|
||||
debugC(5, kDebugDisk, "AmigaDisk_br::selectArchive");
|
||||
|
||||
Common::String oldPath = _currentPart;
|
||||
_currentPart = name;
|
||||
|
||||
debugC(5, kDebugDisk, "AmigaDisk_br::selectArchive: adding part directory to search set");
|
||||
_sset.remove("part");
|
||||
_sset.add("part", _baseDir->getSubDirectory(Common::Path(name), 3), 10);
|
||||
|
||||
return oldPath;
|
||||
}
|
||||
|
||||
|
||||
Disk_br::Disk_br(Parallaction *vm) : _vm(vm), _baseDir(nullptr), _language(0) {
|
||||
}
|
||||
|
||||
Disk_br::~Disk_br() {
|
||||
_sset.clear();
|
||||
}
|
||||
|
||||
} // namespace Parallaction
|
||||
956
engines/parallaction/disk_ns.cpp
Normal file
956
engines/parallaction/disk_ns.cpp
Normal file
@@ -0,0 +1,956 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/config-manager.h"
|
||||
#include "common/fs.h"
|
||||
#include "common/formats/iff_container.h"
|
||||
#include "common/memstream.h"
|
||||
#include "common/substream.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/compression/powerpacker.h"
|
||||
#include "image/iff.h"
|
||||
#include "parallaction/parser.h"
|
||||
#include "parallaction/parallaction.h"
|
||||
|
||||
|
||||
namespace Parallaction {
|
||||
|
||||
|
||||
// HACK: Several archives ('de', 'en', 'fr' and 'disk0') in the multi-lingual
|
||||
// Amiga version of Nippon Safes, and one archive ('fr') in the Amiga Demo of
|
||||
// Nippon Safes used different internal offsets than all the other archives.
|
||||
//
|
||||
// When an archive is opened in the Amiga demo, its size is checked against
|
||||
// SIZEOF_SMALL_ARCHIVE to detect when the smaller archive is used.
|
||||
//
|
||||
// When an archive is opened in Amiga multi-lingual version, the header is
|
||||
// checked again NDOS to detect when a smaller archive is used.
|
||||
//
|
||||
#define SIZEOF_SMALL_ARCHIVE 12778
|
||||
|
||||
#define ARCHIVE_FILENAMES_OFS 0x16
|
||||
|
||||
#define NORMAL_ARCHIVE_FILES_NUM 384
|
||||
#define SMALL_ARCHIVE_FILES_NUM 180
|
||||
|
||||
#define NORMAL_ARCHIVE_SIZES_OFS 0x3016
|
||||
#define SMALL_ARCHIVE_SIZES_OFS 0x1696
|
||||
|
||||
#define NORMAL_ARCHIVE_DATA_OFS 0x4000
|
||||
#define SMALL_ARCHIVE_DATA_OFS 0x1966
|
||||
|
||||
#define MAX_ARCHIVE_ENTRIES 384
|
||||
|
||||
class NSArchive : public Common::Archive {
|
||||
|
||||
Common::SeekableReadStream *_stream;
|
||||
|
||||
char _archiveDir[MAX_ARCHIVE_ENTRIES][32];
|
||||
uint32 _archiveLengths[MAX_ARCHIVE_ENTRIES];
|
||||
uint32 _archiveOffsets[MAX_ARCHIVE_ENTRIES];
|
||||
uint32 _numFiles;
|
||||
|
||||
uint32 lookup(const char *name) const;
|
||||
|
||||
public:
|
||||
NSArchive(Common::SeekableReadStream *stream, Common::Platform platform, uint32 features);
|
||||
~NSArchive() override;
|
||||
|
||||
Common::SeekableReadStream *createReadStreamForMember(const Common::Path &path) const override;
|
||||
bool hasFile(const Common::Path &path) const override;
|
||||
int listMembers(Common::ArchiveMemberList &list) const override;
|
||||
const Common::ArchiveMemberPtr getMember(const Common::Path &path) const override;
|
||||
};
|
||||
|
||||
|
||||
NSArchive::NSArchive(Common::SeekableReadStream *stream, Common::Platform platform, uint32 features) : _stream(stream) {
|
||||
if (!_stream) {
|
||||
error("NSArchive: invalid stream passed to constructor");
|
||||
}
|
||||
|
||||
bool isSmallArchive = false;
|
||||
if (platform == Common::kPlatformAmiga) {
|
||||
if (features & GF_DEMO) {
|
||||
isSmallArchive = stream->size() == SIZEOF_SMALL_ARCHIVE;
|
||||
} else if (features & GF_LANG_MULT) {
|
||||
isSmallArchive = (stream->readUint32BE() != MKTAG('N','D','O','S'));
|
||||
}
|
||||
}
|
||||
|
||||
_numFiles = (isSmallArchive) ? SMALL_ARCHIVE_FILES_NUM : NORMAL_ARCHIVE_FILES_NUM;
|
||||
|
||||
_stream->seek(ARCHIVE_FILENAMES_OFS);
|
||||
_stream->read(_archiveDir, _numFiles*32);
|
||||
|
||||
_stream->seek((isSmallArchive) ? SMALL_ARCHIVE_SIZES_OFS : NORMAL_ARCHIVE_SIZES_OFS);
|
||||
|
||||
uint32 dataOffset = (isSmallArchive) ? SMALL_ARCHIVE_DATA_OFS : NORMAL_ARCHIVE_DATA_OFS;
|
||||
for (uint16 i = 0; i < _numFiles; i++) {
|
||||
_archiveOffsets[i] = dataOffset;
|
||||
_archiveLengths[i] = _stream->readUint32BE();
|
||||
dataOffset += _archiveLengths[i];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
NSArchive::~NSArchive() {
|
||||
delete _stream;
|
||||
}
|
||||
|
||||
uint32 NSArchive::lookup(const char *name) const {
|
||||
uint32 i = 0;
|
||||
for ( ; i < _numFiles; i++) {
|
||||
if (!scumm_stricmp(_archiveDir[i], name)) break;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *NSArchive::createReadStreamForMember(const Common::Path &path) const {
|
||||
Common::String name = path.toString();
|
||||
debugC(3, kDebugDisk, "NSArchive::createReadStreamForMember(%s)", name.c_str());
|
||||
|
||||
if (name.empty())
|
||||
return nullptr;
|
||||
|
||||
uint32 index = lookup(name.c_str());
|
||||
if (index == _numFiles) return nullptr;
|
||||
|
||||
debugC(9, kDebugDisk, "NSArchive::createReadStreamForMember: '%s' found in slot %i", name.c_str(), index);
|
||||
|
||||
int offset = _archiveOffsets[index];
|
||||
int endOffset = _archiveOffsets[index] + _archiveLengths[index];
|
||||
return new Common::SeekableSubReadStream(_stream, offset, endOffset, DisposeAfterUse::NO);
|
||||
}
|
||||
|
||||
bool NSArchive::hasFile(const Common::Path &path) const {
|
||||
Common::String name = path.toString();
|
||||
if (name.empty())
|
||||
return false;
|
||||
return lookup(name.c_str()) != _numFiles;
|
||||
}
|
||||
|
||||
int NSArchive::listMembers(Common::ArchiveMemberList &list) const {
|
||||
for (uint32 i = 0; i < _numFiles; i++) {
|
||||
list.push_back(Common::SharedPtr<Common::GenericArchiveMember>(new Common::GenericArchiveMember(Common::String(_archiveDir[i]), *this)));
|
||||
}
|
||||
return _numFiles;
|
||||
}
|
||||
|
||||
const Common::ArchiveMemberPtr NSArchive::getMember(const Common::Path &path) const {
|
||||
Common::String name = path.toString();
|
||||
uint32 index = lookup(name.c_str());
|
||||
|
||||
if (index >= _numFiles) {
|
||||
return Common::ArchiveMemberPtr();
|
||||
}
|
||||
|
||||
const char *item = _archiveDir[index];
|
||||
|
||||
return Common::SharedPtr<Common::GenericArchiveMember>(new Common::GenericArchiveMember(Common::String(item), *this));
|
||||
}
|
||||
|
||||
|
||||
#define HIGHEST_PRIORITY 9
|
||||
#define NORMAL_ARCHIVE_PRIORITY 5
|
||||
#define LOW_ARCHIVE_PRIORITY 2
|
||||
#define LOWEST_ARCHIVE_PRIORITY 1
|
||||
|
||||
Disk_ns::Disk_ns(Parallaction *vm) : _vm(vm) {
|
||||
Common::FSDirectory *baseDir = new Common::FSDirectory(ConfMan.getPath("path"));
|
||||
_sset.add("basedir", baseDir, HIGHEST_PRIORITY);
|
||||
}
|
||||
|
||||
Disk_ns::~Disk_ns() {
|
||||
_sset.clear();
|
||||
}
|
||||
|
||||
void Disk_ns::errorFileNotFound(const char *s) {
|
||||
error("File '%s' not found", s);
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *Disk_ns::openFile(const char *filename) {
|
||||
Common::SeekableReadStream *stream = tryOpenFile(filename);
|
||||
if (!stream)
|
||||
errorFileNotFound(filename);
|
||||
return stream;
|
||||
}
|
||||
|
||||
|
||||
void Disk_ns::addArchive(const Common::String& name, int priority) {
|
||||
Common::SeekableReadStream *stream = _sset.createReadStreamForMember(Common::Path(name));
|
||||
if (!stream)
|
||||
error("Disk_ns::addArchive() couldn't find archive '%s'", name.c_str());
|
||||
|
||||
debugC(1, kDebugDisk, "Disk_ns::addArchive(name = %s, priority = %i)", name.c_str(), priority);
|
||||
|
||||
NSArchive *arc = new NSArchive(stream, _vm->getPlatform(), _vm->getFeatures());
|
||||
_sset.add(name, arc, priority);
|
||||
}
|
||||
|
||||
Common::String Disk_ns::selectArchive(const Common::String& name) {
|
||||
Common::String oldName = _resArchiveName;
|
||||
|
||||
if (_sset.hasArchive(name)) {
|
||||
return oldName;
|
||||
}
|
||||
|
||||
if (!_resArchiveName.empty()) {
|
||||
_sset.remove(_resArchiveName);
|
||||
}
|
||||
_resArchiveName = name;
|
||||
addArchive(name, NORMAL_ARCHIVE_PRIORITY);
|
||||
|
||||
return oldName;
|
||||
}
|
||||
|
||||
void Disk_ns::setLanguage(uint16 language) {
|
||||
debugC(1, kDebugDisk, "setLanguage(%i)", language);
|
||||
assert(language < 4);
|
||||
|
||||
if (!_language.empty()) {
|
||||
_sset.remove(_language);
|
||||
}
|
||||
|
||||
static const char *languages[] = { "it", "fr", "en", "ge" };
|
||||
_language = languages[language];
|
||||
|
||||
if (_sset.hasArchive(_language)) {
|
||||
return;
|
||||
}
|
||||
|
||||
addArchive(_language, LOWEST_ARCHIVE_PRIORITY);
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
|
||||
DosDisk_ns::DosDisk_ns(Parallaction* vm) : Disk_ns(vm), _gfx(nullptr) {
|
||||
|
||||
}
|
||||
|
||||
DosDisk_ns::~DosDisk_ns() {
|
||||
}
|
||||
|
||||
void DosDisk_ns::init() {
|
||||
// setup permament archives
|
||||
addArchive("disk1", LOW_ARCHIVE_PRIORITY);
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *DosDisk_ns::tryOpenFile(const char* name) {
|
||||
debugC(3, kDebugDisk, "DosDisk_ns::tryOpenFile(%s)", name);
|
||||
|
||||
Common::SeekableReadStream *stream = _sset.createReadStreamForMember(name);
|
||||
if (stream)
|
||||
return stream;
|
||||
|
||||
char path[PATH_LEN];
|
||||
Common::sprintf_s(path, "%s.pp", name);
|
||||
return _sset.createReadStreamForMember(path);
|
||||
}
|
||||
|
||||
Script* Disk_ns::loadLocation(const char *name) {
|
||||
char path[PATH_LEN];
|
||||
const char *charName = _vm->_char.getBaseName();
|
||||
|
||||
// WORKAROUND: Special case for the Multilingual DOS version: during the ending
|
||||
// sequence, it tries to load a non-existing file using "Dinor" as a character
|
||||
// name. In this case, the character name should be just "dino".
|
||||
if (!strcmp(charName, "Dinor"))
|
||||
charName = "dino";
|
||||
|
||||
Common::sprintf_s(path, "%s%s/%s.loc", charName, _language.c_str(), name);
|
||||
debugC(3, kDebugDisk, "Disk_ns::loadLocation(%s): trying '%s'", name, path);
|
||||
Common::SeekableReadStream *stream = tryOpenFile(path);
|
||||
|
||||
if (!stream) {
|
||||
Common::sprintf_s(path, "%s/%s.loc", _language.c_str(), name);
|
||||
debugC(3, kDebugDisk, "DosDisk_ns::loadLocation(%s): trying '%s'", name, path);
|
||||
stream = openFile(path);
|
||||
}
|
||||
return new Script(stream, true);
|
||||
}
|
||||
|
||||
Script* Disk_ns::loadScript(const char* name) {
|
||||
debugC(1, kDebugDisk, "Disk_ns::loadScript '%s'", name);
|
||||
char path[PATH_LEN];
|
||||
Common::sprintf_s(path, "%s.script", name);
|
||||
Common::SeekableReadStream *stream = openFile(path);
|
||||
return new Script(stream, true);
|
||||
}
|
||||
|
||||
Cnv *Disk_ns::makeCnv(Common::SeekableReadStream *stream) {
|
||||
assert(stream);
|
||||
|
||||
uint16 numFrames = stream->readByte();
|
||||
uint16 width = stream->readByte();
|
||||
assert((width & 7) == 0);
|
||||
uint16 height = stream->readByte();
|
||||
uint32 decsize = numFrames * width * height;
|
||||
byte *data = new byte[decsize]();
|
||||
assert(data);
|
||||
|
||||
decodeCnv(data, numFrames, width, height, stream);
|
||||
|
||||
delete stream;
|
||||
return new Cnv(numFrames, width, height, data, true);
|
||||
}
|
||||
|
||||
void DosDisk_ns::decodeCnv(byte *data, uint16 numFrames, uint16 width, uint16 height, Common::SeekableReadStream *stream) {
|
||||
int32 decsize = numFrames * width * height;
|
||||
bool packed = (stream->size() - stream->pos()) != decsize;
|
||||
if (packed) {
|
||||
Common::PackBitsReadStream decoder(*stream);
|
||||
decoder.read(data, decsize);
|
||||
} else {
|
||||
stream->read(data, decsize);
|
||||
}
|
||||
}
|
||||
|
||||
Cnv* DosDisk_ns::loadCnv(const char *filename) {
|
||||
Common::SeekableReadStream *stream = openFile(filename);
|
||||
assert(stream);
|
||||
return makeCnv(stream);
|
||||
}
|
||||
|
||||
|
||||
GfxObj* DosDisk_ns::loadTalk(const char *name) {
|
||||
|
||||
const char *ext = strstr(name, ".talk");
|
||||
if (ext) {
|
||||
// npc talk
|
||||
return new GfxObj(0, loadCnv(name), name);
|
||||
}
|
||||
|
||||
char v20[30];
|
||||
if (g_engineFlags & kEngineTransformedDonna) {
|
||||
Common::sprintf_s(v20, "%stta.cnv", name);
|
||||
} else {
|
||||
Common::sprintf_s(v20, "%stal.cnv", name);
|
||||
}
|
||||
|
||||
return new GfxObj(0, loadCnv(v20), name);
|
||||
}
|
||||
|
||||
|
||||
GfxObj* DosDisk_ns::loadHead(const char* name) {
|
||||
char path[PATH_LEN];
|
||||
Common::sprintf_s(path, "%shead", name);
|
||||
path[8] = '\0';
|
||||
Common::strcat_s(path, ".cnv");
|
||||
return new GfxObj(0, loadCnv(path));
|
||||
}
|
||||
|
||||
|
||||
Frames* DosDisk_ns::loadPointer(const char *name) {
|
||||
char path[PATH_LEN];
|
||||
Common::sprintf_s(path, "%s.cnv", name);
|
||||
return loadCnv(path);
|
||||
}
|
||||
|
||||
|
||||
Font* DosDisk_ns::loadFont(const char* name) {
|
||||
char path[PATH_LEN];
|
||||
Common::sprintf_s(path, "%scnv.cnv", name);
|
||||
return createFont(name, loadCnv(path));
|
||||
}
|
||||
|
||||
|
||||
GfxObj* DosDisk_ns::loadObjects(const char *name, uint8 part) {
|
||||
char path[PATH_LEN];
|
||||
Common::sprintf_s(path, "%sobj.cnv", name);
|
||||
return new GfxObj(0, loadCnv(path), name);
|
||||
}
|
||||
|
||||
|
||||
GfxObj* DosDisk_ns::loadStatic(const char* name) {
|
||||
return new GfxObj(0, loadCnv(name), name);
|
||||
}
|
||||
|
||||
Frames* DosDisk_ns::loadFrames(const char* name) {
|
||||
return loadCnv(name);
|
||||
}
|
||||
|
||||
/*
|
||||
Background images are compressed using a RLE algorithm that resembles PackBits.
|
||||
|
||||
The uncompressed data is then unpacked as following:
|
||||
- color data [bits 0-5]
|
||||
- mask data [bits 6-7] (z buffer)
|
||||
- path data [bit 8] (walkable areas)
|
||||
*/
|
||||
void DosDisk_ns::unpackBackground(Common::ReadStream *stream, byte *screen, byte *mask, byte *path) {
|
||||
byte storage[128];
|
||||
uint32 storageLen = 0, len = 0;
|
||||
uint32 j = 0;
|
||||
|
||||
while (1) {
|
||||
// first extracts packbits variant data
|
||||
do {
|
||||
len = stream->readByte();
|
||||
if (stream->eos())
|
||||
return;
|
||||
|
||||
if (len == 128) {
|
||||
storageLen = 0;
|
||||
} else if (len <= 127) {
|
||||
len++;
|
||||
for (uint32 i = 0; i < len; i++) {
|
||||
storage[i] = stream->readByte();
|
||||
}
|
||||
storageLen = len;
|
||||
} else {
|
||||
len = (256 - len) + 1;
|
||||
byte v = stream->readByte();
|
||||
memset(storage, v, len);
|
||||
storageLen = len;
|
||||
}
|
||||
} while (storageLen == 0);
|
||||
|
||||
// then unpacks the bits to the destination buffers
|
||||
for (uint32 i = 0; i < storageLen; i++, j++) {
|
||||
byte b = storage[i];
|
||||
path[j/8] |= ((b & 0x80) >> 7) << (j & 7);
|
||||
mask[j/4] |= ((b & 0x60) >> 5) << ((j & 3) << 1);
|
||||
screen[j] = b & 0x1F;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DosDisk_ns::parseDepths(BackgroundInfo &info, Common::SeekableReadStream &stream) {
|
||||
info.layers[0] = stream.readByte();
|
||||
info.layers[1] = stream.readByte();
|
||||
info.layers[2] = stream.readByte();
|
||||
info.layers[3] = stream.readByte();
|
||||
}
|
||||
|
||||
void DosDisk_ns::createMaskAndPathBuffers(BackgroundInfo &info) {
|
||||
info._mask = new MaskBuffer;
|
||||
assert(info._mask);
|
||||
info._mask->create(info.width, info.height);
|
||||
info._mask->bigEndian = true;
|
||||
|
||||
info._path = new PathBuffer;
|
||||
assert(info._path);
|
||||
info._path->create(info.width, info.height);
|
||||
info._path->bigEndian = true;
|
||||
}
|
||||
|
||||
void DosDisk_ns::loadBackground(BackgroundInfo& info, const char *filename) {
|
||||
Common::SeekableReadStream *stream = openFile(filename);
|
||||
|
||||
info.width = _vm->_screenWidth; // 320
|
||||
info.height = _vm->_screenHeight; // 200
|
||||
|
||||
// read palette
|
||||
byte tmp[3];
|
||||
for (uint i = 0; i < 32; i++) {
|
||||
tmp[0] = stream->readByte();
|
||||
tmp[1] = stream->readByte();
|
||||
tmp[2] = stream->readByte();
|
||||
info.palette.setEntry(i, tmp[0], tmp[1], tmp[2]);
|
||||
}
|
||||
|
||||
// read z coordinates
|
||||
parseDepths(info, *stream);
|
||||
|
||||
// read palette rotation parameters
|
||||
PaletteFxRange range;
|
||||
for (uint32 _si = 0; _si < 6; _si++) {
|
||||
range._timer = stream->readUint16BE();
|
||||
range._step = stream->readUint16BE();
|
||||
range._flags = stream->readUint16BE();
|
||||
range._first = stream->readByte();
|
||||
range._last = stream->readByte();
|
||||
info.setPaletteRange(_si, range);
|
||||
}
|
||||
|
||||
// read bitmap, mask and path data and extract them into the 3 buffers
|
||||
info.bg.create(info.width, info.height, Graphics::PixelFormat::createFormatCLUT8());
|
||||
createMaskAndPathBuffers(info);
|
||||
unpackBackground(stream, (byte *)info.bg.getPixels(), info._mask->data, info._path->data);
|
||||
|
||||
delete stream;
|
||||
}
|
||||
|
||||
|
||||
void DosDisk_ns::loadSlide(BackgroundInfo& info, const char *filename) {
|
||||
char path[PATH_LEN];
|
||||
Common::sprintf_s(path, "%s.slide", filename);
|
||||
loadBackground(info, path);
|
||||
}
|
||||
|
||||
void DosDisk_ns::loadScenery(BackgroundInfo& info, const char *name, const char *mask, const char* path) {
|
||||
char filename[PATH_LEN];
|
||||
Common::sprintf_s(filename, "%s.dyn", name);
|
||||
|
||||
// load bitmap
|
||||
loadBackground(info, filename);
|
||||
|
||||
if (mask == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// load external mask and path if present (overwriting the ones loaded by loadBackground)
|
||||
char maskPath[PATH_LEN];
|
||||
Common::sprintf_s(maskPath, "%s.msk", mask);
|
||||
|
||||
Common::SeekableReadStream *stream = openFile(maskPath);
|
||||
assert(stream);
|
||||
|
||||
parseDepths(info, *stream);
|
||||
|
||||
createMaskAndPathBuffers(info);
|
||||
stream->read(info._path->data, info._path->size);
|
||||
stream->read(info._mask->data, info._mask->size);
|
||||
|
||||
delete stream;
|
||||
}
|
||||
|
||||
Table* DosDisk_ns::loadTable(const char* name) {
|
||||
char path[PATH_LEN];
|
||||
Common::sprintf_s(path, "%s.tab", name);
|
||||
return createTableFromStream(100, openFile(path));
|
||||
}
|
||||
|
||||
Common::SeekableReadStream* DosDisk_ns::loadMusic(const char* name) {
|
||||
char path[PATH_LEN];
|
||||
Common::sprintf_s(path, "%s.mid", name);
|
||||
return openFile(path);
|
||||
}
|
||||
|
||||
|
||||
Common::SeekableReadStream* DosDisk_ns::loadSound(const char* name) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma mark -
|
||||
|
||||
|
||||
|
||||
|
||||
AmigaDisk_ns::AmigaDisk_ns(Parallaction *vm) : Disk_ns(vm) {
|
||||
}
|
||||
|
||||
|
||||
AmigaDisk_ns::~AmigaDisk_ns() {
|
||||
|
||||
}
|
||||
|
||||
void AmigaDisk_ns::init() {
|
||||
// setup permament archives
|
||||
if (_vm->getFeatures() & GF_DEMO) {
|
||||
addArchive("disk0", LOW_ARCHIVE_PRIORITY);
|
||||
} else {
|
||||
addArchive("disk0", LOW_ARCHIVE_PRIORITY);
|
||||
addArchive("disk1", LOW_ARCHIVE_PRIORITY);
|
||||
}
|
||||
}
|
||||
|
||||
#define NUM_PLANES 5
|
||||
|
||||
/*
|
||||
unpackFrame transforms images from 5-bitplanes format to
|
||||
8-bit color-index mode
|
||||
*/
|
||||
void AmigaDisk_ns::unpackFrame(byte *dst, byte *src, uint16 planeSize) {
|
||||
|
||||
byte s0, s1, s2, s3, s4, mask, t0, t1, t2, t3, t4;
|
||||
|
||||
for (uint32 j = 0; j < planeSize; j++) {
|
||||
s0 = src[j];
|
||||
s1 = src[j+planeSize];
|
||||
s2 = src[j+planeSize*2];
|
||||
s3 = src[j+planeSize*3];
|
||||
s4 = src[j+planeSize*4];
|
||||
|
||||
for (uint32 k = 0; k < 8; k++) {
|
||||
mask = 1 << (7 - k);
|
||||
t0 = (s0 & mask ? 1 << 0 : 0);
|
||||
t1 = (s1 & mask ? 1 << 1 : 0);
|
||||
t2 = (s2 & mask ? 1 << 2 : 0);
|
||||
t3 = (s3 & mask ? 1 << 3 : 0);
|
||||
t4 = (s4 & mask ? 1 << 4 : 0);
|
||||
*dst++ = t0 | t1 | t2 | t3 | t4;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
patchFrame applies DLTA data (dlta) to specified buffer (dst)
|
||||
*/
|
||||
void AmigaDisk_ns::patchFrame(byte *dst, byte *dlta, uint16 bytesPerPlane, uint16 height) {
|
||||
|
||||
uint32 *dataIndex = (uint32 *)dlta;
|
||||
uint32 *ofslenIndex = (uint32 *)dlta + 8;
|
||||
|
||||
uint16 *base = (uint16 *)dlta;
|
||||
uint16 wordsPerLine = bytesPerPlane >> 1;
|
||||
|
||||
for (uint j = 0; j < NUM_PLANES; j++) {
|
||||
uint16 *dst16 = (uint16 *)(dst + j * bytesPerPlane * height);
|
||||
|
||||
uint16 *data = base + READ_BE_UINT32(dataIndex);
|
||||
dataIndex++;
|
||||
uint16 *ofslen = base + READ_BE_UINT32(ofslenIndex);
|
||||
ofslenIndex++;
|
||||
|
||||
while (*ofslen != 0xFFFF) {
|
||||
|
||||
uint16 ofs = READ_BE_UINT16(ofslen);
|
||||
ofslen++;
|
||||
uint16 size = READ_BE_UINT16(ofslen);
|
||||
ofslen++;
|
||||
|
||||
while (size > 0) {
|
||||
dst16[ofs] ^= *data++;
|
||||
ofs += wordsPerLine;
|
||||
size--;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// FIXME: no mask is loaded
|
||||
void AmigaDisk_ns::unpackBitmap(byte *dst, byte *src, uint16 numFrames, uint16 bytesPerPlane, uint16 height) {
|
||||
|
||||
byte *baseFrame = src;
|
||||
byte *tempBuffer = nullptr;
|
||||
|
||||
uint16 planeSize = bytesPerPlane * height;
|
||||
|
||||
for (uint32 i = 0; i < numFrames; i++) {
|
||||
if (READ_BE_UINT32(src) == MKTAG('D','L','T','A')) {
|
||||
|
||||
uint size = READ_BE_UINT32(src + 4);
|
||||
|
||||
if (tempBuffer == nullptr)
|
||||
tempBuffer = (byte *)malloc(planeSize * NUM_PLANES);
|
||||
|
||||
memcpy(tempBuffer, baseFrame, planeSize * NUM_PLANES);
|
||||
|
||||
patchFrame(tempBuffer, src + 8, bytesPerPlane, height);
|
||||
unpackFrame(dst, tempBuffer, planeSize);
|
||||
|
||||
src += (size + 8);
|
||||
dst += planeSize * 8;
|
||||
} else {
|
||||
unpackFrame(dst, src, planeSize);
|
||||
src += planeSize * NUM_PLANES;
|
||||
dst += planeSize * 8;
|
||||
}
|
||||
}
|
||||
|
||||
free(tempBuffer);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void AmigaDisk_ns::decodeCnv(byte *data, uint16 numFrames, uint16 width, uint16 height, Common::SeekableReadStream *stream) {
|
||||
byte bytesPerPlane = width / 8;
|
||||
uint32 rawsize = numFrames * bytesPerPlane * NUM_PLANES * height;
|
||||
byte *buf = new byte[rawsize];
|
||||
assert(buf);
|
||||
stream->read(buf, rawsize);
|
||||
unpackBitmap(data, buf, numFrames, bytesPerPlane, height);
|
||||
delete[] buf;
|
||||
}
|
||||
|
||||
#undef NUM_PLANES
|
||||
|
||||
Frames* AmigaDisk_ns::loadPointer(const char* name) {
|
||||
debugC(1, kDebugDisk, "AmigaDisk_ns::loadPointer");
|
||||
Common::SeekableReadStream *stream = openFile(name);
|
||||
return makeCnv(stream);
|
||||
}
|
||||
|
||||
GfxObj* AmigaDisk_ns::loadStatic(const char* name) {
|
||||
debugC(1, kDebugDisk, "AmigaDisk_ns::loadStatic '%s'", name);
|
||||
Common::SeekableReadStream *s = openFile(name);
|
||||
return new GfxObj(0, makeCnv(s), name);
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *AmigaDisk_ns::tryOpenFile(const char* name) {
|
||||
debugC(3, kDebugDisk, "AmigaDisk_ns::tryOpenFile(%s)", name);
|
||||
|
||||
Common::PowerPackerStream *ret;
|
||||
Common::SeekableReadStream *stream = _sset.createReadStreamForMember(name);
|
||||
if (stream)
|
||||
return stream;
|
||||
|
||||
char path[PATH_LEN];
|
||||
Common::sprintf_s(path, "%s.pp", name);
|
||||
stream = _sset.createReadStreamForMember(path);
|
||||
if (stream) {
|
||||
ret = new Common::PowerPackerStream(*stream);
|
||||
delete stream;
|
||||
return ret;
|
||||
}
|
||||
|
||||
Common::sprintf_s(path, "%s.dd", name);
|
||||
stream = _sset.createReadStreamForMember(path);
|
||||
if (stream) {
|
||||
ret = new Common::PowerPackerStream(*stream);
|
||||
delete stream;
|
||||
return ret;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
FIXME: mask values are not computed correctly for level 1 and 2
|
||||
|
||||
NOTE: this routine is only able to build masks for Nippon Safes, since mask widths are hardcoded
|
||||
into the main loop.
|
||||
*/
|
||||
void AmigaDisk_ns::buildMask(byte* buf) {
|
||||
|
||||
byte mask1[16] = { 0, 0x80, 0x20, 0xA0, 8, 0x88, 0x28, 0xA8, 2, 0x82, 0x22, 0xA2, 0xA, 0x8A, 0x2A, 0xAA };
|
||||
byte mask0[16] = { 0, 0x40, 0x10, 0x50, 4, 0x44, 0x14, 0x54, 1, 0x41, 0x11, 0x51, 0x5, 0x45, 0x15, 0x55 };
|
||||
|
||||
byte plane0[40];
|
||||
byte plane1[40];
|
||||
|
||||
for (int32 i = 0; i < _vm->_screenHeight; i++) {
|
||||
|
||||
memcpy(plane0, buf, 40);
|
||||
memcpy(plane1, buf+40, 40);
|
||||
|
||||
for (uint32 j = 0; j < 40; j++) {
|
||||
*buf++ = mask0[(plane0[j] & 0xF0) >> 4] | mask1[(plane1[j] & 0xF0) >> 4];
|
||||
*buf++ = mask0[plane0[j] & 0xF] | mask1[plane1[j] & 0xF];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void AmigaDisk_ns::loadBackground(BackgroundInfo& info, const char *name) {
|
||||
Common::SeekableReadStream *s = openFile(name);
|
||||
Image::IFFDecoder decoder;
|
||||
decoder.loadStream(*s);
|
||||
|
||||
info.bg.copyFrom(*decoder.getSurface());
|
||||
info.width = info.bg.w;
|
||||
info.height = info.bg.h;
|
||||
|
||||
const Graphics::Palette &p = decoder.getPalette();
|
||||
for (uint i = 0; i < 32; i++) {
|
||||
byte r, g, b;
|
||||
p.get(i, r, g, b);
|
||||
info.palette.setEntry(i, r >> 2, g >> 2, b >> 2);
|
||||
}
|
||||
|
||||
const Common::Array<Image::IFFDecoder::PaletteRange> &paletteRanges = decoder.getPaletteRanges();
|
||||
for (uint j = 0; j < 6 && j < paletteRanges.size(); j++) {
|
||||
PaletteFxRange range;
|
||||
range._timer = paletteRanges[j].timer;
|
||||
range._step = paletteRanges[j].step;
|
||||
range._flags = paletteRanges[j].flags;
|
||||
range._first = paletteRanges[j].first;
|
||||
range._last = paletteRanges[j].last;
|
||||
info.setPaletteRange(j, range);
|
||||
}
|
||||
}
|
||||
|
||||
void AmigaDisk_ns::loadMask_internal(BackgroundInfo& info, const char *name) {
|
||||
debugC(5, kDebugDisk, "AmigaDisk_ns::loadMask_internal(%s)", name);
|
||||
|
||||
char path[PATH_LEN];
|
||||
Common::sprintf_s(path, "%s.mask", name);
|
||||
|
||||
Common::SeekableReadStream *s = tryOpenFile(path);
|
||||
if (!s) {
|
||||
debugC(5, kDebugDisk, "Mask file not found");
|
||||
return; // no errors if missing mask files: not every location has one
|
||||
}
|
||||
|
||||
Image::IFFDecoder decoder;
|
||||
decoder.setNumRelevantPlanes(2); // use only 2 first bits from each pixel
|
||||
decoder.setPixelPacking(true); // pack 4 2bit pixels into 1 byte
|
||||
decoder.loadStream(*s);
|
||||
|
||||
const Graphics::Palette &p = decoder.getPalette();
|
||||
for (uint i = 0; i < 4; i++) {
|
||||
byte r, g, b;
|
||||
p.get(i, r, g, b);
|
||||
info.layers[i] = (((r << 4) & 0xF00) | (g & 0xF0) | (b >> 4)) & 0xFF;
|
||||
}
|
||||
|
||||
info._mask = new MaskBuffer;
|
||||
// surface width was shrunk to 1/4th of the bitmap width due to the pixel packing
|
||||
info._mask->create(decoder.getSurface()->w * 4, decoder.getSurface()->h);
|
||||
memcpy(info._mask->data, decoder.getSurface()->getPixels(), info._mask->size);
|
||||
info._mask->bigEndian = true;
|
||||
}
|
||||
|
||||
void AmigaDisk_ns::loadPath_internal(BackgroundInfo& info, const char *name) {
|
||||
|
||||
char path[PATH_LEN];
|
||||
Common::sprintf_s(path, "%s.path", name);
|
||||
|
||||
Common::SeekableReadStream *s = tryOpenFile(path);
|
||||
if (!s) {
|
||||
return; // no errors if missing path files: not every location has one
|
||||
}
|
||||
|
||||
Image::IFFDecoder decoder;
|
||||
decoder.setNumRelevantPlanes(1); // use only first bit from each pixel
|
||||
decoder.setPixelPacking(true); // pack 8 1bit pixels into 1 byte
|
||||
decoder.loadStream(*s);
|
||||
|
||||
info._path = new PathBuffer;
|
||||
// surface width was shrunk to 1/8th of the bitmap width due to the pixel packing
|
||||
info._path->create(decoder.getSurface()->w * 8, decoder.getSurface()->h);
|
||||
memcpy(info._path->data, decoder.getSurface()->getPixels(), info._path->size);
|
||||
info._path->bigEndian = true;
|
||||
}
|
||||
|
||||
void AmigaDisk_ns::loadScenery(BackgroundInfo& info, const char* background, const char* mask, const char* path) {
|
||||
debugC(1, kDebugDisk, "AmigaDisk_ns::loadScenery '%s', '%s'", background, mask);
|
||||
|
||||
char filename[PATH_LEN];
|
||||
Common::sprintf_s(filename, "%s.bkgnd", background);
|
||||
|
||||
loadBackground(info, filename);
|
||||
|
||||
if (mask == nullptr) {
|
||||
loadMask_internal(info, background);
|
||||
loadPath_internal(info, background);
|
||||
} else {
|
||||
loadMask_internal(info, mask);
|
||||
loadPath_internal(info, mask);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void AmigaDisk_ns::loadSlide(BackgroundInfo& info, const char *name) {
|
||||
debugC(1, kDebugDisk, "AmigaDisk_ns::loadSlide '%s'", name);
|
||||
loadBackground(info, name);
|
||||
return;
|
||||
}
|
||||
|
||||
Frames* AmigaDisk_ns::loadFrames(const char* name) {
|
||||
debugC(1, kDebugDisk, "AmigaDisk_ns::loadFrames '%s'", name);
|
||||
|
||||
char path[PATH_LEN];
|
||||
Common::sprintf_s(path, "anims/%s", name);
|
||||
|
||||
Common::SeekableReadStream *s = tryOpenFile(path);
|
||||
if (!s)
|
||||
s = openFile(name);
|
||||
|
||||
return makeCnv(s);
|
||||
}
|
||||
|
||||
GfxObj* AmigaDisk_ns::loadHead(const char* name) {
|
||||
debugC(1, kDebugDisk, "AmigaDisk_ns::loadHead '%s'", name);
|
||||
char path[PATH_LEN];
|
||||
Common::sprintf_s(path, "%s.head", name);
|
||||
Common::SeekableReadStream *s = openFile(path);
|
||||
return new GfxObj(0, makeCnv(s), name);
|
||||
}
|
||||
|
||||
|
||||
GfxObj* AmigaDisk_ns::loadObjects(const char *name, uint8 part) {
|
||||
debugC(1, kDebugDisk, "AmigaDisk_ns::loadObjects");
|
||||
|
||||
char path[PATH_LEN];
|
||||
if (_vm->getFeatures() & GF_DEMO)
|
||||
Common::sprintf_s(path, "%s.objs", name);
|
||||
else
|
||||
Common::sprintf_s(path, "objs/%s.objs", name);
|
||||
|
||||
Common::SeekableReadStream *s = openFile(path);
|
||||
return new GfxObj(0, makeCnv(s), name);
|
||||
}
|
||||
|
||||
|
||||
GfxObj* AmigaDisk_ns::loadTalk(const char *name) {
|
||||
debugC(1, kDebugDisk, "AmigaDisk_ns::loadTalk '%s'", name);
|
||||
|
||||
char path[PATH_LEN];
|
||||
if (_vm->getFeatures() & GF_DEMO)
|
||||
Common::sprintf_s(path, "%s.talk", name);
|
||||
else
|
||||
Common::sprintf_s(path, "talk/%s.talk", name);
|
||||
|
||||
Common::SeekableReadStream *s = tryOpenFile(path);
|
||||
if (!s) {
|
||||
s = openFile(name);
|
||||
}
|
||||
return new GfxObj(0, makeCnv(s), name);
|
||||
}
|
||||
|
||||
Table* AmigaDisk_ns::loadTable(const char* name) {
|
||||
debugC(1, kDebugDisk, "AmigaDisk_ns::loadTable '%s'", name);
|
||||
|
||||
char path[PATH_LEN];
|
||||
if (!scumm_stricmp(name, "global")) {
|
||||
Common::sprintf_s(path, "%s.table", name);
|
||||
} else {
|
||||
if (!(_vm->getFeatures() & GF_DEMO))
|
||||
Common::sprintf_s(path, "objs/%s.table", name);
|
||||
else
|
||||
Common::sprintf_s(path, "%s.table", name);
|
||||
}
|
||||
|
||||
return createTableFromStream(100, openFile(path));
|
||||
}
|
||||
|
||||
Font* AmigaDisk_ns::loadFont(const char* name) {
|
||||
debugC(1, kDebugDisk, "AmigaFullDisk::loadFont '%s'", name);
|
||||
|
||||
char path[PATH_LEN];
|
||||
Common::sprintf_s(path, "%sfont", name);
|
||||
|
||||
Common::SeekableReadStream *stream = openFile(path);
|
||||
Font *font = createFont(name, *stream);
|
||||
delete stream;
|
||||
|
||||
return font;
|
||||
}
|
||||
|
||||
|
||||
Common::SeekableReadStream* AmigaDisk_ns::loadMusic(const char* name) {
|
||||
return tryOpenFile(name);
|
||||
}
|
||||
|
||||
Common::SeekableReadStream* AmigaDisk_ns::loadSound(const char* name) {
|
||||
char path[PATH_LEN];
|
||||
Common::sprintf_s(path, "%s.snd", name);
|
||||
|
||||
return tryOpenFile(path);
|
||||
}
|
||||
|
||||
} // End of namespace Parallaction
|
||||
204
engines/parallaction/exec.cpp
Normal file
204
engines/parallaction/exec.cpp
Normal file
@@ -0,0 +1,204 @@
|
||||
/* 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 "parallaction/exec.h"
|
||||
#include "parallaction/parallaction.h"
|
||||
|
||||
namespace Parallaction {
|
||||
|
||||
void ProgramExec::runScript(ProgramPtr script, AnimationPtr a) {
|
||||
debugC(9, kDebugExec, "runScript(Animation = %s)", a->_name);
|
||||
|
||||
_ctxt._ip = script->_ip;
|
||||
_ctxt._anim = a;
|
||||
_ctxt._program = script;
|
||||
_ctxt._suspend = false;
|
||||
_ctxt._modCounter = _modCounter;
|
||||
|
||||
InstructionPtr inst;
|
||||
for ( ; (a->_flags & kFlagsActing) ; ) {
|
||||
|
||||
inst = script->_instructions[_ctxt._ip];
|
||||
_ctxt._inst = inst;
|
||||
++_ctxt._ip;
|
||||
|
||||
debugC(9, kDebugExec, "inst [%02i] %s\n", inst->_index, _instructionNames[inst->_index - 1]);
|
||||
|
||||
script->_status = kProgramRunning;
|
||||
|
||||
(*_opcodes[inst->_index])(_ctxt);
|
||||
|
||||
if (_ctxt._suspend)
|
||||
break;
|
||||
|
||||
}
|
||||
script->_ip = _ctxt._ip;
|
||||
|
||||
}
|
||||
|
||||
void ProgramExec::runScripts(ProgramList::iterator first, ProgramList::iterator last) {
|
||||
if (g_engineFlags & kEnginePauseJobs) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (ProgramList::iterator it = first; it != last; ++it) {
|
||||
|
||||
AnimationPtr a = (*it)->_anim;
|
||||
|
||||
if (a->_flags & kFlagsCharacter)
|
||||
a->resetZ();
|
||||
|
||||
if ((a->_flags & kFlagsActing) == 0)
|
||||
continue;
|
||||
|
||||
runScript(*it, a);
|
||||
|
||||
if (a->_flags & kFlagsCharacter)
|
||||
a->resetZ();
|
||||
}
|
||||
|
||||
_modCounter++;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
ProgramExec::ProgramExec() : _modCounter(0), _instructionNames(nullptr) {
|
||||
}
|
||||
|
||||
|
||||
void CommandExec::runList(CommandList::iterator first, CommandList::iterator last) {
|
||||
|
||||
uint32 useFlags = 0;
|
||||
bool useLocalFlags;
|
||||
|
||||
_suspend = false;
|
||||
_running = true;
|
||||
|
||||
for ( ; first != last; ++first) {
|
||||
if (_vm->shouldQuit())
|
||||
break;
|
||||
|
||||
CommandPtr cmd = *first;
|
||||
|
||||
if (cmd->_valid && !cmd->_zone && !cmd->_zoneName.empty()) {
|
||||
// try binding the command to a zone
|
||||
cmd->_zone = _vm->_location.findZone(cmd->_zoneName.c_str());
|
||||
cmd->_valid = cmd->_zone != nullptr;
|
||||
}
|
||||
|
||||
if (!cmd->_valid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cmd->_flagsOn & kFlagsGlobal) {
|
||||
useFlags = g_globalFlags | kFlagsGlobal;
|
||||
useLocalFlags = false;
|
||||
} else {
|
||||
useFlags = _vm->getLocationFlags();
|
||||
useLocalFlags = true;
|
||||
}
|
||||
|
||||
bool onMatch = (cmd->_flagsOn & useFlags) == cmd->_flagsOn;
|
||||
bool offMatch = (cmd->_flagsOff & ~useFlags) == cmd->_flagsOff;
|
||||
|
||||
debugC(3, kDebugExec, "runCommands[%i] (on: %x, off: %x), (%s = %x)", cmd->_id, cmd->_flagsOn, cmd->_flagsOff,
|
||||
useLocalFlags ? "LOCALFLAGS" : "GLOBALFLAGS", useFlags);
|
||||
|
||||
if (!onMatch || !offMatch) continue;
|
||||
|
||||
_ctxt._z = _execZone;
|
||||
_ctxt._cmd = cmd;
|
||||
|
||||
(*_opcodes[cmd->_id])(_ctxt);
|
||||
|
||||
if (_suspend) {
|
||||
createSuspendList(++first, last);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_running = false;
|
||||
|
||||
}
|
||||
|
||||
void CommandExec::run(CommandList& list, ZonePtr z) {
|
||||
if (list.size() == 0) {
|
||||
debugC(3, kDebugExec, "runCommands: nothing to do");
|
||||
return;
|
||||
}
|
||||
|
||||
_execZone = z;
|
||||
|
||||
debugC(3, kDebugExec, "runCommands starting");
|
||||
runList(list.begin(), list.end());
|
||||
debugC(3, kDebugExec, "runCommands completed");
|
||||
}
|
||||
|
||||
void CommandExec::createSuspendList(CommandList::iterator first, CommandList::iterator last) {
|
||||
if (first == last) {
|
||||
return;
|
||||
}
|
||||
|
||||
debugC(3, kDebugExec, "CommandExec::createSuspendList()");
|
||||
|
||||
_suspendedCtxt._valid = true;
|
||||
_suspendedCtxt._first = first;
|
||||
_suspendedCtxt._last = last;
|
||||
_suspendedCtxt._zone = _execZone;
|
||||
}
|
||||
|
||||
void CommandExec::cleanSuspendedList() {
|
||||
debugC(3, kDebugExec, "CommandExec::cleanSuspended()");
|
||||
|
||||
_suspendedCtxt._valid = false;
|
||||
_suspendedCtxt._first = _suspendedCtxt._last;
|
||||
_suspendedCtxt._zone.reset();
|
||||
}
|
||||
|
||||
void CommandExec::suspend() {
|
||||
if (!_running)
|
||||
return;
|
||||
|
||||
_suspend = true;
|
||||
}
|
||||
|
||||
void CommandExec::runSuspended() {
|
||||
if (g_engineFlags & kEngineWalking) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_suspendedCtxt._valid) {
|
||||
debugC(3, kDebugExec, "CommandExec::runSuspended()");
|
||||
|
||||
_execZone = _suspendedCtxt._zone;
|
||||
CommandList::iterator first = _suspendedCtxt._first;
|
||||
CommandList::iterator last = _suspendedCtxt._last;
|
||||
cleanSuspendedList();
|
||||
runList(first, last);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CommandExec::CommandExec(Parallaction *vm) : _vm(vm), _suspend(false), _running(false) {
|
||||
_suspendedCtxt._valid = false;
|
||||
}
|
||||
|
||||
}
|
||||
265
engines/parallaction/exec.h
Normal file
265
engines/parallaction/exec.h
Normal file
@@ -0,0 +1,265 @@
|
||||
/* 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 PARALLACTION_EXEC_H
|
||||
#define PARALLACTION_EXEC_H
|
||||
|
||||
#include "common/util.h"
|
||||
#include "parallaction/objects.h"
|
||||
|
||||
|
||||
namespace Parallaction {
|
||||
|
||||
class Parallaction_ns;
|
||||
class Parallaction_br;
|
||||
|
||||
/* NOTE: CommandExec and ProgramExec perform similar tasks on different data.
|
||||
CommandExec executes commands found in location scripts, while ProgramExec
|
||||
runs animation programs.
|
||||
|
||||
The main difference is how suspension is handled. CommandExec is coded with
|
||||
the assumption that there may be at most one suspended list of commands at any
|
||||
moment, and thus stores the suspended context itself. It also offers a
|
||||
runSuspended() routine that resumes execution on request.
|
||||
ProgramExec instead stores the suspension information in the programs themselves.
|
||||
Programs are in fact meant to be run (almost) regularly on each frame .
|
||||
*/
|
||||
|
||||
struct CommandContext {
|
||||
CommandPtr _cmd;
|
||||
ZonePtr _z;
|
||||
|
||||
// TODO: add a way to invoke CommandExec::suspend() from the context. With that
|
||||
// in place, opcodes dependency on CommandExec would be zero, and they could
|
||||
// be moved into a Game object, together with the non-infrastructural code now
|
||||
// in Parallaction_XX
|
||||
};
|
||||
typedef Common::Functor1<CommandContext&, void> CommandOpcode;
|
||||
typedef Common::Array<const CommandOpcode *> CommandOpcodeSet;
|
||||
#define DECLARE_UNQUALIFIED_COMMAND_OPCODE(op) void cmdOp_##op(CommandContext &)
|
||||
|
||||
struct ProgramContext {
|
||||
AnimationPtr _anim;
|
||||
ProgramPtr _program;
|
||||
InstructionPtr _inst;
|
||||
uint32 _ip;
|
||||
uint16 _modCounter;
|
||||
bool _suspend;
|
||||
};
|
||||
typedef Common::Functor1<ProgramContext&, void> ProgramOpcode;
|
||||
typedef Common::Array<const ProgramOpcode *> ProgramOpcodeSet;
|
||||
#define DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(op) void instOp_##op(ProgramContext &)
|
||||
|
||||
|
||||
template<class OpcodeSet>
|
||||
class Exec {
|
||||
protected:
|
||||
OpcodeSet _opcodes;
|
||||
typedef typename OpcodeSet::iterator OpIt;
|
||||
public:
|
||||
virtual ~Exec() {
|
||||
for (OpIt i = _opcodes.begin(); i != _opcodes.end(); ++i)
|
||||
delete *i;
|
||||
_opcodes.clear();
|
||||
}
|
||||
};
|
||||
|
||||
class CommandExec : public Exec<CommandOpcodeSet> {
|
||||
protected:
|
||||
Parallaction *_vm;
|
||||
|
||||
CommandContext _ctxt;
|
||||
ZonePtr _execZone;
|
||||
bool _running;
|
||||
bool _suspend;
|
||||
|
||||
struct SuspendedContext {
|
||||
bool _valid;
|
||||
CommandList::iterator _first;
|
||||
CommandList::iterator _last;
|
||||
ZonePtr _zone;
|
||||
} _suspendedCtxt;
|
||||
|
||||
void runList(CommandList::iterator first, CommandList::iterator last);
|
||||
void createSuspendList(CommandList::iterator first, CommandList::iterator last);
|
||||
void cleanSuspendedList();
|
||||
public:
|
||||
CommandExec(Parallaction *vm);
|
||||
|
||||
void run(CommandList &list, ZonePtr z = ZonePtr());
|
||||
void runSuspended();
|
||||
void suspend();
|
||||
};
|
||||
|
||||
class CommandExec_ns : public CommandExec {
|
||||
protected:
|
||||
Parallaction_ns *_vm;
|
||||
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(invalid);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(set);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(clear);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(start);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(speak);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(get);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(location);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(open);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(close);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(on);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(off);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(call);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(toggle);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(drop);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(quit);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(move);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(stop);
|
||||
public:
|
||||
CommandExec_ns(Parallaction_ns* vm);
|
||||
};
|
||||
|
||||
class CommandExec_br : public CommandExec {
|
||||
protected:
|
||||
Parallaction_br *_vm;
|
||||
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(invalid);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(set);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(clear);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(speak);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(get);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(toggle);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(quit);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(location);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(open);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(close);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(on);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(off);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(call);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(drop);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(move);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(start);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(stop);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(character);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(followme);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(onmouse);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(offmouse);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(add);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(leave);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(inc);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(dec);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(ifeq);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(iflt);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(ifgt);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(let);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(music);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(fix);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(unfix);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(zeta);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(scroll);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(swap);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(give);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(text);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(part);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(testsfx);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(ret);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(onsave);
|
||||
DECLARE_UNQUALIFIED_COMMAND_OPCODE(offsave);
|
||||
public:
|
||||
CommandExec_br(Parallaction_br* vm);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
class ProgramExec : public Exec<ProgramOpcodeSet> {
|
||||
protected:
|
||||
ProgramContext _ctxt;
|
||||
uint16 _modCounter;
|
||||
const char **_instructionNames;
|
||||
|
||||
void runScript(ProgramPtr script, AnimationPtr a);
|
||||
public:
|
||||
void runScripts(ProgramList::iterator first, ProgramList::iterator last);
|
||||
ProgramExec();
|
||||
};
|
||||
|
||||
class ProgramExec_ns : public ProgramExec {
|
||||
protected:
|
||||
int _currentCredit;
|
||||
|
||||
Parallaction_ns *_vm;
|
||||
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(invalid);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(on);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(off);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(loop);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(endloop);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(show);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(call);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(inc);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(set);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(put);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(wait);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(start);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(sound);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(move);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(endscript);
|
||||
public:
|
||||
ProgramExec_ns(Parallaction_ns *vm);
|
||||
};
|
||||
|
||||
class ProgramExec_br : public ProgramExec {
|
||||
protected:
|
||||
Parallaction_br *_vm;
|
||||
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(invalid);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(loop);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(endloop);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(show);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(call);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(endscript);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(on);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(off);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(inc);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(dec);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(set);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(put);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(wait);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(start);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(process);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(move);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(color);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(mask);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(print);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(text);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(mul);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(div);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(ifeq);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(iflt);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(ifgt);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(endif);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_OPCODE(stop);
|
||||
public:
|
||||
ProgramExec_br(Parallaction_br *vm);
|
||||
};
|
||||
|
||||
} // namespace Parallaction
|
||||
|
||||
#endif
|
||||
694
engines/parallaction/exec_br.cpp
Normal file
694
engines/parallaction/exec_br.cpp
Normal file
@@ -0,0 +1,694 @@
|
||||
/* 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 "parallaction/exec.h"
|
||||
#include "parallaction/input.h"
|
||||
#include "parallaction/parallaction.h"
|
||||
|
||||
#include "common/textconsole.h"
|
||||
|
||||
namespace Parallaction {
|
||||
|
||||
#define INST_ON 1
|
||||
#define INST_OFF 2
|
||||
#define INST_X 3
|
||||
#define INST_Y 4
|
||||
#define INST_Z 5
|
||||
#define INST_F 6
|
||||
#define INST_LOOP 7
|
||||
#define INST_ENDLOOP 8
|
||||
#define INST_SHOW 9
|
||||
#define INST_INC 10
|
||||
#define INST_DEC 11
|
||||
#define INST_SET 12
|
||||
#define INST_PUT 13
|
||||
#define INST_CALL 14
|
||||
#define INST_WAIT 15
|
||||
#define INST_START 16
|
||||
#define INST_PROCESS 17
|
||||
#define INST_MOVE 18
|
||||
#define INST_COLOR 19
|
||||
#define INST_SOUND 20
|
||||
#define INST_MASK 21
|
||||
#define INST_PRINT 22
|
||||
#define INST_TEXT 23
|
||||
#define INST_MUL 24
|
||||
#define INST_DIV 25
|
||||
#define INST_IFEQ 26
|
||||
#define INST_IFLT 27
|
||||
#define INST_IFGT 28
|
||||
#define INST_ENDIF 29
|
||||
#define INST_STOP 30
|
||||
#define INST_ENDSCRIPT 31
|
||||
|
||||
#define SetOpcodeTable(x) table = &x;
|
||||
|
||||
typedef Common::Functor1Mem<CommandContext&, void, CommandExec_br> OpcodeV1;
|
||||
#define COMMAND_OPCODE(op) table->push_back(new OpcodeV1(this, &CommandExec_br::cmdOp_##op))
|
||||
#define DECLARE_COMMAND_OPCODE(op) void CommandExec_br::cmdOp_##op(CommandContext &ctxt)
|
||||
|
||||
typedef Common::Functor1Mem<ProgramContext&, void, ProgramExec_br> OpcodeV2;
|
||||
#define INSTRUCTION_OPCODE(op) table->push_back(new OpcodeV2(this, &ProgramExec_br::instOp_##op))
|
||||
#define DECLARE_INSTRUCTION_OPCODE(op) void ProgramExec_br::instOp_##op(ProgramContext &ctxt)
|
||||
|
||||
extern const char *_instructionNamesRes_br[];
|
||||
|
||||
void Parallaction_br::setupSubtitles(const char *s, const char *s2, int y) {
|
||||
debugC(5, kDebugExec, "setupSubtitles(%s, %s, %i)", s, s2, y);
|
||||
|
||||
clearSubtitles();
|
||||
|
||||
if (!scumm_stricmp("clear", s)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (y != -1) {
|
||||
_subtitleY = y;
|
||||
}
|
||||
|
||||
// FIXME: render subtitles using the right color (10 instead of 0).
|
||||
// The original game features a nasty hack, having the font rendering routine
|
||||
// replacing color 12 of font RUSSIA with 10 when preparing subtitles.
|
||||
uint8 color = (getPlatform() == Common::kPlatformAmiga) ? 11 : 0;
|
||||
_subtitle[0] = _gfx->createLabel(_labelFont, s, color);
|
||||
_gfx->showLabel(_subtitle[0], CENTER_LABEL_HORIZONTAL, _subtitleY);
|
||||
if (s2) {
|
||||
_subtitle[1] = _gfx->createLabel(_labelFont, s2, color);
|
||||
_gfx->showLabel(_subtitle[1], CENTER_LABEL_HORIZONTAL, _subtitleY + 5 + _labelFont->height());
|
||||
} else {
|
||||
_subtitle[1] = nullptr;
|
||||
}
|
||||
#if 0 // disabled because no references to lip sync has been found in the scripts
|
||||
_subtitleLipSync = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void Parallaction_br::clearSubtitles() {
|
||||
if (_subtitle[0]) {
|
||||
_gfx->hideLabel(_subtitle[0]);
|
||||
}
|
||||
delete _subtitle[0];
|
||||
_subtitle[0] = nullptr;
|
||||
|
||||
if (_subtitle[1]) {
|
||||
_gfx->hideLabel(_subtitle[1]);
|
||||
}
|
||||
delete _subtitle[1];
|
||||
_subtitle[1] = nullptr;
|
||||
}
|
||||
|
||||
|
||||
DECLARE_COMMAND_OPCODE(location) {
|
||||
_vm->_location._startPosition = ctxt._cmd->_startPos;
|
||||
_vm->_location._startFrame = 0;
|
||||
_vm->_location._followerStartPosition = ctxt._cmd->_startPos2;
|
||||
_vm->_location._followerStartFrame = 0;
|
||||
|
||||
_vm->scheduleLocationSwitch(ctxt._cmd->_string.c_str());
|
||||
}
|
||||
|
||||
|
||||
DECLARE_COMMAND_OPCODE(open) {
|
||||
_vm->updateDoor(ctxt._cmd->_zone, false);
|
||||
}
|
||||
|
||||
|
||||
DECLARE_COMMAND_OPCODE(close) {
|
||||
_vm->updateDoor(ctxt._cmd->_zone, true);
|
||||
}
|
||||
|
||||
|
||||
DECLARE_COMMAND_OPCODE(on) {
|
||||
_vm->showZone(ctxt._cmd->_zone, true);
|
||||
}
|
||||
|
||||
|
||||
DECLARE_COMMAND_OPCODE(off) {
|
||||
_vm->showZone(ctxt._cmd->_zone, false);
|
||||
}
|
||||
|
||||
|
||||
DECLARE_COMMAND_OPCODE(call) {
|
||||
_vm->callFunction(ctxt._cmd->_callable, &ctxt._z);
|
||||
}
|
||||
|
||||
|
||||
DECLARE_COMMAND_OPCODE(drop) {
|
||||
_vm->dropItem(ctxt._cmd->_object);
|
||||
}
|
||||
|
||||
|
||||
DECLARE_COMMAND_OPCODE(move) {
|
||||
_vm->scheduleWalk(ctxt._cmd->_move.x, ctxt._cmd->_move.y, false);
|
||||
suspend();
|
||||
}
|
||||
|
||||
DECLARE_COMMAND_OPCODE(start) {
|
||||
ctxt._cmd->_zone->_flags |= kFlagsActing;
|
||||
}
|
||||
|
||||
DECLARE_COMMAND_OPCODE(stop) {
|
||||
ctxt._cmd->_zone->_flags &= ~kFlagsActing;
|
||||
}
|
||||
|
||||
|
||||
DECLARE_COMMAND_OPCODE(character) {
|
||||
debugC(9, kDebugExec, "Parallaction_br::cmdOp_character(%s)", ctxt._cmd->_string.c_str());
|
||||
_vm->changeCharacter(ctxt._cmd->_string.c_str());
|
||||
}
|
||||
|
||||
|
||||
DECLARE_COMMAND_OPCODE(followme) {
|
||||
Common::String s(ctxt._cmd->_string);
|
||||
if (!s.compareToIgnoreCase("NULL")) {
|
||||
s.clear();
|
||||
}
|
||||
_vm->setFollower(s);
|
||||
}
|
||||
|
||||
|
||||
DECLARE_COMMAND_OPCODE(onmouse) {
|
||||
_vm->_input->setMouseState(MOUSE_ENABLED_SHOW);
|
||||
}
|
||||
|
||||
|
||||
DECLARE_COMMAND_OPCODE(offmouse) {
|
||||
_vm->_input->setMouseState(MOUSE_DISABLED);
|
||||
}
|
||||
|
||||
|
||||
DECLARE_COMMAND_OPCODE(add) {
|
||||
_vm->addInventoryItem(ctxt._cmd->_object);
|
||||
}
|
||||
|
||||
|
||||
DECLARE_COMMAND_OPCODE(leave) {
|
||||
ZonePtr z = ctxt._cmd->_zone;
|
||||
_vm->dropItem(z->u._getIcon);
|
||||
_vm->showZone(z, true);
|
||||
}
|
||||
|
||||
|
||||
DECLARE_COMMAND_OPCODE(inc) {
|
||||
int v = _vm->getCounterValue(ctxt._cmd->_counterName);
|
||||
_vm->setCounterValue(ctxt._cmd->_counterName, v + ctxt._cmd->_counterValue);
|
||||
}
|
||||
|
||||
|
||||
DECLARE_COMMAND_OPCODE(dec) {
|
||||
int v = _vm->getCounterValue(ctxt._cmd->_counterName);
|
||||
_vm->setCounterValue(ctxt._cmd->_counterName, v - ctxt._cmd->_counterValue);
|
||||
}
|
||||
|
||||
// these definitions must match those in parser_br.cpp
|
||||
#define CMD_TEST 25
|
||||
#define CMD_TEST_GT 26
|
||||
#define CMD_TEST_LT 27
|
||||
|
||||
DECLARE_COMMAND_OPCODE(ifeq) {
|
||||
_vm->testCounterCondition(ctxt._cmd->_counterName, CMD_TEST, ctxt._cmd->_counterValue);
|
||||
}
|
||||
|
||||
DECLARE_COMMAND_OPCODE(iflt) {
|
||||
_vm->testCounterCondition(ctxt._cmd->_counterName, CMD_TEST_LT, ctxt._cmd->_counterValue);
|
||||
}
|
||||
|
||||
DECLARE_COMMAND_OPCODE(ifgt) {
|
||||
_vm->testCounterCondition(ctxt._cmd->_counterName, CMD_TEST_GT, ctxt._cmd->_counterValue);
|
||||
}
|
||||
|
||||
|
||||
DECLARE_COMMAND_OPCODE(let) {
|
||||
_vm->setCounterValue(ctxt._cmd->_counterName, ctxt._cmd->_counterValue);
|
||||
}
|
||||
|
||||
|
||||
DECLARE_COMMAND_OPCODE(music) {
|
||||
warning("Parallaction_br::cmdOp_music not yet implemented");
|
||||
}
|
||||
|
||||
|
||||
DECLARE_COMMAND_OPCODE(fix) {
|
||||
ctxt._cmd->_zone->_flags |= kFlagsFixed;
|
||||
}
|
||||
|
||||
|
||||
DECLARE_COMMAND_OPCODE(unfix) {
|
||||
ctxt._cmd->_zone->_flags &= ~kFlagsFixed;
|
||||
}
|
||||
|
||||
|
||||
DECLARE_COMMAND_OPCODE(zeta) {
|
||||
_vm->_location._zeta0 = ctxt._cmd->_zeta0;
|
||||
_vm->_location._zeta1 = ctxt._cmd->_zeta1;
|
||||
_vm->_location._zeta2 = ctxt._cmd->_zeta2;
|
||||
}
|
||||
|
||||
|
||||
DECLARE_COMMAND_OPCODE(scroll) {
|
||||
Common::Point p;
|
||||
_vm->_gfx->getScrollPos(p);
|
||||
_vm->_gfx->initiateScroll(ctxt._cmd->_counterValue - p.x, 0);
|
||||
}
|
||||
|
||||
|
||||
DECLARE_COMMAND_OPCODE(swap) {
|
||||
warning("Parallaction_br::cmdOp_swap does not handle a follower yet");
|
||||
|
||||
/*
|
||||
TODO:
|
||||
- fixup follower
|
||||
- change mouse pointer
|
||||
*/
|
||||
|
||||
const char *newCharacterName = ctxt._cmd->_string.c_str();
|
||||
AnimationPtr newCharacterAnimation = _vm->_location.findAnimation(newCharacterName);
|
||||
AnimationPtr oldCharaterAnimation = _vm->_char._ani;
|
||||
|
||||
Common::strlcpy(oldCharaterAnimation->_name, _vm->_char.getName(), ZONENAME_LENGTH);
|
||||
_vm->_char.setName(newCharacterName);
|
||||
|
||||
_vm->_char._ani = newCharacterAnimation;
|
||||
_vm->_char._talk = _vm->_disk->loadTalk(newCharacterName);
|
||||
Common::strlcpy(_vm->_char._ani->_name, "yourself", ZONENAME_LENGTH);
|
||||
|
||||
_vm->linkUnlinkedZoneAnimations();
|
||||
|
||||
_vm->_inventory = _vm->findInventory(newCharacterName);
|
||||
_vm->_inventoryRenderer->setInventory(_vm->_inventory);
|
||||
|
||||
_vm->_input->setCharacterPointer(newCharacterName);
|
||||
}
|
||||
|
||||
|
||||
DECLARE_COMMAND_OPCODE(give) {
|
||||
int item = ctxt._cmd->_object;
|
||||
Inventory *targetInventory = _vm->findInventory(ctxt._cmd->_characterName.c_str());
|
||||
|
||||
if (targetInventory) {
|
||||
targetInventory->addItem(item);
|
||||
}
|
||||
|
||||
_vm->_inventory->removeItem(item);
|
||||
}
|
||||
|
||||
|
||||
DECLARE_COMMAND_OPCODE(text) {
|
||||
_vm->setupSubtitles(ctxt._cmd->_string.c_str(), ctxt._cmd->_string2.c_str(), ctxt._cmd->_zeta0);
|
||||
}
|
||||
|
||||
|
||||
DECLARE_COMMAND_OPCODE(part) {
|
||||
_vm->_nextPart = ctxt._cmd->_counterValue;
|
||||
}
|
||||
|
||||
|
||||
DECLARE_COMMAND_OPCODE(testsfx) {
|
||||
warning("Parallaction_br::cmdOp_testsfx not completely implemented");
|
||||
_vm->clearLocationFlags(kFlagsTestTrue); // should test if sfx are enabled
|
||||
}
|
||||
|
||||
|
||||
DECLARE_COMMAND_OPCODE(ret) {
|
||||
g_engineFlags |= kEngineReturn;
|
||||
}
|
||||
|
||||
|
||||
DECLARE_COMMAND_OPCODE(onsave) {
|
||||
warning("Parallaction_br::cmdOp_onsave not yet implemented");
|
||||
}
|
||||
|
||||
|
||||
DECLARE_COMMAND_OPCODE(offsave) {
|
||||
warning("Parallaction_br::cmdOp_offsave not yet implemented");
|
||||
}
|
||||
|
||||
DECLARE_INSTRUCTION_OPCODE(invalid) {
|
||||
error("Can't execute invalid opcode %i", ctxt._inst->_index);
|
||||
}
|
||||
|
||||
DECLARE_COMMAND_OPCODE(clear) {
|
||||
if (ctxt._cmd->_flags & kFlagsGlobal) {
|
||||
ctxt._cmd->_flags &= ~kFlagsGlobal;
|
||||
g_globalFlags &= ~ctxt._cmd->_flags;
|
||||
} else {
|
||||
_vm->clearLocationFlags(ctxt._cmd->_flags);
|
||||
}
|
||||
}
|
||||
|
||||
DECLARE_COMMAND_OPCODE(speak) {
|
||||
// WORKAROUND: this avoids crashing when the zone is not parsed, like in the case
|
||||
// of script bug in ticket #4251.
|
||||
if (!ctxt._cmd->_zone) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ACTIONTYPE(ctxt._cmd->_zone) == kZoneSpeak && ctxt._cmd->_zone->u._speakDialogue) {
|
||||
_vm->enterDialogueMode(ctxt._cmd->_zone);
|
||||
} else {
|
||||
_vm->_activeZone = ctxt._cmd->_zone;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DECLARE_COMMAND_OPCODE(get) {
|
||||
ctxt._cmd->_zone->_flags &= ~kFlagsFixed;
|
||||
_vm->runZone(ctxt._cmd->_zone);
|
||||
}
|
||||
|
||||
DECLARE_COMMAND_OPCODE(toggle) {
|
||||
if (ctxt._cmd->_flags & kFlagsGlobal) {
|
||||
ctxt._cmd->_flags &= ~kFlagsGlobal;
|
||||
g_globalFlags ^= ctxt._cmd->_flags;
|
||||
} else {
|
||||
_vm->toggleLocationFlags(ctxt._cmd->_flags);
|
||||
}
|
||||
}
|
||||
|
||||
DECLARE_COMMAND_OPCODE(quit) {
|
||||
_vm->quitGame();
|
||||
}
|
||||
|
||||
DECLARE_COMMAND_OPCODE(invalid) {
|
||||
error("Can't execute invalid command '%i'", ctxt._cmd->_id);
|
||||
}
|
||||
|
||||
DECLARE_COMMAND_OPCODE(set) {
|
||||
if (ctxt._cmd->_flags & kFlagsGlobal) {
|
||||
ctxt._cmd->_flags &= ~kFlagsGlobal;
|
||||
g_globalFlags |= ctxt._cmd->_flags;
|
||||
} else {
|
||||
_vm->setLocationFlags(ctxt._cmd->_flags);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
DECLARE_INSTRUCTION_OPCODE(on) {
|
||||
_vm->showZone(ctxt._inst->_z, true);
|
||||
}
|
||||
|
||||
|
||||
DECLARE_INSTRUCTION_OPCODE(off) {
|
||||
_vm->showZone(ctxt._inst->_z, false);
|
||||
}
|
||||
|
||||
|
||||
DECLARE_INSTRUCTION_OPCODE(set) {
|
||||
ctxt._inst->_opA.setValue(ctxt._inst->_opB.getValue());
|
||||
}
|
||||
|
||||
|
||||
|
||||
DECLARE_INSTRUCTION_OPCODE(inc) {
|
||||
InstructionPtr inst = ctxt._inst;
|
||||
|
||||
int16 rvalue = inst->_opB.getValue();
|
||||
|
||||
if (inst->_flags & kInstMod) { // mod
|
||||
int16 _bx = (rvalue > 0 ? rvalue : -rvalue);
|
||||
if (ctxt._modCounter % _bx != 0) return;
|
||||
|
||||
rvalue = (rvalue > 0 ? 1 : -1);
|
||||
}
|
||||
|
||||
int16 lvalue = inst->_opA.getValue();
|
||||
|
||||
switch (inst->_index) {
|
||||
case INST_INC:
|
||||
lvalue += rvalue;
|
||||
break;
|
||||
|
||||
case INST_DEC:
|
||||
lvalue -= rvalue;
|
||||
break;
|
||||
|
||||
case INST_MUL:
|
||||
lvalue *= rvalue;
|
||||
break;
|
||||
|
||||
case INST_DIV:
|
||||
lvalue /= rvalue;
|
||||
break;
|
||||
|
||||
default:
|
||||
error("This should never happen. Report immediately");
|
||||
}
|
||||
|
||||
inst->_opA.setValue(lvalue);
|
||||
|
||||
}
|
||||
|
||||
|
||||
DECLARE_INSTRUCTION_OPCODE(put) {
|
||||
// NOTE: there is not a single occurrence of PUT in the scripts
|
||||
warning("PUT instruction is not implemented");
|
||||
}
|
||||
|
||||
|
||||
|
||||
DECLARE_INSTRUCTION_OPCODE(wait) {
|
||||
// NOTE: there is not a single occurrence of WAIT in the scripts
|
||||
warning("WAIT instruction is not implemented");
|
||||
}
|
||||
|
||||
|
||||
DECLARE_INSTRUCTION_OPCODE(start) {
|
||||
ctxt._inst->_z->_flags |= kFlagsActing;
|
||||
}
|
||||
|
||||
|
||||
DECLARE_INSTRUCTION_OPCODE(process) {
|
||||
_vm->_activeZone2 = ctxt._inst->_z;
|
||||
}
|
||||
|
||||
|
||||
DECLARE_INSTRUCTION_OPCODE(move) {
|
||||
// NOTE: I couldn't find evidence of scripts containing this instruction being used
|
||||
InstructionPtr inst = ctxt._inst;
|
||||
_vm->scheduleWalk(inst->_opA.getValue(), inst->_opB.getValue(), false);
|
||||
ctxt._suspend = true;
|
||||
}
|
||||
|
||||
|
||||
DECLARE_INSTRUCTION_OPCODE(color) {
|
||||
InstructionPtr inst = ctxt._inst;
|
||||
_vm->_gfx->_palette.setEntry(inst->_opB.getValue(), inst->_colors[0], inst->_colors[1], inst->_colors[2]);
|
||||
_vm->_gfx->setPalette(_vm->_gfx->_palette);
|
||||
}
|
||||
|
||||
|
||||
DECLARE_INSTRUCTION_OPCODE(mask) {
|
||||
#if 0
|
||||
Instruction *inst = *ctxt._inst;
|
||||
_gfx->_bgLayers[0] = inst->_opA.getRValue();
|
||||
_gfx->_bgLayers[1] = inst->_opB.getRValue();
|
||||
_gfx->_bgLayers[2] = inst->_opC.getRValue();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
DECLARE_INSTRUCTION_OPCODE(print) {
|
||||
// NOTE: there is not a single occurrence of PRINT in the scripts
|
||||
// I suppose it was used for debugging
|
||||
warning("PRINT instruction is not implemented");
|
||||
}
|
||||
|
||||
DECLARE_INSTRUCTION_OPCODE(text) {
|
||||
InstructionPtr inst = ctxt._inst;
|
||||
_vm->setupSubtitles(inst->_text.c_str(), inst->_text2.c_str(), inst->_y);
|
||||
}
|
||||
|
||||
|
||||
DECLARE_INSTRUCTION_OPCODE(ifeq) {
|
||||
InstructionPtr inst = ctxt._inst;
|
||||
bool cond = inst->_opA.getValue() == inst->_opB.getValue();
|
||||
if (!cond) {
|
||||
ctxt._ip = inst->_endif;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DECLARE_INSTRUCTION_OPCODE(iflt) {
|
||||
InstructionPtr inst = ctxt._inst;
|
||||
bool cond = inst->_opA.getValue() < inst->_opB.getValue();
|
||||
if (!cond) {
|
||||
ctxt._ip = inst->_endif;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DECLARE_INSTRUCTION_OPCODE(ifgt) {
|
||||
InstructionPtr inst = ctxt._inst;
|
||||
bool cond = inst->_opA.getValue() > inst->_opB.getValue();
|
||||
if (!cond) {
|
||||
ctxt._ip = inst->_endif;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DECLARE_INSTRUCTION_OPCODE(endif) {
|
||||
// nothing to do here
|
||||
}
|
||||
|
||||
|
||||
DECLARE_INSTRUCTION_OPCODE(stop) {
|
||||
ZonePtr z = ctxt._inst->_z;
|
||||
|
||||
// Prevent execution if zone is missing. The known case is "PART2/insegui.scr", which has
|
||||
// "STOP insegui", which doesn't exist (see ticket #9193 for the gory details)
|
||||
if (!z) return;
|
||||
|
||||
if (ACTIONTYPE(z) == kZoneHear) {
|
||||
warning("Parallaction_br::instOp_stop not yet implemented for HEAR zones");
|
||||
// TODO: stop music or sound effects generated by a zone.
|
||||
} else {
|
||||
z->_flags &= ~kFlagsActing;
|
||||
}
|
||||
}
|
||||
|
||||
DECLARE_INSTRUCTION_OPCODE(loop) {
|
||||
InstructionPtr inst = ctxt._inst;
|
||||
|
||||
ctxt._program->_loopCounter = inst->_opB.getValue();
|
||||
ctxt._program->_loopStart = ctxt._ip;
|
||||
}
|
||||
|
||||
|
||||
DECLARE_INSTRUCTION_OPCODE(endloop) {
|
||||
if (--ctxt._program->_loopCounter > 0) {
|
||||
ctxt._ip = ctxt._program->_loopStart;
|
||||
}
|
||||
}
|
||||
|
||||
DECLARE_INSTRUCTION_OPCODE(show) {
|
||||
ctxt._suspend = true;
|
||||
}
|
||||
|
||||
DECLARE_INSTRUCTION_OPCODE(call) {
|
||||
_vm->callFunction(ctxt._inst->_immediate, nullptr);
|
||||
}
|
||||
|
||||
|
||||
DECLARE_INSTRUCTION_OPCODE(endscript) {
|
||||
if ((ctxt._anim->_flags & kFlagsLooping) == 0) {
|
||||
ctxt._anim->_flags &= ~kFlagsActing;
|
||||
_vm->_cmdExec->run(ctxt._anim->_commands, ctxt._anim);
|
||||
ctxt._program->_status = kProgramDone;
|
||||
}
|
||||
|
||||
ctxt._ip = 0;
|
||||
ctxt._suspend = true;
|
||||
}
|
||||
|
||||
|
||||
CommandExec_br::CommandExec_br(Parallaction_br* vm) : CommandExec(vm), _vm(vm) {
|
||||
CommandOpcodeSet *table = nullptr;
|
||||
|
||||
SetOpcodeTable(_opcodes);
|
||||
COMMAND_OPCODE(invalid);
|
||||
COMMAND_OPCODE(set);
|
||||
COMMAND_OPCODE(clear);
|
||||
COMMAND_OPCODE(start);
|
||||
COMMAND_OPCODE(speak);
|
||||
COMMAND_OPCODE(get);
|
||||
COMMAND_OPCODE(location);
|
||||
COMMAND_OPCODE(open);
|
||||
COMMAND_OPCODE(close);
|
||||
COMMAND_OPCODE(on);
|
||||
COMMAND_OPCODE(off);
|
||||
COMMAND_OPCODE(call);
|
||||
COMMAND_OPCODE(toggle);
|
||||
COMMAND_OPCODE(drop);
|
||||
COMMAND_OPCODE(quit);
|
||||
COMMAND_OPCODE(move);
|
||||
COMMAND_OPCODE(stop);
|
||||
COMMAND_OPCODE(character);
|
||||
COMMAND_OPCODE(followme);
|
||||
COMMAND_OPCODE(onmouse);
|
||||
COMMAND_OPCODE(offmouse);
|
||||
COMMAND_OPCODE(add);
|
||||
COMMAND_OPCODE(leave);
|
||||
COMMAND_OPCODE(inc);
|
||||
COMMAND_OPCODE(dec);
|
||||
COMMAND_OPCODE(ifeq);
|
||||
COMMAND_OPCODE(iflt);
|
||||
COMMAND_OPCODE(ifgt);
|
||||
COMMAND_OPCODE(let);
|
||||
COMMAND_OPCODE(music);
|
||||
COMMAND_OPCODE(fix);
|
||||
COMMAND_OPCODE(unfix);
|
||||
COMMAND_OPCODE(zeta);
|
||||
COMMAND_OPCODE(scroll);
|
||||
COMMAND_OPCODE(swap);
|
||||
COMMAND_OPCODE(give);
|
||||
COMMAND_OPCODE(text);
|
||||
COMMAND_OPCODE(part);
|
||||
COMMAND_OPCODE(testsfx);
|
||||
COMMAND_OPCODE(ret);
|
||||
COMMAND_OPCODE(onsave);
|
||||
COMMAND_OPCODE(offsave);
|
||||
}
|
||||
|
||||
|
||||
ProgramExec_br::ProgramExec_br(Parallaction_br *vm) : _vm(vm) {
|
||||
_instructionNames = _instructionNamesRes_br;
|
||||
|
||||
ProgramOpcodeSet *table = nullptr;
|
||||
|
||||
SetOpcodeTable(_opcodes);
|
||||
INSTRUCTION_OPCODE(invalid);
|
||||
INSTRUCTION_OPCODE(on);
|
||||
INSTRUCTION_OPCODE(off);
|
||||
INSTRUCTION_OPCODE(set); // x
|
||||
INSTRUCTION_OPCODE(set); // y
|
||||
INSTRUCTION_OPCODE(set); // z
|
||||
INSTRUCTION_OPCODE(set); // f
|
||||
INSTRUCTION_OPCODE(loop);
|
||||
INSTRUCTION_OPCODE(endloop);
|
||||
INSTRUCTION_OPCODE(show); // show
|
||||
INSTRUCTION_OPCODE(inc);
|
||||
INSTRUCTION_OPCODE(inc); // dec
|
||||
INSTRUCTION_OPCODE(set);
|
||||
INSTRUCTION_OPCODE(put);
|
||||
INSTRUCTION_OPCODE(call);
|
||||
INSTRUCTION_OPCODE(wait);
|
||||
INSTRUCTION_OPCODE(start);
|
||||
INSTRUCTION_OPCODE(process);
|
||||
INSTRUCTION_OPCODE(move);
|
||||
INSTRUCTION_OPCODE(color);
|
||||
INSTRUCTION_OPCODE(process); // sound
|
||||
INSTRUCTION_OPCODE(mask);
|
||||
INSTRUCTION_OPCODE(print);
|
||||
INSTRUCTION_OPCODE(text);
|
||||
INSTRUCTION_OPCODE(inc); // mul
|
||||
INSTRUCTION_OPCODE(inc); // div
|
||||
INSTRUCTION_OPCODE(ifeq);
|
||||
INSTRUCTION_OPCODE(iflt);
|
||||
INSTRUCTION_OPCODE(ifgt);
|
||||
INSTRUCTION_OPCODE(endif);
|
||||
INSTRUCTION_OPCODE(stop);
|
||||
INSTRUCTION_OPCODE(endscript);
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace Parallaction
|
||||
589
engines/parallaction/exec_ns.cpp
Normal file
589
engines/parallaction/exec_ns.cpp
Normal file
@@ -0,0 +1,589 @@
|
||||
/* 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 "parallaction/exec.h"
|
||||
#include "parallaction/input.h"
|
||||
#include "parallaction/parallaction.h"
|
||||
#include "parallaction/sound.h"
|
||||
|
||||
#include "common/config-manager.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
namespace Parallaction {
|
||||
|
||||
#ifdef USE_TTS
|
||||
|
||||
// Transcribed for TTS; only English is translated in-game
|
||||
static const char *openingCreditsSecondLine[] = {
|
||||
"Grafica Totale: Max M.", // Italian
|
||||
"Graphiques Totaux: Max M.", // French
|
||||
"Total Graphics: Max M.", // English
|
||||
"Gesamtgrafik: Max M." // German
|
||||
};
|
||||
|
||||
static const char *openingCreditsThirdLine[] = {
|
||||
"Design del Gioco: Mr. Tzutzumi", // Italian
|
||||
"Conception de Jeux: Mr. Tzutzumi", // French
|
||||
"Game Design: Mr. Tzutzumi", // English
|
||||
"Spieldesign: Mr. Tzutzumi" // German
|
||||
};
|
||||
|
||||
// Transcribed for TTS; only Italian is translated in-game
|
||||
static const char *endCreditsItalian[] = {
|
||||
"La Cassiera",
|
||||
"La Segretaria",
|
||||
"Il Taxista",
|
||||
"Il Giornalaio",
|
||||
"Passante",
|
||||
"Il Segretario",
|
||||
"L'Uccello",
|
||||
"L'Inserviente",
|
||||
"Il Priore",
|
||||
"Il Suicida",
|
||||
"Il Direttore",
|
||||
"Passante",
|
||||
"Fratello Shinpui",
|
||||
"Apollo il Gorilla",
|
||||
"Il Portiere",
|
||||
"Il Guru",
|
||||
"Fratello Baka",
|
||||
"Josko il Barman",
|
||||
"Figaro L' Estetista",
|
||||
"Il Guardiano del Museo",
|
||||
"Passante",
|
||||
"Il Losco Max",
|
||||
"Mister Y",
|
||||
"Il Punk",
|
||||
"Passante",
|
||||
"Il Signor Bemutsu",
|
||||
"L' Annunciatore",
|
||||
"La Punkina",
|
||||
"L' Imperatore",
|
||||
"Il Dottor Ki",
|
||||
"Il Cuoco",
|
||||
"Il Punk",
|
||||
"Passante",
|
||||
"Il Losco Figuro",
|
||||
"Chan L'Onesto",
|
||||
"",
|
||||
"La Geisha",
|
||||
"Kos 'O Professore",
|
||||
"Il Giocatore di Pacinko"
|
||||
};
|
||||
|
||||
static const char *endCreditsFrench[] = {
|
||||
"La Cassi\350re",
|
||||
"La Secr\351taire",
|
||||
"Le Chaffeur de Taxi",
|
||||
"Le Marchand de Journaux",
|
||||
"La Pi\351tonne",
|
||||
"Le Secr\351taire",
|
||||
"La Corneille",
|
||||
"Le Oshiya",
|
||||
"Le Prieur",
|
||||
"Le Candidat au Suicide",
|
||||
"Le Directeur",
|
||||
"La Pi\351tonne",
|
||||
"Fr\350re Shinpui",
|
||||
"Apollo le Garde du Corps",
|
||||
"Le Concierge",
|
||||
"Le Guru",
|
||||
"Fr\350re Baka",
|
||||
"Josko le Barman",
|
||||
"Figaro L'esth\351ticien",
|
||||
"Le Gardien du Mus\351e",
|
||||
"La Pi\351tonne",
|
||||
"Le Ombrag\351e Max",
|
||||
"Monsieur Y",
|
||||
"Le Punk",
|
||||
"La Pi\351tonne",
|
||||
"Monsieur Bemutsu",
|
||||
"Le Annonceur",
|
||||
"Le Punk",
|
||||
"L'Empereur",
|
||||
"Le Docteur Ki",
|
||||
"Le Chef",
|
||||
"Le Punk",
|
||||
"Le Pi\351ton",
|
||||
"Le Louche Personnage",
|
||||
"Honest Chan",
|
||||
"",
|
||||
"La Geisha",
|
||||
"Professeur Kos",
|
||||
"Le Joueur de Pachinko"
|
||||
};
|
||||
|
||||
static const char *endCreditsEnglish[] = {
|
||||
"Cashier",
|
||||
"Secretary",
|
||||
"Taxi-driver",
|
||||
"Newspaper Seller",
|
||||
"Pedestrian",
|
||||
"Secretary",
|
||||
"Grackle",
|
||||
"Oshiya",
|
||||
"Prior",
|
||||
"Suicidal Man",
|
||||
"Governor",
|
||||
"Pedestrian",
|
||||
"Brother Shinpui",
|
||||
"Apollo the Bodyguard",
|
||||
"Door-keeper",
|
||||
"Guru",
|
||||
"Brother Baka",
|
||||
"Josko the Barman",
|
||||
"Figaro the Beautician",
|
||||
"Museum Custodian",
|
||||
"Pedestrian",
|
||||
"Sullen Max",
|
||||
"Mister Y",
|
||||
"Punk",
|
||||
"Pedestrian",
|
||||
"Mister Bemutsu",
|
||||
"Announcer",
|
||||
"Punk",
|
||||
"Emperor",
|
||||
"Doctor Ki",
|
||||
"Cook",
|
||||
"Punk",
|
||||
"Pedestrian",
|
||||
"Shady Type",
|
||||
"Honest Chan",
|
||||
"",
|
||||
"Geisha",
|
||||
"Professor Kos",
|
||||
"Pachinko Player"
|
||||
};
|
||||
|
||||
static const char *endCreditsGerman[] = {
|
||||
"Die Kassiererin",
|
||||
"Die Sekret\344rin",
|
||||
"Der Taxifahrer",
|
||||
"Der Zeitungsverk\344ufer",
|
||||
"Die Passantin",
|
||||
"Der Sekret\344r",
|
||||
"Des Grakula",
|
||||
"Der Oshiya",
|
||||
"Der Prior",
|
||||
"Der Selbstm\366rder",
|
||||
"Der Direktor",
|
||||
"Die Passantin",
|
||||
"Bruder Shinpui",
|
||||
"Apollo der Bodyguard",
|
||||
"Der Portier",
|
||||
"Der Guru",
|
||||
"Bruder Baka",
|
||||
"Josko der Barman",
|
||||
"Figaro, der Kosmetiker",
|
||||
"Der Museumsw\344rter",
|
||||
"Die Passantin",
|
||||
"Schattig Max",
|
||||
"Herr Y",
|
||||
"Der Punker",
|
||||
"Die Passantin",
|
||||
"Herr Bemutsu",
|
||||
"Der Ansager",
|
||||
"Der Punker",
|
||||
"Der Kaiser",
|
||||
"Doktor Ki",
|
||||
"Der Koch",
|
||||
"Der Punker",
|
||||
"Der Passant",
|
||||
"Der Dunkler Typ",
|
||||
"Honest Chan",
|
||||
"",
|
||||
"Die Geisha",
|
||||
"Professor Kos",
|
||||
"Der Pachinko-Spieler"
|
||||
};
|
||||
|
||||
static const int kNumberOfCredits = ARRAYSIZE(endCreditsItalian);
|
||||
|
||||
#endif
|
||||
|
||||
#define INST_ON 1
|
||||
#define INST_OFF 2
|
||||
#define INST_X 3
|
||||
#define INST_Y 4
|
||||
#define INST_Z 5
|
||||
#define INST_F 6
|
||||
#define INST_LOOP 7
|
||||
#define INST_ENDLOOP 8
|
||||
#define INST_SHOW 9
|
||||
#define INST_INC 10
|
||||
#define INST_DEC 11
|
||||
#define INST_SET 12
|
||||
#define INST_PUT 13
|
||||
#define INST_CALL 14
|
||||
#define INST_WAIT 15
|
||||
#define INST_START 16
|
||||
#define INST_SOUND 17
|
||||
#define INST_MOVE 18
|
||||
#define INST_ENDSCRIPT 19
|
||||
|
||||
#define SetOpcodeTable(x) table = &x;
|
||||
|
||||
|
||||
|
||||
typedef Common::Functor1Mem<CommandContext&, void, CommandExec_ns> OpcodeV1;
|
||||
#define COMMAND_OPCODE(op) table->push_back(new OpcodeV1(this, &CommandExec_ns::cmdOp_##op))
|
||||
#define DECLARE_COMMAND_OPCODE(op) void CommandExec_ns::cmdOp_##op(CommandContext& ctxt)
|
||||
|
||||
typedef Common::Functor1Mem<ProgramContext&, void, ProgramExec_ns> OpcodeV2;
|
||||
#define INSTRUCTION_OPCODE(op) table->push_back(new OpcodeV2(this, &ProgramExec_ns::instOp_##op))
|
||||
#define DECLARE_INSTRUCTION_OPCODE(op) void ProgramExec_ns::instOp_##op(ProgramContext& ctxt)
|
||||
|
||||
extern const char *_instructionNamesRes_ns[];
|
||||
|
||||
|
||||
DECLARE_INSTRUCTION_OPCODE(on) {
|
||||
InstructionPtr inst = ctxt._inst;
|
||||
|
||||
inst->_a->_flags |= kFlagsActive;
|
||||
inst->_a->_flags &= ~kFlagsRemove;
|
||||
|
||||
#ifdef USE_TTS
|
||||
if (scumm_stricmp(inst->_a->_name, "telo3") == 0) {
|
||||
_vm->setTTSVoice(kNarratorVoiceID);
|
||||
_vm->sayText(openingCreditsSecondLine[_vm->getInternLanguage()], Common::TextToSpeechManager::INTERRUPT);
|
||||
} else if (scumm_stricmp(inst->_a->_name, "game") == 0) {
|
||||
_vm->setTTSVoice(kNarratorVoiceID);
|
||||
_vm->sayText(openingCreditsThirdLine[_vm->getInternLanguage()], Common::TextToSpeechManager::INTERRUPT);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
DECLARE_INSTRUCTION_OPCODE(off) {
|
||||
ctxt._inst->_a->_flags |= kFlagsRemove;
|
||||
}
|
||||
|
||||
|
||||
DECLARE_INSTRUCTION_OPCODE(loop) {
|
||||
InstructionPtr inst = ctxt._inst;
|
||||
|
||||
ctxt._program->_loopCounter = inst->_opB.getValue();
|
||||
ctxt._program->_loopStart = ctxt._ip;
|
||||
}
|
||||
|
||||
|
||||
DECLARE_INSTRUCTION_OPCODE(endloop) {
|
||||
if (ctxt._program->_loopStart == 11 && scumm_stricmp(_vm->_location._name, "test") == 0) {
|
||||
// Delay moving on from test results until TTS is done or the player clicks,
|
||||
// so the TTS system can speak them fully
|
||||
Common::TextToSpeechManager *ttsMan = g_system->getTextToSpeechManager();
|
||||
if (ttsMan != nullptr && ConfMan.getBool("tts_enabled") && ttsMan->isSpeaking()) {
|
||||
ctxt._program->_loopCounter = 2;
|
||||
int event = _vm->_input->getLastButtonEvent();
|
||||
|
||||
if (event == kMouseLeftUp) {
|
||||
ttsMan->stop();
|
||||
ctxt._program->_loopCounter = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (--ctxt._program->_loopCounter > 0) {
|
||||
ctxt._ip = ctxt._program->_loopStart;
|
||||
}
|
||||
}
|
||||
|
||||
DECLARE_INSTRUCTION_OPCODE(inc) {
|
||||
InstructionPtr inst = ctxt._inst;
|
||||
int16 _si = inst->_opB.getValue();
|
||||
|
||||
if (inst->_flags & kInstMod) { // mod
|
||||
int16 _bx = (_si > 0 ? _si : -_si);
|
||||
if (ctxt._modCounter % _bx != 0) return;
|
||||
|
||||
_si = (_si > 0 ? 1 : -1);
|
||||
}
|
||||
|
||||
int16 lvalue = inst->_opA.getValue();
|
||||
|
||||
if (inst->_index == INST_INC) {
|
||||
#ifdef USE_TTS
|
||||
if (_vm->_endCredits && ctxt._anim->_name[0] == 's' && _currentCredit < kNumberOfCredits) {
|
||||
const char **credits;
|
||||
|
||||
switch (_vm->getInternLanguage()) {
|
||||
case kItalian:
|
||||
credits = endCreditsItalian;
|
||||
break;
|
||||
case kFrench:
|
||||
credits = endCreditsFrench;
|
||||
break;
|
||||
case kEnglish:
|
||||
credits = endCreditsEnglish;
|
||||
break;
|
||||
case kGerman:
|
||||
credits = endCreditsGerman;
|
||||
break;
|
||||
default:
|
||||
credits = endCreditsItalian;
|
||||
break;
|
||||
}
|
||||
|
||||
_vm->sayText(credits[_currentCredit], Common::TextToSpeechManager::QUEUE);
|
||||
_currentCredit++;
|
||||
}
|
||||
#endif
|
||||
|
||||
lvalue += _si;
|
||||
} else {
|
||||
lvalue -= _si;
|
||||
}
|
||||
|
||||
inst->_opA.setValue(lvalue);
|
||||
|
||||
}
|
||||
|
||||
|
||||
DECLARE_INSTRUCTION_OPCODE(set) {
|
||||
ctxt._inst->_opA.setValue(ctxt._inst->_opB.getValue());
|
||||
}
|
||||
|
||||
|
||||
DECLARE_INSTRUCTION_OPCODE(put) {
|
||||
InstructionPtr inst = ctxt._inst;
|
||||
Common::Rect r;
|
||||
inst->_a->getFrameRect(r);
|
||||
|
||||
Graphics::Surface v18;
|
||||
v18.init(r.width(), r.height(), r.width(), inst->_a->getFrameData(), Graphics::PixelFormat::createFormatCLUT8());
|
||||
|
||||
int16 x = inst->_opA.getValue();
|
||||
int16 y = inst->_opB.getValue();
|
||||
bool mask = (inst->_flags & kInstMaskedPut) == kInstMaskedPut;
|
||||
|
||||
_vm->_gfx->patchBackground(v18, x, y, mask);
|
||||
}
|
||||
|
||||
DECLARE_INSTRUCTION_OPCODE(show) {
|
||||
ctxt._suspend = true;
|
||||
}
|
||||
|
||||
DECLARE_INSTRUCTION_OPCODE(invalid) {
|
||||
error("Can't execute invalid opcode %i", ctxt._inst->_index);
|
||||
}
|
||||
|
||||
DECLARE_INSTRUCTION_OPCODE(call) {
|
||||
_vm->callFunction(ctxt._inst->_immediate, nullptr);
|
||||
}
|
||||
|
||||
|
||||
DECLARE_INSTRUCTION_OPCODE(wait) {
|
||||
if (g_engineFlags & kEngineWalking) {
|
||||
ctxt._ip--;
|
||||
ctxt._suspend = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DECLARE_INSTRUCTION_OPCODE(start) {
|
||||
ctxt._inst->_a->_flags |= (kFlagsActing | kFlagsActive);
|
||||
}
|
||||
|
||||
|
||||
DECLARE_INSTRUCTION_OPCODE(sound) {
|
||||
_vm->_activeZone = ctxt._inst->_z;
|
||||
}
|
||||
|
||||
|
||||
DECLARE_INSTRUCTION_OPCODE(move) {
|
||||
InstructionPtr inst = ctxt._inst;
|
||||
|
||||
int16 x = inst->_opA.getValue();
|
||||
int16 y = inst->_opB.getValue();
|
||||
|
||||
_vm->scheduleWalk(x, y, false);
|
||||
}
|
||||
|
||||
DECLARE_INSTRUCTION_OPCODE(endscript) {
|
||||
if ((ctxt._anim->_flags & kFlagsLooping) == 0) {
|
||||
ctxt._anim->_flags &= ~kFlagsActing;
|
||||
_vm->_cmdExec->run(ctxt._anim->_commands, ctxt._anim);
|
||||
ctxt._program->_status = kProgramDone;
|
||||
}
|
||||
|
||||
ctxt._ip = 0;
|
||||
ctxt._suspend = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
DECLARE_COMMAND_OPCODE(invalid) {
|
||||
error("Can't execute invalid command '%i'", ctxt._cmd->_id);
|
||||
}
|
||||
|
||||
DECLARE_COMMAND_OPCODE(set) {
|
||||
if (ctxt._cmd->_flags & kFlagsGlobal) {
|
||||
ctxt._cmd->_flags &= ~kFlagsGlobal;
|
||||
g_globalFlags |= ctxt._cmd->_flags;
|
||||
} else {
|
||||
_vm->setLocationFlags(ctxt._cmd->_flags);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DECLARE_COMMAND_OPCODE(clear) {
|
||||
if (ctxt._cmd->_flags & kFlagsGlobal) {
|
||||
ctxt._cmd->_flags &= ~kFlagsGlobal;
|
||||
g_globalFlags &= ~ctxt._cmd->_flags;
|
||||
} else {
|
||||
_vm->clearLocationFlags(ctxt._cmd->_flags);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DECLARE_COMMAND_OPCODE(start) {
|
||||
ctxt._cmd->_zone->_flags |= kFlagsActing;
|
||||
}
|
||||
|
||||
|
||||
DECLARE_COMMAND_OPCODE(speak) {
|
||||
if (ACTIONTYPE(ctxt._cmd->_zone) == kZoneSpeak) {
|
||||
_vm->enterDialogueMode(ctxt._cmd->_zone);
|
||||
} else {
|
||||
_vm->_activeZone = ctxt._cmd->_zone;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DECLARE_COMMAND_OPCODE(get) {
|
||||
ctxt._cmd->_zone->_flags &= ~kFlagsFixed;
|
||||
_vm->runZone(ctxt._cmd->_zone);
|
||||
}
|
||||
|
||||
|
||||
DECLARE_COMMAND_OPCODE(location) {
|
||||
_vm->scheduleLocationSwitch(ctxt._cmd->_string.c_str());
|
||||
}
|
||||
|
||||
|
||||
DECLARE_COMMAND_OPCODE(open) {
|
||||
_vm->updateDoor(ctxt._cmd->_zone, false);
|
||||
}
|
||||
|
||||
|
||||
DECLARE_COMMAND_OPCODE(close) {
|
||||
_vm->updateDoor(ctxt._cmd->_zone, true);
|
||||
}
|
||||
|
||||
DECLARE_COMMAND_OPCODE(on) {
|
||||
_vm->showZone(ctxt._cmd->_zone, true);
|
||||
}
|
||||
|
||||
|
||||
DECLARE_COMMAND_OPCODE(off) {
|
||||
_vm->showZone(ctxt._cmd->_zone, false);
|
||||
}
|
||||
|
||||
|
||||
DECLARE_COMMAND_OPCODE(call) {
|
||||
_vm->callFunction(ctxt._cmd->_callable, &ctxt._z);
|
||||
}
|
||||
|
||||
|
||||
DECLARE_COMMAND_OPCODE(toggle) {
|
||||
if (ctxt._cmd->_flags & kFlagsGlobal) {
|
||||
ctxt._cmd->_flags &= ~kFlagsGlobal;
|
||||
g_globalFlags ^= ctxt._cmd->_flags;
|
||||
} else {
|
||||
_vm->toggleLocationFlags(ctxt._cmd->_flags);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DECLARE_COMMAND_OPCODE(drop){
|
||||
_vm->dropItem( ctxt._cmd->_object );
|
||||
}
|
||||
|
||||
|
||||
DECLARE_COMMAND_OPCODE(quit) {
|
||||
_vm->quitGame();
|
||||
}
|
||||
|
||||
|
||||
DECLARE_COMMAND_OPCODE(move) {
|
||||
_vm->scheduleWalk(ctxt._cmd->_move.x, ctxt._cmd->_move.y, false);
|
||||
}
|
||||
|
||||
|
||||
DECLARE_COMMAND_OPCODE(stop) {
|
||||
ctxt._cmd->_zone->_flags &= ~kFlagsActing;
|
||||
}
|
||||
|
||||
CommandExec_ns::CommandExec_ns(Parallaction_ns* vm) : CommandExec(vm), _vm(vm) {
|
||||
CommandOpcodeSet *table = nullptr;
|
||||
|
||||
SetOpcodeTable(_opcodes);
|
||||
COMMAND_OPCODE(invalid);
|
||||
COMMAND_OPCODE(set);
|
||||
COMMAND_OPCODE(clear);
|
||||
COMMAND_OPCODE(start);
|
||||
COMMAND_OPCODE(speak);
|
||||
COMMAND_OPCODE(get);
|
||||
COMMAND_OPCODE(location);
|
||||
COMMAND_OPCODE(open);
|
||||
COMMAND_OPCODE(close);
|
||||
COMMAND_OPCODE(on);
|
||||
COMMAND_OPCODE(off);
|
||||
COMMAND_OPCODE(call);
|
||||
COMMAND_OPCODE(toggle);
|
||||
COMMAND_OPCODE(drop);
|
||||
COMMAND_OPCODE(quit);
|
||||
COMMAND_OPCODE(move);
|
||||
COMMAND_OPCODE(stop);
|
||||
}
|
||||
|
||||
ProgramExec_ns::ProgramExec_ns(Parallaction_ns *vm) : _vm(vm) {
|
||||
_currentCredit = 0;
|
||||
|
||||
_instructionNames = _instructionNamesRes_ns;
|
||||
|
||||
ProgramOpcodeSet *table = nullptr;
|
||||
|
||||
SetOpcodeTable(_opcodes);
|
||||
INSTRUCTION_OPCODE(invalid);
|
||||
INSTRUCTION_OPCODE(on);
|
||||
INSTRUCTION_OPCODE(off);
|
||||
INSTRUCTION_OPCODE(set); // x
|
||||
INSTRUCTION_OPCODE(set); // y
|
||||
INSTRUCTION_OPCODE(set); // z
|
||||
INSTRUCTION_OPCODE(set); // f
|
||||
INSTRUCTION_OPCODE(loop);
|
||||
INSTRUCTION_OPCODE(endloop);
|
||||
INSTRUCTION_OPCODE(show);
|
||||
INSTRUCTION_OPCODE(inc);
|
||||
INSTRUCTION_OPCODE(inc); // dec
|
||||
INSTRUCTION_OPCODE(set);
|
||||
INSTRUCTION_OPCODE(put);
|
||||
INSTRUCTION_OPCODE(call);
|
||||
INSTRUCTION_OPCODE(wait);
|
||||
INSTRUCTION_OPCODE(start);
|
||||
INSTRUCTION_OPCODE(sound);
|
||||
INSTRUCTION_OPCODE(move);
|
||||
INSTRUCTION_OPCODE(endscript);
|
||||
}
|
||||
|
||||
} // namespace Parallaction
|
||||
544
engines/parallaction/font.cpp
Normal file
544
engines/parallaction/font.cpp
Normal file
@@ -0,0 +1,544 @@
|
||||
/* 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/endian.h"
|
||||
#include "common/memstream.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "graphics/fonts/amigafont.h"
|
||||
|
||||
#include "parallaction/parallaction.h"
|
||||
|
||||
namespace Parallaction {
|
||||
|
||||
|
||||
class BraFont : public Font {
|
||||
|
||||
protected:
|
||||
byte *_cp;
|
||||
uint _bufPitch;
|
||||
|
||||
uint32 _height;
|
||||
byte _numGlyphs;
|
||||
|
||||
byte *_widths;
|
||||
uint *_offsets;
|
||||
|
||||
byte *_data;
|
||||
const byte *_charMap;
|
||||
|
||||
byte mapChar(byte c) {
|
||||
return (_charMap == nullptr) ? c : _charMap[c];
|
||||
}
|
||||
|
||||
public:
|
||||
BraFont(Common::ReadStream &stream, const byte *charMap = nullptr) {
|
||||
_charMap = charMap;
|
||||
|
||||
_numGlyphs = stream.readByte();
|
||||
_height = stream.readUint32BE();
|
||||
|
||||
_widths = (byte *)malloc(_numGlyphs);
|
||||
stream.read(_widths, _numGlyphs);
|
||||
|
||||
_offsets = (uint *)malloc(_numGlyphs * sizeof(uint));
|
||||
_offsets[0] = 0;
|
||||
for (uint i = 1; i < _numGlyphs; i++)
|
||||
_offsets[i] = _offsets[i-1] + _widths[i-1] * _height;
|
||||
|
||||
uint size = _offsets[_numGlyphs-1] + _widths[_numGlyphs-1] * _height;
|
||||
|
||||
_data = (byte *)malloc(size);
|
||||
stream.read(_data, size);
|
||||
|
||||
_cp = nullptr;
|
||||
_bufPitch = 0;
|
||||
}
|
||||
|
||||
~BraFont() override {
|
||||
free(_widths);
|
||||
free(_offsets);
|
||||
free(_data);
|
||||
}
|
||||
|
||||
|
||||
uint32 getStringWidth(const char *s) override {
|
||||
uint32 len = 0;
|
||||
|
||||
while (*s) {
|
||||
byte c = mapChar(*s);
|
||||
len += (_widths[c] + 2);
|
||||
s++;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
uint16 height() override {
|
||||
return (uint16)_height;
|
||||
}
|
||||
|
||||
uint16 drawChar(unsigned char c) {
|
||||
assert(c < _numGlyphs);
|
||||
|
||||
byte *src = _data + _offsets[c];
|
||||
byte *dst = _cp;
|
||||
uint16 w = _widths[c];
|
||||
|
||||
for (uint16 j = 0; j < height(); j++) {
|
||||
for (uint16 k = 0; k < w; k++) {
|
||||
|
||||
if (*src) {
|
||||
*dst = (_color) ? _color : *src;
|
||||
}
|
||||
|
||||
dst++;
|
||||
src++;
|
||||
}
|
||||
|
||||
dst += (_bufPitch - w);
|
||||
}
|
||||
|
||||
return w + 2;
|
||||
|
||||
}
|
||||
|
||||
void drawString(Graphics::Surface *src, int x, int y, const char *s) override {
|
||||
if (src == nullptr)
|
||||
return;
|
||||
|
||||
_bufPitch = src->pitch;
|
||||
|
||||
_cp = (byte *)src->getBasePtr(x, y);
|
||||
while (*s) {
|
||||
byte c = mapChar(*s);
|
||||
_cp += drawChar(c);
|
||||
s++;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const byte _braDosFullCharMap[256] = {
|
||||
// 0
|
||||
0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
|
||||
// 1
|
||||
0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
|
||||
// 2
|
||||
0x34, 0x49, 0x48, 0x34, 0x34, 0x34, 0x34, 0x47, 0x34, 0x34, 0x34, 0x34, 0x40, 0x34, 0x3F, 0x34,
|
||||
// 3
|
||||
0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x46, 0x45, 0x34, 0x34, 0x34, 0x42,
|
||||
// 4
|
||||
0x34, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
|
||||
// 5
|
||||
0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x34, 0x34, 0x34, 0x34, 0x34,
|
||||
// 6
|
||||
0x34, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
|
||||
// 7
|
||||
0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x34, 0x34, 0x34, 0x34,
|
||||
// 8
|
||||
0x5E, 0x5D, 0x4E, 0x4B, 0x4D, 0x4C, 0x34, 0x5E, 0x4F, 0x51, 0x50, 0x34, 0x34, 0x34, 0x34, 0x34,
|
||||
// 9
|
||||
0x34, 0x34, 0x34, 0x57, 0x59, 0x58, 0x5B, 0x5C, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
|
||||
// A
|
||||
0x4A, 0x52, 0x34, 0x5A, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
|
||||
// B
|
||||
0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
|
||||
// C
|
||||
0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
|
||||
// D
|
||||
0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
|
||||
// E
|
||||
0x34, 0x5F, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
|
||||
// F
|
||||
0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34
|
||||
};
|
||||
|
||||
const byte _braDosDemoComicCharMap[] = {
|
||||
// 0
|
||||
0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
|
||||
// 1
|
||||
0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
|
||||
// 2
|
||||
0x34, 0x49, 0x48, 0x34, 0x34, 0x34, 0x34, 0x47, 0x34, 0x34, 0x34, 0x34, 0x40, 0x34, 0x3F, 0x34,
|
||||
// 3
|
||||
0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x46, 0x45, 0x34, 0x34, 0x34, 0x42,
|
||||
// 4
|
||||
0x34, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
|
||||
// 5
|
||||
0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x34, 0x34, 0x34, 0x34, 0x34,
|
||||
// 6
|
||||
0x34, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
|
||||
// 7
|
||||
0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x34, 0x34, 0x34, 0x34,
|
||||
// 8
|
||||
0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
|
||||
// 9
|
||||
0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
|
||||
// A
|
||||
0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
|
||||
// B
|
||||
0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
|
||||
// C
|
||||
0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
|
||||
// D
|
||||
0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
|
||||
// E
|
||||
0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
|
||||
// F
|
||||
0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34
|
||||
};
|
||||
|
||||
const byte _braDosDemoRussiaCharMap[] = {
|
||||
// 0
|
||||
0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
|
||||
// 1
|
||||
0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
|
||||
// 2
|
||||
0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
|
||||
// 3
|
||||
0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
|
||||
// 4
|
||||
0x34, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
|
||||
// 5
|
||||
0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x34, 0x34, 0x34, 0x34, 0x34,
|
||||
// 6
|
||||
0x34, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
|
||||
// 7
|
||||
0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x34, 0x34, 0x34, 0x34,
|
||||
// 8
|
||||
0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
|
||||
// 9
|
||||
0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
|
||||
// A
|
||||
0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
|
||||
// B
|
||||
0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
|
||||
// C
|
||||
0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
|
||||
// D
|
||||
0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
|
||||
// E
|
||||
0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
|
||||
// F
|
||||
0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34
|
||||
};
|
||||
|
||||
class BraInventoryObjects : public BraFont, public Frames {
|
||||
|
||||
public:
|
||||
BraInventoryObjects(Common::ReadStream &stream) : BraFont(stream) {
|
||||
}
|
||||
|
||||
// Frames implementation
|
||||
uint16 getNum() override {
|
||||
return _numGlyphs;
|
||||
}
|
||||
|
||||
byte* getData(uint16 index) override {
|
||||
assert(index < _numGlyphs);
|
||||
return _data + (_height * _widths[index]) * index;
|
||||
}
|
||||
|
||||
void getRect(uint16 index, Common::Rect &r) override {
|
||||
assert(index < _numGlyphs);
|
||||
r.left = 0;
|
||||
r.top = 0;
|
||||
r.setWidth(_widths[index]);
|
||||
r.setHeight(_height);
|
||||
}
|
||||
|
||||
uint getRawSize(uint16 index) override {
|
||||
assert(index < _numGlyphs);
|
||||
return _widths[index] * _height;
|
||||
}
|
||||
|
||||
uint getSize(uint16 index) override {
|
||||
assert(index < _numGlyphs);
|
||||
return _widths[index] * _height;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class DosFont : public Font {
|
||||
|
||||
protected:
|
||||
// drawing properties
|
||||
byte *_cp;
|
||||
|
||||
Cnv *_data;
|
||||
byte _pitch;
|
||||
uint32 _bufPitch;
|
||||
|
||||
protected:
|
||||
virtual uint16 drawChar(char c) = 0;
|
||||
virtual uint16 width(byte c) = 0;
|
||||
uint16 height() override = 0;
|
||||
|
||||
byte mapChar(byte c) {
|
||||
if (c == 0xA5) return 0x5F;
|
||||
if (c == 0xDF) return 0x60;
|
||||
|
||||
if (c > 0x7F) return c - 0x7F;
|
||||
|
||||
return c - 0x20;
|
||||
}
|
||||
|
||||
public:
|
||||
DosFont(Cnv *cnv) : _data(cnv), _pitch(cnv->_width), _cp(nullptr), _bufPitch(0) {
|
||||
}
|
||||
|
||||
~DosFont() override {
|
||||
delete _data;
|
||||
}
|
||||
|
||||
void setData() {
|
||||
|
||||
}
|
||||
|
||||
uint32 getStringWidth(const char *s) override {
|
||||
uint32 len = 0;
|
||||
|
||||
while (*s) {
|
||||
byte c = mapChar(*s);
|
||||
len += width(c);
|
||||
s++;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
void drawString(Graphics::Surface *src, int x, int y, const char *s) override {
|
||||
if (src == nullptr)
|
||||
return;
|
||||
|
||||
_bufPitch = src->pitch;
|
||||
|
||||
_cp = (byte *)src->getBasePtr(x, y);
|
||||
while (*s) {
|
||||
byte c = mapChar(*s);
|
||||
_cp += drawChar(c);
|
||||
s++;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class DosDialogueFont : public DosFont {
|
||||
|
||||
private:
|
||||
static const byte _glyphWidths[126];
|
||||
|
||||
protected:
|
||||
uint16 width(byte c) override {
|
||||
return _glyphWidths[c];
|
||||
}
|
||||
|
||||
uint16 height() override {
|
||||
return _data->_height;
|
||||
}
|
||||
|
||||
public:
|
||||
DosDialogueFont(Cnv *cnv) : DosFont(cnv) {
|
||||
}
|
||||
|
||||
protected:
|
||||
uint16 drawChar(char c) override {
|
||||
|
||||
byte *src = _data->getFramePtr(c);
|
||||
byte *dst = _cp;
|
||||
uint16 w = width(c);
|
||||
|
||||
for (uint16 j = 0; j < height(); j++) {
|
||||
for (uint16 k = 0; k < w; k++) {
|
||||
|
||||
if (!*src)
|
||||
*dst = _color;
|
||||
|
||||
dst++;
|
||||
src++;
|
||||
}
|
||||
|
||||
src += (_pitch - w);
|
||||
dst += (_bufPitch - w);
|
||||
}
|
||||
|
||||
return w;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const byte DosDialogueFont::_glyphWidths[126] = {
|
||||
0x04, 0x03, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x04, 0x04, 0x06, 0x06, 0x03, 0x05, 0x03, 0x05,
|
||||
0x06, 0x06, 0x06, 0x06, 0x07, 0x06, 0x06, 0x06, 0x06, 0x06, 0x03, 0x03, 0x05, 0x04, 0x05, 0x05,
|
||||
0x03, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x03, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
|
||||
0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x08, 0x07, 0x07, 0x07, 0x05, 0x06, 0x05, 0x08, 0x07,
|
||||
0x04, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x03, 0x04, 0x05, 0x05, 0x06, 0x06, 0x05,
|
||||
0x05, 0x06, 0x05, 0x05, 0x05, 0x05, 0x06, 0x07, 0x05, 0x05, 0x05, 0x05, 0x02, 0x05, 0x05, 0x07,
|
||||
0x08, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x04, 0x04, 0x04,
|
||||
0x05, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x04, 0x06, 0x05, 0x05, 0x05, 0x05
|
||||
};
|
||||
|
||||
|
||||
class DosMonospacedFont : public DosFont {
|
||||
|
||||
protected:
|
||||
uint16 _width;
|
||||
|
||||
protected:
|
||||
uint16 width(byte c) override {
|
||||
return _width;
|
||||
}
|
||||
|
||||
uint16 height() override {
|
||||
return _data->_height;
|
||||
}
|
||||
|
||||
uint16 drawChar(char c) override {
|
||||
|
||||
byte *src = _data->getFramePtr(c);
|
||||
byte *dst = _cp;
|
||||
|
||||
for (uint16 i = 0; i < height(); i++) {
|
||||
for (uint16 j = 0; j < _width; j++) {
|
||||
if (*src)
|
||||
*dst = *src;
|
||||
src++;
|
||||
dst++;
|
||||
}
|
||||
|
||||
dst += (_bufPitch - _width);
|
||||
src += (_pitch - _width);
|
||||
}
|
||||
|
||||
return _width;
|
||||
}
|
||||
|
||||
public:
|
||||
DosMonospacedFont(Cnv *cnv) : DosFont(cnv) {
|
||||
_width = 8;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class AmigaFont : public Font {
|
||||
Graphics::AmigaFont *_font;
|
||||
|
||||
public:
|
||||
AmigaFont(Common::SeekableReadStream *stream = nullptr) {
|
||||
_font = new Graphics::AmigaFont(stream);
|
||||
}
|
||||
~AmigaFont() override {
|
||||
delete _font;
|
||||
}
|
||||
|
||||
uint32 getStringWidth(const char *s) override {
|
||||
return _font->getStringWidth(s);
|
||||
}
|
||||
void drawString(Graphics::Surface *src, int x, int y, const char *s) override {
|
||||
_font->drawString(src, s, x, y, src->w, _color);
|
||||
}
|
||||
|
||||
protected:
|
||||
uint16 height() override {
|
||||
return _font->getFontHeight();
|
||||
}
|
||||
};
|
||||
|
||||
Font *DosDisk_ns::createFont(const char *name, Cnv* cnv) {
|
||||
Font *f = nullptr;
|
||||
|
||||
if (!scumm_stricmp(name, "comic"))
|
||||
f = new DosDialogueFont(cnv);
|
||||
else
|
||||
if (!scumm_stricmp(name, "topaz"))
|
||||
f = new DosMonospacedFont(cnv);
|
||||
else
|
||||
if (!scumm_stricmp(name, "slide"))
|
||||
f = new DosMonospacedFont(cnv);
|
||||
else
|
||||
error("unknown dos font '%s'", name);
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
Font *AmigaDisk_ns::createFont(const char *name, Common::SeekableReadStream &stream) {
|
||||
// TODO: implement AmigaLabelFont for labels
|
||||
return new AmigaFont(&stream);
|
||||
}
|
||||
|
||||
Font *DosDisk_br::createFont(const char *name, Common::ReadStream &stream) {
|
||||
// debug("DosDisk_br::createFont(%s)", name);
|
||||
Font *font;
|
||||
|
||||
if (_vm->getFeatures() & GF_DEMO) {
|
||||
if (!scumm_stricmp(name, "russia")) {
|
||||
font = new BraFont(stream, _braDosDemoRussiaCharMap);
|
||||
} else {
|
||||
font = new BraFont(stream, _braDosDemoComicCharMap);
|
||||
}
|
||||
} else {
|
||||
font = new BraFont(stream, _braDosFullCharMap);
|
||||
}
|
||||
|
||||
return font;
|
||||
}
|
||||
|
||||
Font *AmigaDisk_br::createFont(const char *name, Common::SeekableReadStream &stream) {
|
||||
// TODO: implement AmigaLabelFont for labels
|
||||
return new AmigaFont(&stream);
|
||||
}
|
||||
|
||||
GfxObj* DosDisk_br::createInventoryObjects(Common::SeekableReadStream &stream) {
|
||||
Frames *frames = new BraInventoryObjects(stream);
|
||||
return new GfxObj(0, frames, "inventoryobjects");
|
||||
}
|
||||
|
||||
|
||||
void Parallaction_ns::initFonts() {
|
||||
if (getPlatform() == Common::kPlatformDOS) {
|
||||
_dialogueFont = _disk->loadFont("comic");
|
||||
_labelFont = _disk->loadFont("topaz");
|
||||
_menuFont = _disk->loadFont("slide");
|
||||
_introFont = _disk->loadFont("slide");
|
||||
} else {
|
||||
_dialogueFont = _disk->loadFont("comic");
|
||||
_labelFont = new AmigaFont();
|
||||
_menuFont = _disk->loadFont("slide");
|
||||
_introFont = _disk->loadFont("intro");
|
||||
}
|
||||
}
|
||||
|
||||
void Parallaction_br::initFonts() {
|
||||
if (getPlatform() == Common::kPlatformDOS) {
|
||||
_menuFont = _disk->loadFont("russia");
|
||||
_dialogueFont = _disk->loadFont("comic");
|
||||
_labelFont = _menuFont;
|
||||
} else {
|
||||
// TODO: Where is vanya used?
|
||||
// fonts/vanya/16
|
||||
|
||||
_menuFont = _disk->loadFont("sonya");
|
||||
_dialogueFont = _disk->loadFont("natasha");
|
||||
_labelFont = _menuFont;
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Parallaction
|
||||
444
engines/parallaction/gfxbase.cpp
Normal file
444
engines/parallaction/gfxbase.cpp
Normal file
@@ -0,0 +1,444 @@
|
||||
/* 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.h"
|
||||
#include "disk.h"
|
||||
|
||||
#include "common/algorithm.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "parallaction/parallaction.h"
|
||||
|
||||
namespace Parallaction {
|
||||
|
||||
GfxObj::GfxObj(uint objType, Frames *frames, const char* name) :
|
||||
_name(name), _frames(frames), x(0), y(0), z(0), _prog(0), _flags(0),
|
||||
type(objType), frame(0), layer(3), scale(100), _hasMask(false), _hasPath(false),
|
||||
transparentKey(0), _maskId(0), _pathId(0) {}
|
||||
|
||||
GfxObj::~GfxObj() {
|
||||
delete _frames;
|
||||
}
|
||||
|
||||
void GfxObj::release() {
|
||||
// _keep = false;
|
||||
delete this;
|
||||
}
|
||||
|
||||
const char *GfxObj::getName() const {
|
||||
return _name.c_str();
|
||||
}
|
||||
|
||||
uint GfxObj::getNum() {
|
||||
return _frames->getNum();
|
||||
}
|
||||
|
||||
void GfxObj::getRect(uint f, Common::Rect &r) {
|
||||
_frames->getRect(f, r);
|
||||
}
|
||||
|
||||
byte *GfxObj::getData(uint f) {
|
||||
return _frames->getData(f);
|
||||
}
|
||||
|
||||
uint GfxObj::getRawSize(uint f) {
|
||||
return _frames->getRawSize(f);
|
||||
}
|
||||
uint GfxObj::getSize(uint f) {
|
||||
return _frames->getSize(f);
|
||||
}
|
||||
|
||||
void GfxObj::setFlags(uint32 flags) {
|
||||
_flags |= flags;
|
||||
}
|
||||
|
||||
void GfxObj::clearFlags(uint32 flags) {
|
||||
_flags &= ~flags;
|
||||
}
|
||||
|
||||
void Gfx::addObjectToScene(GfxObj *obj) {
|
||||
if (!obj) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!obj->isVisible()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (SCENE_DRAWLIST_SIZE == _sceneObjects.size()) {
|
||||
warning("number of objects in the current scene is larger than the fixed drawlist size");
|
||||
}
|
||||
|
||||
_sceneObjects.push_back(obj);
|
||||
}
|
||||
|
||||
void Gfx::resetSceneDrawList() {
|
||||
_sceneObjects.clear();
|
||||
_sceneObjects.reserve(SCENE_DRAWLIST_SIZE);
|
||||
}
|
||||
|
||||
GfxObj* Gfx::loadAnim(const char *name) {
|
||||
debugC(1, kDebugGraphics, "Gfx::loadAnim(\"%s\")", name);
|
||||
Frames* frames = _disk->loadFrames(name);
|
||||
assert(frames);
|
||||
|
||||
GfxObj *obj = new GfxObj(kGfxObjTypeAnim, frames, name);
|
||||
assert(obj);
|
||||
|
||||
// animation Z is not set here, but controlled by game scripts and user interaction.
|
||||
// it is always >=0 and <screen height
|
||||
obj->transparentKey = 0;
|
||||
return obj;
|
||||
}
|
||||
|
||||
GfxObj* Gfx::loadCharacterAnim(const char *name) {
|
||||
return loadAnim(name);
|
||||
}
|
||||
|
||||
GfxObj* Gfx::loadGet(const char *name) {
|
||||
GfxObj *obj = _disk->loadStatic(name);
|
||||
assert(obj);
|
||||
|
||||
obj->z = kGfxObjGetZ; // this preset Z value ensures that get zones are drawn after doors but before animations
|
||||
obj->type = kGfxObjTypeGet;
|
||||
obj->transparentKey = 0;
|
||||
return obj;
|
||||
}
|
||||
|
||||
GfxObj* Gfx::loadDoor(const char *name) {
|
||||
Frames *frames = _disk->loadFrames(name);
|
||||
assert(frames);
|
||||
|
||||
GfxObj *obj = new GfxObj(kGfxObjTypeDoor, frames, name);
|
||||
assert(obj);
|
||||
|
||||
obj->z = kGfxObjDoorZ; // this preset Z value ensures that doors are drawn first
|
||||
obj->transparentKey = 0;
|
||||
return obj;
|
||||
}
|
||||
|
||||
void Gfx::freeLocationObjects() {
|
||||
freeDialogueObjects();
|
||||
freeLabels();
|
||||
}
|
||||
|
||||
void Gfx::freeCharacterObjects() {
|
||||
freeDialogueObjects();
|
||||
}
|
||||
|
||||
void BackgroundInfo::loadGfxObjMask(Parallaction *vm, const char *name, GfxObj *obj) {
|
||||
debugC(1, kDebugGraphics, "BackgroundInfo::loadGfxObjMask(\"%s\")", name);
|
||||
Common::Rect rect;
|
||||
obj->getRect(0, rect);
|
||||
|
||||
MaskBuffer *buf = vm->_disk->loadMask(name, rect.width(), rect.height());
|
||||
|
||||
obj->_maskId = addMaskPatch(buf);
|
||||
obj->_hasMask = true;
|
||||
}
|
||||
|
||||
void BackgroundInfo::loadGfxObjPath(Parallaction *vm, const char *name, GfxObj *obj) {
|
||||
Common::Rect rect;
|
||||
obj->getRect(0, rect);
|
||||
|
||||
PathBuffer *buf = vm->_disk->loadPath(name, rect.width(), rect.height());
|
||||
|
||||
obj->_pathId = addPathPatch(buf);
|
||||
obj->_hasPath = true;
|
||||
}
|
||||
|
||||
void Gfx::showGfxObj(GfxObj* obj, bool visible) {
|
||||
if (!obj) {
|
||||
return;
|
||||
}
|
||||
debugC(1, kDebugGraphics, "Gfx::showGfxObj(\"%s\", visible:%d)", obj->getName(), visible ? 1 : 0);
|
||||
|
||||
if (visible) {
|
||||
obj->setFlags(kGfxObjVisible);
|
||||
} else {
|
||||
obj->clearFlags(kGfxObjVisible);
|
||||
}
|
||||
|
||||
if (obj->_hasMask) {
|
||||
debugC(1, kDebugGraphics, "\tHas Mask");
|
||||
_backgroundInfo->toggleMaskPatch(obj->_maskId, obj->x, obj->y, visible);
|
||||
}
|
||||
if (obj->_hasPath) {
|
||||
debugC(1, kDebugGraphics, "\tHas Path");
|
||||
_backgroundInfo->togglePathPatch(obj->_pathId, obj->x, obj->y, visible);
|
||||
}
|
||||
}
|
||||
|
||||
bool compareZ(const GfxObj* a1, const GfxObj* a2) {
|
||||
return (a1->z == a2->z) ? (a1->_prog < a2->_prog) : (a1->z < a2->z);
|
||||
}
|
||||
|
||||
void Gfx::sortScene() {
|
||||
debugC(3, kDebugGraphics, "Gfx::sortScene()");
|
||||
GfxObjArray::iterator first = _sceneObjects.begin();
|
||||
GfxObjArray::iterator last = _sceneObjects.end();
|
||||
|
||||
Common::sort(first, last, compareZ);
|
||||
}
|
||||
|
||||
void Gfx::drawGfxObject(GfxObj *obj, Graphics::Surface &surf) {
|
||||
if (!obj->isVisible()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Common::Rect rect;
|
||||
byte *data;
|
||||
|
||||
obj->getRect(obj->frame, rect);
|
||||
|
||||
int x = obj->x;
|
||||
int y = obj->y;
|
||||
if (_overlayMode) {
|
||||
x += _scrollPosX;
|
||||
y += _scrollPosY;
|
||||
}
|
||||
rect.translate(x, y);
|
||||
data = obj->getData(obj->frame);
|
||||
|
||||
// WORKAROUND: During the end credits, game scripts try to show a
|
||||
// non-existing frame. We change it to an existing one here.
|
||||
if (obj->frame == 14 && obj->getNum() == 9 && !strcmp(obj->getName(), "Dinor"))
|
||||
obj->frame = 8;
|
||||
|
||||
if (obj->getSize(obj->frame) == obj->getRawSize(obj->frame)) {
|
||||
blt(rect, data, &surf, obj->layer, obj->scale, obj->transparentKey);
|
||||
} else {
|
||||
unpackBlt(rect, data, obj->getRawSize(obj->frame), &surf, obj->layer, obj->scale, obj->transparentKey);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Gfx::drawText(Font *font, Graphics::Surface *surf, uint16 x, uint16 y, const char *text, byte color) {
|
||||
font->setColor(color);
|
||||
font->drawString(surf, x, y, text);
|
||||
}
|
||||
|
||||
void Gfx::unpackBlt(const Common::Rect& r, byte *data, uint size, Graphics::Surface *surf, uint16 z, uint scale, byte transparentColor) {
|
||||
byte *d = _unpackedBitmap;
|
||||
uint pixelsLeftInLine = r.width();
|
||||
|
||||
while (size > 0) {
|
||||
uint8 p = *data++;
|
||||
size--;
|
||||
uint8 color = p & 0xF;
|
||||
uint8 repeat = (p & 0xF0) >> 4;
|
||||
if (repeat == 0) {
|
||||
repeat = *data++;
|
||||
size--;
|
||||
}
|
||||
if (repeat == 0) {
|
||||
// end of line
|
||||
repeat = pixelsLeftInLine;
|
||||
pixelsLeftInLine = r.width();
|
||||
} else {
|
||||
pixelsLeftInLine -= repeat;
|
||||
}
|
||||
|
||||
memset(d, color, repeat);
|
||||
d += repeat;
|
||||
}
|
||||
|
||||
blt(r, _unpackedBitmap, surf, z, scale, transparentColor);
|
||||
}
|
||||
|
||||
void Gfx::bltMaskScale(const Common::Rect& r, byte *data, Graphics::Surface *surf, uint16 z, uint scale, byte transparentColor) {
|
||||
if (scale == 100) {
|
||||
// use optimized path
|
||||
bltMaskNoScale(r, data, surf, z, transparentColor);
|
||||
return;
|
||||
}
|
||||
|
||||
// unscaled rectangle size
|
||||
uint width = r.width();
|
||||
uint height = r.height();
|
||||
|
||||
// scaled rectangle size
|
||||
uint scaledWidth = r.width() * scale / 100;
|
||||
uint scaledHeight = r.height() * scale / 100;
|
||||
|
||||
// scaled rectangle origin
|
||||
uint scaledLeft = r.left + (width - scaledWidth) / 2;
|
||||
uint scaledTop = r.top + (height - scaledHeight);
|
||||
|
||||
// clipped scaled destination rectangle
|
||||
Common::Rect dstRect(scaledWidth, scaledHeight);
|
||||
dstRect.moveTo(scaledLeft, scaledTop);
|
||||
|
||||
Common::Rect clipper(surf->w, surf->h);
|
||||
dstRect.clip(clipper);
|
||||
if (!dstRect.isValidRect()) return;
|
||||
|
||||
// clipped source rectangle
|
||||
Common::Rect srcRect;
|
||||
srcRect.left = (dstRect.left - scaledLeft) * 100 / scale;
|
||||
srcRect.top = (dstRect.top - scaledTop) * 100 / scale;
|
||||
srcRect.setWidth(dstRect.width() * 100 / scale);
|
||||
srcRect.setHeight(dstRect.height() * 100 / scale);
|
||||
if (!srcRect.isValidRect()) return;
|
||||
|
||||
Common::Point dp;
|
||||
dp.x = dstRect.left;
|
||||
dp.y = dstRect.top;
|
||||
|
||||
byte *s = data + srcRect.left + srcRect.top * width;
|
||||
byte *d = (byte *)surf->getBasePtr(dp.x, dp.y);
|
||||
|
||||
uint line = 0, col = 0;
|
||||
|
||||
uint xAccum = 0, yAccum = 0;
|
||||
uint inc = width * (100 - scale);
|
||||
uint thr = width * 100;
|
||||
|
||||
for (uint16 i = 0; i < srcRect.height(); i++) {
|
||||
yAccum += inc;
|
||||
|
||||
if (yAccum >= thr) {
|
||||
yAccum -= thr;
|
||||
s += width;
|
||||
continue;
|
||||
}
|
||||
|
||||
xAccum = 0;
|
||||
byte *d2 = d;
|
||||
col = 0;
|
||||
|
||||
for (uint16 j = 0; j < srcRect.width(); j++) {
|
||||
xAccum += inc;
|
||||
|
||||
if (xAccum >= thr) {
|
||||
xAccum -= thr;
|
||||
s++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (*s != transparentColor) {
|
||||
if (_backgroundInfo->hasMask()) {
|
||||
byte v = _backgroundInfo->_mask->getValue(dp.x + col, dp.y + line);
|
||||
if (z >= v) *d2 = *s;
|
||||
} else {
|
||||
*d2 = *s;
|
||||
}
|
||||
}
|
||||
|
||||
s++;
|
||||
d2++;
|
||||
col++;
|
||||
}
|
||||
|
||||
s += width - srcRect.width();
|
||||
d += surf->w;
|
||||
line++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Gfx::bltMaskNoScale(const Common::Rect& r, byte *data, Graphics::Surface *surf, uint16 z, byte transparentColor) {
|
||||
if (!_backgroundInfo->hasMask() || (z == LAYER_FOREGROUND)) {
|
||||
// use optimized path
|
||||
bltNoMaskNoScale(r, data, surf, transparentColor);
|
||||
return;
|
||||
}
|
||||
|
||||
Common::Point dp;
|
||||
Common::Rect q(r);
|
||||
|
||||
Common::Rect clipper(surf->w, surf->h);
|
||||
|
||||
q.clip(clipper);
|
||||
if (!q.isValidRect()) return;
|
||||
|
||||
dp.x = q.left;
|
||||
dp.y = q.top;
|
||||
|
||||
q.translate(-r.left, -r.top);
|
||||
|
||||
byte *s = data + q.left + q.top * r.width();
|
||||
byte *d = (byte *)surf->getBasePtr(dp.x, dp.y);
|
||||
|
||||
uint sPitch = r.width() - q.width();
|
||||
uint dPitch = surf->w - q.width();
|
||||
|
||||
for (uint16 i = 0; i < q.height(); i++) {
|
||||
|
||||
for (uint16 j = 0; j < q.width(); j++) {
|
||||
if (*s != transparentColor) {
|
||||
if (_backgroundInfo->hasMask()) {
|
||||
byte v = _backgroundInfo->_mask->getValue(dp.x + j, dp.y + i);
|
||||
if (z >= v) *d = *s;
|
||||
} else {
|
||||
*d = *s;
|
||||
}
|
||||
}
|
||||
|
||||
s++;
|
||||
d++;
|
||||
}
|
||||
|
||||
s += sPitch;
|
||||
d += dPitch;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Gfx::bltNoMaskNoScale(const Common::Rect& r, byte *data, Graphics::Surface *surf, byte transparentColor) {
|
||||
Common::Point dp;
|
||||
Common::Rect q(r);
|
||||
|
||||
Common::Rect clipper(surf->w, surf->h);
|
||||
|
||||
q.clip(clipper);
|
||||
if (!q.isValidRect()) return;
|
||||
|
||||
dp.x = q.left;
|
||||
dp.y = q.top;
|
||||
|
||||
q.translate(-r.left, -r.top);
|
||||
|
||||
byte *s = data + q.left + q.top * r.width();
|
||||
byte *d = (byte *)surf->getBasePtr(dp.x, dp.y);
|
||||
|
||||
uint sPitch = r.width() - q.width();
|
||||
uint dPitch = surf->w - q.width();
|
||||
|
||||
for (uint16 i = q.top; i < q.bottom; i++) {
|
||||
for (uint16 j = q.left; j < q.right; j++) {
|
||||
if (*s != transparentColor)
|
||||
*d = *s;
|
||||
|
||||
s++;
|
||||
d++;
|
||||
}
|
||||
|
||||
s += sPitch;
|
||||
d += dPitch;
|
||||
}
|
||||
}
|
||||
|
||||
void Gfx::blt(const Common::Rect& r, byte *data, Graphics::Surface *surf, uint16 z, uint scale, byte transparentColor) {
|
||||
bltMaskScale(r, data, surf, z, scale, transparentColor);
|
||||
}
|
||||
|
||||
} // namespace Parallaction
|
||||
1161
engines/parallaction/graphics.cpp
Normal file
1161
engines/parallaction/graphics.cpp
Normal file
File diff suppressed because it is too large
Load Diff
574
engines/parallaction/graphics.h
Normal file
574
engines/parallaction/graphics.h
Normal file
@@ -0,0 +1,574 @@
|
||||
/* 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 PARALLACTION_GRAPHICS_H
|
||||
#define PARALLACTION_GRAPHICS_H
|
||||
|
||||
#include "common/list.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/hashmap.h"
|
||||
#include "common/hash-str.h"
|
||||
#include "common/str.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/array.h"
|
||||
|
||||
#include "graphics/surface.h"
|
||||
|
||||
|
||||
namespace Parallaction {
|
||||
|
||||
|
||||
#include "common/pack-start.h" // START STRUCT PACKING
|
||||
|
||||
struct PaletteFxRange {
|
||||
|
||||
uint16 _timer;
|
||||
uint16 _step;
|
||||
uint16 _flags;
|
||||
byte _first;
|
||||
byte _last;
|
||||
|
||||
} PACKED_STRUCT;
|
||||
|
||||
#include "common/pack-end.h" // END STRUCT PACKING
|
||||
|
||||
class Font {
|
||||
|
||||
protected:
|
||||
byte _color;
|
||||
|
||||
|
||||
public:
|
||||
Font() : _color(0) {}
|
||||
virtual ~Font() {}
|
||||
|
||||
virtual void setColor(byte color) {
|
||||
_color = color;
|
||||
}
|
||||
virtual uint32 getStringWidth(const char *s) = 0;
|
||||
virtual uint16 height() = 0;
|
||||
|
||||
virtual void drawString(Graphics::Surface *dst, int x, int y, const char *s) = 0;
|
||||
};
|
||||
|
||||
|
||||
struct Frames {
|
||||
|
||||
virtual uint16 getNum() = 0;
|
||||
virtual byte* getData(uint16 index) = 0;
|
||||
virtual void getRect(uint16 index, Common::Rect &r) = 0;
|
||||
virtual uint getRawSize(uint16 index) = 0;
|
||||
virtual uint getSize(uint16 index) = 0;
|
||||
|
||||
virtual ~Frames() { }
|
||||
|
||||
};
|
||||
|
||||
|
||||
struct SurfaceToFrames : public Frames {
|
||||
|
||||
Graphics::Surface *_surf;
|
||||
|
||||
public:
|
||||
SurfaceToFrames(Graphics::Surface *surf) : _surf(surf) {
|
||||
}
|
||||
|
||||
~SurfaceToFrames() override {
|
||||
_surf->free();
|
||||
delete _surf;
|
||||
}
|
||||
|
||||
uint16 getNum() override {
|
||||
return 1;
|
||||
}
|
||||
byte* getData(uint16 index) override {
|
||||
assert(index == 0);
|
||||
return (byte *)_surf->getBasePtr(0,0);
|
||||
}
|
||||
void getRect(uint16 index, Common::Rect &r) override {
|
||||
assert(index == 0);
|
||||
r.left = 0;
|
||||
r.top = 0;
|
||||
r.setWidth(_surf->w);
|
||||
r.setHeight(_surf->h);
|
||||
}
|
||||
uint getRawSize(uint16 index) override {
|
||||
assert(index == 0);
|
||||
return getSize(index);
|
||||
}
|
||||
uint getSize(uint16 index) override {
|
||||
assert(index == 0);
|
||||
return _surf->w * _surf->h;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
struct Cnv : public Frames {
|
||||
uint16 _count; // # of frames
|
||||
uint16 _width; //
|
||||
uint16 _height; //
|
||||
byte** field_8; // unused
|
||||
byte* _data;
|
||||
bool _freeData;
|
||||
Graphics::Surface *_surf;
|
||||
|
||||
public:
|
||||
Cnv() {
|
||||
_width = _height = _count = 0;
|
||||
_data = NULL;
|
||||
_surf = NULL;
|
||||
_freeData = false;
|
||||
field_8 = 0;
|
||||
}
|
||||
|
||||
Cnv(uint16 numFrames, uint16 width, uint16 height, byte *data, bool freeData = false)
|
||||
: _count(numFrames), _width(width), _height(height), _data(data), _freeData(freeData), field_8(0), _surf(NULL) {
|
||||
}
|
||||
|
||||
Cnv(uint16 numFrames, uint16 width, uint16 height, Graphics::Surface *surf)
|
||||
: _count(numFrames), _width(width), _height(height), _data(NULL), _freeData(true), field_8(0), _surf(surf) {
|
||||
_data = (byte *)_surf->getBasePtr(0, 0);
|
||||
}
|
||||
|
||||
~Cnv() override {
|
||||
if (_freeData) {
|
||||
if (_surf) {
|
||||
_surf->free();
|
||||
delete _surf;
|
||||
} else {
|
||||
delete[] _data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
byte* getFramePtr(uint16 index) {
|
||||
if (index >= _count)
|
||||
return NULL;
|
||||
return &_data[index * _width * _height];
|
||||
}
|
||||
|
||||
uint16 getNum() override {
|
||||
return _count;
|
||||
}
|
||||
|
||||
byte *getData(uint16 index) override {
|
||||
return getFramePtr(index);
|
||||
}
|
||||
|
||||
void getRect(uint16 index, Common::Rect &r) override {
|
||||
r.left = 0;
|
||||
r.top = 0;
|
||||
r.setWidth(_width);
|
||||
r.setHeight(_height);
|
||||
}
|
||||
uint getRawSize(uint16 index) override {
|
||||
assert(index < _count);
|
||||
return getSize(index);
|
||||
}
|
||||
uint getSize(uint16 index) override {
|
||||
assert(index < _count);
|
||||
return _width * _height;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
struct MaskBuffer {
|
||||
// handles a 2-bit depth buffer used for z-buffering
|
||||
|
||||
uint16 w;
|
||||
uint16 internalWidth;
|
||||
uint16 h;
|
||||
uint size;
|
||||
byte *data;
|
||||
bool bigEndian;
|
||||
|
||||
byte* getPtr(uint16 x, uint16 y) const;
|
||||
void bltOr(uint16 dx, uint16 dy, const MaskBuffer &src, uint16 sx, uint16 sy, uint width, uint height);
|
||||
void bltCopy(uint16 dx, uint16 dy, const MaskBuffer &src, uint16 sx, uint16 sy, uint width, uint height);
|
||||
|
||||
public:
|
||||
MaskBuffer();
|
||||
~MaskBuffer();
|
||||
|
||||
void clone(const MaskBuffer &buf);
|
||||
void create(uint16 width, uint16 height);
|
||||
void free();
|
||||
|
||||
byte getValue(uint16 x, uint16 y) const;
|
||||
};
|
||||
|
||||
|
||||
struct PathBuffer {
|
||||
// handles a 1-bit depth buffer used for masking non-walkable areas
|
||||
|
||||
uint16 w;
|
||||
uint16 internalWidth;
|
||||
uint16 h;
|
||||
uint size;
|
||||
byte *data;
|
||||
bool bigEndian;
|
||||
|
||||
byte* getPtr(uint16 x, uint16 y) const;
|
||||
void bltCopy(uint16 dx, uint16 dy, const PathBuffer &src, uint16 sx, uint16 sy, uint width, uint height);
|
||||
|
||||
public:
|
||||
PathBuffer();
|
||||
~PathBuffer();
|
||||
|
||||
void clone(const PathBuffer &buf);
|
||||
void create(uint16 width, uint16 height);
|
||||
void free();
|
||||
byte getValue(uint16 x, uint16 y) const;
|
||||
};
|
||||
|
||||
|
||||
class Palette {
|
||||
|
||||
byte _data[768];
|
||||
uint _colors;
|
||||
uint _size;
|
||||
bool _hb;
|
||||
|
||||
public:
|
||||
Palette();
|
||||
Palette(const Palette &pal);
|
||||
|
||||
void clone(const Palette &pal);
|
||||
|
||||
void makeBlack();
|
||||
void setEntries(byte* data, uint first, uint num);
|
||||
void getEntry(uint index, int &red, int &green, int &blue);
|
||||
void setEntry(uint index, int red, int green, int blue);
|
||||
void makeGrayscale();
|
||||
void fadeTo(const Palette& target, uint step);
|
||||
uint fillRGB(byte *rgb);
|
||||
|
||||
void rotate(uint first, uint last, bool forward);
|
||||
};
|
||||
|
||||
|
||||
#define CENTER_LABEL_HORIZONTAL -1
|
||||
#define CENTER_LABEL_VERTICAL -1
|
||||
|
||||
|
||||
|
||||
#define MAX_BALLOON_WIDTH 130
|
||||
|
||||
class Parallaction;
|
||||
|
||||
struct DoorData;
|
||||
struct GetData;
|
||||
struct Label;
|
||||
class Disk;
|
||||
|
||||
enum {
|
||||
kGfxObjVisible = 1,
|
||||
|
||||
kGfxObjTypeDoor = 0,
|
||||
kGfxObjTypeGet = 1,
|
||||
kGfxObjTypeAnim = 2,
|
||||
kGfxObjTypeLabel = 3,
|
||||
kGfxObjTypeBalloon = 4,
|
||||
kGfxObjTypeCharacter = 8,
|
||||
kGfxObjTypeMenu = 16
|
||||
};
|
||||
|
||||
enum {
|
||||
kGfxObjDoorZ = -200,
|
||||
kGfxObjGetZ = -100
|
||||
};
|
||||
|
||||
class GfxObj {
|
||||
Common::String _name;
|
||||
Frames *_frames;
|
||||
|
||||
public:
|
||||
int16 x, y;
|
||||
|
||||
int32 z;
|
||||
uint32 _prog; // this value is used when sorting, in case that comparing z is not enough to tell which object goes on front
|
||||
|
||||
uint32 _flags;
|
||||
|
||||
uint type;
|
||||
uint frame;
|
||||
uint layer;
|
||||
uint transparentKey;
|
||||
uint scale;
|
||||
|
||||
int _maskId;
|
||||
bool _hasMask;
|
||||
int _pathId;
|
||||
bool _hasPath;
|
||||
|
||||
Common::String _text;
|
||||
|
||||
|
||||
GfxObj(uint type, Frames *frames, const char *name = NULL);
|
||||
virtual ~GfxObj();
|
||||
|
||||
const char *getName() const;
|
||||
|
||||
uint getNum();
|
||||
void getRect(uint frame, Common::Rect &r);
|
||||
byte *getData(uint frame);
|
||||
uint getRawSize(uint frame);
|
||||
uint getSize(uint frame);
|
||||
|
||||
|
||||
void setFlags(uint32 flags);
|
||||
void clearFlags(uint32 flags);
|
||||
bool isVisible() {
|
||||
return (_flags & kGfxObjVisible) == kGfxObjVisible;
|
||||
}
|
||||
|
||||
void release();
|
||||
};
|
||||
|
||||
#define LAYER_FOREGROUND 3
|
||||
|
||||
/*
|
||||
BackgroundInfo keeps information about the background bitmap that can be seen in the game.
|
||||
These bitmaps can be of any size, smaller or larger than the visible screen, the latter
|
||||
being the most common options.
|
||||
*/
|
||||
struct BackgroundInfo {
|
||||
protected:
|
||||
typedef Common::Array<MaskBuffer *> MaskPatches;
|
||||
MaskPatches _maskPatches;
|
||||
MaskBuffer _maskBackup;
|
||||
void clearMaskData();
|
||||
|
||||
typedef Common::Array<PathBuffer *> PathPatches;
|
||||
PathPatches _pathPatches;
|
||||
PathBuffer _pathBackup;
|
||||
void clearPathData();
|
||||
|
||||
public:
|
||||
int _x, _y; // used to display bitmaps smaller than the screen
|
||||
int width;
|
||||
int height;
|
||||
|
||||
Graphics::Surface bg;
|
||||
MaskBuffer *_mask;
|
||||
PathBuffer *_path;
|
||||
|
||||
Palette palette;
|
||||
|
||||
int layers[4];
|
||||
PaletteFxRange ranges[6];
|
||||
|
||||
|
||||
BackgroundInfo();
|
||||
~BackgroundInfo();
|
||||
|
||||
void setPaletteRange(int index, const PaletteFxRange& range);
|
||||
|
||||
// mask management
|
||||
bool hasMask();
|
||||
uint addMaskPatch(MaskBuffer *patch);
|
||||
void toggleMaskPatch(uint id, int x, int y, bool apply);
|
||||
uint16 getMaskLayer(uint16 z) const;
|
||||
void finalizeMask();
|
||||
void loadGfxObjMask(Parallaction *vm, const char *name, GfxObj *obj);
|
||||
|
||||
// path management
|
||||
bool hasPath();
|
||||
uint addPathPatch(PathBuffer *patch);
|
||||
void togglePathPatch(uint id, int x, int y, bool apply);
|
||||
void finalizePath();
|
||||
void loadGfxObjPath(Parallaction *vm, const char *name, GfxObj *obj);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
enum {
|
||||
kBackgroundLocation = 1,
|
||||
kBackgroundSlide = 2
|
||||
};
|
||||
|
||||
|
||||
class BalloonManager {
|
||||
public:
|
||||
enum TextColor {
|
||||
kSelectedColor = 0,
|
||||
kUnselectedColor = 1,
|
||||
kNormalColor = 2
|
||||
};
|
||||
|
||||
virtual ~BalloonManager() { }
|
||||
|
||||
virtual void reset() = 0;
|
||||
virtual int setLocationBalloon(const Common::String &text, bool endGame) = 0;
|
||||
virtual int setDialogueBalloon(const Common::String &text, uint16 winding, TextColor textColor) = 0;
|
||||
virtual int setSingleBalloon(const Common::String &text, uint16 x, uint16 y, uint16 winding, TextColor textColor) = 0;
|
||||
virtual void setBalloonText(uint id, const Common::String &text, TextColor textColor) = 0;
|
||||
virtual int hitTestDialogueBalloon(int x, int y) = 0;
|
||||
};
|
||||
|
||||
|
||||
typedef Common::Array<GfxObj *> GfxObjArray;
|
||||
#define SCENE_DRAWLIST_SIZE 100
|
||||
|
||||
class Gfx {
|
||||
|
||||
protected:
|
||||
Parallaction* _vm;
|
||||
void resetSceneDrawList();
|
||||
|
||||
public:
|
||||
Disk *_disk;
|
||||
|
||||
void beginFrame();
|
||||
void addObjectToScene(GfxObj *obj);
|
||||
GfxObjArray _sceneObjects;
|
||||
GfxObj* loadAnim(const char *name);
|
||||
GfxObj* loadGet(const char *name);
|
||||
GfxObj* loadDoor(const char *name);
|
||||
GfxObj* loadCharacterAnim(const char *name);
|
||||
void sortScene();
|
||||
void freeCharacterObjects();
|
||||
void freeLocationObjects();
|
||||
void showGfxObj(GfxObj* obj, bool visible);
|
||||
void blt(const Common::Rect& r, byte *data, Graphics::Surface *surf, uint16 z, uint scale, byte transparentColor);
|
||||
void unpackBlt(const Common::Rect& r, byte *data, uint size, Graphics::Surface *surf, uint16 z, uint scale, byte transparentColor);
|
||||
|
||||
// labels
|
||||
void showFloatingLabel(GfxObj *label);
|
||||
void hideFloatingLabel();
|
||||
|
||||
GfxObj *renderFloatingLabel(Font *font, char *text);
|
||||
GfxObj *createLabel(Font *font, const char *text, byte color);
|
||||
void showLabel(GfxObj *label, int16 x, int16 y, bool queueTTS = true, bool voiceText = true);
|
||||
void hideLabel(GfxObj *label);
|
||||
void freeLabels();
|
||||
void unregisterLabel(GfxObj *label);
|
||||
|
||||
// dialogue handling
|
||||
GfxObj* registerBalloon(Frames *frames, const char *text);
|
||||
int setItem(GfxObj* obj, uint16 x, uint16 y, byte transparentColor = 0);
|
||||
void setItemFrame(uint item, uint16 f);
|
||||
void freeDialogueObjects();
|
||||
|
||||
// background surface
|
||||
BackgroundInfo *_backgroundInfo;
|
||||
void setBackground(uint type, BackgroundInfo *info);
|
||||
void patchBackground(Graphics::Surface &surf, int16 x, int16 y, bool mask = false);
|
||||
void grabBackground(const Common::Rect& r, Graphics::Surface &dst);
|
||||
void fillBackground(const Common::Rect& r, byte color);
|
||||
void invertBackground(const Common::Rect& r);
|
||||
|
||||
// palette
|
||||
void setPalette(Palette &palette);
|
||||
void setBlackPalette();
|
||||
void animatePalette();
|
||||
|
||||
// amiga specific
|
||||
void applyHalfbriteEffect_NS(Graphics::Surface &surf);
|
||||
void setHalfbriteMode(bool enable);
|
||||
void setProjectorPos(int x, int y);
|
||||
void setProjectorProgram(int16 *data);
|
||||
int16 *_nextProjectorPos;
|
||||
|
||||
// start programmatic relative scroll
|
||||
void initiateScroll(int deltaX, int deltaY);
|
||||
// immediate and absolute x,y scroll
|
||||
void setScrollPosX(int scrollX);
|
||||
void setScrollPosY(int scrollY);
|
||||
// return current scroll position
|
||||
void getScrollPos(Common::Point &p);
|
||||
|
||||
// init
|
||||
Gfx(Parallaction* vm);
|
||||
virtual ~Gfx();
|
||||
|
||||
void clearScreen();
|
||||
void updateScreen();
|
||||
|
||||
public:
|
||||
Palette _palette;
|
||||
|
||||
byte *_unpackedBitmap;
|
||||
|
||||
protected:
|
||||
bool _halfbrite;
|
||||
|
||||
Common::Point _hbCirclePos;
|
||||
int _hbCircleRadius;
|
||||
|
||||
// BRA specific
|
||||
Palette _backupPal;
|
||||
|
||||
|
||||
Graphics::Surface *lockScreen();
|
||||
void unlockScreen();
|
||||
void updateScreenIntern();
|
||||
|
||||
bool _doubleBuffering;
|
||||
int _gameType;
|
||||
Graphics::Surface _backBuffer;
|
||||
void copyRectToScreen(const byte *buf, int pitch, int x, int y, int w, int h);
|
||||
|
||||
int _scrollPosX, _scrollPosY;
|
||||
int _minScrollX, _maxScrollX, _minScrollY, _maxScrollY;
|
||||
|
||||
uint32 _requestedHScrollSteps;
|
||||
uint32 _requestedVScrollSteps;
|
||||
int32 _requestedHScrollDir;
|
||||
int32 _requestedVScrollDir;
|
||||
void scroll();
|
||||
#define NO_FLOATING_LABEL 1000
|
||||
|
||||
struct Label {
|
||||
Common::String _text;
|
||||
int _x, _y;
|
||||
int color;
|
||||
bool _floating;
|
||||
};
|
||||
|
||||
GfxObjArray _labels;
|
||||
GfxObjArray _balloons;
|
||||
GfxObjArray _items;
|
||||
|
||||
GfxObj *_floatingLabel;
|
||||
|
||||
// overlay mode enables drawing of graphics with automatic screen-to-game coordinate translation
|
||||
bool _overlayMode;
|
||||
void drawOverlay(Graphics::Surface &surf);
|
||||
void drawInventory();
|
||||
|
||||
void drawList(Graphics::Surface &surface, GfxObjArray &list);
|
||||
void updateFloatingLabel();
|
||||
void copyRect(const Common::Rect &r, Graphics::Surface &src, Graphics::Surface &dst);
|
||||
void drawText(Font *font, Graphics::Surface* surf, uint16 x, uint16 y, const char *text, byte color);
|
||||
void drawGfxObject(GfxObj *obj, Graphics::Surface &surf);
|
||||
void bltMaskScale(const Common::Rect& r, byte *data, Graphics::Surface *surf, uint16 z, uint scale, byte transparentColor);
|
||||
void bltMaskNoScale(const Common::Rect& r, byte *data, Graphics::Surface *surf, uint16 z, byte transparentColor);
|
||||
void bltNoMaskNoScale(const Common::Rect& r, byte *data, Graphics::Surface *surf, byte transparentColor);
|
||||
};
|
||||
|
||||
|
||||
} // Parallaction
|
||||
|
||||
|
||||
#endif
|
||||
90
engines/parallaction/gui.cpp
Normal file
90
engines/parallaction/gui.cpp
Normal file
@@ -0,0 +1,90 @@
|
||||
/* 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/textconsole.h"
|
||||
|
||||
#include "parallaction/gui.h"
|
||||
|
||||
namespace Parallaction {
|
||||
|
||||
bool MenuInputHelper::run() {
|
||||
if (_newState == nullptr) {
|
||||
debugC(3, kDebugExec, "MenuInputHelper has set NULL state");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_newState != _state) {
|
||||
debugC(3, kDebugExec, "MenuInputHelper changing state to '%s'", _newState->_name.c_str());
|
||||
|
||||
_newState->enter();
|
||||
_state = _newState;
|
||||
}
|
||||
|
||||
_newState = _state->run();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
MenuInputHelper::~MenuInputHelper() {
|
||||
StateMap::iterator b = _map.begin();
|
||||
for ( ; b != _map.end(); ++b) {
|
||||
delete b->_value;
|
||||
}
|
||||
_map.clear();
|
||||
}
|
||||
|
||||
|
||||
void Parallaction::runGuiFrame() {
|
||||
if (_input->_inputMode != Input::kInputModeMenu) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_menuHelper) {
|
||||
error("No menu helper defined");
|
||||
}
|
||||
|
||||
bool res = _menuHelper->run();
|
||||
|
||||
if (!res) {
|
||||
cleanupGui();
|
||||
_input->_inputMode = Input::kInputModeGame;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Parallaction::cleanupGui() {
|
||||
delete _menuHelper;
|
||||
_menuHelper = nullptr;
|
||||
}
|
||||
|
||||
void Parallaction::setInternLanguage(uint id) {
|
||||
//TODO: assert id!
|
||||
|
||||
_language = id;
|
||||
_disk->setLanguage(id);
|
||||
}
|
||||
|
||||
uint Parallaction::getInternLanguage() {
|
||||
return _language;
|
||||
}
|
||||
|
||||
|
||||
} // namespace Parallaction
|
||||
89
engines/parallaction/gui.h
Normal file
89
engines/parallaction/gui.h
Normal file
@@ -0,0 +1,89 @@
|
||||
/* 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 PARALLACTION_GUI_H
|
||||
#define PARALLACTION_GUI_H
|
||||
|
||||
#include "common/system.h"
|
||||
#include "common/hashmap.h"
|
||||
|
||||
#include "parallaction/input.h"
|
||||
#include "parallaction/parallaction.h"
|
||||
#include "parallaction/sound.h"
|
||||
|
||||
|
||||
namespace Parallaction {
|
||||
|
||||
class MenuInputState;
|
||||
|
||||
class MenuInputHelper {
|
||||
typedef Common::HashMap<Common::String, MenuInputState *> StateMap;
|
||||
|
||||
StateMap _map;
|
||||
MenuInputState *_state;
|
||||
MenuInputState *_newState;
|
||||
|
||||
public:
|
||||
MenuInputHelper() : _state(0), _newState(0) {
|
||||
}
|
||||
|
||||
~MenuInputHelper();
|
||||
|
||||
void setState(const Common::String &name) {
|
||||
// bootstrap routine
|
||||
_newState = getState(name);
|
||||
assert(_newState);
|
||||
}
|
||||
|
||||
void addState(const Common::String &name, MenuInputState *state) {
|
||||
_map.setVal(name, state);
|
||||
}
|
||||
|
||||
MenuInputState *getState(const Common::String &name) {
|
||||
return _map[name];
|
||||
}
|
||||
|
||||
bool run();
|
||||
};
|
||||
|
||||
class MenuInputState {
|
||||
|
||||
protected:
|
||||
MenuInputHelper *_helper;
|
||||
|
||||
public:
|
||||
MenuInputState(const Common::String &name, MenuInputHelper *helper) : _helper(helper), _name(name) {
|
||||
debugC(3, kDebugExec, "MenuInputState(%s)", name.c_str());
|
||||
_helper->addState(name, this);
|
||||
}
|
||||
|
||||
Common::String _name;
|
||||
|
||||
virtual ~MenuInputState() { }
|
||||
|
||||
virtual MenuInputState* run() = 0;
|
||||
virtual void enter() = 0;
|
||||
};
|
||||
|
||||
|
||||
} // namespace Parallaction
|
||||
|
||||
#endif
|
||||
559
engines/parallaction/gui_br.cpp
Normal file
559
engines/parallaction/gui_br.cpp
Normal file
@@ -0,0 +1,559 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/system.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
#include "parallaction/gui.h"
|
||||
#include "parallaction/input.h"
|
||||
#include "parallaction/parallaction.h"
|
||||
#include "parallaction/saveload.h"
|
||||
|
||||
namespace Parallaction {
|
||||
|
||||
class SplashInputState_BR : public MenuInputState {
|
||||
protected:
|
||||
Common::String _slideName;
|
||||
uint32 _timeOut;
|
||||
Common::String _nextState;
|
||||
uint32 _startTime;
|
||||
Palette blackPal;
|
||||
Palette pal;
|
||||
|
||||
Parallaction *_vm;
|
||||
int _fadeSteps;
|
||||
|
||||
public:
|
||||
SplashInputState_BR(Parallaction *vm, const Common::String &name, MenuInputHelper *helper) : MenuInputState(name, helper), _vm(vm),
|
||||
_timeOut(0), _startTime(0), _fadeSteps(0) {
|
||||
}
|
||||
|
||||
MenuInputState* run() override {
|
||||
if (_fadeSteps > 0) {
|
||||
pal.fadeTo(blackPal, 1);
|
||||
_vm->_gfx->setPalette(pal);
|
||||
_fadeSteps--;
|
||||
return this;
|
||||
}
|
||||
|
||||
if (_fadeSteps == 0) {
|
||||
return _helper->getState(_nextState);
|
||||
}
|
||||
|
||||
uint32 curTime = _vm->_system->getMillis();
|
||||
if (curTime - _startTime > _timeOut) {
|
||||
_fadeSteps = 64;
|
||||
pal.clone(_vm->_gfx->_backgroundInfo->palette);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
void enter() override {
|
||||
_vm->_gfx->clearScreen();
|
||||
_vm->showSlide(_slideName.c_str(), CENTER_LABEL_HORIZONTAL, CENTER_LABEL_VERTICAL);
|
||||
_vm->_input->setMouseState(MOUSE_DISABLED);
|
||||
|
||||
_startTime = _vm->_system->getMillis();
|
||||
_fadeSteps = -1;
|
||||
}
|
||||
};
|
||||
|
||||
class SplashInputState0_BR : public SplashInputState_BR {
|
||||
|
||||
public:
|
||||
SplashInputState0_BR(Parallaction_br *vm, MenuInputHelper *helper) : SplashInputState_BR(vm, "intro0", helper) {
|
||||
_slideName = "dyna";
|
||||
_timeOut = 600;
|
||||
_nextState = "intro1";
|
||||
}
|
||||
};
|
||||
|
||||
class SplashInputState1_BR : public SplashInputState_BR {
|
||||
|
||||
public:
|
||||
SplashInputState1_BR(Parallaction_br *vm, MenuInputHelper *helper) : SplashInputState_BR(vm, "intro1", helper) {
|
||||
_slideName = "core";
|
||||
_timeOut = 600;
|
||||
_nextState = "mainmenu";
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct LocationPart {
|
||||
int part;
|
||||
const char *location;
|
||||
};
|
||||
|
||||
class MainMenuInputState_BR : public MenuInputState {
|
||||
Parallaction_br *_vm;
|
||||
|
||||
#define MENUITEMS_X 250
|
||||
#define MENUITEMS_Y 200
|
||||
|
||||
#define MENUITEM_WIDTH 200
|
||||
#define MENUITEM_HEIGHT 20
|
||||
|
||||
Frames* renderMenuItem(const char *text) {
|
||||
// this builds a surface containing two copies of the text.
|
||||
// one is in normal color, the other is inverted.
|
||||
// the two 'frames' are used to display selected/unselected menu items
|
||||
|
||||
Graphics::Surface *surf = new Graphics::Surface();
|
||||
surf->create(MENUITEM_WIDTH, MENUITEM_HEIGHT * 2, Graphics::PixelFormat::createFormatCLUT8());
|
||||
|
||||
// build first frame to be displayed when item is not selected
|
||||
if (_vm->getPlatform() == Common::kPlatformDOS) {
|
||||
_vm->_menuFont->setColor(0);
|
||||
} else {
|
||||
_vm->_menuFont->setColor(23);
|
||||
}
|
||||
_vm->_menuFont->drawString(surf, 5, 2, text);
|
||||
|
||||
// build second frame to be displayed when item is selected
|
||||
_vm->_menuFont->drawString(surf, 5, 2 + MENUITEM_HEIGHT, text);
|
||||
|
||||
byte *dst = (byte *)surf->getPixels() + MENUITEM_WIDTH * MENUITEM_HEIGHT;
|
||||
for (int i = 0; i < MENUITEM_WIDTH * MENUITEM_HEIGHT; i++) {
|
||||
*dst++ ^= 0xD;
|
||||
}
|
||||
|
||||
// wrap the surface into the suitable Frames adapter
|
||||
return new Cnv(2, MENUITEM_WIDTH, MENUITEM_HEIGHT, surf);
|
||||
}
|
||||
|
||||
enum MenuOptions {
|
||||
kMenuPart0 = 0,
|
||||
kMenuPart1 = 1,
|
||||
kMenuPart2 = 2,
|
||||
kMenuPart3 = 3,
|
||||
kMenuPart4 = 4,
|
||||
kMenuLoadGame = 5,
|
||||
kMenuQuit = 6
|
||||
};
|
||||
|
||||
#define NUM_MENULINES 7
|
||||
GfxObj *_lines[NUM_MENULINES];
|
||||
|
||||
static const char *_menuStringsAmiga[NUM_MENULINES];
|
||||
static const char *_menuStringsPC[NUM_MENULINES];
|
||||
static const MenuOptions _optionsAmiga[NUM_MENULINES];
|
||||
static const MenuOptions _optionsPC[NUM_MENULINES];
|
||||
|
||||
const char **_menuStrings;
|
||||
const MenuOptions *_options;
|
||||
|
||||
static LocationPart _firstLocation[];
|
||||
|
||||
int _availItems;
|
||||
int _selection;
|
||||
|
||||
void cleanup() {
|
||||
_vm->_gfx->freeDialogueObjects();
|
||||
|
||||
for (int i = 0; i < _availItems; i++) {
|
||||
delete _lines[i];
|
||||
_lines[i] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void redrawMenu() {
|
||||
Common::Point p;
|
||||
_vm->_input->getCursorPos(p);
|
||||
|
||||
if ((p.x > MENUITEMS_X) && (p.x < (MENUITEMS_X+MENUITEM_WIDTH)) && (p.y > MENUITEMS_Y)) {
|
||||
_selection = (p.y - MENUITEMS_Y) / MENUITEM_HEIGHT;
|
||||
|
||||
if (!(_selection < _availItems))
|
||||
_selection = -1;
|
||||
} else
|
||||
_selection = -1;
|
||||
|
||||
for (int i = 0; i < _availItems; i++) {
|
||||
_vm->_gfx->setItemFrame(i, _selection == i ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
MainMenuInputState_BR(Parallaction_br *vm, MenuInputHelper *helper) : MenuInputState("mainmenu", helper), _vm(vm) {
|
||||
memset(_lines, 0, sizeof(_lines));
|
||||
|
||||
_menuStrings = nullptr;
|
||||
_options = nullptr;
|
||||
_availItems = 0;
|
||||
_selection = 0;
|
||||
}
|
||||
|
||||
~MainMenuInputState_BR() override {
|
||||
cleanup();
|
||||
}
|
||||
|
||||
MenuInputState* run() override {
|
||||
int event = _vm->_input->getLastButtonEvent();
|
||||
if (!((event == kMouseLeftUp) && _selection >= 0)) {
|
||||
redrawMenu();
|
||||
return this;
|
||||
}
|
||||
|
||||
int selection = _options[_selection];
|
||||
switch (selection) {
|
||||
case kMenuQuit: {
|
||||
_vm->quitGame();
|
||||
break;
|
||||
}
|
||||
|
||||
case kMenuLoadGame:
|
||||
warning("loadgame not yet implemented");
|
||||
if (!_vm->_saveLoad->loadGame()) {
|
||||
return this;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
_vm->_nextPart = _firstLocation[selection].part;
|
||||
_vm->scheduleLocationSwitch(_firstLocation[selection].location);
|
||||
|
||||
}
|
||||
|
||||
_vm->_system->showMouse(false);
|
||||
cleanup();
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void enter() override {
|
||||
_vm->_gfx->clearScreen();
|
||||
int x = 0, y = 0, i = 0;
|
||||
if (_vm->getPlatform() == Common::kPlatformDOS) {
|
||||
x = 20;
|
||||
y = 50;
|
||||
}
|
||||
_vm->showSlide("tbra", x, y);
|
||||
|
||||
_availItems = 4;
|
||||
|
||||
bool complete[3];
|
||||
_vm->_saveLoad->getGamePartProgress(complete, 3);
|
||||
for (i = 0; i < 3 && complete[i]; i++, _availItems++)
|
||||
;
|
||||
|
||||
if (_vm->getPlatform() == Common::kPlatformAmiga) {
|
||||
_menuStrings = _menuStringsAmiga;
|
||||
_options = _optionsAmiga;
|
||||
} else {
|
||||
_menuStrings = _menuStringsPC;
|
||||
_options = _optionsPC;
|
||||
}
|
||||
|
||||
for (i = 0; i < _availItems; i++) {
|
||||
_lines[i] = new GfxObj(0, renderMenuItem(_menuStrings[i]), "MenuItem");
|
||||
_vm->_gfx->setItem(_lines[i], MENUITEMS_X, MENUITEMS_Y + MENUITEM_HEIGHT * i, 0xFF);
|
||||
}
|
||||
_selection = -1;
|
||||
_vm->_input->setArrowCursor();
|
||||
_vm->_input->setMouseState(MOUSE_ENABLED_SHOW);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
LocationPart MainMenuInputState_BR::_firstLocation[] = {
|
||||
{ 0, "intro" },
|
||||
{ 1, "museo" },
|
||||
{ 2, "start" },
|
||||
{ 3, "bolscoi" },
|
||||
{ 4, "treno" }
|
||||
};
|
||||
|
||||
|
||||
const char *MainMenuInputState_BR::_menuStringsAmiga[NUM_MENULINES] = {
|
||||
"See the introduction",
|
||||
"Load a Saved Game",
|
||||
"Exit to WorkBench",
|
||||
"Start a new game",
|
||||
"Start PART 2",
|
||||
"Start PART 3",
|
||||
"Start PART 4"
|
||||
};
|
||||
|
||||
const MainMenuInputState_BR::MenuOptions MainMenuInputState_BR::_optionsAmiga[NUM_MENULINES] = {
|
||||
kMenuPart0,
|
||||
kMenuLoadGame,
|
||||
kMenuQuit,
|
||||
kMenuPart1,
|
||||
kMenuPart2,
|
||||
kMenuPart3,
|
||||
kMenuPart4
|
||||
};
|
||||
|
||||
const char *MainMenuInputState_BR::_menuStringsPC[NUM_MENULINES] = {
|
||||
"SEE INTRO",
|
||||
"NEW GAME",
|
||||
"SAVED GAME",
|
||||
"EXIT TO DOS",
|
||||
"PART 2",
|
||||
"PART 3",
|
||||
"PART 4"
|
||||
};
|
||||
|
||||
const MainMenuInputState_BR::MenuOptions MainMenuInputState_BR::_optionsPC[NUM_MENULINES] = {
|
||||
kMenuPart0,
|
||||
kMenuPart1,
|
||||
kMenuLoadGame,
|
||||
kMenuQuit,
|
||||
kMenuPart2,
|
||||
kMenuPart3,
|
||||
kMenuPart4
|
||||
};
|
||||
|
||||
void Parallaction_br::startGui(bool showSplash) {
|
||||
_menuHelper = new MenuInputHelper;
|
||||
|
||||
new MainMenuInputState_BR(this, _menuHelper);
|
||||
|
||||
if (showSplash) {
|
||||
new SplashInputState0_BR(this, _menuHelper);
|
||||
new SplashInputState1_BR(this, _menuHelper);
|
||||
_menuHelper->setState("intro0");
|
||||
} else {
|
||||
_menuHelper->setState("mainmenu");
|
||||
}
|
||||
|
||||
_input->_inputMode = Input::kInputModeMenu;
|
||||
}
|
||||
|
||||
|
||||
class IngameMenuInputState_BR : public MenuInputState {
|
||||
Parallaction_br *_vm;
|
||||
GfxObj *_menuObj, *_mscMenuObj, *_sfxMenuObj;
|
||||
int _menuObjId, _mscMenuObjId, _sfxMenuObjId;
|
||||
|
||||
Common::Rect _menuRect;
|
||||
int _cellW, _cellH;
|
||||
|
||||
int _sfxStatus, _mscStatus;
|
||||
|
||||
int frameFromStatus(int status) const {
|
||||
int frame;
|
||||
if (status == 0) {
|
||||
frame = 1;
|
||||
} else
|
||||
if (status == 1) {
|
||||
frame = 0;
|
||||
} else {
|
||||
frame = 2;
|
||||
}
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
public:
|
||||
IngameMenuInputState_BR(Parallaction_br *vm, MenuInputHelper *helper) : MenuInputState("ingamemenu", helper), _vm(vm) {
|
||||
Frames *menuFrames = _vm->_disk->loadFrames("request.win");
|
||||
assert(menuFrames);
|
||||
_menuObj = new GfxObj(kGfxObjTypeMenu, menuFrames, "ingamemenu");
|
||||
|
||||
Frames *mscFrames = _vm->_disk->loadFrames("onoff.win");
|
||||
assert(mscFrames);
|
||||
_mscMenuObj = new GfxObj(kGfxObjTypeMenu, mscFrames, "msc");
|
||||
|
||||
Frames *sfxFrames = _vm->_disk->loadFrames("sfx.win");
|
||||
assert(sfxFrames);
|
||||
_sfxMenuObj = new GfxObj(kGfxObjTypeMenu, sfxFrames, "sfx");
|
||||
|
||||
_menuObj->getRect(0, _menuRect);
|
||||
_cellW = _menuRect.width() / 3;
|
||||
_cellH = _menuRect.height() / 2;
|
||||
|
||||
_menuObjId = _mscMenuObjId = _sfxMenuObjId = 0;
|
||||
_sfxStatus = _mscStatus = 0;
|
||||
}
|
||||
|
||||
~IngameMenuInputState_BR() override {
|
||||
delete _menuObj;
|
||||
delete _mscMenuObj;
|
||||
delete _sfxMenuObj;
|
||||
}
|
||||
|
||||
MenuInputState *run() override {
|
||||
if (_vm->_input->getLastButtonEvent() != kMouseLeftUp) {
|
||||
return this;
|
||||
}
|
||||
|
||||
int cell = -1;
|
||||
|
||||
Common::Point p;
|
||||
_vm->_input->getCursorPos(p);
|
||||
if (_menuRect.contains(p)) {
|
||||
cell = (p.x - _menuRect.left) / _cellW + 3 * ((p.y - _menuRect.top) / _cellH);
|
||||
}
|
||||
|
||||
bool close = false;
|
||||
|
||||
switch (cell) {
|
||||
case 4: // resume
|
||||
case -1: // invalid cell
|
||||
close = true;
|
||||
break;
|
||||
|
||||
case 0: // toggle music
|
||||
if (_mscStatus != -1) {
|
||||
_vm->enableMusic(!_mscStatus);
|
||||
_mscStatus = _vm->getMusicStatus();
|
||||
_vm->_gfx->setItemFrame(_mscMenuObjId, frameFromStatus(_mscStatus));
|
||||
}
|
||||
break;
|
||||
|
||||
case 1: // toggle sfx
|
||||
if (_sfxStatus != -1) {
|
||||
_vm->enableSfx(!_sfxStatus);
|
||||
_sfxStatus = _vm->getSfxStatus();
|
||||
_vm->_gfx->setItemFrame(_sfxMenuObjId, frameFromStatus(_sfxStatus));
|
||||
}
|
||||
break;
|
||||
|
||||
case 2: // save
|
||||
warning("Saving is not supported yet");
|
||||
_vm->_saveLoad->saveGame();
|
||||
break;
|
||||
|
||||
case 3: // load
|
||||
warning("Loading is not supported yet");
|
||||
close = _vm->_saveLoad->loadGame();
|
||||
break;
|
||||
|
||||
case 5: // quit
|
||||
return _helper->getState("quitdialog");
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (close) {
|
||||
_vm->_gfx->freeDialogueObjects();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
_vm->_input->setArrowCursor();
|
||||
return this;
|
||||
}
|
||||
|
||||
void enter() override {
|
||||
// TODO: find the right position of the menu object
|
||||
_menuObjId = _vm->_gfx->setItem(_menuObj, 0, 0, 0);
|
||||
_vm->_gfx->setItemFrame(_menuObjId, 0);
|
||||
|
||||
_mscMenuObjId = _vm->_gfx->setItem(_mscMenuObj, 0, 0, 0);
|
||||
_mscStatus = _vm->getMusicStatus();
|
||||
_vm->_gfx->setItemFrame(_mscMenuObjId, frameFromStatus(_mscStatus));
|
||||
|
||||
_sfxMenuObjId = _vm->_gfx->setItem(_sfxMenuObj, 0, 0, 0);
|
||||
_sfxStatus = _vm->getSfxStatus();
|
||||
_vm->_gfx->setItemFrame(_sfxMenuObjId, frameFromStatus(_sfxStatus));
|
||||
}
|
||||
};
|
||||
|
||||
class QuitDialogInputState_BR : public MenuInputState {
|
||||
Parallaction_br *_vm;
|
||||
Font *_font;
|
||||
int _x, _y;
|
||||
GfxObj *_obj;
|
||||
|
||||
public:
|
||||
QuitDialogInputState_BR(Parallaction_br *vm, MenuInputHelper *helper) : MenuInputState("quitdialog", helper), _vm(vm) {
|
||||
_font = _vm->_dialogueFont;
|
||||
|
||||
const char *question = "Do you really want to quit ?";
|
||||
const char *option = "Yes No";
|
||||
|
||||
int questionW = _font->getStringWidth(question);
|
||||
int optionW = _font->getStringWidth(option);
|
||||
int w = MAX(questionW, optionW) + 30;
|
||||
|
||||
_x = (640 - w) / 2;
|
||||
_y = 90;
|
||||
|
||||
Graphics::Surface *surf = new Graphics::Surface;
|
||||
surf->create(w, 110, Graphics::PixelFormat::createFormatCLUT8());
|
||||
surf->fillRect(Common::Rect(0, 0, w, 110), 12);
|
||||
surf->fillRect(Common::Rect(10, 10, w-10, 100), 15);
|
||||
|
||||
_font->setColor(0);
|
||||
int x = (w - questionW)/2;
|
||||
int y = 13;
|
||||
_font->drawString(surf, x, y, question);
|
||||
x = (w - optionW)/2;
|
||||
y = 13 + _font->height()*2;
|
||||
_font->drawString(surf, x, y, option);
|
||||
|
||||
_obj = new GfxObj(kGfxObjTypeMenu, new SurfaceToFrames(surf), "quitdialog");
|
||||
assert(_obj);
|
||||
}
|
||||
|
||||
~QuitDialogInputState_BR() override {
|
||||
delete _obj;
|
||||
}
|
||||
|
||||
MenuInputState *run() override {
|
||||
uint16 key;
|
||||
bool e = _vm->_input->getLastKeyDown(key);
|
||||
if (!e) {
|
||||
return this;
|
||||
}
|
||||
|
||||
if (key == 'y' || key == 'Y') {
|
||||
_vm->quitGame();
|
||||
return nullptr;
|
||||
} else
|
||||
if (key == 'n' || key == 'N') {
|
||||
// NOTE: when the quit dialog is hidden, the in-game menu is
|
||||
// deleted for a frame, and then redrawn. This is because the
|
||||
// current implementation of graphic 'items' doesn't allow
|
||||
// deletion of a single 'item'.
|
||||
_vm->_gfx->freeDialogueObjects();
|
||||
return _helper->getState("ingamemenu");
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
void enter() override {
|
||||
// setPaletteEntry(1, 0, 0, 0); // text color
|
||||
// setPaletteEntry(15, 255, 255, 255); // background color
|
||||
int id = _vm->_gfx->setItem(_obj, _x, _y, 0);
|
||||
_vm->_gfx->setItemFrame(id, 0);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void Parallaction_br::startIngameMenu() {
|
||||
_menuHelper = new MenuInputHelper;
|
||||
|
||||
new IngameMenuInputState_BR(this, _menuHelper);
|
||||
new QuitDialogInputState_BR(this, _menuHelper);
|
||||
|
||||
_menuHelper->setState("ingamemenu");
|
||||
|
||||
_input->_inputMode = Input::kInputModeMenu;
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace Parallaction
|
||||
1094
engines/parallaction/gui_ns.cpp
Normal file
1094
engines/parallaction/gui_ns.cpp
Normal file
File diff suppressed because it is too large
Load Diff
619
engines/parallaction/input.cpp
Normal file
619
engines/parallaction/input.cpp
Normal file
@@ -0,0 +1,619 @@
|
||||
/* 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/events.h"
|
||||
#include "common/system.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "graphics/cursorman.h"
|
||||
|
||||
#include "parallaction/exec.h"
|
||||
#include "parallaction/input.h"
|
||||
#include "parallaction/parallaction.h"
|
||||
#include "parallaction/debug.h"
|
||||
|
||||
namespace Parallaction {
|
||||
|
||||
#define MOUSEARROW_WIDTH_NS 16
|
||||
#define MOUSEARROW_HEIGHT_NS 16
|
||||
|
||||
#define MOUSECOMBO_WIDTH_NS 32 // sizes for cursor + selected inventory item
|
||||
#define MOUSECOMBO_HEIGHT_NS 32
|
||||
|
||||
struct MouseComboProperties {
|
||||
int _xOffset;
|
||||
int _yOffset;
|
||||
int _width;
|
||||
int _height;
|
||||
};
|
||||
/*
|
||||
// TODO: improve NS's handling of normal cursor before merging cursor code.
|
||||
MouseComboProperties _mouseComboProps_NS = {
|
||||
7, // combo x offset (the icon from the inventory will be rendered from here)
|
||||
7, // combo y offset (ditto)
|
||||
32, // combo (arrow + icon) width
|
||||
32 // combo (arrow + icon) height
|
||||
};
|
||||
*/
|
||||
MouseComboProperties _mouseComboProps_BR = {
|
||||
8, // combo x offset (the icon from the inventory will be rendered from here)
|
||||
8, // combo y offset (ditto)
|
||||
68, // combo (arrow + icon) width
|
||||
68 // combo (arrow + icon) height
|
||||
};
|
||||
|
||||
Input::Input(Parallaction *vm) : _vm(vm) {
|
||||
_gameType = _vm->getGameType();
|
||||
_transCurrentHoverItem = 0;
|
||||
_hasDelayedAction = false; // actived when the character needs to move before taking an action
|
||||
_mouseState = MOUSE_DISABLED;
|
||||
_activeItem._index = 0;
|
||||
_activeItem._id = 0;
|
||||
_mouseButtons = 0;
|
||||
_delayedActionZone.reset();
|
||||
|
||||
_inputMode = 0;
|
||||
_hasKeyPressEvent = false;
|
||||
|
||||
_dinoCursor = nullptr;
|
||||
_dougCursor = nullptr;
|
||||
_donnaCursor = nullptr;
|
||||
_comboArrow = nullptr;
|
||||
_mouseArrow = nullptr;
|
||||
|
||||
initCursors();
|
||||
}
|
||||
|
||||
Input::~Input() {
|
||||
if (_gameType == GType_Nippon) {
|
||||
delete _mouseArrow;
|
||||
}
|
||||
|
||||
delete _comboArrow;
|
||||
delete _dinoCursor;
|
||||
delete _dougCursor;
|
||||
delete _donnaCursor;
|
||||
}
|
||||
|
||||
// FIXME: the engine has 3 event loops. The following routine hosts the main one,
|
||||
// and it's called from 8 different places in the code. There exist 2 more specialised
|
||||
// loops which could possibly be merged into this one with some effort in changing
|
||||
// caller code, i.e. adding condition checks.
|
||||
//
|
||||
void Input::readInput() {
|
||||
bool updateMousePos = false;
|
||||
|
||||
Common::Event e;
|
||||
|
||||
_mouseButtons = kMouseNone;
|
||||
_hasKeyPressEvent = false;
|
||||
|
||||
Common::EventManager *eventMan = _vm->_system->getEventManager();
|
||||
while (eventMan->pollEvent(e)) {
|
||||
updateMousePos = true;
|
||||
|
||||
switch (e.type) {
|
||||
case Common::EVENT_KEYDOWN:
|
||||
_hasKeyPressEvent = true;
|
||||
_keyPressed = e.kbd;
|
||||
|
||||
updateMousePos = false;
|
||||
break;
|
||||
|
||||
case Common::EVENT_LBUTTONDOWN:
|
||||
_mouseButtons = kMouseLeftDown;
|
||||
break;
|
||||
|
||||
case Common::EVENT_LBUTTONUP:
|
||||
_mouseButtons = kMouseLeftUp;
|
||||
break;
|
||||
|
||||
case Common::EVENT_RBUTTONDOWN:
|
||||
_mouseButtons = kMouseRightDown;
|
||||
break;
|
||||
|
||||
case Common::EVENT_RBUTTONUP:
|
||||
_mouseButtons = kMouseRightUp;
|
||||
break;
|
||||
|
||||
case Common::EVENT_RETURN_TO_LAUNCHER:
|
||||
case Common::EVENT_QUIT:
|
||||
return;
|
||||
|
||||
default:
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (updateMousePos) {
|
||||
setCursorPos(e.mouse);
|
||||
}
|
||||
}
|
||||
|
||||
bool Input::getLastKeyDown(uint16 &ascii) {
|
||||
ascii = _keyPressed.ascii;
|
||||
return (_hasKeyPressEvent);
|
||||
}
|
||||
|
||||
// FIXME: see comment for readInput()
|
||||
void Input::waitForButtonEvent(uint32 buttonEventMask, int32 timeout) {
|
||||
|
||||
if (buttonEventMask == kMouseNone) {
|
||||
_mouseButtons = kMouseNone; // don't wait on nothing
|
||||
return;
|
||||
}
|
||||
|
||||
const int32 LOOP_RESOLUTION = 30;
|
||||
if (timeout <= 0) {
|
||||
do {
|
||||
readInput();
|
||||
_vm->_system->delayMillis(LOOP_RESOLUTION);
|
||||
} while ((_mouseButtons & buttonEventMask) == 0);
|
||||
} else {
|
||||
do {
|
||||
readInput();
|
||||
_vm->_system->delayMillis(LOOP_RESOLUTION);
|
||||
timeout -= LOOP_RESOLUTION;
|
||||
} while ((timeout > 0) && (_mouseButtons & buttonEventMask) == 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
int Input::updateGameInput() {
|
||||
|
||||
int event = kEvNone;
|
||||
|
||||
if (!isMouseEnabled() ||
|
||||
(g_engineFlags & kEngineBlockInput) ||
|
||||
(g_engineFlags & kEngineWalking) ||
|
||||
(g_engineFlags & kEngineChangeLocation)) {
|
||||
|
||||
debugC(3, kDebugInput, "updateGameInput: input flags (mouse: %i, block: %i, walking: %i, changeloc: %i)",
|
||||
isMouseEnabled(),
|
||||
(g_engineFlags & kEngineBlockInput) == 0,
|
||||
(g_engineFlags & kEngineWalking) == 0,
|
||||
(g_engineFlags & kEngineChangeLocation) == 0
|
||||
);
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
if (_gameType == GType_Nippon) {
|
||||
if (_hasKeyPressEvent && (_vm->getFeatures() & GF_DEMO) == 0) {
|
||||
if (_keyPressed.keycode == Common::KEYCODE_l) event = kEvLoadGame;
|
||||
if (_keyPressed.keycode == Common::KEYCODE_s) event = kEvSaveGame;
|
||||
}
|
||||
} else
|
||||
if (_gameType == GType_BRA) {
|
||||
if (_hasKeyPressEvent && (_vm->getFeatures() & GF_DEMO) == 0) {
|
||||
if (_keyPressed.keycode == Common::KEYCODE_F5) event = kEvIngameMenu;
|
||||
}
|
||||
} else {
|
||||
error("unsupported gametype in updateGameInput");
|
||||
}
|
||||
|
||||
|
||||
if (event == kEvNone) {
|
||||
translateGameInput();
|
||||
}
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
|
||||
int Input::updateInput() {
|
||||
|
||||
int oldMode = _inputMode;
|
||||
|
||||
int event = kEvNone;
|
||||
readInput();
|
||||
|
||||
switch (_inputMode) {
|
||||
case kInputModeGame:
|
||||
event = updateGameInput();
|
||||
break;
|
||||
|
||||
case kInputModeInventory:
|
||||
updateInventoryInput();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// when mode changes, then consider any input consumed
|
||||
// for the current frame
|
||||
if (oldMode != _inputMode) {
|
||||
_mouseButtons = kEvNone;
|
||||
_hasKeyPressEvent = false;
|
||||
}
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
void Input::trackMouse(ZonePtr z) {
|
||||
if ((z != _hoverZone) && (_hoverZone)) {
|
||||
stopHovering();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!z) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((!_hoverZone) && ((z->_flags & kFlagsNoName) == 0)) {
|
||||
_hoverZone = z;
|
||||
_vm->_gfx->showFloatingLabel(_hoverZone->_label);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void Input::stopHovering() {
|
||||
_hoverZone.reset();
|
||||
_vm->_gfx->hideFloatingLabel();
|
||||
}
|
||||
|
||||
void Input::takeAction(ZonePtr z) {
|
||||
stopHovering();
|
||||
_vm->pauseJobs();
|
||||
_vm->runZone(z);
|
||||
_vm->resumeJobs();
|
||||
}
|
||||
|
||||
void Input::walkTo(const Common::Point &dest) {
|
||||
stopHovering();
|
||||
setArrowCursor();
|
||||
_vm->scheduleWalk(dest.x, dest.y, true);
|
||||
}
|
||||
|
||||
bool Input::translateGameInput() {
|
||||
|
||||
if (g_engineFlags & kEnginePauseJobs) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_hasDelayedAction) {
|
||||
// if walking is over, then take programmed action
|
||||
takeAction(_delayedActionZone);
|
||||
_hasDelayedAction = false;
|
||||
_delayedActionZone.reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_mouseButtons == kMouseRightDown) {
|
||||
// right button down shows inventory
|
||||
enterInventoryMode();
|
||||
return true;
|
||||
}
|
||||
|
||||
Common::Point mousePos;
|
||||
getAbsoluteCursorPos(mousePos);
|
||||
// test if mouse is hovering on an interactive zone for the currently selected inventory item
|
||||
ZonePtr z = _vm->hitZone(_activeItem._id, mousePos.x, mousePos.y);
|
||||
|
||||
if (((_mouseButtons == kMouseLeftUp) && (_activeItem._id == 0) && ((g_engineFlags & kEngineWalking) == 0)) && ((!z) || (ACTIONTYPE(z) != kZoneCommand))) {
|
||||
walkTo(mousePos);
|
||||
return true;
|
||||
}
|
||||
|
||||
trackMouse(z);
|
||||
if (!z) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((_mouseButtons == kMouseLeftUp) && ((_activeItem._id != 0) || (ACTIONTYPE(z) == kZoneCommand))) {
|
||||
|
||||
bool noWalk = z->_flags & kFlagsNoWalk; // check the explicit no-walk flag
|
||||
if (_gameType == GType_BRA) {
|
||||
// action performed on object marked for self-use do not need walk in BRA
|
||||
noWalk |= ((z->_flags & kFlagsYourself) != 0);
|
||||
}
|
||||
|
||||
if (noWalk) {
|
||||
takeAction(z);
|
||||
} else {
|
||||
// action delayed: if Zone defined a moveto position the character is programmed to move there,
|
||||
// else it will move to the mouse position
|
||||
_delayedActionZone = z;
|
||||
_hasDelayedAction = true;
|
||||
if (z->_moveTo.y != 0) {
|
||||
mousePos = z->_moveTo;
|
||||
}
|
||||
|
||||
walkTo(mousePos);
|
||||
}
|
||||
|
||||
_vm->beep();
|
||||
setArrowCursor();
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void Input::enterInventoryMode() {
|
||||
Common::Point mousePos;
|
||||
getAbsoluteCursorPos(mousePos);
|
||||
bool hitCharacter = _vm->hitZone(kZoneYou, mousePos.x, mousePos.y);
|
||||
|
||||
if (hitCharacter) {
|
||||
if (_activeItem._id != 0) {
|
||||
_activeItem._index = (_activeItem._id >> 16) & 0xFFFF;
|
||||
g_engineFlags |= kEngineDragging;
|
||||
} else {
|
||||
setArrowCursor();
|
||||
}
|
||||
}
|
||||
|
||||
stopHovering();
|
||||
_vm->pauseJobs();
|
||||
_vm->openInventory();
|
||||
|
||||
_transCurrentHoverItem = -1;
|
||||
|
||||
_inputMode = kInputModeInventory;
|
||||
}
|
||||
|
||||
void Input::exitInventoryMode() {
|
||||
// right up hides inventory
|
||||
Common::Point mousePos;
|
||||
getCursorPos(mousePos);
|
||||
|
||||
int pos = _vm->getHoverInventoryItem(mousePos.x, mousePos.y);
|
||||
_vm->highlightInventoryItem(-1); // disable
|
||||
|
||||
if ((g_engineFlags & kEngineDragging)) {
|
||||
|
||||
g_engineFlags &= ~kEngineDragging;
|
||||
ZonePtr z = _vm->hitZone(kZoneMerge, _activeItem._index, _vm->getInventoryItemIndex(pos));
|
||||
|
||||
if (z) {
|
||||
_vm->dropItem(z->u._mergeObj1);
|
||||
_vm->dropItem(z->u._mergeObj2);
|
||||
_vm->addInventoryItem(z->u._mergeObj3);
|
||||
_vm->_cmdExec->run(z->_commands); // commands might set a new _inputMode
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
_vm->closeInventory();
|
||||
if (pos == -1) {
|
||||
setArrowCursor();
|
||||
} else {
|
||||
const InventoryItem *item = _vm->getInventoryItem(pos);
|
||||
if (item->_index != 0) {
|
||||
_activeItem._id = item->_id;
|
||||
setInventoryCursor(item->_index);
|
||||
}
|
||||
}
|
||||
_vm->resumeJobs();
|
||||
|
||||
// in case the input mode was not changed by the code above (especially by the commands
|
||||
// executed in case of a merge), then assume we are going back to game mode
|
||||
if (_inputMode == kInputModeInventory) {
|
||||
_inputMode = kInputModeGame;
|
||||
}
|
||||
}
|
||||
|
||||
bool Input::updateInventoryInput() {
|
||||
if (_mouseButtons == kMouseRightUp) {
|
||||
exitInventoryMode();
|
||||
return true;
|
||||
}
|
||||
|
||||
Common::Point mousePos;
|
||||
getCursorPos(mousePos);
|
||||
|
||||
int16 _si = _vm->getHoverInventoryItem(mousePos.x, mousePos.y);
|
||||
if (_si != _transCurrentHoverItem) {
|
||||
_transCurrentHoverItem = _si;
|
||||
_vm->highlightInventoryItem(_si); // enable
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
void Input::setMouseState(MouseTriState state) {
|
||||
assert(state == MOUSE_ENABLED_SHOW || state == MOUSE_ENABLED_HIDE || state == MOUSE_DISABLED);
|
||||
_mouseState = state;
|
||||
|
||||
switch (_mouseState) {
|
||||
case MOUSE_ENABLED_HIDE:
|
||||
case MOUSE_DISABLED:
|
||||
CursorMan.showMouse(false);
|
||||
break;
|
||||
|
||||
case MOUSE_ENABLED_SHOW:
|
||||
CursorMan.showMouse(true);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
MouseTriState Input::getMouseState() {
|
||||
return _mouseState;
|
||||
}
|
||||
|
||||
bool Input::isMouseEnabled() {
|
||||
return (_mouseState == MOUSE_ENABLED_SHOW) || (_mouseState == MOUSE_ENABLED_HIDE);
|
||||
}
|
||||
|
||||
void Input::getAbsoluteCursorPos(Common::Point& p) const {
|
||||
_vm->_gfx->getScrollPos(p);
|
||||
p.x += _mousePos.x;
|
||||
p.y += _mousePos.y;
|
||||
}
|
||||
|
||||
|
||||
void Input::initCursors() {
|
||||
|
||||
_dinoCursor = _donnaCursor = _dougCursor = nullptr;
|
||||
|
||||
switch (_gameType) {
|
||||
case GType_Nippon:
|
||||
_comboArrow = _vm->_disk->loadPointer("pointer");
|
||||
_mouseArrow = new Cnv(1, MOUSEARROW_WIDTH_NS, MOUSEARROW_HEIGHT_NS, _resMouseArrow_NS, false);
|
||||
break;
|
||||
|
||||
case GType_BRA:
|
||||
if (_vm->getPlatform() == Common::kPlatformDOS) {
|
||||
_dinoCursor = _vm->_disk->loadPointer("pointer1");
|
||||
_dougCursor = _vm->_disk->loadPointer("pointer2");
|
||||
_donnaCursor = _vm->_disk->loadPointer("pointer3");
|
||||
|
||||
Graphics::Surface *surf = new Graphics::Surface;
|
||||
surf->create(_mouseComboProps_BR._width, _mouseComboProps_BR._height, Graphics::PixelFormat::createFormatCLUT8());
|
||||
_comboArrow = new SurfaceToFrames(surf);
|
||||
} else {
|
||||
// TODO: Where are the Amiga cursors?
|
||||
Graphics::Surface *surf1 = new Graphics::Surface;
|
||||
surf1->create(_mouseComboProps_BR._width, _mouseComboProps_BR._height, Graphics::PixelFormat::createFormatCLUT8());
|
||||
_comboArrow = new SurfaceToFrames(surf1);
|
||||
|
||||
// TODO: scale mouse cursor (see staticres.cpp)
|
||||
Graphics::Surface *surf2 = new Graphics::Surface;
|
||||
surf2->create(32, 16, Graphics::PixelFormat::createFormatCLUT8());
|
||||
memcpy(surf2->getPixels(), _resMouseArrow_BR_Amiga, 32*16);
|
||||
_mouseArrow = new SurfaceToFrames(surf2);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
warning("Input::initCursors: unknown gametype");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Input::setArrowCursor() {
|
||||
|
||||
switch (_gameType) {
|
||||
case GType_Nippon:
|
||||
debugC(1, kDebugInput, "setting mouse cursor to arrow");
|
||||
// this stuff is needed to avoid artifacts with labels and selected items when switching cursors
|
||||
stopHovering();
|
||||
_activeItem._id = 0;
|
||||
CursorMan.replaceCursor(_mouseArrow->getData(0), MOUSEARROW_WIDTH_NS, MOUSEARROW_HEIGHT_NS, 0, 0, 0);
|
||||
break;
|
||||
|
||||
case GType_BRA: {
|
||||
Common::Rect r;
|
||||
_mouseArrow->getRect(0, r);
|
||||
CursorMan.replaceCursor(_mouseArrow->getData(0), r.width(), r.height(), 0, 0, 0);
|
||||
CursorMan.showMouse(true);
|
||||
_activeItem._id = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
warning("Input::setArrowCursor: unknown gametype");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Input::setInventoryCursor(ItemName name) {
|
||||
assert(name > 0);
|
||||
|
||||
switch (_gameType) {
|
||||
case GType_Nippon: {
|
||||
byte *v8 = _comboArrow->getData(0);
|
||||
// FIXME: destination offseting is not clear
|
||||
_vm->_inventoryRenderer->drawItem(name, v8 + 7 * MOUSECOMBO_WIDTH_NS + 7, MOUSECOMBO_WIDTH_NS);
|
||||
CursorMan.replaceCursor(v8, MOUSECOMBO_WIDTH_NS, MOUSECOMBO_HEIGHT_NS, 0, 0, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
case GType_BRA: {
|
||||
byte *src = _mouseArrow->getData(0);
|
||||
byte *dst = _comboArrow->getData(0);
|
||||
// FIXME: destination offseting is not clear
|
||||
Common::Rect srcRect, dstRect;
|
||||
_mouseArrow->getRect(0, srcRect);
|
||||
_comboArrow->getRect(0, dstRect);
|
||||
for (uint y = 0; y < (uint)srcRect.height(); y++)
|
||||
memcpy(dst + y * dstRect.width(), src + y * srcRect.width(), srcRect.width());
|
||||
_vm->_inventoryRenderer->drawItem(name, dst + _mouseComboProps_BR._yOffset * _mouseComboProps_BR._width + _mouseComboProps_BR._xOffset, _mouseComboProps_BR._width);
|
||||
CursorMan.replaceCursor(dst, _mouseComboProps_BR._width, _mouseComboProps_BR._height, 0, 0, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
warning("Input::setInventoryCursor: unknown gametype");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Input::setMenuPointer() {
|
||||
switch (_gameType) {
|
||||
case GType_Nippon: {
|
||||
error("Input::setMenuPointer not supported for Nippon Safes");
|
||||
}
|
||||
|
||||
case GType_BRA: {
|
||||
if (_vm->getPlatform() == Common::kPlatformDOS) {
|
||||
// Donna's cursor doubles as the cursor used
|
||||
// in the startup menu.
|
||||
_mouseArrow = _donnaCursor;
|
||||
setArrowCursor();
|
||||
} else {
|
||||
warning("Input::setMenuPointer not yet implemented for Amiga");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
warning("Input::setMenuPointer: unknown gametype");
|
||||
}
|
||||
}
|
||||
|
||||
void Input::setCharacterPointer(const char *name) {
|
||||
switch (_gameType) {
|
||||
case GType_Nippon: {
|
||||
error("Input::setCharacterPointer not supported for Nippon Safes");
|
||||
}
|
||||
|
||||
case GType_BRA: {
|
||||
if (_vm->getPlatform() == Common::kPlatformDOS) {
|
||||
if (!scumm_stricmp(name, "dino")) {
|
||||
_mouseArrow = _dinoCursor;
|
||||
} else if (!scumm_stricmp(name, "donna")) {
|
||||
_mouseArrow = _donnaCursor;
|
||||
} else if (!scumm_stricmp(name, "doug")) {
|
||||
_mouseArrow = _dougCursor;
|
||||
}
|
||||
setArrowCursor();
|
||||
} else {
|
||||
warning("Input::setCharacterPointer not yet implemented for Amiga");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
warning("Input::setCharacterPointer: unknown gametype");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Parallaction
|
||||
135
engines/parallaction/input.h
Normal file
135
engines/parallaction/input.h
Normal file
@@ -0,0 +1,135 @@
|
||||
/* 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 PARALLACTION_INPUT_H
|
||||
#define PARALLACTION_INPUT_H
|
||||
|
||||
#include "common/keyboard.h"
|
||||
|
||||
#include "parallaction/objects.h"
|
||||
#include "parallaction/inventory.h"
|
||||
|
||||
namespace Parallaction {
|
||||
|
||||
enum {
|
||||
kMouseNone = 0,
|
||||
kMouseLeftUp = 1,
|
||||
kMouseLeftDown = 2,
|
||||
kMouseRightUp = 4,
|
||||
kMouseRightDown = 8
|
||||
};
|
||||
|
||||
enum MouseTriState {
|
||||
MOUSE_ENABLED_SHOW,
|
||||
MOUSE_ENABLED_HIDE,
|
||||
MOUSE_DISABLED
|
||||
};
|
||||
|
||||
class Input {
|
||||
int updateGameInput();
|
||||
|
||||
bool _hasKeyPressEvent;
|
||||
Common::KeyState _keyPressed;
|
||||
|
||||
bool _hasDelayedAction; // actived when the character needs to move before taking an action
|
||||
ZonePtr _delayedActionZone;
|
||||
|
||||
int16 _transCurrentHoverItem;
|
||||
|
||||
void translateInput();
|
||||
bool translateGameInput();
|
||||
bool updateInventoryInput();
|
||||
void takeAction(ZonePtr z);
|
||||
void walkTo(const Common::Point &dest);
|
||||
|
||||
Parallaction *_vm;
|
||||
|
||||
Common::Point _mousePos;
|
||||
uint16 _mouseButtons;
|
||||
|
||||
ZonePtr _hoverZone;
|
||||
|
||||
void enterInventoryMode();
|
||||
void exitInventoryMode();
|
||||
|
||||
int _gameType;
|
||||
|
||||
static byte _resMouseArrow_NS[256];
|
||||
static byte _resMouseArrow_BR_Amiga[512];
|
||||
Frames *_mouseArrow;
|
||||
Frames *_comboArrow;
|
||||
Frames *_dinoCursor;
|
||||
Frames *_dougCursor;
|
||||
Frames *_donnaCursor;
|
||||
|
||||
void initCursors();
|
||||
|
||||
public:
|
||||
enum {
|
||||
kInputModeGame = 0,
|
||||
kInputModeComment = 1,
|
||||
kInputModeDialogue = 2,
|
||||
kInputModeInventory = 3,
|
||||
kInputModeMenu = 4
|
||||
};
|
||||
|
||||
|
||||
Input(Parallaction *vm);
|
||||
virtual ~Input();
|
||||
|
||||
void getAbsoluteCursorPos(Common::Point& p) const;
|
||||
|
||||
void getCursorPos(Common::Point& p) const {
|
||||
p = _mousePos;
|
||||
}
|
||||
|
||||
void setCursorPos(const Common::Point& p) {
|
||||
_mousePos = p;
|
||||
}
|
||||
|
||||
int _inputMode;
|
||||
InventoryItem _activeItem;
|
||||
|
||||
void readInput();
|
||||
int updateInput();
|
||||
void trackMouse(ZonePtr z);
|
||||
void waitForButtonEvent(uint32 buttonEventMask, int32 timeout = -1);
|
||||
uint32 getLastButtonEvent() { return _mouseButtons; }
|
||||
bool getLastKeyDown(uint16 &ascii);
|
||||
|
||||
void stopHovering();
|
||||
|
||||
MouseTriState _mouseState;
|
||||
|
||||
void setMouseState(MouseTriState state);
|
||||
MouseTriState getMouseState();
|
||||
bool isMouseEnabled();
|
||||
|
||||
void setArrowCursor();
|
||||
void setInventoryCursor(ItemName name);
|
||||
|
||||
void setMenuPointer();
|
||||
void setCharacterPointer(const char *name);
|
||||
};
|
||||
|
||||
} // namespace Parallaction
|
||||
|
||||
#endif
|
||||
395
engines/parallaction/inventory.cpp
Normal file
395
engines/parallaction/inventory.cpp
Normal file
@@ -0,0 +1,395 @@
|
||||
/* 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 "parallaction/input.h"
|
||||
#include "parallaction/parallaction.h"
|
||||
|
||||
#include "common/textconsole.h"
|
||||
|
||||
namespace Parallaction {
|
||||
|
||||
|
||||
/*
|
||||
#define INVENTORYITEM_PITCH 32
|
||||
#define INVENTORYITEM_WIDTH 24
|
||||
#define INVENTORYITEM_HEIGHT 24
|
||||
|
||||
#define INVENTORY_MAX_ITEMS 30
|
||||
|
||||
#define INVENTORY_ITEMS_PER_LINE 5
|
||||
#define INVENTORY_LINES 6
|
||||
|
||||
#define INVENTORY_WIDTH (INVENTORY_ITEMS_PER_LINE*INVENTORYITEM_WIDTH)
|
||||
#define INVENTORY_HEIGHT (INVENTORY_LINES*INVENTORYITEM_HEIGHT)
|
||||
*/
|
||||
|
||||
InventoryItem _verbs_NS[] = {
|
||||
{ 1, kZoneDoor },
|
||||
{ 3, kZoneExamine },
|
||||
{ 2, kZoneGet },
|
||||
{ 4, kZoneSpeak },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
InventoryItem _verbs_BR[] = {
|
||||
{ 1, kZoneBox },
|
||||
{ 2, kZoneGet },
|
||||
{ 3, kZoneExamine },
|
||||
{ 4, kZoneSpeak },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
InventoryProperties _invProps_NS = {
|
||||
32, // INVENTORYITEM_PITCH
|
||||
24, // INVENTORYITEM_WIDTH
|
||||
24, // INVENTORYITEM_HEIGHT
|
||||
30, // INVENTORY_MAX_ITEMS
|
||||
5, // INVENTORY_ITEMS_PER_LINE
|
||||
6, // INVENTORY_LINES
|
||||
5 * 24, // INVENTORY_WIDTH =(INVENTORY_ITEMS_PER_LINE*INVENTORYITEM_WIDTH)
|
||||
6 * 24 // INVENTORY_HEIGHT = (INVENTORY_LINES*INVENTORYITEM_HEIGHT)
|
||||
};
|
||||
|
||||
InventoryProperties _invProps_BR = {
|
||||
51, // INVENTORYITEM_PITCH
|
||||
51, // INVENTORYITEM_WIDTH
|
||||
51, // INVENTORYITEM_HEIGHT
|
||||
48, // INVENTORY_MAX_ITEMS
|
||||
6, // INVENTORY_ITEMS_PER_LINE
|
||||
8, // INVENTORY_LINES
|
||||
6 * 51, // INVENTORY_WIDTH =(INVENTORY_ITEMS_PER_LINE*INVENTORYITEM_WIDTH)
|
||||
8 * 51 // INVENTORY_HEIGHT = (INVENTORY_LINES*INVENTORYITEM_HEIGHT)
|
||||
};
|
||||
|
||||
int16 Parallaction::getHoverInventoryItem(int16 x, int16 y) {
|
||||
return _inventoryRenderer->hitTest(Common::Point(x,y));
|
||||
}
|
||||
|
||||
void Parallaction::highlightInventoryItem(ItemPosition pos) {
|
||||
static ItemPosition lastHighlightedPos = -1;
|
||||
|
||||
if (lastHighlightedPos != -1) {
|
||||
_inventoryRenderer->highlightItem(lastHighlightedPos, 12);
|
||||
}
|
||||
|
||||
if (pos != -1) {
|
||||
_inventoryRenderer->highlightItem(pos, 19);
|
||||
}
|
||||
|
||||
lastHighlightedPos = pos;
|
||||
}
|
||||
|
||||
int Parallaction::addInventoryItem(ItemName item) {
|
||||
return _inventory->addItem(item);
|
||||
}
|
||||
|
||||
int Parallaction::addInventoryItem(ItemName item, uint32 value) {
|
||||
return _inventory->addItem(item, value);
|
||||
}
|
||||
|
||||
void Parallaction::dropItem(uint16 v) {
|
||||
_inventory->removeItem(v);
|
||||
}
|
||||
|
||||
bool Parallaction::isItemInInventory(int32 v) {
|
||||
return (_inventory->findItem(v) != -1);
|
||||
}
|
||||
|
||||
const InventoryItem* Parallaction::getInventoryItem(int16 pos) {
|
||||
return _inventory->getItem(pos);
|
||||
}
|
||||
|
||||
int16 Parallaction::getInventoryItemIndex(int16 pos) {
|
||||
return _inventory->getItemName(pos);
|
||||
}
|
||||
|
||||
void Parallaction_ns::cleanInventory(bool keepVerbs) {
|
||||
_inventory->clear(keepVerbs);
|
||||
}
|
||||
|
||||
void Parallaction_br::cleanInventory(bool keepVerbs) {
|
||||
_dinoInventory->clear(keepVerbs);
|
||||
_donnaInventory->clear(keepVerbs);
|
||||
_dougInventory->clear(keepVerbs);
|
||||
}
|
||||
|
||||
void Parallaction::openInventory() {
|
||||
_inventoryRenderer->showInventory();
|
||||
}
|
||||
|
||||
void Parallaction::closeInventory() {
|
||||
_inventoryRenderer->hideInventory();
|
||||
}
|
||||
|
||||
Inventory *Parallaction_br::findInventory(const char *name) {
|
||||
if (!scumm_stricmp(name, "dino")) {
|
||||
return _dinoInventory;
|
||||
}
|
||||
if (!scumm_stricmp(name, "donna")) {
|
||||
return _donnaInventory;
|
||||
}
|
||||
if (!scumm_stricmp(name, "doug")) {
|
||||
return _dougInventory;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
InventoryRenderer::InventoryRenderer(Parallaction *vm, InventoryProperties *props) : _vm(vm), _props(props) {
|
||||
_surf.create(_props->_width, _props->_height, Graphics::PixelFormat::createFormatCLUT8());
|
||||
}
|
||||
|
||||
InventoryRenderer::~InventoryRenderer() {
|
||||
_surf.free();
|
||||
}
|
||||
|
||||
void InventoryRenderer::setInventory(Inventory *inventory) {
|
||||
_inv = inventory;
|
||||
}
|
||||
|
||||
void InventoryRenderer::showInventory() {
|
||||
if (!_inv)
|
||||
error("InventoryRenderer not bound to inventory");
|
||||
|
||||
uint16 lines = getNumLines();
|
||||
|
||||
Common::Point p;
|
||||
_vm->_input->getCursorPos(p);
|
||||
|
||||
_pos.x = CLIP((int)(p.x - (_props->_width / 2)), 0, (int)(_vm->_screenWidth - _props->_width));
|
||||
_pos.y = CLIP((int)(p.y - 2 - (lines * _props->_itemHeight)), 0, (int)(_vm->_screenHeight - lines * _props->_itemHeight));
|
||||
|
||||
refresh();
|
||||
}
|
||||
|
||||
void InventoryRenderer::hideInventory() {
|
||||
if (!_inv)
|
||||
error("InventoryRenderer not bound to inventory");
|
||||
}
|
||||
|
||||
void InventoryRenderer::getRect(Common::Rect& r) const {
|
||||
r.setWidth(_props->_width);
|
||||
r.setHeight(_props->_itemHeight * getNumLines());
|
||||
r.moveTo(_pos);
|
||||
}
|
||||
|
||||
ItemPosition InventoryRenderer::hitTest(const Common::Point &p) const {
|
||||
Common::Rect r;
|
||||
getRect(r);
|
||||
if (!r.contains(p))
|
||||
return -1;
|
||||
|
||||
return ((p.x - _pos.x) / _props->_itemWidth) + (_props->_itemsPerLine * ((p.y - _pos.y) / _props->_itemHeight));
|
||||
}
|
||||
|
||||
void InventoryRenderer::drawItem(ItemPosition pos, ItemName name) {
|
||||
Common::Rect r;
|
||||
getItemRect(pos, r);
|
||||
byte* d = (byte *)_surf.getBasePtr(r.left, r.top);
|
||||
drawItem(name, d, _surf.pitch);
|
||||
}
|
||||
|
||||
void InventoryRenderer::drawItem(ItemName name, byte *buffer, uint pitch) {
|
||||
byte* s = _vm->_objects->getData(name);
|
||||
byte* d = buffer;
|
||||
for (uint i = 0; i < _props->_itemHeight; i++) {
|
||||
memcpy(d, s, _props->_itemWidth);
|
||||
|
||||
s += _props->_itemPitch;
|
||||
d += pitch;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int16 InventoryRenderer::getNumLines() const {
|
||||
int16 num = _inv->getNumItems();
|
||||
return (num / _props->_itemsPerLine) + ((num % _props->_itemsPerLine) > 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
|
||||
void InventoryRenderer::refresh() {
|
||||
for (uint16 i = 0; i < _props->_maxItems; i++) {
|
||||
ItemName name = _inv->getItemName(i);
|
||||
drawItem(i, name);
|
||||
}
|
||||
}
|
||||
|
||||
void InventoryRenderer::highlightItem(ItemPosition pos, byte color) {
|
||||
if (pos == -1)
|
||||
return;
|
||||
|
||||
Common::Rect r;
|
||||
getItemRect(pos, r);
|
||||
|
||||
if (color != 12)
|
||||
color = 19;
|
||||
|
||||
_surf.frameRect(r, color);
|
||||
}
|
||||
|
||||
void InventoryRenderer::getItemRect(ItemPosition pos, Common::Rect &r) {
|
||||
|
||||
r.setHeight(_props->_itemHeight);
|
||||
r.setWidth(_props->_itemWidth);
|
||||
|
||||
uint16 line = pos / _props->_itemsPerLine;
|
||||
uint16 col = pos % _props->_itemsPerLine;
|
||||
|
||||
r.moveTo(col * _props->_itemWidth, line * _props->_itemHeight);
|
||||
|
||||
}
|
||||
|
||||
Inventory::Inventory(int maxItems, InventoryItem *verbs) : _numItems(0), _maxItems(maxItems) {
|
||||
_items = (InventoryItem *)calloc(_maxItems, sizeof(InventoryItem));
|
||||
|
||||
int i = 0;
|
||||
for ( ; verbs[i]._id; i++) {
|
||||
addItem(verbs[i]._id, verbs[i]._index);
|
||||
}
|
||||
_numVerbs = i;
|
||||
}
|
||||
|
||||
|
||||
Inventory::~Inventory() {
|
||||
free(_items);
|
||||
}
|
||||
|
||||
ItemPosition Inventory::addItem(ItemName name, uint32 value) {
|
||||
debugC(1, kDebugInventory, "addItem(%i, %i)", name, value);
|
||||
|
||||
if (_numItems == _maxItems) {
|
||||
debugC(3, kDebugInventory, "addItem: inventory is full");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// NOTE: items whose name == 0 aren't really inventory items,
|
||||
// but the engine expects the inventory to accept them as valid.
|
||||
// This nasty trick has been discovered because of regression
|
||||
// after r29060.
|
||||
if (name == 0)
|
||||
return 0;
|
||||
|
||||
_items[_numItems]._id = value;
|
||||
_items[_numItems]._index = name;
|
||||
|
||||
_numItems++;
|
||||
|
||||
debugC(3, kDebugInventory, "addItem: done");
|
||||
|
||||
return _numItems;
|
||||
}
|
||||
|
||||
ItemPosition Inventory::addItem(ItemName name) {
|
||||
return addItem(name, MAKE_INVENTORY_ID(name));
|
||||
}
|
||||
|
||||
ItemPosition Inventory::findItem(ItemName name) const {
|
||||
for (ItemPosition slot = 0; slot < _numItems; slot++) {
|
||||
if (name == _items[slot]._index)
|
||||
return slot;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void Inventory::removeItem(ItemName name) {
|
||||
debugC(1, kDebugInventory, "removeItem(%i)", name);
|
||||
|
||||
ItemPosition pos = findItem(name);
|
||||
if (pos == -1) {
|
||||
debugC(3, kDebugInventory, "removeItem: can't find item, nothing to remove");
|
||||
return;
|
||||
}
|
||||
|
||||
_numItems--;
|
||||
|
||||
if (_numItems != pos) {
|
||||
memmove(&_items[pos], &_items[pos+1], (_numItems - pos) * sizeof(InventoryItem));
|
||||
}
|
||||
|
||||
_items[_numItems]._id = 0;
|
||||
_items[_numItems]._index = 0;
|
||||
|
||||
debugC(3, kDebugInventory, "removeItem: item removed");
|
||||
}
|
||||
|
||||
void Inventory::clear(bool keepVerbs) {
|
||||
debugC(1, kDebugInventory, "clearInventory()");
|
||||
|
||||
uint first = (keepVerbs ? _numVerbs : 0);
|
||||
|
||||
for (uint16 slot = first; slot < _numItems; slot++) {
|
||||
_items[slot]._id = 0;
|
||||
_items[slot]._index = 0;
|
||||
}
|
||||
|
||||
_numItems = first;
|
||||
}
|
||||
|
||||
|
||||
ItemName Inventory::getItemName(ItemPosition pos) const {
|
||||
return (pos >= 0 && pos < _maxItems) ? _items[pos]._index : 0;
|
||||
}
|
||||
|
||||
const InventoryItem* Inventory::getItem(ItemPosition pos) const {
|
||||
return &_items[pos];
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Parallaction_ns::initInventory() {
|
||||
_inventory = new Inventory(_invProps_NS._maxItems, _verbs_NS);
|
||||
assert(_inventory);
|
||||
_inventoryRenderer = new InventoryRenderer(this, &_invProps_NS);
|
||||
assert(_inventoryRenderer);
|
||||
_inventoryRenderer->setInventory(_inventory);
|
||||
}
|
||||
|
||||
void Parallaction_br::initInventory() {
|
||||
_inventoryRenderer = new InventoryRenderer(this, &_invProps_BR);
|
||||
assert(_inventoryRenderer);
|
||||
|
||||
_dinoInventory = new Inventory(_invProps_BR._maxItems, _verbs_BR);
|
||||
_donnaInventory = new Inventory(_invProps_BR._maxItems, _verbs_BR);
|
||||
_dougInventory = new Inventory(_invProps_BR._maxItems, _verbs_BR);
|
||||
}
|
||||
|
||||
void Parallaction_ns::destroyInventory() {
|
||||
delete _inventoryRenderer;
|
||||
delete _inventory;
|
||||
_inventory = nullptr;
|
||||
_inventoryRenderer = nullptr;
|
||||
}
|
||||
|
||||
void Parallaction_br::destroyInventory() {
|
||||
delete _inventoryRenderer;
|
||||
_inventory = nullptr;
|
||||
_inventoryRenderer = nullptr;
|
||||
|
||||
delete _dinoInventory;
|
||||
delete _donnaInventory;
|
||||
delete _dougInventory;
|
||||
_dinoInventory = nullptr;
|
||||
_donnaInventory = nullptr;
|
||||
_dougInventory = nullptr;
|
||||
}
|
||||
|
||||
|
||||
} // namespace Parallaction
|
||||
120
engines/parallaction/inventory.h
Normal file
120
engines/parallaction/inventory.h
Normal file
@@ -0,0 +1,120 @@
|
||||
/* 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 PARALLACTION_INVENTORY_H
|
||||
#define PARALLACTION_INVENTORY_H
|
||||
|
||||
|
||||
#include "graphics/surface.h"
|
||||
|
||||
namespace Parallaction {
|
||||
|
||||
class Parallaction;
|
||||
|
||||
struct InventoryItem {
|
||||
uint32 _id; // object name (lowest 16 bits are always zero)
|
||||
uint16 _index; // index to frame in objs file
|
||||
};
|
||||
|
||||
struct InventoryProperties {
|
||||
uint _itemPitch;
|
||||
uint _itemWidth;
|
||||
uint _itemHeight;
|
||||
|
||||
int _maxItems;
|
||||
|
||||
int _itemsPerLine;
|
||||
int _maxLines;
|
||||
|
||||
int _width;
|
||||
int _height;
|
||||
};
|
||||
|
||||
#define MAKE_INVENTORY_ID(x) (((x) & 0xFFFF) << 16)
|
||||
|
||||
typedef int16 ItemPosition;
|
||||
typedef uint16 ItemName;
|
||||
|
||||
class Inventory {
|
||||
|
||||
protected:
|
||||
uint16 _numVerbs;
|
||||
|
||||
InventoryItem *_items;
|
||||
uint16 _numItems;
|
||||
int _maxItems;
|
||||
|
||||
public:
|
||||
Inventory(int maxItems, InventoryItem *verbs);
|
||||
virtual ~Inventory();
|
||||
|
||||
ItemPosition addItem(ItemName name, uint32 value);
|
||||
ItemPosition addItem(ItemName item);
|
||||
void removeItem(ItemName name);
|
||||
void clear(bool keepVerbs = true);
|
||||
|
||||
const InventoryItem* getItem(ItemPosition pos) const;
|
||||
ItemName getItemName(ItemPosition pos) const;
|
||||
|
||||
ItemPosition findItem(ItemName name) const;
|
||||
|
||||
int16 getNumItems() const { return _numItems; }
|
||||
};
|
||||
|
||||
|
||||
|
||||
class InventoryRenderer {
|
||||
Parallaction *_vm;
|
||||
InventoryProperties *_props;
|
||||
|
||||
Inventory *_inv;
|
||||
Common::Point _pos;
|
||||
|
||||
Graphics::Surface _surf;
|
||||
|
||||
protected:
|
||||
void getItemRect(ItemPosition pos, Common::Rect &r);
|
||||
|
||||
void drawItem(ItemPosition pos, ItemName name);
|
||||
void refresh();
|
||||
|
||||
public:
|
||||
InventoryRenderer(Parallaction *vm, InventoryProperties *props);
|
||||
virtual ~InventoryRenderer();
|
||||
|
||||
void setInventory(Inventory *inventory);
|
||||
|
||||
void showInventory();
|
||||
void hideInventory();
|
||||
|
||||
ItemPosition hitTest(const Common::Point &p) const;
|
||||
void highlightItem(ItemPosition pos, byte color);
|
||||
void drawItem(ItemName name, byte *buffer, uint pitch);
|
||||
|
||||
byte *getData() { return (byte *)_surf.getPixels(); }
|
||||
|
||||
void getRect(Common::Rect &r) const;
|
||||
int16 getNumLines() const;
|
||||
};
|
||||
|
||||
} // namespace Parallaction
|
||||
|
||||
#endif
|
||||
220
engines/parallaction/metaengine.cpp
Normal file
220
engines/parallaction/metaengine.cpp
Normal file
@@ -0,0 +1,220 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "base/plugins.h"
|
||||
|
||||
#include "common/config-manager.h"
|
||||
#include "engines/advancedDetector.h"
|
||||
#include "common/system.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/translation.h"
|
||||
|
||||
#include "backends/keymapper/action.h"
|
||||
#include "backends/keymapper/keymap.h"
|
||||
#include "backends/keymapper/standard-actions.h"
|
||||
|
||||
#include "parallaction/parallaction.h"
|
||||
#include "parallaction/detection.h"
|
||||
|
||||
namespace Parallaction {
|
||||
|
||||
int Parallaction::getGameType() const { return _gameDescription->gameType; }
|
||||
uint32 Parallaction::getFeatures() const { return _gameDescription->features; }
|
||||
Common::Language Parallaction::getLanguage() const { return _gameDescription->desc.language; }
|
||||
Common::Platform Parallaction::getPlatform() const { return _gameDescription->desc.platform; }
|
||||
|
||||
} // End of namespace Parallaction
|
||||
|
||||
#ifdef USE_TTS
|
||||
|
||||
static const ADExtraGuiOptionsMap optionsList[] = {
|
||||
{
|
||||
GAMEOPTION_TTS,
|
||||
{
|
||||
_s("Enable Text to Speech"),
|
||||
_s("Use TTS to read the descriptions (if TTS is available)"),
|
||||
"tts_enabled",
|
||||
false,
|
||||
0,
|
||||
0
|
||||
}
|
||||
},
|
||||
|
||||
AD_EXTRA_GUI_OPTIONS_TERMINATOR
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
class ParallactionMetaEngine : public AdvancedMetaEngine<Parallaction::PARALLACTIONGameDescription> {
|
||||
public:
|
||||
const char *getName() const override {
|
||||
return "parallaction";
|
||||
}
|
||||
|
||||
#ifdef USE_TTS
|
||||
const ADExtraGuiOptionsMap *getAdvancedExtraGuiOptions() const override {
|
||||
return optionsList;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool hasFeature(MetaEngineFeature f) const override;
|
||||
Common::Error createInstance(OSystem *syst, Engine **engine, const Parallaction::PARALLACTIONGameDescription *desc) const override;
|
||||
Common::KeymapArray initKeymaps(const char *target) const override;
|
||||
|
||||
SaveStateList listSaves(const char *target) const override;
|
||||
int getMaximumSaveSlot() const override;
|
||||
bool removeSaveState(const char *target, int slot) const override;
|
||||
Common::String getSavegameFile(int saveGameIdx, const char *target) const override {
|
||||
if (!target)
|
||||
target = getName();
|
||||
const Common::String prefix = ConfMan.getDomain(target)->getVal("gameid");
|
||||
if (saveGameIdx == kSavegameFilePattern)
|
||||
return prefix + ".###";
|
||||
else
|
||||
return prefix + Common::String::format(".%03d", saveGameIdx);
|
||||
}
|
||||
};
|
||||
|
||||
bool ParallactionMetaEngine::hasFeature(MetaEngineFeature f) const {
|
||||
return
|
||||
(f == kSupportsListSaves) ||
|
||||
(f == kSimpleSavesNames) ||
|
||||
(f == kSupportsDeleteSave);
|
||||
}
|
||||
|
||||
bool Parallaction::Parallaction::hasFeature(EngineFeature f) const {
|
||||
return
|
||||
(f == kSupportsReturnToLauncher);
|
||||
}
|
||||
|
||||
Common::Error ParallactionMetaEngine::createInstance(OSystem *syst, Engine **engine, const Parallaction::PARALLACTIONGameDescription *gd) const {
|
||||
switch (gd->gameType) {
|
||||
case Parallaction::GType_Nippon:
|
||||
*engine = new Parallaction::Parallaction_ns(syst, gd);
|
||||
break;
|
||||
case Parallaction::GType_BRA:
|
||||
*engine = new Parallaction::Parallaction_br(syst, gd);
|
||||
break;
|
||||
default:
|
||||
return Common::kUnsupportedGameidError;
|
||||
}
|
||||
|
||||
return Common::kNoError;
|
||||
}
|
||||
|
||||
Common::KeymapArray ParallactionMetaEngine::initKeymaps(const char *target) const {
|
||||
using namespace Common;
|
||||
|
||||
Common::String gameId = ConfMan.get("gameid", target);
|
||||
Keymap *engineKeymap;
|
||||
|
||||
if (gameId == "nippon")
|
||||
engineKeymap = new Keymap(Keymap::kKeymapTypeGame, "parallaction", "Nippon Safes, Inc.");
|
||||
else if (gameId == "bra")
|
||||
engineKeymap = new Keymap(Keymap::kKeymapTypeGame, "parallaction", "The Big Red Adventure");
|
||||
else
|
||||
return AdvancedMetaEngine::initKeymaps(target);
|
||||
|
||||
Action *act;
|
||||
|
||||
act = new Action(kStandardActionLeftClick, _("Left click"));
|
||||
act->setLeftClickEvent();
|
||||
act->addDefaultInputMapping("MOUSE_LEFT");
|
||||
act->addDefaultInputMapping("JOY_A");
|
||||
engineKeymap->addAction(act);
|
||||
|
||||
act = new Action(kStandardActionRightClick, _("Right click"));
|
||||
act->setRightClickEvent();
|
||||
act->addDefaultInputMapping("MOUSE_RIGHT");
|
||||
act->addDefaultInputMapping("JOY_B");
|
||||
engineKeymap->addAction(act);
|
||||
|
||||
if (gameId == "nippon") {
|
||||
act = new Action(kStandardActionLoad, _("Load"));
|
||||
act->setKeyEvent(KeyState(KEYCODE_l, 'l'));
|
||||
act->addDefaultInputMapping("l");
|
||||
act->addDefaultInputMapping("JOY_LEFT_SHOULDER");
|
||||
engineKeymap->addAction(act);
|
||||
|
||||
act = new Action(kStandardActionSave, _("Save"));
|
||||
act->setKeyEvent(KeyState(KEYCODE_s, 's'));
|
||||
act->addDefaultInputMapping("s");
|
||||
act->addDefaultInputMapping("JOY_RIGHT_SHOULDER");
|
||||
engineKeymap->addAction(act);
|
||||
} else if (gameId == "bra") {
|
||||
act = new Action(kStandardActionOpenMainMenu, _("Game menu"));
|
||||
act->setKeyEvent(KeyState(KEYCODE_F5, ASCII_F5));
|
||||
act->addDefaultInputMapping("F5");
|
||||
act->addDefaultInputMapping("JOY_LEFT_SHOULDER");
|
||||
engineKeymap->addAction(act);
|
||||
|
||||
act = new Action("YES", _("Yes"));
|
||||
act->setKeyEvent(KeyState(KEYCODE_y, 'y'));
|
||||
act->addDefaultInputMapping("y");
|
||||
act->addDefaultInputMapping("JOY_Y");
|
||||
engineKeymap->addAction(act);
|
||||
|
||||
act = new Action("NO", _("No"));
|
||||
act->setKeyEvent(KeyState(KEYCODE_n, 'n'));
|
||||
act->addDefaultInputMapping("n");
|
||||
act->addDefaultInputMapping("JOY_X");
|
||||
engineKeymap->addAction(act);
|
||||
}
|
||||
|
||||
return Keymap::arrayOf(engineKeymap);
|
||||
}
|
||||
|
||||
SaveStateList ParallactionMetaEngine::listSaves(const char *target) const {
|
||||
Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
|
||||
|
||||
Common::StringArray filenames = saveFileMan->listSavefiles(getSavegameFilePattern(target));
|
||||
|
||||
SaveStateList saveList;
|
||||
for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); ++file) {
|
||||
// Obtain the last 2 digits of the filename, since they correspond to the save slot
|
||||
int slotNum = atoi(file->c_str() + file->size() - 2);
|
||||
|
||||
if (slotNum >= 0 && slotNum <= 99) {
|
||||
Common::InSaveFile *in = saveFileMan->openForLoading(*file);
|
||||
if (in) {
|
||||
Common::String saveDesc = in->readLine();
|
||||
saveList.push_back(SaveStateDescriptor(this, slotNum, saveDesc));
|
||||
delete in;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort saves based on slot number.
|
||||
Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
|
||||
return saveList;
|
||||
}
|
||||
|
||||
int ParallactionMetaEngine::getMaximumSaveSlot() const { return 99; }
|
||||
|
||||
bool ParallactionMetaEngine::removeSaveState(const char *target, int slot) const {
|
||||
return g_system->getSavefileManager()->removeSavefile(getSavegameFile(slot, target));
|
||||
}
|
||||
|
||||
#if PLUGIN_ENABLED_DYNAMIC(PARALLACTION)
|
||||
REGISTER_PLUGIN_DYNAMIC(PARALLACTION, PLUGIN_TYPE_ENGINE, ParallactionMetaEngine);
|
||||
#else
|
||||
REGISTER_PLUGIN_STATIC(PARALLACTION, PLUGIN_TYPE_ENGINE, ParallactionMetaEngine);
|
||||
#endif
|
||||
46
engines/parallaction/module.mk
Normal file
46
engines/parallaction/module.mk
Normal file
@@ -0,0 +1,46 @@
|
||||
MODULE := engines/parallaction
|
||||
|
||||
MODULE_OBJS := \
|
||||
adlib.o \
|
||||
balloons.o \
|
||||
callables_br.o \
|
||||
callables_ns.o \
|
||||
debug.o \
|
||||
dialogue.o \
|
||||
disk_br.o \
|
||||
disk_ns.o \
|
||||
exec.o \
|
||||
exec_br.o \
|
||||
exec_ns.o \
|
||||
font.o \
|
||||
gfxbase.o \
|
||||
graphics.o \
|
||||
gui.o \
|
||||
gui_br.o \
|
||||
gui_ns.o \
|
||||
input.o \
|
||||
inventory.o \
|
||||
metaengine.o \
|
||||
objects.o \
|
||||
parallaction.o \
|
||||
parallaction_br.o \
|
||||
parallaction_ns.o \
|
||||
parser.o \
|
||||
parser_br.o \
|
||||
parser_ns.o \
|
||||
saveload.o \
|
||||
sound_ns.o \
|
||||
sound_br.o \
|
||||
staticres.o \
|
||||
walk.o
|
||||
|
||||
# This module can be built as a plugin
|
||||
ifeq ($(ENABLE_PARALLACTION), DYNAMIC_PLUGIN)
|
||||
PLUGIN := 1
|
||||
endif
|
||||
|
||||
# Include common rules
|
||||
include $(srcdir)/rules.mk
|
||||
|
||||
# Detection objects
|
||||
DETECT_OBJS += $(MODULE)/detection.o
|
||||
458
engines/parallaction/objects.cpp
Normal file
458
engines/parallaction/objects.cpp
Normal file
@@ -0,0 +1,458 @@
|
||||
/* 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/textconsole.h"
|
||||
|
||||
#include "parallaction/parallaction.h"
|
||||
#include "parallaction/objects.h"
|
||||
#include "parallaction/parser.h"
|
||||
|
||||
namespace Parallaction {
|
||||
|
||||
|
||||
Command::Command() {
|
||||
_id = 0;
|
||||
_flagsOn = 0;
|
||||
_flagsOff = 0;
|
||||
_valid = false;
|
||||
|
||||
_flags = 0;
|
||||
_callable = 0;
|
||||
_object = 0;
|
||||
_counterValue = 0;
|
||||
_zeta0 = 0;
|
||||
_zeta1 = 0;
|
||||
_zeta2 = 0;
|
||||
_musicCommand = 0;
|
||||
_musicParm = 0;
|
||||
}
|
||||
|
||||
Animation::Animation() {
|
||||
gfxobj = nullptr;
|
||||
_frame = 0;
|
||||
_z = 0;
|
||||
}
|
||||
|
||||
Animation::~Animation() {
|
||||
if (gfxobj) {
|
||||
gfxobj->release();
|
||||
}
|
||||
}
|
||||
|
||||
void Animation::getFrameRect(Common::Rect &r) const {
|
||||
r.setWidth(0); r.setHeight(0);
|
||||
if (!gfxobj) {
|
||||
return;
|
||||
}
|
||||
gfxobj->getRect(_frame, r);
|
||||
r.translate(_left, _top);
|
||||
}
|
||||
|
||||
bool Animation::hitFrameRect(int x, int y) const {
|
||||
if (!gfxobj) {
|
||||
return false;
|
||||
}
|
||||
Common::Rect r;
|
||||
getFrameRect(r);
|
||||
return r.contains(x, y);
|
||||
}
|
||||
|
||||
int16 Animation::getBottom() const {
|
||||
int bottom = _top;
|
||||
if (gfxobj) {
|
||||
Common::Rect r;
|
||||
getFrameRect(r);
|
||||
bottom = r.bottom;
|
||||
}
|
||||
return bottom;
|
||||
}
|
||||
|
||||
void Animation::resetZ() {
|
||||
setZ(getBottom());
|
||||
}
|
||||
|
||||
uint16 Animation::getFrameNum() const {
|
||||
if (!gfxobj) return 0;
|
||||
return gfxobj->getNum();
|
||||
}
|
||||
|
||||
byte* Animation::getFrameData() const {
|
||||
if (!gfxobj) return nullptr;
|
||||
return gfxobj->getData(_frame);
|
||||
}
|
||||
|
||||
void Animation::setF(int16 value) {
|
||||
int16 min = MIN(0, getFrameNum() - 1);
|
||||
int16 max = MAX(0, getFrameNum() - 1);
|
||||
_frame = CLIP(value, min, max);
|
||||
}
|
||||
|
||||
void Animation::forceXYZF(int16 x, int16 y, int16 z, int16 f) {
|
||||
_left = x;
|
||||
_top = y;
|
||||
_z = z;
|
||||
_frame = f;
|
||||
}
|
||||
|
||||
void Animation::getFoot(Common::Point &foot) {
|
||||
Common::Rect rect;
|
||||
gfxobj->getRect(_frame, rect);
|
||||
foot.x = getX() + (rect.left + rect.width() / 2);
|
||||
foot.y = getY() + (rect.top + rect.height());
|
||||
}
|
||||
|
||||
void Animation::setFoot(const Common::Point &foot) {
|
||||
Common::Rect rect;
|
||||
gfxobj->getRect(_frame, rect);
|
||||
|
||||
setX(foot.x - (rect.left + rect.width() / 2));
|
||||
setY(foot.y - (rect.top + rect.height()));
|
||||
}
|
||||
|
||||
#define NUM_LOCALS 10
|
||||
char _localNames[NUM_LOCALS][10];
|
||||
|
||||
Program::Program() {
|
||||
_loopCounter = 0;
|
||||
_locals = new LocalVariable[NUM_LOCALS];
|
||||
_numLocals = 0;
|
||||
_status = kProgramIdle;
|
||||
_ip = 0;
|
||||
_loopStart = 0;
|
||||
}
|
||||
|
||||
Program::~Program() {
|
||||
delete[] _locals;
|
||||
}
|
||||
|
||||
int16 Program::findLocal(const char* name) {
|
||||
for (uint16 _si = 0; _si < NUM_LOCALS; _si++) {
|
||||
if (!scumm_stricmp(name, _localNames[_si]))
|
||||
return _si;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int16 Program::addLocal(const char *name, int16 value, int16 min, int16 max) {
|
||||
assert(_numLocals < NUM_LOCALS);
|
||||
|
||||
Common::strlcpy(_localNames[_numLocals], name, 10);
|
||||
_locals[_numLocals].setRange(min, max);
|
||||
_locals[_numLocals].setValue(value);
|
||||
|
||||
return _numLocals++;
|
||||
}
|
||||
|
||||
void LocalVariable::setValue(int16 value) {
|
||||
if (value >= _max)
|
||||
value = _min;
|
||||
if (value < _min)
|
||||
value = _max - 1;
|
||||
|
||||
_value = value;
|
||||
}
|
||||
|
||||
void LocalVariable::setRange(int16 min, int16 max) {
|
||||
_max = max;
|
||||
_min = min;
|
||||
}
|
||||
|
||||
int16 LocalVariable::getValue() const {
|
||||
return _value;
|
||||
}
|
||||
|
||||
|
||||
Zone::Zone() {
|
||||
_left = _top = _right = _bottom = 0;
|
||||
|
||||
_type = 0;
|
||||
|
||||
_flags = kFlagsNoName;
|
||||
_label = nullptr;
|
||||
|
||||
// BRA specific
|
||||
_index = INVALID_ZONE_INDEX;
|
||||
_locationIndex = INVALID_LOCATION_INDEX;
|
||||
}
|
||||
|
||||
Zone::~Zone() {
|
||||
g_vm->_gfx->unregisterLabel(_label);
|
||||
delete _label;
|
||||
}
|
||||
|
||||
void Zone::translate(int16 x, int16 y) {
|
||||
_left += x;
|
||||
_right += x;
|
||||
_top += y;
|
||||
_bottom += y;
|
||||
}
|
||||
|
||||
bool Zone::hitRect(int x, int y) const {
|
||||
// The scripts of Nippon Safes are full of invalid rectangles, used to
|
||||
// provide 'special' features.
|
||||
if (_right < _left || _bottom < _top) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Common::Rect r(_left, _top, _right + 1, _bottom + 1);
|
||||
r.grow(-1);
|
||||
|
||||
return r.contains(x, y);
|
||||
}
|
||||
|
||||
Dialogue::Dialogue() {
|
||||
memset(_questions, 0, sizeof(_questions));
|
||||
_numQuestions = 0;
|
||||
}
|
||||
|
||||
Dialogue::~Dialogue() {
|
||||
for (int i = 0; i < NUM_QUESTIONS; i++) {
|
||||
delete _questions[i];
|
||||
}
|
||||
}
|
||||
|
||||
Question *Dialogue::findQuestion(const Common::String &name) const {
|
||||
for (uint i = 0; _questions[i]; ++i) {
|
||||
if (_questions[i]->_name == name) {
|
||||
return _questions[i];
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Dialogue::addQuestion(Question *q) {
|
||||
assert(_numQuestions < NUM_QUESTIONS);
|
||||
assert(q);
|
||||
_questions[_numQuestions] = q;
|
||||
_numQuestions++;
|
||||
}
|
||||
|
||||
Answer::Answer() {
|
||||
_mood = 0;
|
||||
_noFlags = 0;
|
||||
_yesFlags = 0;
|
||||
_hasCounterCondition = false;
|
||||
_counterValue = 0;
|
||||
_counterOp = 0;
|
||||
}
|
||||
|
||||
bool Answer::textIsNull() {
|
||||
return (_text.equalsIgnoreCase("NULL"));
|
||||
}
|
||||
|
||||
int Answer::speakerMood() {
|
||||
return _mood & 0xF;
|
||||
}
|
||||
|
||||
Question::Question(const Common::String &name) : _name(name), _mood(0) {
|
||||
memset(_answers, 0, sizeof(_answers));
|
||||
}
|
||||
|
||||
Question::~Question() {
|
||||
for (uint32 i = 0; i < NUM_ANSWERS; i++) {
|
||||
delete _answers[i];
|
||||
}
|
||||
}
|
||||
|
||||
bool Question::textIsNull() {
|
||||
return (_text.equalsIgnoreCase("NULL"));
|
||||
}
|
||||
|
||||
int Question::speakerMood() {
|
||||
return _mood & 0xF;
|
||||
}
|
||||
|
||||
int Question::balloonWinding() {
|
||||
return _mood & 0x10;
|
||||
}
|
||||
|
||||
|
||||
Instruction::Instruction() {
|
||||
_index = 0;
|
||||
_flags = 0;
|
||||
|
||||
// common
|
||||
_immediate = 0;
|
||||
_endif = 0;
|
||||
|
||||
// BRA specific
|
||||
_y = 0;
|
||||
}
|
||||
|
||||
int16 ScriptVar::getValue() {
|
||||
|
||||
if (_flags & kParaImmediate) {
|
||||
return _value;
|
||||
}
|
||||
|
||||
if (_flags & kParaLocal) {
|
||||
return _local->getValue();
|
||||
}
|
||||
|
||||
if (_flags & kParaField) {
|
||||
return _field->getValue();
|
||||
}
|
||||
|
||||
if (_flags & kParaRandom) {
|
||||
return (g_vm->_rnd.getRandomNumber(65536) * _value) >> 16;
|
||||
}
|
||||
|
||||
error("Parameter is not an r-value");
|
||||
return 0; // for compilers that don't support NORETURN
|
||||
}
|
||||
|
||||
void ScriptVar::setValue(int16 value) {
|
||||
if ((_flags & kParaLValue) == 0) {
|
||||
error("Only l-value can be set");
|
||||
}
|
||||
|
||||
if (_flags & kParaLocal) {
|
||||
_local->setValue(value);
|
||||
}
|
||||
|
||||
if (_flags & kParaField) {
|
||||
_field->setValue(value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ScriptVar::setLocal(LocalVariable *local) {
|
||||
_local = local;
|
||||
_flags |= (kParaLocal | kParaLValue);
|
||||
}
|
||||
|
||||
void ScriptVar::setField(Animation *anim, AnimationField::AccessorFunc accessor, AnimationField::MutatorFunc mutator) {
|
||||
_field = new AnimationField(anim, accessor, mutator);
|
||||
_flags |= (kParaField | kParaLValue);
|
||||
}
|
||||
|
||||
void ScriptVar::setField(Animation *anim, AnimationField::AccessorFunc accessor) {
|
||||
_field = new AnimationField(anim, accessor);
|
||||
_flags |= kParaField;
|
||||
}
|
||||
|
||||
void ScriptVar::setImmediate(int16 value) {
|
||||
_value = value;
|
||||
_flags |= kParaImmediate;
|
||||
}
|
||||
|
||||
void ScriptVar::setRandom(int16 seed) {
|
||||
_value = seed;
|
||||
_flags |= kParaRandom;
|
||||
}
|
||||
|
||||
|
||||
ScriptVar::ScriptVar() {
|
||||
_flags = 0;
|
||||
_local = nullptr;
|
||||
_value = 0;
|
||||
_field = nullptr;
|
||||
}
|
||||
|
||||
ScriptVar::~ScriptVar() {
|
||||
delete _field;
|
||||
}
|
||||
|
||||
|
||||
Table::Table(uint32 size) : _size(size), _used(0), _disposeMemory(true) {
|
||||
_data = (char**)calloc(size, sizeof(char *));
|
||||
}
|
||||
|
||||
Table::Table(uint32 size, const char **data) : _size(size), _used(size), _disposeMemory(false) {
|
||||
_data = const_cast<char **>(data);
|
||||
}
|
||||
|
||||
Table::~Table() {
|
||||
|
||||
if (!_disposeMemory) return;
|
||||
|
||||
clear();
|
||||
|
||||
free(_data);
|
||||
|
||||
}
|
||||
|
||||
void Table::addData(const char* s) {
|
||||
|
||||
if (!(_used < _size))
|
||||
error("Table overflow");
|
||||
|
||||
size_t ln = strlen(s) + 1;
|
||||
char *data = (char *)malloc(ln);
|
||||
Common::strcpy_s(data, ln, s);
|
||||
_data[_used++] = data;
|
||||
}
|
||||
|
||||
uint16 Table::lookup(const char* s) {
|
||||
|
||||
for (uint16 i = 0; i < _used; i++) {
|
||||
if (!scumm_stricmp(_data[i], s)) return i + 1;
|
||||
}
|
||||
|
||||
return notFound;
|
||||
}
|
||||
|
||||
void Table::clear() {
|
||||
for (uint32 i = 0; i < _used; i++)
|
||||
free(_data[i]);
|
||||
|
||||
_used = 0;
|
||||
}
|
||||
|
||||
const char *Table::item(uint index) const {
|
||||
assert(index < _used);
|
||||
return _data[index];
|
||||
}
|
||||
|
||||
|
||||
FixedTable::FixedTable(uint32 size, uint32 fixed) : Table(size), _numFixed(fixed) {
|
||||
}
|
||||
|
||||
void FixedTable::clear() {
|
||||
uint32 deleted = 0;
|
||||
for (uint32 i = _numFixed; i < _used; i++) {
|
||||
free(_data[i]);
|
||||
_data[i] = nullptr;
|
||||
deleted++;
|
||||
}
|
||||
|
||||
_used -= deleted;
|
||||
}
|
||||
|
||||
Table* createTableFromStream(uint32 size, Common::SeekableReadStream *stream) {
|
||||
assert(stream);
|
||||
|
||||
Table *t = new Table(size);
|
||||
assert(t);
|
||||
|
||||
Script s(stream, false);
|
||||
s.readLineToken();
|
||||
while (scumm_stricmp(_tokens[0], "ENDTABLE")) {
|
||||
t->addData(_tokens[0]);
|
||||
s.readLineToken();
|
||||
}
|
||||
delete stream;
|
||||
return t;
|
||||
}
|
||||
|
||||
|
||||
} // namespace Parallaction
|
||||
548
engines/parallaction/objects.h
Normal file
548
engines/parallaction/objects.h
Normal file
@@ -0,0 +1,548 @@
|
||||
/* 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 PARALLACTION_ZONE_H
|
||||
#define PARALLACTION_ZONE_H
|
||||
|
||||
#include "common/list.h"
|
||||
#include "common/ptr.h"
|
||||
|
||||
#include "parallaction/graphics.h"
|
||||
|
||||
|
||||
namespace Parallaction {
|
||||
|
||||
struct Zone;
|
||||
struct Animation;
|
||||
struct Command;
|
||||
struct Question;
|
||||
struct Answer;
|
||||
struct Instruction;
|
||||
struct Program;
|
||||
|
||||
typedef Common::SharedPtr<Zone> ZonePtr;
|
||||
typedef Common::List<ZonePtr> ZoneList;
|
||||
|
||||
typedef Common::SharedPtr<Animation> AnimationPtr;
|
||||
typedef Common::List<AnimationPtr> AnimationList;
|
||||
|
||||
typedef Common::SharedPtr<Instruction> InstructionPtr;
|
||||
typedef Common::Array<InstructionPtr> InstructionList;
|
||||
|
||||
typedef Common::List<Common::Point> PointList;
|
||||
|
||||
enum ZoneTypes {
|
||||
kZoneExamine = 1, // zone displays comment if activated
|
||||
kZoneDoor = 2, // zone activated on click (after some walk if needed)
|
||||
kZoneGet = 3, // for pickable items
|
||||
kZoneMerge = 4, // tags items which can be merged in inventory
|
||||
kZoneTaste = 5, // NEVER USED
|
||||
kZoneHear = 6, // NEVER USED: they ran out of time before integrating sfx
|
||||
kZoneFeel = 7, // NEVER USED
|
||||
kZoneSpeak = 8, // tags NPCs the character can talk with
|
||||
kZoneNone = 9, // used to prevent parsing on peculiar Animations
|
||||
kZoneTrap = 10, // zone activated when character enters
|
||||
kZoneYou = 11, // marks the character
|
||||
kZoneCommand = 12,
|
||||
|
||||
// BRA specific
|
||||
kZonePath = 13, // defines nodes for assisting walk calculation routines
|
||||
kZoneBox = 14
|
||||
};
|
||||
|
||||
|
||||
enum ZoneFlags {
|
||||
kFlagsClosed = 1, // Zone: door is closed / switch is off
|
||||
kFlagsActive = 2, // Zone/Animation: object is visible
|
||||
kFlagsRemove = 4, // Zone/Animation: object is soon to be removed
|
||||
kFlagsActing = 8, // Animation: script execution is active
|
||||
kFlagsLocked = 0x10, // Zone: door or switch cannot be toggled
|
||||
kFlagsFixed = 0x20, // Zone: Zone item cannot be picked up
|
||||
kFlagsNoName = 0x40, // Zone with no name (used to prevent some kEvEnterZone events)
|
||||
kFlagsNoMasked = 0x80, // Animation is to be drawn ignoring z buffer
|
||||
kFlagsLooping = 0x100, // Animation: script is to be executed repeatedly
|
||||
kFlagsAdded = 0x200, // NEVER USED in Nippon Safes
|
||||
kFlagsCharacter = 0x400, //
|
||||
kFlagsNoWalk = 0x800, // Zone: character doesn't need to walk towards object to interact
|
||||
|
||||
// BRA specific
|
||||
kFlagsYourself = 0x1000, // BRA: marks zones used by the character on him/herself
|
||||
kFlagsScaled = 0x2000,
|
||||
kFlagsSelfuse = 0x4000, // BRA: marks zones to be preserved across location changes (see Parallaction::freeZones)
|
||||
kFlagsIsAnimation = 0x1000000, // BRA: used in walk code (trap check), to tell is a Zone is an Animation
|
||||
kFlagsAnimLinked = 0x2000000
|
||||
};
|
||||
|
||||
|
||||
enum CommandFlags : uint {
|
||||
kFlagsAll = 0xFFFFFFFFU,
|
||||
|
||||
kFlagsVisited = 1,
|
||||
kFlagsExit = 0x10000000,
|
||||
kFlagsEnter = 0x20000000,
|
||||
kFlagsGlobal = 0x40000000,
|
||||
|
||||
// BRA specific
|
||||
kFlagsTestTrue = 2
|
||||
};
|
||||
|
||||
struct Command {
|
||||
uint16 _id;
|
||||
uint32 _flagsOn;
|
||||
uint32 _flagsOff;
|
||||
bool _valid;
|
||||
|
||||
Command();
|
||||
|
||||
// Common fields
|
||||
uint32 _flags;
|
||||
ZonePtr _zone;
|
||||
Common::String _zoneName;
|
||||
Common::String _string;
|
||||
uint16 _callable;
|
||||
uint16 _object;
|
||||
Common::Point _move;
|
||||
|
||||
// BRA specific
|
||||
Common::Point _startPos;
|
||||
Common::Point _startPos2;
|
||||
Common::String _counterName;
|
||||
int _counterValue;
|
||||
int _zeta0;
|
||||
int _zeta1;
|
||||
int _zeta2;
|
||||
Common::String _characterName;
|
||||
Common::String _string2;
|
||||
int _musicCommand;
|
||||
int _musicParm;
|
||||
};
|
||||
|
||||
typedef Common::SharedPtr<Command> CommandPtr;
|
||||
typedef Common::List<CommandPtr> CommandList;
|
||||
|
||||
|
||||
#define NUM_QUESTIONS 40
|
||||
#define NUM_ANSWERS 20
|
||||
|
||||
struct Answer {
|
||||
Common::String _text;
|
||||
uint16 _mood;
|
||||
Common::String _followingName;
|
||||
|
||||
CommandList _commands;
|
||||
uint32 _noFlags;
|
||||
uint32 _yesFlags;
|
||||
|
||||
// BRA specific
|
||||
bool _hasCounterCondition;
|
||||
Common::String _counterName;
|
||||
int _counterValue;
|
||||
int _counterOp;
|
||||
|
||||
Answer();
|
||||
bool textIsNull();
|
||||
int speakerMood();
|
||||
};
|
||||
|
||||
struct Question {
|
||||
Common::String _name;
|
||||
Common::String _text;
|
||||
uint16 _mood;
|
||||
Answer* _answers[NUM_ANSWERS];
|
||||
|
||||
Question(const Common::String &name);
|
||||
~Question();
|
||||
bool textIsNull();
|
||||
int speakerMood();
|
||||
int balloonWinding();
|
||||
};
|
||||
|
||||
struct Dialogue {
|
||||
Question *_questions[NUM_QUESTIONS];
|
||||
uint _numQuestions;
|
||||
|
||||
Question *findQuestion(const Common::String &name) const;
|
||||
void addQuestion(Question *q);
|
||||
|
||||
Dialogue();
|
||||
~Dialogue();
|
||||
};
|
||||
|
||||
#define MAX_WALKPOINT_LISTS 20
|
||||
#define FREE_HEAR_CHANNEL -1
|
||||
#define MUSIC_HEAR_CHANNEL -2
|
||||
|
||||
struct TypeData {
|
||||
// common
|
||||
GfxObj *_gfxobj; // get, examine, door
|
||||
Common::String _filename; // speak, examine, hear
|
||||
|
||||
// get
|
||||
uint32 _getIcon;
|
||||
|
||||
// speak
|
||||
Dialogue *_speakDialogue;
|
||||
|
||||
// examine
|
||||
Common::String _examineText;
|
||||
|
||||
// door
|
||||
Common::String _doorLocation;
|
||||
Common::Point _doorStartPos;
|
||||
uint16 _doorStartFrame;
|
||||
Common::Point _doorStartPos2_br;
|
||||
uint16 _doorStartFrame2_br;
|
||||
|
||||
// hear
|
||||
int _hearChannel;
|
||||
int _hearFreq;
|
||||
|
||||
// merge
|
||||
uint32 _mergeObj1;
|
||||
uint32 _mergeObj2;
|
||||
uint32 _mergeObj3;
|
||||
|
||||
// path
|
||||
int _pathNumLists;
|
||||
PointList _pathLists[MAX_WALKPOINT_LISTS];
|
||||
|
||||
TypeData() {
|
||||
_gfxobj = 0;
|
||||
_getIcon = 0;
|
||||
_speakDialogue = 0;
|
||||
_doorStartFrame = 0;
|
||||
_doorStartPos.x = -1000;
|
||||
_doorStartPos.y = -1000;
|
||||
_doorStartFrame2_br = 0;
|
||||
_doorStartPos2_br.x = -1000;
|
||||
_doorStartPos2_br.y = -1000;
|
||||
_hearChannel = FREE_HEAR_CHANNEL;
|
||||
_hearFreq = -1;
|
||||
_mergeObj1 = 0;
|
||||
_mergeObj2 = 0;
|
||||
_mergeObj3 = 0;
|
||||
_pathNumLists = 0;
|
||||
}
|
||||
|
||||
~TypeData() {
|
||||
if (_gfxobj) {
|
||||
_gfxobj->release();
|
||||
}
|
||||
delete _speakDialogue;
|
||||
}
|
||||
};
|
||||
|
||||
#define ACTIONTYPE(z) ((z)->_type & 0xFFFF)
|
||||
#define ITEMTYPE(z) ((z)->_type & 0xFFFF0000)
|
||||
|
||||
#define PACK_ZONETYPE(zt,it) (((zt) & 0xFFFF) | (((it) & 0xFFFF) << 16))
|
||||
|
||||
#define ZONENAME_LENGTH 32
|
||||
|
||||
#define INVALID_LOCATION_INDEX ((uint32)-1)
|
||||
#define INVALID_ZONE_INDEX ((uint32)-1)
|
||||
|
||||
struct Zone {
|
||||
private:
|
||||
int16 _right;
|
||||
int16 _bottom;
|
||||
|
||||
protected:
|
||||
int16 _left;
|
||||
int16 _top;
|
||||
|
||||
public:
|
||||
char _name[ZONENAME_LENGTH];
|
||||
|
||||
uint32 _type;
|
||||
uint32 _flags;
|
||||
GfxObj *_label;
|
||||
|
||||
TypeData u;
|
||||
CommandList _commands;
|
||||
Common::Point _moveTo;
|
||||
|
||||
// BRA specific
|
||||
uint _index;
|
||||
uint _locationIndex;
|
||||
Common::String _linkedName;
|
||||
AnimationPtr _linkedAnim;
|
||||
|
||||
Zone();
|
||||
virtual ~Zone();
|
||||
|
||||
void translate(int16 x, int16 y);
|
||||
|
||||
bool hitRect(int x, int y) const;
|
||||
|
||||
void setRect(int16 left, int16 top, int16 right, int16 bottom) {
|
||||
setX(left);
|
||||
setY(top);
|
||||
_right = right;
|
||||
_bottom = bottom;
|
||||
}
|
||||
|
||||
void getRect(Common::Rect& r) {
|
||||
r.left = _left; r.right = _right;
|
||||
r.top = _top; r.bottom = _bottom;
|
||||
}
|
||||
|
||||
|
||||
// getters/setters
|
||||
virtual int16 getX() { return _left; }
|
||||
virtual void setX(int16 value) { _left = value; }
|
||||
|
||||
virtual int16 getY() { return _top; }
|
||||
virtual void setY(int16 value) { _top = value; }
|
||||
};
|
||||
|
||||
|
||||
struct LocalVariable {
|
||||
protected:
|
||||
int16 _value;
|
||||
int16 _min;
|
||||
int16 _max;
|
||||
|
||||
public:
|
||||
|
||||
LocalVariable() {
|
||||
_value = 0;
|
||||
_min = -10000;
|
||||
_max = 10000;
|
||||
}
|
||||
|
||||
void setRange(int16 min, int16 max);
|
||||
|
||||
int16 getValue() const;
|
||||
void setValue(int16 value);
|
||||
};
|
||||
|
||||
|
||||
enum ParaFlags {
|
||||
kParaImmediate = 1, // instruction is using an immediate parameter
|
||||
kParaLocal = 2, // instruction is using a local variable
|
||||
kParaField = 0x10, // instruction is using an animation's field
|
||||
kParaRandom = 0x100,
|
||||
|
||||
kParaLValue = 0x20
|
||||
};
|
||||
|
||||
|
||||
struct AnimationField {
|
||||
typedef Common::Functor0Mem<int16, Animation> Accessor;
|
||||
typedef Common::Functor1Mem<int16, void, Animation> Mutator;
|
||||
|
||||
typedef Accessor::FuncType AccessorFunc;
|
||||
typedef Mutator::FuncType MutatorFunc;
|
||||
|
||||
protected:
|
||||
Accessor *_accessor;
|
||||
Mutator *_mutator;
|
||||
|
||||
public:
|
||||
AnimationField(Animation* instance, AccessorFunc accessor, MutatorFunc mutator) {
|
||||
_accessor = new Accessor(instance, accessor);
|
||||
_mutator = new Mutator(instance, mutator);
|
||||
}
|
||||
|
||||
AnimationField(Animation* instance, AccessorFunc accessor) {
|
||||
_accessor = new Accessor(instance, accessor);
|
||||
_mutator = 0;
|
||||
}
|
||||
|
||||
~AnimationField() {
|
||||
delete _accessor;
|
||||
delete _mutator;
|
||||
}
|
||||
|
||||
int16 getValue() const {
|
||||
assert(_accessor);
|
||||
return (*_accessor)();
|
||||
}
|
||||
|
||||
void setValue(int16 value) {
|
||||
assert(_mutator);
|
||||
(*_mutator)(value);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct ScriptVar {
|
||||
uint32 _flags;
|
||||
|
||||
int16 _value;
|
||||
LocalVariable* _local;
|
||||
AnimationField* _field;
|
||||
|
||||
ScriptVar();
|
||||
~ScriptVar();
|
||||
|
||||
int16 getValue();
|
||||
void setValue(int16 value);
|
||||
|
||||
void setLocal(LocalVariable *local);
|
||||
void setField(Animation *anim, AnimationField::AccessorFunc accessor, AnimationField::MutatorFunc mutator);
|
||||
void setField(Animation *anim, AnimationField::AccessorFunc accessor);
|
||||
void setImmediate(int16 value);
|
||||
void setRandom(int16 seed);
|
||||
};
|
||||
|
||||
enum InstructionFlags {
|
||||
kInstMod = 4,
|
||||
kInstMaskedPut = 8,
|
||||
kInstUnk20 = 0x20
|
||||
};
|
||||
|
||||
|
||||
struct Instruction {
|
||||
uint32 _index;
|
||||
uint32 _flags;
|
||||
|
||||
// common
|
||||
AnimationPtr _a;
|
||||
ZonePtr _z;
|
||||
int16 _immediate;
|
||||
ScriptVar _opA;
|
||||
ScriptVar _opB;
|
||||
|
||||
// BRA specific
|
||||
byte _colors[3];
|
||||
ScriptVar _opC;
|
||||
Common::String _text;
|
||||
Common::String _text2;
|
||||
int _y;
|
||||
uint32 _endif;
|
||||
|
||||
Instruction();
|
||||
};
|
||||
|
||||
enum {
|
||||
kProgramIdle, // awaiting execution
|
||||
kProgramRunning, // running
|
||||
kProgramDone // execution completed
|
||||
};
|
||||
|
||||
struct Program {
|
||||
AnimationPtr _anim;
|
||||
LocalVariable *_locals;
|
||||
|
||||
uint16 _loopCounter;
|
||||
uint16 _numLocals;
|
||||
|
||||
uint32 _ip;
|
||||
uint32 _loopStart;
|
||||
InstructionList _instructions;
|
||||
|
||||
uint32 _status;
|
||||
|
||||
Program();
|
||||
~Program();
|
||||
|
||||
int16 findLocal(const char* name);
|
||||
int16 addLocal(const char *name, int16 value = 0, int16 min = -10000, int16 max = 10000);
|
||||
};
|
||||
|
||||
typedef Common::SharedPtr<Program> ProgramPtr;
|
||||
typedef Common::List<ProgramPtr> ProgramList;
|
||||
|
||||
struct Animation : public Zone {
|
||||
protected:
|
||||
int16 _frame;
|
||||
int16 _z;
|
||||
public:
|
||||
|
||||
GfxObj *gfxobj;
|
||||
Common::String _scriptName;
|
||||
|
||||
Animation();
|
||||
~Animation() override;
|
||||
uint16 getFrameNum() const;
|
||||
byte* getFrameData() const;
|
||||
|
||||
void resetZ();
|
||||
bool hitFrameRect(int x, int y) const;
|
||||
void getFrameRect(Common::Rect &r) const;
|
||||
int16 getBottom() const;
|
||||
|
||||
// HACK: this routine is only used to download initialisation
|
||||
// parameter to a script used when moving sarcophagi around in
|
||||
// the museum. It bypasses all the consistency checks that
|
||||
// can be performed by the individual setters. See the comment
|
||||
// in startMovingSarcophagus() in callables_ns.cpp
|
||||
void forceXYZF(int16 x, int16 y, int16 z, int16 f);
|
||||
|
||||
// getters/setters used by scripts
|
||||
int16 getX() override { return _left; }
|
||||
void setX(int16 value) override { _left = value; }
|
||||
|
||||
int16 getY() override { return _top; }
|
||||
void setY(int16 value) override { _top = value; }
|
||||
|
||||
int16 getZ() { return _z; }
|
||||
void setZ(int16 value) { _z = value; }
|
||||
|
||||
int16 getF() { return _frame; }
|
||||
void setF(int16 value);
|
||||
|
||||
void getFoot(Common::Point &foot);
|
||||
void setFoot(const Common::Point &foot);
|
||||
};
|
||||
|
||||
class Table {
|
||||
|
||||
protected:
|
||||
char **_data;
|
||||
uint16 _size;
|
||||
uint16 _used;
|
||||
bool _disposeMemory;
|
||||
|
||||
public:
|
||||
Table(uint32 size);
|
||||
Table(uint32 size, const char** data);
|
||||
|
||||
virtual ~Table();
|
||||
|
||||
enum {
|
||||
notFound = 0
|
||||
};
|
||||
|
||||
uint count() const { return _used; }
|
||||
const char *item(uint index) const;
|
||||
|
||||
virtual void addData(const char* s);
|
||||
virtual void clear();
|
||||
virtual uint16 lookup(const char* s);
|
||||
};
|
||||
|
||||
class FixedTable : public Table {
|
||||
|
||||
uint16 _numFixed;
|
||||
|
||||
public:
|
||||
FixedTable(uint32 size, uint32 fixed);
|
||||
void clear() override;
|
||||
};
|
||||
|
||||
Table* createTableFromStream(uint32 size, Common::SeekableReadStream *stream);
|
||||
|
||||
} // namespace Parallaction
|
||||
|
||||
#endif
|
||||
1057
engines/parallaction/parallaction.cpp
Normal file
1057
engines/parallaction/parallaction.cpp
Normal file
File diff suppressed because it is too large
Load Diff
697
engines/parallaction/parallaction.h
Normal file
697
engines/parallaction/parallaction.h
Normal file
@@ -0,0 +1,697 @@
|
||||
/* 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 PARALLACTION_PARALLACTION_H
|
||||
#define PARALLACTION_PARALLACTION_H
|
||||
|
||||
#include "common/str.h"
|
||||
#include "common/stack.h"
|
||||
#include "common/array.h"
|
||||
#include "common/func.h"
|
||||
#include "common/random.h"
|
||||
#include "common/savefile.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/text-to-speech.h"
|
||||
|
||||
#include "engines/engine.h"
|
||||
|
||||
#include "parallaction/input.h"
|
||||
#include "parallaction/inventory.h"
|
||||
#include "parallaction/objects.h"
|
||||
#include "parallaction/disk.h"
|
||||
#include "parallaction/detection.h"
|
||||
|
||||
#define PATH_LEN 200
|
||||
|
||||
|
||||
/**
|
||||
* This is the namespace of the Parallaction engine.
|
||||
*
|
||||
* Status of this engine: ???
|
||||
*
|
||||
* Games using this engine:
|
||||
* - Nippon Safes Inc. (complete)
|
||||
* - The Big Red Adventure (work in progress)
|
||||
*/
|
||||
namespace Parallaction {
|
||||
|
||||
enum {
|
||||
kDebugDisk = 1,
|
||||
kDebugWalk,
|
||||
kDebugParser,
|
||||
kDebugDialogue,
|
||||
kDebugGraphics,
|
||||
kDebugExec,
|
||||
kDebugInput,
|
||||
kDebugAudio,
|
||||
kDebugMenu,
|
||||
kDebugInventory,
|
||||
};
|
||||
|
||||
enum EngineFlags {
|
||||
kEnginePauseJobs = (1 << 1),
|
||||
kEngineWalking = (1 << 3),
|
||||
kEngineChangeLocation = (1 << 4),
|
||||
kEngineBlockInput = (1 << 5),
|
||||
kEngineDragging = (1 << 6),
|
||||
kEngineTransformedDonna = (1 << 7),
|
||||
|
||||
// BRA specific
|
||||
kEngineReturn = (1 << 10)
|
||||
};
|
||||
|
||||
enum {
|
||||
kEvNone = 0,
|
||||
kEvSaveGame = 2000,
|
||||
kEvLoadGame = 4000,
|
||||
kEvIngameMenu = 8000
|
||||
};
|
||||
|
||||
enum LanguageIndex {
|
||||
kItalian = 0,
|
||||
kFrench = 1,
|
||||
kEnglish = 2,
|
||||
kGerman = 3
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
extern uint32 g_engineFlags;
|
||||
extern char g_saveData1[];
|
||||
extern uint32 g_globalFlags;
|
||||
extern const char *g_dinoName;
|
||||
extern const char *g_donnaName;
|
||||
extern const char *g_doughName;
|
||||
extern const char *g_drkiName;
|
||||
extern const char *g_minidinoName;
|
||||
extern const char *g_minidonnaName;
|
||||
extern const char *g_minidoughName;
|
||||
extern const char *g_minidrkiName;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class Debugger;
|
||||
class Gfx;
|
||||
class Input;
|
||||
class DialogueManager;
|
||||
class MenuInputHelper;
|
||||
class PathWalker_NS;
|
||||
class PathWalker_BR;
|
||||
class CommandExec;
|
||||
class ProgramExec;
|
||||
class SoundMan;
|
||||
class SoundMan_ns;
|
||||
class SoundMan_br;
|
||||
class LocationParser_ns;
|
||||
class LocationParser_br;
|
||||
class ProgramParser_ns;
|
||||
class ProgramParser_br;
|
||||
class BalloonManager;
|
||||
|
||||
struct Location {
|
||||
|
||||
Common::Point _startPosition;
|
||||
uint16 _startFrame;
|
||||
char _name[100];
|
||||
|
||||
CommandList _aCommands;
|
||||
CommandList _commands;
|
||||
Common::String _comment;
|
||||
Common::String _endComment;
|
||||
|
||||
ZoneList _zones;
|
||||
AnimationList _animations;
|
||||
ProgramList _programs;
|
||||
|
||||
bool _hasSound;
|
||||
char _soundFile[50];
|
||||
|
||||
// NS specific
|
||||
PointList _walkPoints;
|
||||
Common::String _slideText[2];
|
||||
|
||||
// BRA specific
|
||||
int _zeta0;
|
||||
int _zeta1;
|
||||
int _zeta2;
|
||||
CommandList _escapeCommands;
|
||||
Common::Point _followerStartPosition;
|
||||
uint16 _followerStartFrame;
|
||||
|
||||
|
||||
protected:
|
||||
int _gameType;
|
||||
|
||||
bool keepZone_br(ZonePtr z);
|
||||
bool keepZone_ns(ZonePtr z);
|
||||
bool keepAnimation_ns(AnimationPtr a);
|
||||
bool keepAnimation_br(AnimationPtr a);
|
||||
|
||||
template<class T>
|
||||
void freeList(Common::List<T> &list, bool removeAll, Common::MemFunc1<bool, T, Location> filter);
|
||||
|
||||
public:
|
||||
Location(int gameType);
|
||||
~Location();
|
||||
|
||||
AnimationPtr findAnimation(const char *name);
|
||||
ZonePtr findZone(const char *name);
|
||||
|
||||
void cleanup(bool removeAll);
|
||||
void freeZones(bool removeAll);
|
||||
|
||||
int getScale(int z) const;
|
||||
};
|
||||
|
||||
|
||||
class CharacterName {
|
||||
const char *_prefix;
|
||||
const char *_suffix;
|
||||
bool _dummy;
|
||||
char _name[30];
|
||||
char _baseName[30];
|
||||
char _fullName[30];
|
||||
static const char _prefixMini[];
|
||||
static const char _suffixTras[];
|
||||
static const char _empty[];
|
||||
void dummify();
|
||||
public:
|
||||
CharacterName();
|
||||
CharacterName(const char *name);
|
||||
void bind(const char *name);
|
||||
const char *getName() const;
|
||||
const char *getBaseName() const;
|
||||
const char *getFullName() const;
|
||||
bool dummy() const;
|
||||
};
|
||||
|
||||
|
||||
struct Character {
|
||||
AnimationPtr _ani;
|
||||
GfxObj *_head;
|
||||
GfxObj *_talk;
|
||||
|
||||
Character();
|
||||
|
||||
protected:
|
||||
CharacterName _name;
|
||||
|
||||
public:
|
||||
void setName(const char *name);
|
||||
const char *getName() const;
|
||||
const char *getBaseName() const;
|
||||
const char *getFullName() const;
|
||||
bool dummy() const;
|
||||
};
|
||||
|
||||
#ifdef USE_TTS
|
||||
|
||||
struct CharacterVoiceData {
|
||||
const char *characterName;
|
||||
uint8 voiceID;
|
||||
bool male;
|
||||
};
|
||||
|
||||
static const CharacterVoiceData characterVoiceDatas[] = {
|
||||
{ nullptr, 0, true }, // Used as the narrator for cutscene text
|
||||
{ "dough", 1, true },
|
||||
{ "donna", 0, false },
|
||||
{ "dino", 2, true },
|
||||
{ "drki", 3, true },
|
||||
{ "ddd.talk", 1, true },
|
||||
{ nullptr, 0, false }, // Donna in ddd.talk
|
||||
{ nullptr, 2, true }, // Dino in ddd.talk
|
||||
{ "police.talk", 4, true },
|
||||
{ "vecchio.talk", 5, true },
|
||||
{ "karaoke.talk", 6, true },
|
||||
{ "suicida.talk", 7, true },
|
||||
{ "direttore.talk", 8, true },
|
||||
{ "guardiano.talk", 9, true },
|
||||
{ "sento.talk", 10, true },
|
||||
{ "giocatore.talk", 11, true },
|
||||
{ "mona.talk", 12, true },
|
||||
{ "monaco2.talk", 13, true },
|
||||
{ "uccello.talk", 14, true },
|
||||
{ "guru.talk", 15, true },
|
||||
{ "monaco1.talk", 16, true },
|
||||
{ "segretario.talk", 17, true },
|
||||
{ "imperatore.talk", 18, true },
|
||||
{ "secondo.talk", 19, true },
|
||||
{ "taxista.talk", 20, true },
|
||||
{ "bemutsu.talk", 21, true },
|
||||
{ "negro.talk", 22, true },
|
||||
{ "punk.talk", 23, true },
|
||||
{ "chan.talk", 24, true },
|
||||
{ "donna0.talk", 0, false },
|
||||
{ "maxkos.talk", 25, true },
|
||||
{ nullptr, 26, true }, // Second person in the maxkos.talk dialogue
|
||||
{ "drki.talk", 3, true },
|
||||
{ "barman.talk", 27, true },
|
||||
{ "losco.talk", 28, true },
|
||||
{ "mitsu.talk", 29, true },
|
||||
{ "autoradio.talk", 30, true },
|
||||
{ "passa1.talk", 31, true },
|
||||
{ "passa2.talk", 1, false },
|
||||
{ "passa3.talk", 32, true },
|
||||
{ "giornalaio.talk", 33, true },
|
||||
{ "pazza1.talk", 2, false },
|
||||
{ "pazza2.talk", 3, false },
|
||||
{ "pazza3.talk", 4, false },
|
||||
{ "citofono.talk", 34, true },
|
||||
{ "perdelook.talk", 0, false },
|
||||
{ "tele.talk", 35, true },
|
||||
{ "dough.talk", 1, true },
|
||||
{ "donna.talk", 0, false },
|
||||
{ "guanti.talk", 36, true },
|
||||
{ "cuoco.talk", 37, true },
|
||||
{ "punks.talk", 38, true },
|
||||
{ nullptr, 39, true }, // Second person in the punks.talk dialogue
|
||||
{ "punko.talk", 40, true },
|
||||
{ "hotdog.talk", 41, true },
|
||||
{ "cameriera.talk", 5, false },
|
||||
{ "rice1.talk", 42, true },
|
||||
{ nullptr, 43, true }, // Second person in the rice1.talk dialogue
|
||||
{ "segretaria.talk", 6, false },
|
||||
{ "robot.talk", 44, true },
|
||||
{ "portiere.talk", 45, true },
|
||||
{ "figaro.talk", 46, true },
|
||||
{ "ominos.talk", 47, true },
|
||||
{ "cassiera.talk", 7, false },
|
||||
{ "dino.talk", 2, true }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
enum PlayableCharacterVoiceID {
|
||||
kDoug = 1,
|
||||
kDonna = 2,
|
||||
kDino = 3,
|
||||
kDrki = 4
|
||||
};
|
||||
|
||||
static const int kNarratorVoiceID = 0;
|
||||
|
||||
class SaveLoad;
|
||||
|
||||
#define NUM_LOCATIONS 120
|
||||
|
||||
class Parallaction : public Engine {
|
||||
friend class Debugger;
|
||||
|
||||
public:
|
||||
int getGameType() const;
|
||||
uint32 getFeatures() const;
|
||||
Common::Language getLanguage() const;
|
||||
Common::Platform getPlatform() const;
|
||||
|
||||
protected: // members
|
||||
bool detectGame();
|
||||
|
||||
private:
|
||||
const PARALLACTIONGameDescription *_gameDescription;
|
||||
uint16 _language;
|
||||
|
||||
public:
|
||||
Parallaction(OSystem *syst, const PARALLACTIONGameDescription *gameDesc);
|
||||
~Parallaction() override;
|
||||
|
||||
// Engine APIs
|
||||
virtual Common::Error init();
|
||||
virtual Common::Error go() = 0;
|
||||
Common::Error run() override {
|
||||
Common::Error err;
|
||||
err = init();
|
||||
if (err.getCode() != Common::kNoError)
|
||||
return err;
|
||||
return go();
|
||||
}
|
||||
|
||||
bool hasFeature(EngineFeature f) const override;
|
||||
void pauseEngineIntern(bool pause) override;
|
||||
|
||||
// info
|
||||
int32 _screenWidth;
|
||||
int32 _screenHeight;
|
||||
int32 _screenSize;
|
||||
int _gameType;
|
||||
|
||||
// subsystems
|
||||
Gfx *_gfx;
|
||||
Disk *_disk;
|
||||
Input *_input;
|
||||
SaveLoad *_saveLoad;
|
||||
MenuInputHelper *_menuHelper;
|
||||
Common::RandomSource _rnd;
|
||||
SoundMan *_soundMan;
|
||||
|
||||
// fonts
|
||||
Font *_labelFont;
|
||||
Font *_menuFont;
|
||||
Font *_introFont;
|
||||
Font *_dialogueFont;
|
||||
|
||||
// game utilities
|
||||
Table *_globalFlagsNames;
|
||||
Table *_objectsNames;
|
||||
GfxObj *_objects;
|
||||
Table *_callableNames;
|
||||
Table *_localFlagNames;
|
||||
CommandExec *_cmdExec;
|
||||
ProgramExec *_programExec;
|
||||
BalloonManager *_balloonMan;
|
||||
DialogueManager *_dialogueMan;
|
||||
InventoryRenderer *_inventoryRenderer;
|
||||
Inventory *_inventory; // inventory for the current character
|
||||
|
||||
// game data
|
||||
Character _char;
|
||||
uint32 _localFlags[NUM_LOCATIONS];
|
||||
char _locationNames[NUM_LOCATIONS][32];
|
||||
int16 _currentLocationIndex;
|
||||
uint16 _numLocations;
|
||||
Location _location;
|
||||
ZonePtr _activeZone;
|
||||
char _characterName1[50]; // only used in changeCharacter
|
||||
int _characterVoiceID;
|
||||
ZonePtr _zoneTrap;
|
||||
ZonePtr _commentZone;
|
||||
Common::String _newLocationName;
|
||||
|
||||
protected:
|
||||
void runGame();
|
||||
void runGameFrame(int event);
|
||||
void runGuiFrame();
|
||||
void cleanupGui();
|
||||
void runDialogueFrame();
|
||||
void exitDialogueMode();
|
||||
void runCommentFrame();
|
||||
void enterCommentMode(ZonePtr z);
|
||||
void exitCommentMode();
|
||||
void updateView();
|
||||
void drawAnimation(AnimationPtr anim);
|
||||
void drawZone(ZonePtr zone);
|
||||
void updateZones();
|
||||
void doLocationEnterTransition();
|
||||
void allocateLocationSlot(const char *name);
|
||||
void finalizeLocationParsing();
|
||||
void showLocationComment(const Common::String &text, bool end);
|
||||
void destroyDialogueManager();
|
||||
|
||||
public:
|
||||
void beep();
|
||||
void pauseJobs();
|
||||
void resumeJobs();
|
||||
uint getInternLanguage();
|
||||
void setInternLanguage(uint id);
|
||||
void enterDialogueMode(ZonePtr z);
|
||||
void scheduleLocationSwitch(const char *location);
|
||||
void showSlide(const char *name, int x = 0, int y = 0);
|
||||
|
||||
public:
|
||||
void setLocationFlags(uint32 flags);
|
||||
void clearLocationFlags(uint32 flags);
|
||||
void toggleLocationFlags(uint32 flags);
|
||||
uint32 getLocationFlags();
|
||||
bool checkSpecialZoneBox(ZonePtr z, uint32 type, uint x, uint y);
|
||||
bool checkZoneBox(ZonePtr z, uint32 type, uint x, uint y);
|
||||
bool checkZoneType(ZonePtr z, uint32 type);
|
||||
bool checkLinkedAnimBox(ZonePtr z, uint32 type, uint x, uint y);
|
||||
ZonePtr hitZone(uint32 type, uint16 x, uint16 y);
|
||||
void runZone(ZonePtr z);
|
||||
bool pickupItem(ZonePtr z);
|
||||
void updateDoor(ZonePtr z, bool close);
|
||||
void showZone(ZonePtr z, bool visible);
|
||||
void highlightInventoryItem(ItemPosition pos);
|
||||
int16 getHoverInventoryItem(int16 x, int16 y);
|
||||
int addInventoryItem(ItemName item);
|
||||
int addInventoryItem(ItemName item, uint32 value);
|
||||
void dropItem(uint16 v);
|
||||
bool isItemInInventory(int32 v);
|
||||
const InventoryItem* getInventoryItem(int16 pos);
|
||||
int16 getInventoryItemIndex(int16 pos);
|
||||
void openInventory();
|
||||
void closeInventory();
|
||||
void sayText(const Common::String &text, Common::TextToSpeechManager::Action action) const;
|
||||
void setTTSVoice(int id) const;
|
||||
void stopTextToSpeech() const;
|
||||
|
||||
virtual void parseLocation(const char* name) = 0;
|
||||
virtual void changeLocation() = 0;
|
||||
virtual void changeCharacter(const char *name) = 0;
|
||||
virtual void callFunction(uint index, void* parm) = 0;
|
||||
virtual void runPendingZones() = 0;
|
||||
virtual void cleanupGame() = 0;
|
||||
virtual void updateWalkers() = 0;
|
||||
virtual void scheduleWalk(int16 x, int16 y, bool fromUser) = 0;
|
||||
virtual DialogueManager *createDialogueManager(ZonePtr z) = 0;
|
||||
virtual bool processGameEvent(int event) = 0;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class Parallaction_ns : public Parallaction {
|
||||
|
||||
public:
|
||||
Parallaction_ns(OSystem* syst, const PARALLACTIONGameDescription *gameDesc);
|
||||
~Parallaction_ns() override;
|
||||
|
||||
// Engine APIs
|
||||
Common::Error init() override;
|
||||
Common::Error go() override;
|
||||
|
||||
SoundMan_ns* _soundManI;
|
||||
|
||||
uint16 _score;
|
||||
Common::String _password;
|
||||
|
||||
bool _endCredits;
|
||||
|
||||
|
||||
public:
|
||||
void parseLocation(const char *filename) override;
|
||||
void changeLocation() override;
|
||||
void changeCharacter(const char *name) override;
|
||||
void callFunction(uint index, void* parm) override;
|
||||
void runPendingZones() override;
|
||||
void cleanupGame() override;
|
||||
void updateWalkers() override;
|
||||
void scheduleWalk(int16 x, int16 y, bool fromUser) override;
|
||||
DialogueManager *createDialogueManager(ZonePtr z) override;
|
||||
bool processGameEvent(int event) override;
|
||||
void cleanInventory(bool keepVerbs);
|
||||
void changeBackground(const char *background, const char *mask = 0, const char *path = 0);
|
||||
|
||||
private:
|
||||
bool _inTestResult;
|
||||
LocationParser_ns *_locationParser;
|
||||
ProgramParser_ns *_programParser;
|
||||
|
||||
private:
|
||||
void initFonts();
|
||||
void freeFonts();
|
||||
void initResources();
|
||||
void initInventory();
|
||||
void destroyInventory();
|
||||
void setupBalloonManager();
|
||||
void startGui();
|
||||
void startCreditSequence();
|
||||
void startEndPartSequence();
|
||||
void loadProgram(AnimationPtr a, const char *filename);
|
||||
void freeLocation(bool removeAll);
|
||||
void freeCharacter();
|
||||
void destroyTestResultLabels();
|
||||
void startMovingSarcophagus(ZonePtr sarc);
|
||||
void stopMovingSarcophagus();
|
||||
|
||||
|
||||
// callables data
|
||||
typedef void (Parallaction_ns::*Callable)(void *);
|
||||
const Callable *_callables;
|
||||
ZonePtr _moveSarcGetZone;
|
||||
ZonePtr _moveSarcExaZone;
|
||||
ZonePtr _moveSarcGetZones[5];
|
||||
ZonePtr _moveSarcExaZones[5];
|
||||
uint16 num_foglie;
|
||||
|
||||
int16 _sarcophagusDeltaX;
|
||||
bool _movingSarcophagus; // sarcophagus stuff to be saved
|
||||
uint16 _freeSarcophagusSlotX; // sarcophagus stuff to be saved
|
||||
AnimationPtr _rightHandAnim;
|
||||
bool _intro;
|
||||
static const Callable _dosCallables[25];
|
||||
static const Callable _amigaCallables[25];
|
||||
|
||||
GfxObj *_testResultLabels[2];
|
||||
|
||||
PathWalker_NS *_walker;
|
||||
|
||||
// common callables
|
||||
void _c_play_boogie(void *);
|
||||
void _c_startIntro(void *);
|
||||
void _c_endIntro(void *);
|
||||
void _c_moveSheet(void *);
|
||||
void _c_sketch(void *);
|
||||
void _c_shade(void *);
|
||||
void _c_score(void *);
|
||||
void _c_fade(void *);
|
||||
void _c_moveSarc(void *);
|
||||
void _c_contaFoglie(void *);
|
||||
void _c_zeroFoglie(void *);
|
||||
void _c_trasformata(void *);
|
||||
void _c_offMouse(void *);
|
||||
void _c_onMouse(void *);
|
||||
void _c_setMask(void *);
|
||||
void _c_endComment(void *);
|
||||
void _c_frankenstein(void *);
|
||||
void _c_finito(void *);
|
||||
void _c_ridux(void *);
|
||||
void _c_testResult(void *);
|
||||
|
||||
// dos specific callables
|
||||
void _c_null(void *);
|
||||
|
||||
// amiga specific callables
|
||||
void _c_projector(void *);
|
||||
void _c_HBOff(void *);
|
||||
void _c_offSound(void *);
|
||||
void _c_startMusic(void *);
|
||||
void _c_closeMusic(void *);
|
||||
void _c_HBOn(void *);
|
||||
};
|
||||
|
||||
|
||||
|
||||
#define NUM_ZONES 100
|
||||
|
||||
class Parallaction_br : public Parallaction {
|
||||
|
||||
public:
|
||||
Parallaction_br(OSystem* syst, const PARALLACTIONGameDescription *gameDesc);
|
||||
~Parallaction_br() override;
|
||||
|
||||
Common::Error init() override;
|
||||
Common::Error go() override;
|
||||
|
||||
public:
|
||||
void parseLocation(const char* name) override;
|
||||
void changeLocation() override;
|
||||
void changeCharacter(const char *name) override;
|
||||
void callFunction(uint index, void* parm) override;
|
||||
void runPendingZones() override;
|
||||
void cleanupGame() override;
|
||||
void updateWalkers() override;
|
||||
void scheduleWalk(int16 x, int16 y, bool fromUser) override;
|
||||
DialogueManager *createDialogueManager(ZonePtr z) override;
|
||||
bool processGameEvent(int event) override;
|
||||
|
||||
void setupSubtitles(const char *s, const char *s2, int y);
|
||||
void clearSubtitles();
|
||||
|
||||
Inventory *findInventory(const char *name);
|
||||
void linkUnlinkedZoneAnimations();
|
||||
|
||||
void testCounterCondition(const Common::String &name, int op, int value);
|
||||
void restoreOrSaveZoneFlags(ZonePtr z, bool restore);
|
||||
|
||||
public:
|
||||
bool counterExists(const Common::String &name);
|
||||
int getCounterValue(const Common::String &name);
|
||||
void setCounterValue(const Common::String &name, int value);
|
||||
|
||||
void setFollower(const Common::String &name);
|
||||
|
||||
int getSfxStatus();
|
||||
int getMusicStatus();
|
||||
void enableSfx(bool enable);
|
||||
void enableMusic(bool enable);
|
||||
|
||||
const char **_audioCommandsNamesRes;
|
||||
static const char *_partNames[];
|
||||
int _part;
|
||||
int _nextPart;
|
||||
|
||||
|
||||
#if 0 // disabled since I couldn't find any references to lip sync in the scripts
|
||||
int16 _lipSyncVal;
|
||||
uint _subtitleLipSync;
|
||||
#endif
|
||||
int _subtitleY;
|
||||
GfxObj *_subtitle[2];
|
||||
ZonePtr _activeZone2;
|
||||
uint32 _zoneFlags[NUM_LOCATIONS][NUM_ZONES];
|
||||
|
||||
private:
|
||||
LocationParser_br *_locationParser;
|
||||
ProgramParser_br *_programParser;
|
||||
SoundMan_br *_soundManI;
|
||||
Inventory *_dinoInventory;
|
||||
Inventory *_donnaInventory;
|
||||
Inventory *_dougInventory;
|
||||
|
||||
int32 _counters[32];
|
||||
Table *_countersNames;
|
||||
|
||||
private:
|
||||
void initResources();
|
||||
void initInventory();
|
||||
void destroyInventory();
|
||||
|
||||
void cleanInventory(bool keepVerbs);
|
||||
void setupBalloonManager();
|
||||
void initFonts();
|
||||
void freeFonts();
|
||||
void freeLocation(bool removeAll);
|
||||
void loadProgram(AnimationPtr a, const char *filename);
|
||||
void startGui(bool showSplash);
|
||||
void startIngameMenu();
|
||||
void freeCharacter();
|
||||
|
||||
typedef void (Parallaction_br::*Callable)(void *);
|
||||
const Callable *_callables;
|
||||
static const Callable _dosCallables[6];
|
||||
static const Callable _amigaCallables[6];
|
||||
|
||||
Common::String _followerName;
|
||||
AnimationPtr _follower;
|
||||
PathWalker_BR *_walker;
|
||||
int _ferrcycleMode;
|
||||
|
||||
// dos callables
|
||||
void _c_null(void *);
|
||||
void _c_blufade(void *);
|
||||
void _c_resetpalette(void *);
|
||||
void _c_ferrcycle(void *);
|
||||
void _c_lipsinc(void *);
|
||||
void _c_albcycle(void *);
|
||||
void _c_password(void *);
|
||||
};
|
||||
|
||||
extern Parallaction *g_vm;
|
||||
|
||||
|
||||
} // End of namespace Parallaction
|
||||
|
||||
|
||||
#endif
|
||||
620
engines/parallaction/parallaction_br.cpp
Normal file
620
engines/parallaction/parallaction_br.cpp
Normal file
@@ -0,0 +1,620 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/system.h"
|
||||
#include "common/util.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
#include "parallaction/parallaction.h"
|
||||
#include "parallaction/exec.h"
|
||||
#include "parallaction/input.h"
|
||||
#include "parallaction/parser.h"
|
||||
#include "parallaction/saveload.h"
|
||||
#include "parallaction/sound.h"
|
||||
#include "parallaction/walk.h"
|
||||
|
||||
namespace Parallaction {
|
||||
|
||||
|
||||
const char *Parallaction_br::_partNames[] = {
|
||||
"PART0",
|
||||
"PART1",
|
||||
"PART2",
|
||||
"PART3",
|
||||
"PART4"
|
||||
};
|
||||
|
||||
Parallaction_br::Parallaction_br(OSystem* syst, const PARALLACTIONGameDescription *gameDesc) : Parallaction(syst, gameDesc),
|
||||
_locationParser(nullptr), _programParser(nullptr), _soundManI(nullptr) {
|
||||
_audioCommandsNamesRes = nullptr;
|
||||
_part = 0;
|
||||
_nextPart = 0;
|
||||
_subtitleY = 0;
|
||||
_subtitle[0] = nullptr;
|
||||
_subtitle[1] = nullptr;
|
||||
_countersNames = nullptr;
|
||||
_callables = nullptr;
|
||||
_walker = nullptr;
|
||||
}
|
||||
|
||||
Common::Error Parallaction_br::init() {
|
||||
|
||||
_screenWidth = 640;
|
||||
_screenHeight = 400;
|
||||
|
||||
if (getPlatform() == Common::kPlatformDOS) {
|
||||
if (getFeatures() & GF_DEMO) {
|
||||
_disk = new DosDemoDisk_br(this);
|
||||
} else {
|
||||
_disk = new DosDisk_br(this);
|
||||
}
|
||||
_disk->setLanguage(2); // NOTE: language is now hardcoded to English. Original used command-line parameters.
|
||||
_soundManI = new DosSoundMan_br(this);
|
||||
} else {
|
||||
_disk = new AmigaDisk_br(this);
|
||||
_disk->setLanguage(2); // NOTE: language is now hardcoded to English. Original used command-line parameters.
|
||||
_soundManI = new AmigaSoundMan_br(this);
|
||||
}
|
||||
|
||||
_disk->init();
|
||||
_soundMan = new SoundMan(_soundManI);
|
||||
|
||||
initResources();
|
||||
initFonts();
|
||||
_locationParser = new LocationParser_br(this);
|
||||
_locationParser->init();
|
||||
_programParser = new ProgramParser_br(this);
|
||||
_programParser->init();
|
||||
|
||||
_cmdExec = new CommandExec_br(this);
|
||||
_programExec = new ProgramExec_br(this);
|
||||
|
||||
_walker = new PathWalker_BR(this);
|
||||
|
||||
_part = -1;
|
||||
_nextPart = -1;
|
||||
|
||||
_subtitle[0] = nullptr;
|
||||
_subtitle[1] = nullptr;
|
||||
|
||||
memset(_zoneFlags, 0, sizeof(_zoneFlags));
|
||||
|
||||
_countersNames = nullptr;
|
||||
|
||||
_saveLoad = new SaveLoad_br(this, _saveFileMan);
|
||||
|
||||
initInventory();
|
||||
setupBalloonManager();
|
||||
|
||||
Parallaction::init();
|
||||
|
||||
return Common::kNoError;
|
||||
}
|
||||
|
||||
Parallaction_br::~Parallaction_br() {
|
||||
freeFonts();
|
||||
freeCharacter();
|
||||
|
||||
destroyInventory();
|
||||
|
||||
delete _objects;
|
||||
|
||||
delete _locationParser;
|
||||
delete _programParser;
|
||||
|
||||
_location._animations.remove(_char._ani);
|
||||
|
||||
delete _walker;
|
||||
}
|
||||
|
||||
void Parallaction_br::callFunction(uint index, void* parm) {
|
||||
assert(index < 6); // magic value 6 is maximum # of callables for Big Red Adventure
|
||||
|
||||
(this->*_callables[index])(parm);
|
||||
}
|
||||
|
||||
bool Parallaction_br::processGameEvent(int event) {
|
||||
if (event == kEvNone) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool c = true;
|
||||
_input->stopHovering();
|
||||
|
||||
switch (event) {
|
||||
case kEvIngameMenu:
|
||||
startIngameMenu();
|
||||
c = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
_input->setArrowCursor();
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
Common::Error Parallaction_br::go() {
|
||||
|
||||
bool splash = true;
|
||||
|
||||
while (!shouldQuit()) {
|
||||
|
||||
if (getFeatures() & GF_DEMO) {
|
||||
scheduleLocationSwitch("camalb");
|
||||
_nextPart = 1;
|
||||
_input->_inputMode = Input::kInputModeGame;
|
||||
} else {
|
||||
_input->setMenuPointer();
|
||||
startGui(splash);
|
||||
// don't show splash after first time
|
||||
splash = false;
|
||||
}
|
||||
|
||||
// initCharacter();
|
||||
|
||||
while (((g_engineFlags & kEngineReturn) == 0) && (!shouldQuit())) {
|
||||
runGame();
|
||||
}
|
||||
g_engineFlags &= ~kEngineReturn;
|
||||
|
||||
cleanupGame();
|
||||
}
|
||||
|
||||
return Common::kNoError;
|
||||
}
|
||||
|
||||
|
||||
void Parallaction_br::freeFonts() {
|
||||
delete _menuFont;
|
||||
_menuFont = nullptr;
|
||||
|
||||
delete _dialogueFont;
|
||||
_dialogueFont = nullptr;
|
||||
|
||||
// no need to delete _labelFont, since it is using the same buffer as _menuFont
|
||||
_labelFont = nullptr;
|
||||
}
|
||||
|
||||
|
||||
void Parallaction_br::runPendingZones() {
|
||||
ZonePtr z;
|
||||
|
||||
_cmdExec->runSuspended();
|
||||
|
||||
if (_activeZone) {
|
||||
z = _activeZone; // speak Zone or sound
|
||||
_activeZone.reset();
|
||||
if (ACTIONTYPE(z) == kZoneSpeak && z->u._speakDialogue) {
|
||||
enterDialogueMode(z);
|
||||
} else {
|
||||
runZone(z); // FIXME: BRA doesn't handle sound yet
|
||||
}
|
||||
}
|
||||
|
||||
if (_activeZone2) {
|
||||
z = _activeZone2; // speak Zone or sound
|
||||
_activeZone2.reset();
|
||||
if (ACTIONTYPE(z) == kZoneSpeak && z->u._speakDialogue) {
|
||||
enterDialogueMode(z);
|
||||
} else {
|
||||
runZone(z); // FIXME: BRA doesn't handle sound yet
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Parallaction_br::freeCharacter() {
|
||||
_gfx->freeCharacterObjects();
|
||||
|
||||
delete _char._talk;
|
||||
delete _char._ani->gfxobj;
|
||||
|
||||
_char._talk = nullptr;
|
||||
_char._ani->gfxobj = nullptr;
|
||||
}
|
||||
|
||||
void Parallaction_br::freeLocation(bool removeAll) {
|
||||
// free open location stuff
|
||||
clearSubtitles();
|
||||
|
||||
_localFlagNames->clear();
|
||||
|
||||
_gfx->freeLocationObjects();
|
||||
|
||||
// save zone and animation flags
|
||||
ZoneList::iterator zit = _location._zones.begin();
|
||||
for ( ; zit != _location._zones.end(); ++zit) {
|
||||
restoreOrSaveZoneFlags(*zit, false);
|
||||
}
|
||||
AnimationList::iterator ait = _location._animations.begin();
|
||||
for ( ; ait != _location._animations.end(); ++ait) {
|
||||
restoreOrSaveZoneFlags(*ait, false);
|
||||
}
|
||||
|
||||
_location._animations.remove(_char._ani);
|
||||
_location.cleanup(removeAll);
|
||||
_location._animations.push_front(_char._ani);
|
||||
|
||||
}
|
||||
|
||||
void Parallaction_br::cleanupGame() {
|
||||
freeLocation(true);
|
||||
|
||||
freeCharacter();
|
||||
|
||||
delete _globalFlagsNames;
|
||||
delete _objectsNames;
|
||||
delete _countersNames;
|
||||
|
||||
_globalFlagsNames = nullptr;
|
||||
_objectsNames = nullptr;
|
||||
_countersNames = nullptr;
|
||||
|
||||
_numLocations = 0;
|
||||
g_globalFlags = 0;
|
||||
memset(_localFlags, 0, sizeof(_localFlags));
|
||||
memset(_locationNames, 0, sizeof(_locationNames));
|
||||
memset(_zoneFlags, 0, sizeof(_zoneFlags));
|
||||
}
|
||||
|
||||
|
||||
void Parallaction_br::changeLocation() {
|
||||
if (_newLocationName.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_nextPart != -1) {
|
||||
cleanupGame();
|
||||
|
||||
// more cleanup needed for part changes (see also saveload)
|
||||
g_globalFlags = 0;
|
||||
cleanInventory(true);
|
||||
Common::strcpy_s(_characterName1, "null");
|
||||
|
||||
_part = _nextPart;
|
||||
|
||||
if (getFeatures() & GF_DEMO) {
|
||||
assert(_part == 1);
|
||||
} else {
|
||||
assert(_part >= 0 && _part <= 4);
|
||||
}
|
||||
|
||||
_disk->selectArchive(_partNames[_part]);
|
||||
|
||||
memset(_counters, 0, sizeof(_counters));
|
||||
|
||||
_globalFlagsNames = _disk->loadTable("global");
|
||||
_objectsNames = _disk->loadTable("objects");
|
||||
_countersNames = _disk->loadTable("counters");
|
||||
|
||||
// TODO: maybe handle this into Disk
|
||||
delete _objects;
|
||||
if (getPlatform() == Common::kPlatformDOS) {
|
||||
_objects = _disk->loadObjects("icone.ico");
|
||||
} else {
|
||||
_objects = _disk->loadObjects("icons.ico", _part);
|
||||
}
|
||||
|
||||
parseLocation("common.slf");
|
||||
}
|
||||
|
||||
freeLocation(false);
|
||||
// load new location
|
||||
Common::strlcpy(_location._name, _newLocationName.c_str(), 100);
|
||||
parseLocation(_location._name);
|
||||
|
||||
if (_location._startPosition.x != -1000) {
|
||||
_char._ani->setFoot(_location._startPosition);
|
||||
_char._ani->setF(_location._startFrame);
|
||||
}
|
||||
|
||||
// re-link the follower animation
|
||||
setFollower(_followerName);
|
||||
if (_follower) {
|
||||
Common::Point p = _location._followerStartPosition;
|
||||
if (p.x == -1000) {
|
||||
_char._ani->getFoot(p);
|
||||
}
|
||||
_follower->setFoot(p);
|
||||
_follower->setF(_location._followerStartFrame);
|
||||
}
|
||||
|
||||
_location._startPosition.x = -1000;
|
||||
_location._startPosition.y = -1000;
|
||||
_location._followerStartPosition.x = -1000;
|
||||
_location._followerStartPosition.y = -1000;
|
||||
|
||||
_gfx->setScrollPosX(0);
|
||||
_gfx->setScrollPosY(0);
|
||||
if (_char._ani->gfxobj) {
|
||||
Common::Point foot;
|
||||
_char._ani->getFoot(foot);
|
||||
|
||||
if (foot.x > 550)
|
||||
_gfx->setScrollPosX(320);
|
||||
|
||||
if (foot.y > 350)
|
||||
_gfx->setScrollPosY(foot.y - 350);
|
||||
}
|
||||
|
||||
// kFlagsRemove is cleared because the character is visible by default.
|
||||
// Commands can hide the character, anyway.
|
||||
_char._ani->_flags &= ~kFlagsRemove;
|
||||
_cmdExec->run(_location._commands);
|
||||
|
||||
doLocationEnterTransition();
|
||||
|
||||
_cmdExec->run(_location._aCommands);
|
||||
|
||||
// NOTE: music should not started here!
|
||||
// TODO: implement the music commands which control music execution
|
||||
_soundMan->execute(SC_PLAYMUSIC);
|
||||
|
||||
g_engineFlags &= ~kEngineChangeLocation;
|
||||
_newLocationName.clear();
|
||||
_nextPart = -1;
|
||||
}
|
||||
|
||||
// FIXME: Parallaction_br::parseLocation() is now a verbatim copy of the same routine from Parallaction_ns.
|
||||
void Parallaction_br::parseLocation(const char *filename) {
|
||||
debugC(1, kDebugParser, "parseLocation('%s')", filename);
|
||||
|
||||
// find a new available slot
|
||||
allocateLocationSlot(filename);
|
||||
Script *script = _disk->loadLocation(filename);
|
||||
|
||||
// parse the text file
|
||||
LocationParserOutput_br out;
|
||||
_locationParser->parse(script, &out);
|
||||
assert(out._info);
|
||||
delete script;
|
||||
|
||||
bool visited = getLocationFlags() & kFlagsVisited;
|
||||
|
||||
// load background, mask and path
|
||||
_disk->loadScenery(*out._info,
|
||||
out._backgroundName.empty() ? nullptr : out._backgroundName.c_str(),
|
||||
out._maskName.empty() ? nullptr : out._maskName.c_str(),
|
||||
out._pathName.empty() ? nullptr : out._pathName.c_str());
|
||||
// assign background
|
||||
_gfx->setBackground(kBackgroundLocation, out._info);
|
||||
|
||||
|
||||
// process zones
|
||||
ZoneList::iterator zit = _location._zones.begin();
|
||||
for ( ; zit != _location._zones.end(); ++zit) {
|
||||
ZonePtr z = *zit;
|
||||
// restore the flags if the location has already been visited
|
||||
restoreOrSaveZoneFlags(z, visited);
|
||||
|
||||
// (re)link the bounding animation if needed
|
||||
if (z->_flags & kFlagsAnimLinked) {
|
||||
z->_linkedAnim = _location.findAnimation(z->_linkedName.c_str());
|
||||
}
|
||||
|
||||
bool visible = (z->_flags & kFlagsRemove) == 0;
|
||||
if (visible) {
|
||||
showZone(z, visible);
|
||||
}
|
||||
}
|
||||
|
||||
// load the character (must be done before animations are processed)
|
||||
if (!out._characterName.empty()) {
|
||||
changeCharacter(out._characterName.c_str());
|
||||
}
|
||||
|
||||
// process animations
|
||||
AnimationList::iterator ait = _location._animations.begin();
|
||||
for ( ; ait != _location._animations.end(); ++ait) {
|
||||
// restore the flags if the location has already been visited
|
||||
restoreOrSaveZoneFlags(*ait, visited);
|
||||
|
||||
// load the script
|
||||
if (!(*ait)->_scriptName.empty()) {
|
||||
loadProgram(*ait, (*ait)->_scriptName.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
debugC(1, kDebugParser, "parseLocation('%s') done", filename);
|
||||
return;
|
||||
}
|
||||
|
||||
void Parallaction_br::loadProgram(AnimationPtr a, const char *filename) {
|
||||
debugC(1, kDebugParser, "loadProgram(Animation: %s, script: %s)", a->_name, filename);
|
||||
|
||||
Script *script = _disk->loadScript(filename);
|
||||
ProgramPtr program(new Program);
|
||||
program->_anim = a;
|
||||
|
||||
_programParser->parse(script, program);
|
||||
|
||||
delete script;
|
||||
|
||||
_location._programs.push_back(program);
|
||||
|
||||
debugC(1, kDebugParser, "loadProgram() done");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void Parallaction_br::linkUnlinkedZoneAnimations() {
|
||||
ZoneList::iterator zit = _location._zones.begin();
|
||||
for ( ; zit != _location._zones.end(); ++zit) {
|
||||
if ((*zit)->_flags & kFlagsActive) {
|
||||
(*zit)->_linkedAnim = _location.findAnimation((*zit)->_linkedName.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Parallaction_br::changeCharacter(const char *name) {
|
||||
|
||||
const char *charName = _char.getName();
|
||||
|
||||
if (scumm_stricmp(charName, name)) {
|
||||
freeCharacter();
|
||||
|
||||
debugC(1, kDebugExec, "changeCharacter(%s)", name);
|
||||
|
||||
_char.setName(name);
|
||||
_char._ani->gfxobj = _gfx->loadCharacterAnim(name);
|
||||
_char._talk = _disk->loadTalk(name);
|
||||
|
||||
_inventory = findInventory(name);
|
||||
_inventoryRenderer->setInventory(_inventory);
|
||||
|
||||
_input->setCharacterPointer(name);
|
||||
}
|
||||
|
||||
_char._ani->_flags |= kFlagsActive;
|
||||
}
|
||||
|
||||
bool Parallaction_br::counterExists(const Common::String &name) {
|
||||
return Table::notFound != _countersNames->lookup(name.c_str());
|
||||
}
|
||||
|
||||
int Parallaction_br::getCounterValue(const Common::String &name) {
|
||||
int index = _countersNames->lookup(name.c_str());
|
||||
if (index != Table::notFound) {
|
||||
return _counters[index - 1];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Parallaction_br::setCounterValue(const Common::String &name, int value) {
|
||||
int index = _countersNames->lookup(name.c_str());
|
||||
if (index != Table::notFound) {
|
||||
_counters[index - 1] = value;
|
||||
}
|
||||
}
|
||||
|
||||
void Parallaction_br::testCounterCondition(const Common::String &name, int op, int value) {
|
||||
int index = _countersNames->lookup(name.c_str());
|
||||
if (index == Table::notFound) {
|
||||
clearLocationFlags(kFlagsTestTrue);
|
||||
return;
|
||||
}
|
||||
|
||||
int c = _counters[index - 1];
|
||||
|
||||
// these definitions must match those in parser_br.cpp
|
||||
#define CMD_TEST 25
|
||||
#define CMD_TEST_GT 26
|
||||
#define CMD_TEST_LT 27
|
||||
|
||||
bool res = false;
|
||||
switch (op) {
|
||||
case CMD_TEST:
|
||||
res = (c == value);
|
||||
break;
|
||||
|
||||
case CMD_TEST_GT:
|
||||
res = (c > value);
|
||||
break;
|
||||
|
||||
case CMD_TEST_LT:
|
||||
res = (c < value);
|
||||
break;
|
||||
|
||||
default:
|
||||
error("unknown operator in testCounterCondition");
|
||||
}
|
||||
|
||||
if (res) {
|
||||
setLocationFlags(kFlagsTestTrue);
|
||||
} else {
|
||||
clearLocationFlags(kFlagsTestTrue);
|
||||
}
|
||||
}
|
||||
|
||||
void Parallaction_br::updateWalkers() {
|
||||
_walker->walk();
|
||||
}
|
||||
|
||||
void Parallaction_br::scheduleWalk(int16 x, int16 y, bool fromUser) {
|
||||
AnimationPtr a = _char._ani;
|
||||
|
||||
if ((a->_flags & kFlagsRemove) || (a->_flags & kFlagsActive) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
_walker->setCharacterPath(a, x, y);
|
||||
|
||||
if (!fromUser) {
|
||||
_walker->stopFollower();
|
||||
} else {
|
||||
if (_follower) {
|
||||
_walker->setFollowerPath(_follower, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
g_engineFlags |= kEngineWalking;
|
||||
}
|
||||
|
||||
void Parallaction_br::setFollower(const Common::String &name) {
|
||||
if (name.empty()) {
|
||||
_followerName.clear();
|
||||
_follower.reset();
|
||||
} else {
|
||||
_followerName = name;
|
||||
_follower = _location.findAnimation(name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void Parallaction_br::restoreOrSaveZoneFlags(ZonePtr z, bool restore) {
|
||||
if ((z->_locationIndex == INVALID_LOCATION_INDEX) || (z->_index == INVALID_ZONE_INDEX)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (restore) {
|
||||
z->_flags = _zoneFlags[z->_locationIndex][z->_index];
|
||||
} else {
|
||||
_zoneFlags[z->_locationIndex][z->_index] = z->_flags;
|
||||
}
|
||||
}
|
||||
|
||||
int Parallaction_br::getSfxStatus() {
|
||||
if (!_soundManI) {
|
||||
return -1;
|
||||
}
|
||||
return _soundManI->isSfxEnabled() ? 1 : 0;
|
||||
}
|
||||
|
||||
int Parallaction_br::getMusicStatus() {
|
||||
if (!_soundManI) {
|
||||
return -1;
|
||||
}
|
||||
return _soundManI->isMusicEnabled() ? 1 : 0;
|
||||
}
|
||||
|
||||
void Parallaction_br::enableSfx(bool enable) {
|
||||
if (_soundManI) {
|
||||
_soundManI->enableSfx(enable);
|
||||
}
|
||||
}
|
||||
|
||||
void Parallaction_br::enableMusic(bool enable) {
|
||||
if (_soundManI) {
|
||||
_soundManI->enableMusic(enable);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Parallaction
|
||||
638
engines/parallaction/parallaction_ns.cpp
Normal file
638
engines/parallaction/parallaction_ns.cpp
Normal file
@@ -0,0 +1,638 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/system.h"
|
||||
#include "common/config-manager.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
#include "parallaction/parallaction.h"
|
||||
#include "parallaction/exec.h"
|
||||
#include "parallaction/input.h"
|
||||
#include "parallaction/parser.h"
|
||||
#include "parallaction/saveload.h"
|
||||
#include "parallaction/sound.h"
|
||||
#include "parallaction/walk.h"
|
||||
|
||||
|
||||
namespace Parallaction {
|
||||
|
||||
#ifdef USE_TTS
|
||||
|
||||
static const char *openingCreditsFirstLine[] = {
|
||||
"Programmatore Assoluto: Paolo Costabel", // Italian
|
||||
"Programmeur Absolu: Paolo Costabel", // French
|
||||
"Absolute Programmer: Paolo Costabel", // English
|
||||
"Absoluter Programmierer: Paolo Costabel" // German
|
||||
};
|
||||
|
||||
// Transcribed for TTS
|
||||
// First four names of the end credits are best voiced as one block; the remaining credits
|
||||
// are voiced one at a time elsewhere
|
||||
static const char *endCreditsFirstSection[] = {
|
||||
// Italian
|
||||
"Il Dottor Buoz\n"
|
||||
"Lo Studente\n"
|
||||
"Il Poliziotto\n"
|
||||
"Secondo il Secondino",
|
||||
// French
|
||||
"Le Professeur Buoz\n"
|
||||
"Le \311tudiant\n"
|
||||
"Le Police\n"
|
||||
"Secondo le Garde",
|
||||
// English
|
||||
"Doctor Buoz\n"
|
||||
"Student\n"
|
||||
"Police\n"
|
||||
"Secondo the Warder",
|
||||
// German
|
||||
"Professor Buoz\n"
|
||||
"Der Student\n"
|
||||
"Die Polizei\n"
|
||||
"Secondo der W\344chter"
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#define INITIAL_FREE_SARCOPHAGUS_SLOT_X 200
|
||||
|
||||
|
||||
class LocationName {
|
||||
|
||||
Common::String _slide;
|
||||
Common::String _character;
|
||||
Common::String _location;
|
||||
|
||||
bool _hasCharacter;
|
||||
bool _hasSlide;
|
||||
Common::String _buf;
|
||||
|
||||
public:
|
||||
LocationName() {
|
||||
_hasSlide = false;
|
||||
_hasCharacter = false;
|
||||
}
|
||||
|
||||
void bind(const char*);
|
||||
|
||||
const char *location() const {
|
||||
return _location.c_str();
|
||||
}
|
||||
|
||||
bool hasCharacter() const {
|
||||
return _hasCharacter;
|
||||
}
|
||||
|
||||
const char *character() const {
|
||||
return _character.c_str();
|
||||
}
|
||||
|
||||
bool hasSlide() const {
|
||||
return _hasSlide;
|
||||
}
|
||||
|
||||
const char *slide() const {
|
||||
return _slide.c_str();
|
||||
}
|
||||
|
||||
const char *c_str() const {
|
||||
return _buf.c_str();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*
|
||||
bind accept the following input formats:
|
||||
|
||||
1 - [S].slide.[L]{.[C]}
|
||||
2 - [L]{.[C]}
|
||||
|
||||
where:
|
||||
|
||||
[S] is the slide to be shown
|
||||
[L] is the location to switch to (immediately in case 2, or right after slide [S] in case 1)
|
||||
[C] is the character to be selected, and is optional
|
||||
|
||||
The routine tells one form from the other by searching for the '.slide.'
|
||||
|
||||
NOTE: there exists one script in which [L] is not used in the case 1, but its use
|
||||
is commented out, and would definitely crash the current implementation.
|
||||
*/
|
||||
void LocationName::bind(const char *s) {
|
||||
_buf = s;
|
||||
_hasSlide = false;
|
||||
_hasCharacter = false;
|
||||
|
||||
Common::StringArray list;
|
||||
char *tok = strtok(_buf.begin(), ".");
|
||||
while (tok) {
|
||||
list.push_back(tok);
|
||||
tok = strtok(nullptr, ".");
|
||||
}
|
||||
|
||||
if (list.size() < 1 || list.size() > 4)
|
||||
error("changeLocation: ill-formed location name '%s'", s);
|
||||
|
||||
if (list.size() > 1) {
|
||||
if (list[1] == "slide") {
|
||||
_hasSlide = true;
|
||||
_slide = list[0];
|
||||
|
||||
list.remove_at(0); // removes slide name
|
||||
list.remove_at(0); // removes 'slide'
|
||||
}
|
||||
|
||||
if (list.size() == 2) {
|
||||
_hasCharacter = true;
|
||||
_character = list[1];
|
||||
}
|
||||
}
|
||||
|
||||
_location = list[0];
|
||||
_buf = s; // kept as reference
|
||||
}
|
||||
|
||||
Parallaction_ns::Parallaction_ns(OSystem* syst, const PARALLACTIONGameDescription *gameDesc) : Parallaction(syst, gameDesc),
|
||||
_locationParser(nullptr), _programParser(nullptr), _walker(nullptr) {
|
||||
_soundManI = nullptr;
|
||||
_score = 0;
|
||||
_inTestResult = 0;
|
||||
_callables = nullptr;
|
||||
num_foglie = 0;
|
||||
_sarcophagusDeltaX = 0;
|
||||
_movingSarcophagus = 0;
|
||||
_freeSarcophagusSlotX = 0;
|
||||
_intro = 0;
|
||||
|
||||
_testResultLabels[0] = nullptr;
|
||||
_testResultLabels[1] = nullptr;
|
||||
}
|
||||
|
||||
Common::Error Parallaction_ns::init() {
|
||||
|
||||
_screenWidth = 320;
|
||||
_screenHeight = 200;
|
||||
|
||||
if (getPlatform() == Common::kPlatformDOS) {
|
||||
_disk = new DosDisk_ns(this);
|
||||
} else {
|
||||
if (getFeatures() & GF_DEMO) {
|
||||
Common::strcpy_s(_location._name, "fognedemo");
|
||||
}
|
||||
_disk = new AmigaDisk_ns(this);
|
||||
}
|
||||
|
||||
_disk->init();
|
||||
|
||||
if (getPlatform() == Common::kPlatformDOS) {
|
||||
_soundManI = new DosSoundMan_ns(this);
|
||||
_soundManI->setMusicVolume(ConfMan.getInt("music_volume"));
|
||||
} else {
|
||||
_soundManI = new AmigaSoundMan_ns(this);
|
||||
}
|
||||
|
||||
_soundMan = new SoundMan(_soundManI);
|
||||
|
||||
initResources();
|
||||
initFonts();
|
||||
_locationParser = new LocationParser_ns(this);
|
||||
_locationParser->init();
|
||||
_programParser = new ProgramParser_ns(this);
|
||||
_programParser->init();
|
||||
|
||||
_cmdExec = new CommandExec_ns(this);
|
||||
_programExec = new ProgramExec_ns(this);
|
||||
|
||||
_walker = new PathWalker_NS(this);
|
||||
|
||||
_sarcophagusDeltaX = 0;
|
||||
_movingSarcophagus = false;
|
||||
_freeSarcophagusSlotX = INITIAL_FREE_SARCOPHAGUS_SLOT_X;
|
||||
|
||||
num_foglie = 0;
|
||||
|
||||
_intro = false;
|
||||
_inTestResult = false;
|
||||
_endCredits = false;
|
||||
|
||||
_location._animations.push_front(_char._ani);
|
||||
|
||||
_saveLoad = new SaveLoad_ns(this, _saveFileMan);
|
||||
|
||||
initInventory();
|
||||
setupBalloonManager();
|
||||
|
||||
_score = 1;
|
||||
|
||||
_testResultLabels[0] = nullptr;
|
||||
_testResultLabels[1] = nullptr;
|
||||
|
||||
Parallaction::init();
|
||||
|
||||
return Common::kNoError;
|
||||
}
|
||||
|
||||
Parallaction_ns::~Parallaction_ns() {
|
||||
freeFonts();
|
||||
|
||||
// TODO: we may want to add a ~Character instead
|
||||
freeCharacter();
|
||||
_char._ani.reset();
|
||||
|
||||
destroyInventory();
|
||||
|
||||
delete _locationParser;
|
||||
delete _programParser;
|
||||
freeLocation(true);
|
||||
|
||||
_location._animations.remove(_char._ani);
|
||||
|
||||
delete _walker;
|
||||
|
||||
destroyTestResultLabels();
|
||||
}
|
||||
|
||||
void Parallaction_ns::destroyTestResultLabels() {
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
_gfx->unregisterLabel(_testResultLabels[i]);
|
||||
delete _testResultLabels[i];
|
||||
_testResultLabels[i] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Parallaction_ns::freeFonts() {
|
||||
|
||||
delete _dialogueFont;
|
||||
delete _labelFont;
|
||||
delete _menuFont;
|
||||
delete _introFont;
|
||||
|
||||
_menuFont = nullptr;
|
||||
_dialogueFont = nullptr;
|
||||
_labelFont = nullptr;
|
||||
_introFont = nullptr;
|
||||
}
|
||||
|
||||
|
||||
void Parallaction_ns::callFunction(uint index, void* parm) {
|
||||
assert(index < 25); // magic value 25 is maximum # of callables for Nippon Safes
|
||||
|
||||
(this->*_callables[index])(parm);
|
||||
}
|
||||
|
||||
bool Parallaction_ns::processGameEvent(int event) {
|
||||
if (event == kEvNone) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool c = true;
|
||||
_input->stopHovering();
|
||||
|
||||
switch (event) {
|
||||
case kEvSaveGame:
|
||||
_saveLoad->saveGame();
|
||||
break;
|
||||
|
||||
case kEvLoadGame:
|
||||
_saveLoad->loadGame();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
_input->setArrowCursor();
|
||||
_input->setMouseState(MOUSE_ENABLED_SHOW);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
Common::Error Parallaction_ns::go() {
|
||||
_saveLoad->renameOldSavefiles();
|
||||
|
||||
_globalFlagsNames = _disk->loadTable("global");
|
||||
|
||||
startGui();
|
||||
|
||||
while (!shouldQuit()) {
|
||||
runGame();
|
||||
}
|
||||
|
||||
return Common::kNoError;
|
||||
}
|
||||
|
||||
void Parallaction_ns::changeBackground(const char* background, const char* mask, const char* path) {
|
||||
Palette pal;
|
||||
|
||||
uint16 v2 = 0;
|
||||
if (!scumm_stricmp(background, "final")) {
|
||||
_gfx->clearScreen();
|
||||
for (uint16 _si = 0; _si < 32; _si++) {
|
||||
pal.setEntry(_si, v2, v2, v2);
|
||||
v2 += 4;
|
||||
}
|
||||
|
||||
_system->delayMillis(20);
|
||||
_gfx->setPalette(pal);
|
||||
_gfx->updateScreen();
|
||||
return;
|
||||
}
|
||||
|
||||
if (path == nullptr) {
|
||||
path = mask;
|
||||
}
|
||||
|
||||
BackgroundInfo *info = new BackgroundInfo;
|
||||
_disk->loadScenery(*info, background, mask, path);
|
||||
_gfx->setBackground(kBackgroundLocation, info);
|
||||
}
|
||||
|
||||
|
||||
void Parallaction_ns::runPendingZones() {
|
||||
if (_activeZone) {
|
||||
ZonePtr z = _activeZone; // speak Zone or sound
|
||||
_activeZone.reset();
|
||||
runZone(z);
|
||||
}
|
||||
}
|
||||
|
||||
// changeLocation handles transitions between locations, and is able to display slides
|
||||
// between one and the other.
|
||||
//
|
||||
void Parallaction_ns::changeLocation() {
|
||||
if (_newLocationName.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
char location[200];
|
||||
Common::strlcpy(location, _newLocationName.c_str(), 200);
|
||||
Common::strlcpy(_location._name, _newLocationName.c_str(), 100);
|
||||
|
||||
debugC(1, kDebugExec, "changeLocation(%s)", location);
|
||||
|
||||
MouseTriState oldMouseState = _input->getMouseState();
|
||||
_input->setMouseState(MOUSE_DISABLED);
|
||||
|
||||
if (!_intro) {
|
||||
// prevent music changes during the introduction
|
||||
_soundManI->playLocationMusic(location);
|
||||
}
|
||||
|
||||
_input->stopHovering();
|
||||
// this is still needed to remove the floatingLabel
|
||||
_gfx->freeLabels();
|
||||
|
||||
_zoneTrap.reset();
|
||||
|
||||
_input->setArrowCursor();
|
||||
|
||||
_gfx->showGfxObj(_char._ani->gfxobj, false);
|
||||
|
||||
LocationName locname;
|
||||
locname.bind(location);
|
||||
|
||||
freeLocation(false);
|
||||
|
||||
if (locname.hasSlide()) {
|
||||
showSlide(locname.slide());
|
||||
GfxObj *label = _gfx->createLabel(_menuFont, _location._slideText[0].c_str(), 1);
|
||||
_gfx->showLabel(label, CENTER_LABEL_HORIZONTAL, 14);
|
||||
_gfx->updateScreen();
|
||||
|
||||
_input->waitForButtonEvent(kMouseLeftUp);
|
||||
_gfx->unregisterLabel(label);
|
||||
delete label;
|
||||
|
||||
if (!locname.hasCharacter()) {
|
||||
setTTSVoice(_characterVoiceID);
|
||||
}
|
||||
}
|
||||
|
||||
if (locname.hasCharacter()) {
|
||||
changeCharacter(locname.character());
|
||||
}
|
||||
|
||||
Common::strlcpy(g_saveData1, locname.location(), 30);
|
||||
parseLocation(g_saveData1);
|
||||
|
||||
if (_location._startPosition.x != -1000) {
|
||||
_char._ani->setX(_location._startPosition.x);
|
||||
_char._ani->setY(_location._startPosition.y);
|
||||
_char._ani->setF(_location._startFrame);
|
||||
_location._startPosition.y = -1000;
|
||||
_location._startPosition.x = -1000;
|
||||
}
|
||||
|
||||
|
||||
_gfx->setBlackPalette();
|
||||
_gfx->updateScreen();
|
||||
|
||||
// BUG #3459: kEngineChangeLocation flag must be cleared *before* commands
|
||||
// and acommands are executed, so that it can be set again if needed.
|
||||
g_engineFlags &= ~kEngineChangeLocation;
|
||||
|
||||
_cmdExec->run(_location._commands);
|
||||
|
||||
doLocationEnterTransition();
|
||||
|
||||
_cmdExec->run(_location._aCommands);
|
||||
|
||||
if (_location._hasSound)
|
||||
_soundManI->playSfx(_location._soundFile, 0, true);
|
||||
|
||||
if (!_intro) {
|
||||
_input->setMouseState(oldMouseState);
|
||||
// WORKAROUND: Fix a script bug in the Multilingual DOS version of
|
||||
// Nippon Safes: the mouse cursor is incorrectly hidden outside the
|
||||
// cave at the end of the game. Fix it here.
|
||||
if (!strcmp(_location._name, "ingressocav"))
|
||||
_input->setMouseState(MOUSE_ENABLED_SHOW);
|
||||
#ifdef USE_TTS
|
||||
else if (!strcmp(_location._name, "final")) { // End credits
|
||||
setTTSVoice(kNarratorVoiceID);
|
||||
sayText(endCreditsFirstSection[getInternLanguage()], Common::TextToSpeechManager::INTERRUPT);
|
||||
_endCredits = true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef USE_TTS
|
||||
if (!strcmp(_location._name, "title1")) { // First screen of opening credits
|
||||
setTTSVoice(kNarratorVoiceID);
|
||||
sayText(openingCreditsFirstLine[getInternLanguage()], Common::TextToSpeechManager::INTERRUPT);
|
||||
}
|
||||
#endif
|
||||
|
||||
debugC(1, kDebugExec, "changeLocation() done");
|
||||
_newLocationName.clear();
|
||||
}
|
||||
|
||||
|
||||
void Parallaction_ns::parseLocation(const char *filename) {
|
||||
debugC(1, kDebugParser, "parseLocation('%s')", filename);
|
||||
|
||||
allocateLocationSlot(filename);
|
||||
Script *script = _disk->loadLocation(filename);
|
||||
|
||||
// TODO: the following two lines are specific to Nippon Safes
|
||||
// and should be moved into something like 'initializeParsing()'
|
||||
_location._hasSound = false;
|
||||
|
||||
_locationParser->parse(script);
|
||||
|
||||
delete script;
|
||||
|
||||
// this loads animation scripts
|
||||
AnimationList::iterator it = _location._animations.begin();
|
||||
for ( ; it != _location._animations.end(); ++it) {
|
||||
if (!(*it)->_scriptName.empty()) {
|
||||
loadProgram(*it, (*it)->_scriptName.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
debugC(1, kDebugParser, "parseLocation('%s') done", filename);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Parallaction_ns::changeCharacter(const char *name) {
|
||||
debugC(1, kDebugExec, "changeCharacter(%s)", name);
|
||||
|
||||
_char.setName(name);
|
||||
|
||||
if (!scumm_stricmp(_char.getFullName(), _characterName1)) {
|
||||
debugC(3, kDebugExec, "changeCharacter: nothing done");
|
||||
return;
|
||||
}
|
||||
|
||||
freeCharacter();
|
||||
|
||||
_char._ani->gfxobj = _gfx->loadCharacterAnim(_char.getFullName());
|
||||
|
||||
if (scumm_stricmp(_char.getBaseName(), g_doughName) == 0) {
|
||||
_characterVoiceID = kDoug;
|
||||
} else if (scumm_stricmp(_char.getBaseName(), g_donnaName) == 0) {
|
||||
_characterVoiceID = kDonna;
|
||||
} else if (scumm_stricmp(_char.getBaseName(), g_dinoName) == 0) {
|
||||
_characterVoiceID = kDino;
|
||||
} else {
|
||||
_characterVoiceID = kDrki;
|
||||
}
|
||||
|
||||
setTTSVoice(_characterVoiceID);
|
||||
|
||||
if (!_char.dummy()) {
|
||||
_char._head = _disk->loadHead(_char.getBaseName());
|
||||
_char._talk = _disk->loadTalk(_char.getBaseName());
|
||||
_objects = _disk->loadObjects(_char.getBaseName());
|
||||
_objectsNames = _disk->loadTable(_char.getBaseName());
|
||||
|
||||
if (!_intro) {
|
||||
// prevent music changes during the introduction
|
||||
_soundManI->playCharacterMusic(_char.getBaseName());
|
||||
}
|
||||
|
||||
// The original engine used to reload 'common' only on loadgames. We are reloading here since 'common'
|
||||
// contains character specific stuff. This causes crashes like bug #3440, because parseLocation tries
|
||||
// to reload scripts but the data archive selected is occasionally wrong. This has been solved by having
|
||||
// parseLocation only load scripts when they aren't already loaded - which it should have done since the
|
||||
// beginning nevertheless.
|
||||
if (!(getFeatures() & GF_DEMO))
|
||||
parseLocation("common");
|
||||
}
|
||||
|
||||
Common::strcpy_s(_characterName1, _char.getFullName());
|
||||
|
||||
debugC(3, kDebugExec, "changeCharacter: switch completed");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void Parallaction_ns::freeCharacter() {
|
||||
_gfx->freeCharacterObjects();
|
||||
|
||||
delete _char._talk;
|
||||
delete _char._head;
|
||||
delete _char._ani->gfxobj;
|
||||
delete _objects;
|
||||
delete _objectsNames;
|
||||
|
||||
_char._talk = nullptr;
|
||||
_char._head = nullptr;
|
||||
_char._ani->gfxobj = nullptr;
|
||||
|
||||
_objects = nullptr;
|
||||
_objectsNames = nullptr;
|
||||
}
|
||||
|
||||
void Parallaction_ns::freeLocation(bool removeAll) {
|
||||
debugC(2, kDebugExec, "freeLocation");
|
||||
|
||||
_soundManI->stopSfx(0);
|
||||
_soundManI->stopSfx(1);
|
||||
_soundManI->stopSfx(2);
|
||||
_soundManI->stopSfx(3);
|
||||
|
||||
_localFlagNames->clear();
|
||||
|
||||
_gfx->freeLocationObjects();
|
||||
|
||||
_location._animations.remove(_char._ani);
|
||||
_location.cleanup(removeAll);
|
||||
_location._animations.push_front(_char._ani);
|
||||
}
|
||||
|
||||
void Parallaction_ns::cleanupGame() {
|
||||
_soundManI->stopMusic();
|
||||
|
||||
_inTestResult = false;
|
||||
g_engineFlags &= ~kEngineTransformedDonna;
|
||||
|
||||
_numLocations = 0;
|
||||
g_globalFlags = 0;
|
||||
memset(_localFlags, 0, sizeof(_localFlags));
|
||||
memset(_locationNames, 0, sizeof(_locationNames));
|
||||
|
||||
_location.freeZones(true);
|
||||
|
||||
_score = 0;
|
||||
_freeSarcophagusSlotX = INITIAL_FREE_SARCOPHAGUS_SLOT_X;
|
||||
_movingSarcophagus = false;
|
||||
}
|
||||
|
||||
void Parallaction_ns::updateWalkers() {
|
||||
_walker->walk();
|
||||
}
|
||||
|
||||
|
||||
void Parallaction_ns::scheduleWalk(int16 x, int16 y, bool fromUser) {
|
||||
AnimationPtr a = _char._ani;
|
||||
|
||||
if ((a->_flags & kFlagsRemove) || (a->_flags & kFlagsActive) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
_walker->buildPath(a, x, y);
|
||||
g_engineFlags |= kEngineWalking;
|
||||
}
|
||||
|
||||
}// namespace Parallaction
|
||||
262
engines/parallaction/parser.cpp
Normal file
262
engines/parallaction/parser.cpp
Normal file
@@ -0,0 +1,262 @@
|
||||
/* 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/textconsole.h"
|
||||
|
||||
#include "parallaction/parallaction.h"
|
||||
#include "parallaction/parser.h"
|
||||
|
||||
|
||||
namespace Parallaction {
|
||||
|
||||
#define MAX_TOKENS 50
|
||||
|
||||
int _numTokens;
|
||||
char _tokens[MAX_TOKENS][MAX_TOKEN_LEN];
|
||||
|
||||
Script::Script(Common::ReadStream *input, bool disposeSource) : _input(input), _disposeSource(disposeSource), _line(0) {}
|
||||
|
||||
Script::~Script() {
|
||||
if (_disposeSource)
|
||||
delete _input;
|
||||
}
|
||||
|
||||
/*
|
||||
* readLineIntern read a text line and prepares it for
|
||||
* parsing, by stripping the leading whitespace and
|
||||
* changing tabs to spaces. It will stop on a CR, LF, or
|
||||
* SUB (0x1A), which may all occur at the end of a script
|
||||
* line.
|
||||
* Returns an empty string (length = 0) when a line
|
||||
* has no printable text in it.
|
||||
*/
|
||||
char *Script::readLineIntern(char *buf, size_t bufSize) {
|
||||
uint i = 0;
|
||||
for ( ; i < bufSize; ) {
|
||||
char c = _input->readSByte();
|
||||
if (_input->eos())
|
||||
break;
|
||||
// break if EOL
|
||||
if (c == '\n' || c == '\r' || c == (char)0x1A)
|
||||
break;
|
||||
if (c == '\t')
|
||||
c = ' ';
|
||||
|
||||
if ((c != ' ') || (i > 0)) {
|
||||
buf[i] = c;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
_line++;
|
||||
if (i == bufSize) {
|
||||
warning("overflow in readLineIntern (line %i)", _line);
|
||||
}
|
||||
if (i == 0 && _input->eos()) {
|
||||
return nullptr;
|
||||
}
|
||||
buf[i] = '\0';
|
||||
return buf;
|
||||
}
|
||||
|
||||
bool isCommentLine(char *text) {
|
||||
return text[0] == '#';
|
||||
}
|
||||
|
||||
bool isStartOfCommentBlock(char *text) {
|
||||
return (text[0] == '[');
|
||||
}
|
||||
|
||||
bool isEndOfCommentBlock(char *text) {
|
||||
return (text[0] == ']');
|
||||
}
|
||||
|
||||
char *Script::readLine(char *buf, size_t bufSize) {
|
||||
bool inBlockComment = false;
|
||||
bool ignoreLine = true;
|
||||
|
||||
char *line = nullptr;
|
||||
do {
|
||||
line = readLineIntern(buf, bufSize);
|
||||
if (line == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (line[0] == '\0')
|
||||
continue;
|
||||
|
||||
ignoreLine = false;
|
||||
|
||||
line = Common::ltrim(line);
|
||||
if (isCommentLine(line)) {
|
||||
// ignore this line
|
||||
ignoreLine = true;
|
||||
} else
|
||||
if (isStartOfCommentBlock(line)) {
|
||||
// mark this and the following lines as comment
|
||||
inBlockComment = true;
|
||||
} else
|
||||
if (isEndOfCommentBlock(line)) {
|
||||
// comment is finished, so stop ignoring
|
||||
inBlockComment = false;
|
||||
// the current line must be skipped, though,
|
||||
// as it contains the end-of-comment marker
|
||||
ignoreLine = true;
|
||||
}
|
||||
|
||||
} while (inBlockComment || ignoreLine);
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Script::clearTokens() {
|
||||
memset(_tokens, 0, sizeof(_tokens));
|
||||
_numTokens = 0;
|
||||
}
|
||||
|
||||
void Script::skip(const char* endToken) {
|
||||
|
||||
while (scumm_stricmp(_tokens[0], endToken)) {
|
||||
readLineToken(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
// Scans 's' until one of the stop-chars in 'brk' is found, building a token.
|
||||
// If the routine encounters quotes, it will extract the contained text and
|
||||
// make a proper token. When scanning inside quotes, 'brk' is ignored and
|
||||
// only newlines are considered stop-chars.
|
||||
//
|
||||
// The routine returns the unparsed portion of the input string 's'.
|
||||
//
|
||||
char *Script::parseNextToken(char *s, char *tok, uint16 count, const char *brk) {
|
||||
|
||||
enum STATES { NORMAL, QUOTED };
|
||||
|
||||
STATES state = NORMAL;
|
||||
|
||||
while (count > 0) {
|
||||
|
||||
switch (state) {
|
||||
case NORMAL:
|
||||
if (*s == '\0') {
|
||||
*tok = '\0';
|
||||
return s;
|
||||
} else
|
||||
if (strchr(brk, *s)) {
|
||||
*tok = '\0';
|
||||
return ++s;
|
||||
} else
|
||||
if (*s == '"') {
|
||||
state = QUOTED;
|
||||
s++;
|
||||
} else {
|
||||
*tok++ = *s++;
|
||||
count--;
|
||||
}
|
||||
break;
|
||||
|
||||
case QUOTED:
|
||||
if (*s == '\0') {
|
||||
*tok = '\0';
|
||||
return s;
|
||||
} else
|
||||
if (*s == '"') {
|
||||
*tok = '\0';
|
||||
return ++s;
|
||||
} else {
|
||||
*tok++ = *s++;
|
||||
count--;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
*tok = '\0';
|
||||
// TODO: if execution flows here, make *REALLY* sure everything has been parsed
|
||||
// out of the input string. This is what is supposed to happen, but never ever
|
||||
// allocated time to properly check.
|
||||
|
||||
return tok;
|
||||
|
||||
}
|
||||
|
||||
uint16 Script::readLineToken(bool errorOnEOF) {
|
||||
char buf[200];
|
||||
char *line = readLine(buf, 200);
|
||||
if (!line) {
|
||||
if (errorOnEOF)
|
||||
error("unexpected end of file while parsing");
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
clearTokens();
|
||||
while (*line && _numTokens < MAX_TOKENS) {
|
||||
line = parseNextToken(line, _tokens[_numTokens], MAX_TOKEN_LEN, " ");
|
||||
line = Common::ltrim(line);
|
||||
_numTokens++;
|
||||
}
|
||||
return _numTokens;
|
||||
}
|
||||
|
||||
|
||||
void Parser::reset() {
|
||||
_currentOpcodes = nullptr;
|
||||
_currentStatements = nullptr;
|
||||
_lookup = 0;
|
||||
|
||||
_statements.clear();
|
||||
_opcodes.clear();
|
||||
}
|
||||
|
||||
void Parser::pushTables(OpcodeSet *opcodes, Table *statements) {
|
||||
_opcodes.push(_currentOpcodes);
|
||||
_statements.push(_currentStatements);
|
||||
|
||||
_currentOpcodes = opcodes;
|
||||
_currentStatements = statements;
|
||||
}
|
||||
|
||||
void Parser::popTables() {
|
||||
assert(_opcodes.size() > 0);
|
||||
|
||||
_currentOpcodes = _opcodes.pop();
|
||||
_currentStatements = _statements.pop();
|
||||
}
|
||||
|
||||
void Parser::parseStatement() {
|
||||
assert(_currentOpcodes != nullptr);
|
||||
|
||||
_lookup = _currentStatements->lookup(_tokens[0]);
|
||||
|
||||
debugC(9, kDebugParser, "parseStatement: %s (lookup = %i)", _tokens[0], _lookup);
|
||||
|
||||
(*(*_currentOpcodes)[_lookup])();
|
||||
}
|
||||
|
||||
} // End of namespace Parallaction
|
||||
416
engines/parallaction/parser.h
Normal file
416
engines/parallaction/parser.h
Normal file
@@ -0,0 +1,416 @@
|
||||
/* 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 PARALLACTION_PARSER_H
|
||||
#define PARALLACTION_PARSER_H
|
||||
|
||||
#include "common/stream.h"
|
||||
#include "common/stack.h"
|
||||
#include "parallaction/objects.h"
|
||||
|
||||
namespace Parallaction {
|
||||
|
||||
#define MAX_TOKEN_LEN 50
|
||||
extern int _numTokens;
|
||||
extern char _tokens[][MAX_TOKEN_LEN];
|
||||
|
||||
class Script {
|
||||
|
||||
Common::ReadStream *_input;
|
||||
bool _disposeSource;
|
||||
uint _line; // for debug messages
|
||||
|
||||
void clearTokens();
|
||||
char *parseNextToken(char *s, char *tok, uint16 count, const char *brk);
|
||||
char *readLineIntern(char *buf, size_t bufSize);
|
||||
|
||||
public:
|
||||
Script(Common::ReadStream *, bool _disposeSource = false);
|
||||
~Script();
|
||||
|
||||
char *readLine(char *buf, size_t bufSize);
|
||||
uint16 readLineToken(bool errorOnEOF = false);
|
||||
|
||||
void skip(const char* endToken);
|
||||
|
||||
uint getLine() { return _line; }
|
||||
};
|
||||
|
||||
|
||||
typedef Common::Functor0<void> Opcode;
|
||||
typedef Common::Array<const Opcode *> OpcodeSet;
|
||||
|
||||
|
||||
|
||||
class Parser {
|
||||
|
||||
public:
|
||||
Parser() { reset(); }
|
||||
~Parser() { reset(); }
|
||||
|
||||
uint _lookup;
|
||||
|
||||
Common::Stack<OpcodeSet *> _opcodes;
|
||||
Common::Stack<Table *> _statements;
|
||||
|
||||
OpcodeSet *_currentOpcodes;
|
||||
Table *_currentStatements;
|
||||
|
||||
void reset();
|
||||
void pushTables(OpcodeSet *opcodes, Table* statements);
|
||||
void popTables();
|
||||
void parseStatement();
|
||||
|
||||
};
|
||||
|
||||
#define DECLARE_UNQUALIFIED_ZONE_PARSER(sig) void locZoneParse_##sig()
|
||||
#define DECLARE_UNQUALIFIED_ANIM_PARSER(sig) void locAnimParse_##sig()
|
||||
#define DECLARE_UNQUALIFIED_COMMAND_PARSER(sig) void cmdParse_##sig()
|
||||
#define DECLARE_UNQUALIFIED_LOCATION_PARSER(sig) void locParse_##sig()
|
||||
#define DECLARE_UNQUALIFIED_INSTRUCTION_PARSER(sig) void instParse_##sig()
|
||||
|
||||
#define MAX_FORWARDS 50
|
||||
|
||||
class Parallaction_ns;
|
||||
class Parallaction_br;
|
||||
|
||||
|
||||
class LocationParser_ns {
|
||||
|
||||
protected:
|
||||
Parallaction_ns* _vm;
|
||||
Script *_script;
|
||||
Parser *_parser;
|
||||
|
||||
Table *_zoneTypeNames;
|
||||
Table *_zoneFlagNames;
|
||||
uint _zoneProg;
|
||||
|
||||
// location parser
|
||||
OpcodeSet _locationParsers;
|
||||
OpcodeSet _locationZoneParsers;
|
||||
OpcodeSet _locationAnimParsers;
|
||||
OpcodeSet _commandParsers;
|
||||
Table *_commandsNames;
|
||||
Table *_locationStmt;
|
||||
Table *_locationZoneStmt;
|
||||
Table *_locationAnimStmt;
|
||||
|
||||
struct ParserContext {
|
||||
bool end;
|
||||
|
||||
const char *filename;
|
||||
ZonePtr z;
|
||||
AnimationPtr a;
|
||||
int nextToken;
|
||||
CommandList *list;
|
||||
bool endcommands;
|
||||
CommandPtr cmd;
|
||||
} ctxt;
|
||||
|
||||
void warning_unexpected();
|
||||
|
||||
DECLARE_UNQUALIFIED_LOCATION_PARSER(endlocation);
|
||||
DECLARE_UNQUALIFIED_LOCATION_PARSER(location);
|
||||
DECLARE_UNQUALIFIED_LOCATION_PARSER(disk);
|
||||
DECLARE_UNQUALIFIED_LOCATION_PARSER(nodes);
|
||||
DECLARE_UNQUALIFIED_LOCATION_PARSER(zone);
|
||||
DECLARE_UNQUALIFIED_LOCATION_PARSER(animation);
|
||||
DECLARE_UNQUALIFIED_LOCATION_PARSER(localflags);
|
||||
DECLARE_UNQUALIFIED_LOCATION_PARSER(commands);
|
||||
DECLARE_UNQUALIFIED_LOCATION_PARSER(acommands);
|
||||
DECLARE_UNQUALIFIED_LOCATION_PARSER(flags);
|
||||
DECLARE_UNQUALIFIED_LOCATION_PARSER(comment);
|
||||
DECLARE_UNQUALIFIED_LOCATION_PARSER(endcomment);
|
||||
DECLARE_UNQUALIFIED_LOCATION_PARSER(sound);
|
||||
DECLARE_UNQUALIFIED_LOCATION_PARSER(music);
|
||||
DECLARE_UNQUALIFIED_ZONE_PARSER(limits);
|
||||
DECLARE_UNQUALIFIED_ZONE_PARSER(moveto);
|
||||
DECLARE_UNQUALIFIED_ZONE_PARSER(type);
|
||||
DECLARE_UNQUALIFIED_ZONE_PARSER(commands);
|
||||
DECLARE_UNQUALIFIED_ZONE_PARSER(label);
|
||||
DECLARE_UNQUALIFIED_ZONE_PARSER(flags);
|
||||
DECLARE_UNQUALIFIED_ZONE_PARSER(endzone);
|
||||
DECLARE_UNQUALIFIED_ZONE_PARSER(null);
|
||||
DECLARE_UNQUALIFIED_ANIM_PARSER(script);
|
||||
DECLARE_UNQUALIFIED_ANIM_PARSER(commands);
|
||||
DECLARE_UNQUALIFIED_ANIM_PARSER(type);
|
||||
DECLARE_UNQUALIFIED_ANIM_PARSER(label);
|
||||
DECLARE_UNQUALIFIED_ANIM_PARSER(flags);
|
||||
DECLARE_UNQUALIFIED_ANIM_PARSER(file);
|
||||
DECLARE_UNQUALIFIED_ANIM_PARSER(position);
|
||||
DECLARE_UNQUALIFIED_ANIM_PARSER(moveto);
|
||||
DECLARE_UNQUALIFIED_ANIM_PARSER(endanimation);
|
||||
DECLARE_UNQUALIFIED_COMMAND_PARSER(flags);
|
||||
DECLARE_UNQUALIFIED_COMMAND_PARSER(animation);
|
||||
DECLARE_UNQUALIFIED_COMMAND_PARSER(zone);
|
||||
DECLARE_UNQUALIFIED_COMMAND_PARSER(location);
|
||||
DECLARE_UNQUALIFIED_COMMAND_PARSER(invObject);
|
||||
DECLARE_UNQUALIFIED_COMMAND_PARSER(call);
|
||||
DECLARE_UNQUALIFIED_COMMAND_PARSER(simple);
|
||||
DECLARE_UNQUALIFIED_COMMAND_PARSER(move);
|
||||
DECLARE_UNQUALIFIED_COMMAND_PARSER(endcommands);
|
||||
public:
|
||||
virtual void parseGetData(ZonePtr z);
|
||||
virtual void parseExamineData(ZonePtr z);
|
||||
virtual void parseDoorData(ZonePtr z);
|
||||
virtual void parseMergeData(ZonePtr z);
|
||||
virtual void parseHearData(ZonePtr z);
|
||||
virtual void parseSpeakData(ZonePtr z);
|
||||
virtual void parseNoneData(ZonePtr z);
|
||||
protected:
|
||||
Common::String parseComment();
|
||||
Common::String parseDialogueString();
|
||||
Dialogue *parseDialogue();
|
||||
virtual Answer *parseAnswer();
|
||||
void parseAnswerFlags(Answer *answer);
|
||||
void parseAnswerBody(Answer *answer);
|
||||
void parseQuestion(Question *q);
|
||||
|
||||
uint32 buildZoneType(const char *t0, const char* t1);
|
||||
|
||||
void parseZone(ZoneList &list, char *name);
|
||||
virtual void parseZoneTypeBlock(ZonePtr z);
|
||||
void parsePointList(PointList &list);
|
||||
void parseAnimation(AnimationList &list, char *name);
|
||||
void parseCommands(CommandList&);
|
||||
void parseCommandFlags();
|
||||
void parseCommandFlag(CommandPtr cmd, const char *flag, Table *table);
|
||||
void createCommand(uint id);
|
||||
void addCommand();
|
||||
|
||||
void clearSet(OpcodeSet &opcodes) {
|
||||
for (Common::Array<const Opcode *>::iterator i = opcodes.begin(); i != opcodes.end(); ++i)
|
||||
delete *i;
|
||||
opcodes.clear();
|
||||
}
|
||||
|
||||
public:
|
||||
LocationParser_ns(Parallaction_ns *vm) : _vm(vm), _commandsNames(0), _locationStmt(0),
|
||||
_locationZoneStmt(0), _locationAnimStmt(0) {
|
||||
_script = 0;
|
||||
_parser = 0;
|
||||
_zoneTypeNames = 0;
|
||||
_zoneFlagNames = 0;
|
||||
_zoneProg = 0;
|
||||
}
|
||||
|
||||
virtual void init();
|
||||
|
||||
virtual ~LocationParser_ns() {
|
||||
delete _parser;
|
||||
delete _commandsNames;
|
||||
delete _locationStmt;
|
||||
delete _locationZoneStmt;
|
||||
delete _locationAnimStmt;
|
||||
delete _zoneTypeNames;
|
||||
delete _zoneFlagNames;
|
||||
|
||||
clearSet(_commandParsers);
|
||||
clearSet(_locationAnimParsers);
|
||||
clearSet(_locationZoneParsers);
|
||||
clearSet(_locationParsers);
|
||||
}
|
||||
|
||||
void parse(Script *script);
|
||||
|
||||
};
|
||||
|
||||
|
||||
struct LocationParserOutput_br {
|
||||
BackgroundInfo *_info;
|
||||
|
||||
Common::String _characterName;
|
||||
Common::String _backgroundName;
|
||||
Common::String _maskName;
|
||||
Common::String _pathName;
|
||||
};
|
||||
|
||||
class LocationParser_br : public LocationParser_ns {
|
||||
|
||||
protected:
|
||||
Parallaction_br* _vm;
|
||||
Table *_audioCommandsNames;
|
||||
|
||||
LocationParserOutput_br *_out;
|
||||
|
||||
DECLARE_UNQUALIFIED_LOCATION_PARSER(location);
|
||||
DECLARE_UNQUALIFIED_LOCATION_PARSER(zone);
|
||||
DECLARE_UNQUALIFIED_LOCATION_PARSER(animation);
|
||||
DECLARE_UNQUALIFIED_LOCATION_PARSER(localflags);
|
||||
DECLARE_UNQUALIFIED_LOCATION_PARSER(flags);
|
||||
DECLARE_UNQUALIFIED_LOCATION_PARSER(comment);
|
||||
DECLARE_UNQUALIFIED_LOCATION_PARSER(endcomment);
|
||||
DECLARE_UNQUALIFIED_LOCATION_PARSER(sound);
|
||||
DECLARE_UNQUALIFIED_LOCATION_PARSER(music);
|
||||
DECLARE_UNQUALIFIED_LOCATION_PARSER(redundant);
|
||||
DECLARE_UNQUALIFIED_LOCATION_PARSER(ifchar);
|
||||
DECLARE_UNQUALIFIED_LOCATION_PARSER(character);
|
||||
DECLARE_UNQUALIFIED_LOCATION_PARSER(mask);
|
||||
DECLARE_UNQUALIFIED_LOCATION_PARSER(path);
|
||||
DECLARE_UNQUALIFIED_LOCATION_PARSER(escape);
|
||||
DECLARE_UNQUALIFIED_LOCATION_PARSER(zeta);
|
||||
DECLARE_UNQUALIFIED_LOCATION_PARSER(null);
|
||||
DECLARE_UNQUALIFIED_COMMAND_PARSER(ifchar);
|
||||
DECLARE_UNQUALIFIED_COMMAND_PARSER(endif);
|
||||
DECLARE_UNQUALIFIED_COMMAND_PARSER(location);
|
||||
DECLARE_UNQUALIFIED_COMMAND_PARSER(toggle);
|
||||
DECLARE_UNQUALIFIED_COMMAND_PARSER(string);
|
||||
DECLARE_UNQUALIFIED_COMMAND_PARSER(math);
|
||||
DECLARE_UNQUALIFIED_COMMAND_PARSER(test);
|
||||
DECLARE_UNQUALIFIED_COMMAND_PARSER(music);
|
||||
DECLARE_UNQUALIFIED_COMMAND_PARSER(zeta);
|
||||
DECLARE_UNQUALIFIED_COMMAND_PARSER(swap);
|
||||
DECLARE_UNQUALIFIED_COMMAND_PARSER(give);
|
||||
DECLARE_UNQUALIFIED_COMMAND_PARSER(text);
|
||||
DECLARE_UNQUALIFIED_COMMAND_PARSER(unary);
|
||||
DECLARE_UNQUALIFIED_ZONE_PARSER(limits);
|
||||
DECLARE_UNQUALIFIED_ZONE_PARSER(moveto);
|
||||
DECLARE_UNQUALIFIED_ZONE_PARSER(type);
|
||||
DECLARE_UNQUALIFIED_ANIM_PARSER(file);
|
||||
DECLARE_UNQUALIFIED_ANIM_PARSER(position);
|
||||
DECLARE_UNQUALIFIED_ANIM_PARSER(moveto);
|
||||
DECLARE_UNQUALIFIED_ANIM_PARSER(endanimation);
|
||||
|
||||
void parseZoneTypeBlock(ZonePtr z) override;
|
||||
public:
|
||||
virtual void parsePathData(ZonePtr z);
|
||||
void parseGetData(ZonePtr z) override;
|
||||
void parseDoorData(ZonePtr z) override;
|
||||
void parseHearData(ZonePtr z) override;
|
||||
void parseNoneData(ZonePtr z) override;
|
||||
protected:
|
||||
void parseAnswerCounter(Answer *answer);
|
||||
Answer *parseAnswer() override;
|
||||
|
||||
public:
|
||||
LocationParser_br(Parallaction_br *vm) : LocationParser_ns((Parallaction_ns*)vm), _vm(vm),
|
||||
_audioCommandsNames(0), _out(0) {
|
||||
}
|
||||
|
||||
void init() override;
|
||||
|
||||
~LocationParser_br() override {
|
||||
delete _audioCommandsNames;
|
||||
}
|
||||
|
||||
void parse(Script *script, LocationParserOutput_br *out);
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
class ProgramParser_ns {
|
||||
|
||||
protected:
|
||||
Parallaction_ns *_vm;
|
||||
Parser *_parser;
|
||||
|
||||
Script *_script;
|
||||
ProgramPtr _program;
|
||||
|
||||
// program parser
|
||||
OpcodeSet _instructionParsers;
|
||||
Table *_instructionNames;
|
||||
|
||||
uint32 _currentInstruction; // index of the instruction being parsed
|
||||
|
||||
struct ParserContext {
|
||||
bool end;
|
||||
AnimationPtr a;
|
||||
InstructionPtr inst;
|
||||
LocalVariable *locals;
|
||||
} ctxt;
|
||||
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_PARSER(defLocal);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_PARSER(animation);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_PARSER(loop);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_PARSER(x);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_PARSER(y);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_PARSER(z);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_PARSER(f);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_PARSER(inc);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_PARSER(set);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_PARSER(move);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_PARSER(put);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_PARSER(call);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_PARSER(sound);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_PARSER(null);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_PARSER(endscript);
|
||||
|
||||
void parseInstruction();
|
||||
void parseLValue(ScriptVar &var, const char *str);
|
||||
virtual void parseRValue(ScriptVar &var, const char *str);
|
||||
|
||||
void clearSet(OpcodeSet &opcodes) {
|
||||
for (Common::Array<const Opcode *>::iterator i = opcodes.begin(); i != opcodes.end(); ++i)
|
||||
delete *i;
|
||||
opcodes.clear();
|
||||
}
|
||||
|
||||
public:
|
||||
ProgramParser_ns(Parallaction_ns *vm) : _vm(vm), _parser(0), _instructionNames(0), _script(0), _currentInstruction(0) {
|
||||
}
|
||||
|
||||
virtual void init();
|
||||
|
||||
virtual ~ProgramParser_ns() {
|
||||
delete _parser;
|
||||
delete _instructionNames;
|
||||
|
||||
clearSet(_instructionParsers);
|
||||
}
|
||||
|
||||
virtual void parse(Script *script, ProgramPtr program);
|
||||
|
||||
};
|
||||
|
||||
|
||||
class ProgramParser_br : public ProgramParser_ns {
|
||||
|
||||
protected:
|
||||
Parallaction_br *_vm;
|
||||
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_PARSER(zone);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_PARSER(color);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_PARSER(mask);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_PARSER(print);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_PARSER(text);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_PARSER(if_op);
|
||||
DECLARE_UNQUALIFIED_INSTRUCTION_PARSER(endif);
|
||||
|
||||
int32 _openIfStatement;
|
||||
void beginIfStatement();
|
||||
void endIfStatement();
|
||||
|
||||
void parseRValue(ScriptVar &var, const char *str) override;
|
||||
|
||||
public:
|
||||
ProgramParser_br(Parallaction_br *vm) : ProgramParser_ns((Parallaction_ns*)vm), _vm(vm), _openIfStatement(0) {
|
||||
}
|
||||
|
||||
void init() override;
|
||||
void parse(Script *script, ProgramPtr program) override;
|
||||
};
|
||||
|
||||
} // End of namespace Parallaction
|
||||
|
||||
#endif
|
||||
1284
engines/parallaction/parser_br.cpp
Normal file
1284
engines/parallaction/parser_br.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1477
engines/parallaction/parser_ns.cpp
Normal file
1477
engines/parallaction/parser_ns.cpp
Normal file
File diff suppressed because it is too large
Load Diff
347
engines/parallaction/saveload.cpp
Normal file
347
engines/parallaction/saveload.cpp
Normal file
@@ -0,0 +1,347 @@
|
||||
/* 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/savefile.h"
|
||||
#include "common/config-manager.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/translation.h"
|
||||
|
||||
#include "gui/saveload.h"
|
||||
#include "gui/message.h"
|
||||
|
||||
#include "parallaction/parallaction.h"
|
||||
#include "parallaction/saveload.h"
|
||||
#include "parallaction/sound.h"
|
||||
|
||||
/* Nippon Safes savefiles are called 'nippon.000' to 'nippon.099'.
|
||||
*
|
||||
* A special savefile named 'nippon.999' holds information on whether the user completed one or more parts of the game.
|
||||
*/
|
||||
|
||||
#define NUM_SAVESLOTS 100
|
||||
#define SPECIAL_SAVESLOT 999
|
||||
|
||||
namespace Parallaction {
|
||||
|
||||
Common::String SaveLoad::genSaveFileName(uint slot) {
|
||||
assert(slot < NUM_SAVESLOTS || slot == SPECIAL_SAVESLOT);
|
||||
|
||||
char s[20];
|
||||
Common::sprintf_s(s, "%s.%.3u", _saveFilePrefix.c_str(), slot);
|
||||
|
||||
return Common::String(s);
|
||||
}
|
||||
|
||||
Common::InSaveFile *SaveLoad::getInSaveFile(uint slot) {
|
||||
Common::String name = genSaveFileName(slot);
|
||||
return _saveFileMan->openForLoading(name);
|
||||
}
|
||||
|
||||
Common::OutSaveFile *SaveLoad::getOutSaveFile(uint slot) {
|
||||
Common::String name = genSaveFileName(slot);
|
||||
return _saveFileMan->openForSaving(name);
|
||||
}
|
||||
|
||||
void SaveLoad_ns::doLoadGame(uint16 slot) {
|
||||
_vm->cleanupGame();
|
||||
|
||||
Common::InSaveFile *f = getInSaveFile(slot);
|
||||
if (!f) return;
|
||||
|
||||
Common::String s, character, location;
|
||||
|
||||
// scrap the line with the savefile name
|
||||
f->readLine();
|
||||
|
||||
character = f->readLine();
|
||||
location = f->readLine();
|
||||
|
||||
s = f->readLine();
|
||||
_vm->_location._startPosition.x = atoi(s.c_str());
|
||||
|
||||
s = f->readLine();
|
||||
_vm->_location._startPosition.y = atoi(s.c_str());
|
||||
|
||||
s = f->readLine();
|
||||
_vm->_score = atoi(s.c_str());
|
||||
|
||||
s = f->readLine();
|
||||
g_globalFlags = atoi(s.c_str());
|
||||
|
||||
s = f->readLine();
|
||||
_vm->_numLocations = atoi(s.c_str());
|
||||
|
||||
uint16 _si;
|
||||
for (_si = 0; _si < _vm->_numLocations; _si++) {
|
||||
s = f->readLine();
|
||||
Common::strlcpy(_vm->_locationNames[_si], s.c_str(), 32);
|
||||
|
||||
s = f->readLine();
|
||||
_vm->_localFlags[_si] = atoi(s.c_str());
|
||||
}
|
||||
|
||||
_vm->cleanInventory(false);
|
||||
ItemName name;
|
||||
uint32 value;
|
||||
|
||||
for (_si = 0; _si < 30; _si++) {
|
||||
s = f->readLine();
|
||||
value = atoi(s.c_str());
|
||||
|
||||
s = f->readLine();
|
||||
name = atoi(s.c_str());
|
||||
|
||||
_vm->addInventoryItem(name, value);
|
||||
}
|
||||
|
||||
delete f;
|
||||
|
||||
// force reload of character to solve inventory
|
||||
// bugs, but it's a good maneuver anyway
|
||||
Common::strcpy_s(_vm->_characterName1, "null");
|
||||
|
||||
char tmp[PATH_LEN];
|
||||
Common::sprintf_s(tmp, "%s.%s" , location.c_str(), character.c_str());
|
||||
_vm->scheduleLocationSwitch(tmp);
|
||||
}
|
||||
|
||||
void SaveLoad_ns::doSaveGame(uint16 slot, const char* name) {
|
||||
Common::OutSaveFile *f = getOutSaveFile(slot);
|
||||
if (f == nullptr) {
|
||||
Common::U32String buf = Common::U32String::format(_("Can't save game in slot %i\n\n"), slot);
|
||||
GUI::MessageDialog dialog(buf);
|
||||
dialog.runModal();
|
||||
return;
|
||||
}
|
||||
|
||||
char s[200];
|
||||
memset(s, 0, sizeof(s));
|
||||
|
||||
if (!name || name[0] == '\0') {
|
||||
Common::sprintf_s(s, "default_%i", slot);
|
||||
} else {
|
||||
strncpy(s, name, 199);
|
||||
}
|
||||
|
||||
f->writeString(s);
|
||||
f->writeString("\n");
|
||||
|
||||
Common::sprintf_s(s, "%s\n", _vm->_char.getFullName());
|
||||
f->writeString(s);
|
||||
|
||||
Common::sprintf_s(s, "%s\n", g_saveData1);
|
||||
f->writeString(s);
|
||||
Common::sprintf_s(s, "%d\n", _vm->_char._ani->getX());
|
||||
f->writeString(s);
|
||||
Common::sprintf_s(s, "%d\n", _vm->_char._ani->getY());
|
||||
f->writeString(s);
|
||||
Common::sprintf_s(s, "%d\n", _vm->_score);
|
||||
f->writeString(s);
|
||||
Common::sprintf_s(s, "%u\n", g_globalFlags);
|
||||
f->writeString(s);
|
||||
|
||||
Common::sprintf_s(s, "%d\n", _vm->_numLocations);
|
||||
f->writeString(s);
|
||||
for (uint16 _si = 0; _si < _vm->_numLocations; _si++) {
|
||||
Common::sprintf_s(s, "%s\n%u\n", _vm->_locationNames[_si], _vm->_localFlags[_si]);
|
||||
f->writeString(s);
|
||||
}
|
||||
|
||||
const InventoryItem *item;
|
||||
for (uint16 _si = 0; _si < 30; _si++) {
|
||||
item = _vm->getInventoryItem(_si);
|
||||
Common::sprintf_s(s, "%u\n%d\n", item->_id, item->_index);
|
||||
f->writeString(s);
|
||||
}
|
||||
|
||||
delete f;
|
||||
}
|
||||
|
||||
int SaveLoad::selectSaveFile(Common::String &selectedName, bool saveMode, const Common::U32String &caption, const Common::U32String &button) {
|
||||
GUI::SaveLoadChooser slc(caption, button, saveMode);
|
||||
|
||||
selectedName.clear();
|
||||
|
||||
int idx = slc.runModalWithCurrentTarget();
|
||||
if (idx >= 0) {
|
||||
selectedName = slc.getResultString();
|
||||
}
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
bool SaveLoad::loadGame() {
|
||||
Common::String null;
|
||||
int _di = selectSaveFile(null, false, _("Load file"), _("Load"));
|
||||
if (_di == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
doLoadGame(_di);
|
||||
|
||||
GUI::TimedMessageDialog dialog(_("Loading game..."), 1500);
|
||||
dialog.runModal();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SaveLoad::saveGame() {
|
||||
Common::String saveName;
|
||||
int slot = selectSaveFile(saveName, true, _("Save file"), _("Save"));
|
||||
if (slot == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
doSaveGame(slot, saveName.c_str());
|
||||
|
||||
GUI::TimedMessageDialog dialog(_("Saving game..."), 1500);
|
||||
dialog.runModal();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SaveLoad_ns::saveGame() {
|
||||
// NOTE: shouldn't this check be done before, so that the
|
||||
// user can't even select 'save'?
|
||||
if (!scumm_stricmp(_vm->_location._name, "caveau")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return SaveLoad::saveGame();
|
||||
}
|
||||
|
||||
void SaveLoad_ns::setPartComplete(const char *part) {
|
||||
Common::String s;
|
||||
bool alreadyPresent = false;
|
||||
|
||||
Common::InSaveFile *inFile = getInSaveFile(SPECIAL_SAVESLOT);
|
||||
if (inFile) {
|
||||
s = inFile->readLine();
|
||||
delete inFile;
|
||||
|
||||
if (s.contains(part)) {
|
||||
alreadyPresent = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!alreadyPresent) {
|
||||
Common::OutSaveFile *outFile = getOutSaveFile(SPECIAL_SAVESLOT);
|
||||
outFile->writeString(s);
|
||||
outFile->writeString(part);
|
||||
outFile->finalize();
|
||||
delete outFile;
|
||||
}
|
||||
}
|
||||
|
||||
void SaveLoad_ns::getGamePartProgress(bool *complete, int size) {
|
||||
assert(complete && size >= 3);
|
||||
|
||||
Common::InSaveFile *inFile = getInSaveFile(SPECIAL_SAVESLOT);
|
||||
Common::String s = inFile->readLine();
|
||||
delete inFile;
|
||||
|
||||
complete[0] = s.contains("dino");
|
||||
complete[1] = s.contains("donna");
|
||||
complete[2] = s.contains("dough");
|
||||
}
|
||||
|
||||
static bool askRenameOldSavefiles() {
|
||||
GUI::MessageDialog dialog0(
|
||||
_("ScummVM found that you have old saved games for Nippon Safes that should be renamed.\n"
|
||||
"The old names are no longer supported, so you will not be able to load your games if you don't convert them.\n\n"
|
||||
"Press OK to convert them now, otherwise you will be asked next time.\n"), _("OK"), _("Cancel"));
|
||||
|
||||
return (dialog0.runModal() == GUI::kMessageOK);
|
||||
}
|
||||
|
||||
void SaveLoad_ns::renameOldSavefiles() {
|
||||
Common::StringArray oldFilenames = _saveFileMan->listSavefiles("game.*");
|
||||
uint numOldSaves = oldFilenames.size();
|
||||
|
||||
bool rename = false;
|
||||
uint success = 0, id;
|
||||
Common::String oldName, newName;
|
||||
for (uint i = 0; i < oldFilenames.size(); ++i) {
|
||||
oldName = oldFilenames[i];
|
||||
int e = sscanf(oldName.c_str(), "game.%u", &id);
|
||||
if (e != 1) {
|
||||
// this wasn't a savefile, so adjust numOldSaves accordingly
|
||||
--numOldSaves;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!rename) {
|
||||
rename = askRenameOldSavefiles();
|
||||
}
|
||||
if (!rename) {
|
||||
// return immediately if the user doesn't want to rename the files
|
||||
return;
|
||||
}
|
||||
|
||||
newName = genSaveFileName(id);
|
||||
if (_saveFileMan->renameSavefile(oldName, newName)) {
|
||||
success++;
|
||||
} else {
|
||||
warning("Error %i (%s) occurred while renaming %s to %s", _saveFileMan->getError().getCode(),
|
||||
_saveFileMan->getErrorDesc().c_str(), oldName.c_str(), newName.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
if (numOldSaves == 0) {
|
||||
// there were no old savefiles: nothing to notify
|
||||
return;
|
||||
}
|
||||
|
||||
Common::U32String msg;
|
||||
if (success == numOldSaves) {
|
||||
msg = _("ScummVM successfully converted all your saved games.");
|
||||
} else {
|
||||
msg = _("ScummVM printed some warnings in your console window and can't guarantee all your files have been converted.\n\n"
|
||||
"Please report to the team.");
|
||||
}
|
||||
|
||||
GUI::MessageDialog dialog1(msg);
|
||||
dialog1.runModal();
|
||||
}
|
||||
|
||||
void SaveLoad_br::doLoadGame(uint16 slot) {
|
||||
// TODO: implement loadgame
|
||||
}
|
||||
|
||||
void SaveLoad_br::doSaveGame(uint16 slot, const char* name) {
|
||||
// TODO: implement savegame
|
||||
}
|
||||
|
||||
void SaveLoad_br::getGamePartProgress(bool *complete, int size) {
|
||||
assert(complete && size >= 3);
|
||||
|
||||
// TODO: implement progress loading
|
||||
|
||||
complete[0] = true;
|
||||
complete[1] = true;
|
||||
complete[2] = true;
|
||||
}
|
||||
|
||||
void SaveLoad_br::setPartComplete(const char *part) {
|
||||
// TODO: implement progress saving
|
||||
}
|
||||
|
||||
} // namespace Parallaction
|
||||
86
engines/parallaction/saveload.h
Normal file
86
engines/parallaction/saveload.h
Normal file
@@ -0,0 +1,86 @@
|
||||
/* 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 PARALLACTION_SAVELOAD_H
|
||||
#define PARALLACTION_SAVELOAD_H
|
||||
|
||||
namespace Parallaction {
|
||||
|
||||
struct Character;
|
||||
|
||||
class SaveLoad {
|
||||
protected:
|
||||
Common::SaveFileManager *_saveFileMan;
|
||||
Common::String _saveFilePrefix;
|
||||
|
||||
Common::String genSaveFileName(uint slot);
|
||||
Common::InSaveFile *getInSaveFile(uint slot);
|
||||
Common::OutSaveFile *getOutSaveFile(uint slot);
|
||||
int selectSaveFile(Common::String &selectedName, bool saveMode, const Common::U32String &caption, const Common::U32String &button);
|
||||
int buildSaveFileList(Common::StringArray& l);
|
||||
virtual void doLoadGame(uint16 slot) = 0;
|
||||
virtual void doSaveGame(uint16 slot, const char* name) = 0;
|
||||
|
||||
public:
|
||||
SaveLoad(Common::SaveFileManager* saveFileMan, const char *prefix) : _saveFileMan(saveFileMan), _saveFilePrefix(prefix) { }
|
||||
virtual ~SaveLoad() { }
|
||||
|
||||
virtual bool loadGame();
|
||||
virtual bool saveGame();
|
||||
|
||||
virtual void getGamePartProgress(bool *complete, int size) = 0;
|
||||
virtual void setPartComplete(const char *part) = 0;
|
||||
|
||||
virtual void renameOldSavefiles() { }
|
||||
};
|
||||
|
||||
class SaveLoad_ns : public SaveLoad {
|
||||
Parallaction_ns *_vm;
|
||||
|
||||
protected:
|
||||
void renameOldSavefiles() override;
|
||||
void doLoadGame(uint16 slot) override;
|
||||
void doSaveGame(uint16 slot, const char* name) override;
|
||||
|
||||
public:
|
||||
SaveLoad_ns(Parallaction_ns *vm, Common::SaveFileManager *saveFileMan) : SaveLoad(saveFileMan, "nippon"), _vm(vm) { }
|
||||
|
||||
bool saveGame() override;
|
||||
|
||||
void getGamePartProgress(bool *complete, int size) override;
|
||||
void setPartComplete(const char *part) override;
|
||||
};
|
||||
|
||||
class SaveLoad_br : public SaveLoad {
|
||||
// Parallaction_br *_vm;
|
||||
void doLoadGame(uint16 slot) override;
|
||||
void doSaveGame(uint16 slot, const char* name) override;
|
||||
|
||||
public:
|
||||
SaveLoad_br(Parallaction_br *vm, Common::SaveFileManager *saveFileMan) : SaveLoad(saveFileMan, "bra") { }
|
||||
|
||||
void getGamePartProgress(bool *complete, int size) override;
|
||||
void setPartComplete(const char *part) override;
|
||||
};
|
||||
|
||||
} // namespace Parallaction
|
||||
|
||||
#endif
|
||||
258
engines/parallaction/sound.h
Normal file
258
engines/parallaction/sound.h
Normal file
@@ -0,0 +1,258 @@
|
||||
/* 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 PARALLACTION_MUSIC_H
|
||||
#define PARALLACTION_MUSIC_H
|
||||
|
||||
#include "common/util.h"
|
||||
#include "common/mutex.h"
|
||||
|
||||
#include "audio/mixer.h"
|
||||
#include "audio/audiostream.h"
|
||||
#include "audio/decoders/iff_sound.h"
|
||||
|
||||
#define PATH_LEN 200
|
||||
|
||||
class MidiParser;
|
||||
class MidiDriver;
|
||||
|
||||
namespace Parallaction {
|
||||
|
||||
class Parallaction_ns;
|
||||
class MidiPlayer;
|
||||
class Parallaction_br;
|
||||
class MidiPlayer_MSC;
|
||||
|
||||
MidiDriver *createAdLibDriver();
|
||||
|
||||
class SoundManImpl {
|
||||
public:
|
||||
virtual void execute(int command, const char *parm = 0) = 0;
|
||||
virtual ~SoundManImpl() { }
|
||||
};
|
||||
|
||||
class SoundMan {
|
||||
SoundManImpl *_impl;
|
||||
public:
|
||||
SoundMan(SoundManImpl *impl) : _impl(impl) { }
|
||||
virtual ~SoundMan() { delete _impl; }
|
||||
void execute(int command, int32 parm) {
|
||||
char n[12];
|
||||
Common::sprintf_s(n, "%i", parm);
|
||||
execute(command, n);
|
||||
}
|
||||
void execute(int command, const char *parm = 0) {
|
||||
if (_impl) {
|
||||
_impl->execute(command, parm);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
enum {
|
||||
// soundMan commands
|
||||
SC_PLAYMUSIC,
|
||||
SC_STOPMUSIC,
|
||||
SC_SETMUSICTYPE,
|
||||
SC_SETMUSICFILE,
|
||||
SC_PLAYSFX,
|
||||
SC_STOPSFX,
|
||||
SC_SETSFXCHANNEL,
|
||||
SC_SETSFXLOOPING,
|
||||
SC_SETSFXVOLUME,
|
||||
SC_SETSFXRATE,
|
||||
SC_PAUSE
|
||||
};
|
||||
|
||||
struct Channel {
|
||||
Audio::AudioStream *stream;
|
||||
Audio::SoundHandle handle;
|
||||
uint32 volume;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class SoundMan_ns : public SoundManImpl {
|
||||
public:
|
||||
enum {
|
||||
MUSIC_ANY,
|
||||
MUSIC_CHARACTER,
|
||||
MUSIC_LOCATION
|
||||
};
|
||||
|
||||
protected:
|
||||
Parallaction_ns *_vm;
|
||||
Audio::Mixer *_mixer;
|
||||
char _musicFile[PATH_LEN];
|
||||
|
||||
bool _sfxLooping;
|
||||
int _sfxVolume;
|
||||
int _sfxRate;
|
||||
uint _sfxChannel;
|
||||
|
||||
int _musicType;
|
||||
|
||||
public:
|
||||
SoundMan_ns(Parallaction_ns *vm);
|
||||
~SoundMan_ns() override {}
|
||||
|
||||
virtual void playSfx(const char *filename, uint channel, bool looping, int volume = -1) { }
|
||||
virtual void stopSfx(uint channel) { }
|
||||
|
||||
void setMusicFile(const char *filename);
|
||||
virtual void playMusic() = 0;
|
||||
virtual void stopMusic() = 0;
|
||||
virtual void playCharacterMusic(const char *character) = 0;
|
||||
virtual void playLocationMusic(const char *location) = 0;
|
||||
virtual void pause(bool p) { }
|
||||
void execute(int command, const char *parm) override;
|
||||
|
||||
void setMusicVolume(int value);
|
||||
};
|
||||
|
||||
class DosSoundMan_ns : public SoundMan_ns {
|
||||
|
||||
MidiPlayer *_midiPlayer;
|
||||
bool _playing;
|
||||
|
||||
bool isLocationSilent(const char *locationName);
|
||||
bool locationHasOwnSoftMusic(const char *locationName);
|
||||
|
||||
|
||||
public:
|
||||
DosSoundMan_ns(Parallaction_ns *vm);
|
||||
~DosSoundMan_ns() override;
|
||||
void playMusic() override;
|
||||
void stopMusic() override;
|
||||
|
||||
void playCharacterMusic(const char *character) override;
|
||||
void playLocationMusic(const char *location) override;
|
||||
|
||||
void pause(bool p) override;
|
||||
};
|
||||
|
||||
#define NUM_SFX_CHANNELS 4
|
||||
|
||||
class AmigaSoundMan_ns : public SoundMan_ns {
|
||||
|
||||
Audio::AudioStream *_musicStream;
|
||||
Audio::SoundHandle _musicHandle;
|
||||
|
||||
uint32 beepSoundBufferSize;
|
||||
int8 *beepSoundBuffer;
|
||||
|
||||
Channel _channels[NUM_SFX_CHANNELS];
|
||||
|
||||
Audio::AudioStream *loadChannelData(const char *filename, Channel *ch, bool looping);
|
||||
|
||||
public:
|
||||
AmigaSoundMan_ns(Parallaction_ns *vm);
|
||||
~AmigaSoundMan_ns() override;
|
||||
void playMusic() override;
|
||||
void stopMusic() override;
|
||||
|
||||
void playSfx(const char *filename, uint channel, bool looping, int volume) override;
|
||||
void stopSfx(uint channel) override;
|
||||
|
||||
void playCharacterMusic(const char *character) override;
|
||||
void playLocationMusic(const char *location) override;
|
||||
};
|
||||
|
||||
class DummySoundMan : public SoundManImpl {
|
||||
public:
|
||||
void execute(int command, const char *parm) override { }
|
||||
};
|
||||
|
||||
class SoundMan_br : public SoundManImpl {
|
||||
protected:
|
||||
Parallaction_br *_vm;
|
||||
Audio::Mixer *_mixer;
|
||||
|
||||
Common::String _musicFile;
|
||||
|
||||
bool _sfxLooping;
|
||||
int _sfxVolume;
|
||||
int _sfxRate;
|
||||
uint _sfxChannel;
|
||||
|
||||
bool _musicEnabled;
|
||||
bool _sfxEnabled;
|
||||
|
||||
Channel _channels[NUM_SFX_CHANNELS];
|
||||
|
||||
virtual void playMusic() = 0;
|
||||
virtual void stopMusic() = 0;
|
||||
virtual void pause(bool p) = 0;
|
||||
|
||||
public:
|
||||
SoundMan_br(Parallaction_br *vm);
|
||||
~SoundMan_br() override;
|
||||
|
||||
virtual void playSfx(const char *filename, uint channel, bool looping, int volume = -1) { }
|
||||
void stopSfx(uint channel);
|
||||
void stopAllSfx();
|
||||
|
||||
void execute(int command, const char *parm) override;
|
||||
void setMusicFile(const char *parm);
|
||||
|
||||
void enableSfx(bool enable);
|
||||
void enableMusic(bool enable);
|
||||
bool isSfxEnabled() const;
|
||||
bool isMusicEnabled() const;
|
||||
};
|
||||
|
||||
class DosSoundMan_br : public SoundMan_br {
|
||||
|
||||
MidiPlayer_MSC *_midiPlayer;
|
||||
|
||||
Audio::AudioStream *loadChannelData(const char *filename, Channel *ch, bool looping);
|
||||
|
||||
public:
|
||||
DosSoundMan_br(Parallaction_br *vm);
|
||||
~DosSoundMan_br() override;
|
||||
|
||||
void playMusic() override;
|
||||
void stopMusic() override;
|
||||
void pause(bool p) override;
|
||||
|
||||
void playSfx(const char *filename, uint channel, bool looping, int volume) override;
|
||||
};
|
||||
|
||||
class AmigaSoundMan_br : public SoundMan_br {
|
||||
|
||||
Audio::AudioStream *_musicStream;
|
||||
Audio::SoundHandle _musicHandle;
|
||||
|
||||
Audio::AudioStream *loadChannelData(const char *filename, Channel *ch, bool looping);
|
||||
|
||||
public:
|
||||
AmigaSoundMan_br(Parallaction_br *vm);
|
||||
~AmigaSoundMan_br() override;
|
||||
|
||||
void playMusic() override;
|
||||
void stopMusic() override;
|
||||
void pause(bool p) override;
|
||||
|
||||
void playSfx(const char *filename, uint channel, bool looping, int volume) override;
|
||||
};
|
||||
|
||||
} // namespace Parallaction
|
||||
|
||||
#endif
|
||||
583
engines/parallaction/sound_br.cpp
Normal file
583
engines/parallaction/sound_br.cpp
Normal file
@@ -0,0 +1,583 @@
|
||||
/* 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/stream.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/util.h"
|
||||
|
||||
#include "audio/mixer.h"
|
||||
#include "audio/midiparser.h"
|
||||
#include "audio/midiplayer.h"
|
||||
#include "audio/mods/protracker.h"
|
||||
#include "audio/decoders/raw.h"
|
||||
|
||||
#include "parallaction/disk.h"
|
||||
#include "parallaction/parallaction.h"
|
||||
#include "parallaction/sound.h"
|
||||
|
||||
|
||||
namespace Parallaction {
|
||||
|
||||
|
||||
/*
|
||||
* List of calls to the original music driver.
|
||||
*
|
||||
*
|
||||
* 1 set music buffer segment
|
||||
* 2 set music buffer offset
|
||||
* 3 set music buffer size
|
||||
* 4 play/resume
|
||||
* 5 stop
|
||||
* 6 pause
|
||||
* 7 set channel volume
|
||||
* 8 set fade in flag
|
||||
* 9 set fade out flag
|
||||
* 10 set global volume
|
||||
* 11 shutdown
|
||||
* 12 get status (stopped, playing, paused)
|
||||
* 13 set fade volume change rate
|
||||
* 14 get global volume
|
||||
* 15 get fade in flag
|
||||
* 16 get fade out flag
|
||||
* 17 set tempo
|
||||
* 18 get tempo
|
||||
* 19 get fade volume change rate
|
||||
* 20 get looping flag
|
||||
* 21 set looping flag
|
||||
* 22 get version??
|
||||
* 23 get version??
|
||||
* 24 get busy flag (dsp has pending data)
|
||||
*/
|
||||
|
||||
class MidiParser_MSC : public MidiParser {
|
||||
protected:
|
||||
void parseNextEvent(EventInfo &info) override;
|
||||
bool loadMusic(const byte *data, uint32 size) override;
|
||||
|
||||
uint8 read1(const byte *&data) {
|
||||
return *data++;
|
||||
}
|
||||
|
||||
void parseMetaEvent(EventInfo &info);
|
||||
void parseMidiEvent(EventInfo &info);
|
||||
|
||||
bool byte_11C5A;
|
||||
uint8 _beats;
|
||||
uint8 _lastEvent;
|
||||
const byte *_trackEnd;
|
||||
|
||||
public:
|
||||
MidiParser_MSC() : byte_11C5A(false), _beats(0), _lastEvent(0), _trackEnd(nullptr) {
|
||||
}
|
||||
};
|
||||
|
||||
void MidiParser_MSC::parseMetaEvent(EventInfo &info) {
|
||||
const byte *playPos = _position._subtracks[0]._playPos;
|
||||
|
||||
uint8 type = read1(playPos);
|
||||
uint8 len = read1(playPos);
|
||||
info.ext.type = type;
|
||||
info.length = len;
|
||||
info.ext.data = nullptr;
|
||||
|
||||
if (type == 0x51) {
|
||||
info.ext.data = playPos;
|
||||
} else {
|
||||
warning("unknown meta event 0x%02X", type);
|
||||
info.ext.type = 0;
|
||||
}
|
||||
|
||||
playPos += len;
|
||||
|
||||
_position._subtracks[0]._playPos = playPos;
|
||||
}
|
||||
|
||||
void MidiParser_MSC::parseMidiEvent(EventInfo &info) {
|
||||
const byte *playPos = _position._subtracks[0]._playPos;
|
||||
uint8 type = info.command();
|
||||
|
||||
switch (type) {
|
||||
case 0x8:
|
||||
case 0x9:
|
||||
case 0xA:
|
||||
case 0xB:
|
||||
case 0xE:
|
||||
info.basic.param1 = read1(playPos);
|
||||
info.basic.param2 = read1(playPos);
|
||||
break;
|
||||
|
||||
case 0xC:
|
||||
case 0xD:
|
||||
info.basic.param1 = read1(playPos);
|
||||
info.basic.param2 = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
warning("Unexpected midi event 0x%02X in midi data", info.event);
|
||||
}
|
||||
|
||||
//if ((type == 0xB) && (info.basic.param1 == 64)) info.basic.param2 = 127;
|
||||
_position._subtracks[0]._playPos = playPos;
|
||||
}
|
||||
|
||||
void MidiParser_MSC::parseNextEvent(EventInfo &info) {
|
||||
const byte *playPos = _position._subtracks[0]._playPos;
|
||||
|
||||
info.start = playPos;
|
||||
|
||||
if (playPos >= _trackEnd) {
|
||||
// fake an end-of-track meta event
|
||||
info.delta = 0;
|
||||
info.event = 0xFF;
|
||||
info.ext.type = 0x2F;
|
||||
info.length = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
info.length = 0;
|
||||
info.delta = readVLQ(playPos);
|
||||
info.event = read1(playPos);
|
||||
|
||||
if (info.event == 0xFF) {
|
||||
_position._subtracks[0]._playPos = playPos;
|
||||
parseMetaEvent(info);
|
||||
return;
|
||||
}
|
||||
|
||||
if (info.event < 0x80) {
|
||||
playPos--;
|
||||
info.event = _lastEvent;
|
||||
}
|
||||
|
||||
_position._subtracks[0]._playPos = playPos;
|
||||
parseMidiEvent(info);
|
||||
_lastEvent = info.event;
|
||||
}
|
||||
|
||||
bool MidiParser_MSC::loadMusic(const byte *data, uint32 size) {
|
||||
unloadMusic();
|
||||
|
||||
const byte *pos = data;
|
||||
|
||||
if (memcmp("MSCt", pos, 4)) {
|
||||
warning("Expected header not found in music file");
|
||||
return false;
|
||||
}
|
||||
pos += 4;
|
||||
|
||||
_beats = read1(pos);
|
||||
_ppqn = read2low(pos);
|
||||
|
||||
if (byte_11C5A) {
|
||||
// do something with byte_11C4D
|
||||
}
|
||||
|
||||
_lastEvent = 0;
|
||||
_trackEnd = data + size;
|
||||
|
||||
_numTracks = 1;
|
||||
_numSubtracks[0] = 1;
|
||||
_tracks[0][0] = pos;
|
||||
|
||||
setTempo(500000);
|
||||
setTrack(0);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
MidiParser *createParser_MSC() {
|
||||
return new MidiParser_MSC;
|
||||
}
|
||||
|
||||
|
||||
class MidiPlayer_MSC : public Audio::MidiPlayer {
|
||||
public:
|
||||
|
||||
MidiPlayer_MSC();
|
||||
|
||||
void play(Common::SeekableReadStream *stream);
|
||||
virtual void pause(bool p);
|
||||
void pause() override { assert(0); } // overridden
|
||||
void setVolume(int volume) override;
|
||||
void onTimer() override;
|
||||
|
||||
// MidiDriver_BASE interface
|
||||
void send(uint32 b) override;
|
||||
|
||||
|
||||
private:
|
||||
void setVolumeInternal(int volume);
|
||||
bool _paused;
|
||||
};
|
||||
|
||||
|
||||
|
||||
MidiPlayer_MSC::MidiPlayer_MSC()
|
||||
: _paused(false) {
|
||||
|
||||
MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
|
||||
const MusicType musicType = MidiDriver::getMusicType(dev);
|
||||
if (musicType == MT_ADLIB) {
|
||||
_driver = createAdLibDriver();
|
||||
} else {
|
||||
_driver = MidiDriver::createMidi(dev);
|
||||
}
|
||||
assert(_driver);
|
||||
|
||||
int ret = _driver->open();
|
||||
if (ret == 0) {
|
||||
_driver->setTimerCallback(this, &timerCallback);
|
||||
}
|
||||
}
|
||||
|
||||
void MidiPlayer_MSC::play(Common::SeekableReadStream *stream) {
|
||||
Common::StackLock lock(_mutex);
|
||||
|
||||
stop();
|
||||
if (!stream)
|
||||
return;
|
||||
|
||||
int size = stream->size();
|
||||
_midiData = (uint8 *)malloc(size);
|
||||
if (_midiData) {
|
||||
stream->read(_midiData, size);
|
||||
delete stream;
|
||||
|
||||
_parser = createParser_MSC();
|
||||
_parser->loadMusic(_midiData, size);
|
||||
_parser->setTrack(0);
|
||||
_parser->setMidiDriver(this);
|
||||
_parser->setTimerRate(_driver->getBaseTempo());
|
||||
_isLooping = true;
|
||||
_isPlaying = true;
|
||||
}
|
||||
}
|
||||
|
||||
void MidiPlayer_MSC::pause(bool p) {
|
||||
_paused = p;
|
||||
setVolumeInternal(_paused ? 0 : _masterVolume);
|
||||
}
|
||||
|
||||
void MidiPlayer_MSC::onTimer() {
|
||||
Common::StackLock lock(_mutex);
|
||||
|
||||
if (!_paused && _isPlaying && _parser) {
|
||||
_parser->onTimer();
|
||||
}
|
||||
}
|
||||
|
||||
void MidiPlayer_MSC::setVolume(int volume) {
|
||||
_masterVolume = CLIP(volume, 0, 255);
|
||||
setVolumeInternal(_masterVolume);
|
||||
}
|
||||
|
||||
void MidiPlayer_MSC::setVolumeInternal(int volume) {
|
||||
Common::StackLock lock(_mutex);
|
||||
for (int i = 0; i < kNumChannels; ++i) {
|
||||
if (_channelsTable[i]) {
|
||||
_channelsTable[i]->volume(_channelsVolume[i] * volume / 255);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MidiPlayer_MSC::send(uint32 b) {
|
||||
// FIXME/TODO: Unlike Audio::MidiPlayer::send(), this code
|
||||
// does not handle All Note Off. Is this on purpose?
|
||||
// If not, we could simply remove this method, and use the
|
||||
// inherited one.
|
||||
const byte ch = b & 0x0F;
|
||||
byte param2 = (b >> 16) & 0xFF;
|
||||
|
||||
switch (b & 0xFFF0) {
|
||||
case 0x07B0: // volume change
|
||||
_channelsVolume[ch] = param2;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
sendToChannel(ch, b);
|
||||
}
|
||||
|
||||
DosSoundMan_br::DosSoundMan_br(Parallaction_br *vm) : SoundMan_br(vm) {
|
||||
_midiPlayer = new MidiPlayer_MSC();
|
||||
assert(_midiPlayer);
|
||||
}
|
||||
|
||||
DosSoundMan_br::~DosSoundMan_br() {
|
||||
delete _midiPlayer;
|
||||
}
|
||||
|
||||
Audio::AudioStream *DosSoundMan_br::loadChannelData(const char *filename, Channel *ch, bool looping) {
|
||||
Common::SeekableReadStream *stream = _vm->_disk->loadSound(filename);
|
||||
|
||||
uint32 dataSize = stream->size();
|
||||
byte *data = (byte *)malloc(dataSize);
|
||||
if (stream->read(data, dataSize) != dataSize)
|
||||
error("DosSoundMan_br::loadChannelData: Read failed");
|
||||
|
||||
delete stream;
|
||||
|
||||
// TODO: Confirm sound rate
|
||||
int rate = 11025;
|
||||
|
||||
ch->stream = Audio::makeLoopingAudioStream(
|
||||
Audio::makeRawStream(data, dataSize, rate, Audio::FLAG_UNSIGNED),
|
||||
looping ? 0 : 1);
|
||||
return ch->stream;
|
||||
}
|
||||
|
||||
void DosSoundMan_br::playSfx(const char *filename, uint channel, bool looping, int volume) {
|
||||
stopSfx(channel);
|
||||
|
||||
if (!_sfxEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
debugC(1, kDebugAudio, "DosSoundMan_br::playSfx(%s, %u, %i, %i)", filename, channel, looping, volume);
|
||||
|
||||
Channel *ch = &_channels[channel];
|
||||
Audio::AudioStream *input = loadChannelData(filename, ch, looping);
|
||||
_mixer->playStream(Audio::Mixer::kSFXSoundType, &ch->handle, input, -1, volume);
|
||||
}
|
||||
|
||||
void DosSoundMan_br::playMusic() {
|
||||
if (_musicFile.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_musicEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *s = _vm->_disk->loadMusic(_musicFile.c_str());
|
||||
assert(s);
|
||||
_midiPlayer->play(s);
|
||||
}
|
||||
|
||||
void DosSoundMan_br::stopMusic() {
|
||||
_midiPlayer->stop();
|
||||
}
|
||||
|
||||
void DosSoundMan_br::pause(bool p) {
|
||||
_midiPlayer->pause(p);
|
||||
}
|
||||
|
||||
AmigaSoundMan_br::AmigaSoundMan_br(Parallaction_br *vm) : SoundMan_br(vm) {
|
||||
_musicStream = nullptr;
|
||||
}
|
||||
|
||||
AmigaSoundMan_br::~AmigaSoundMan_br() {
|
||||
stopMusic();
|
||||
}
|
||||
|
||||
Audio::AudioStream *AmigaSoundMan_br::loadChannelData(const char *filename, Channel *ch, bool looping) {
|
||||
Common::SeekableReadStream *stream = _vm->_disk->loadSound(filename);
|
||||
Audio::AudioStream *input = nullptr;
|
||||
|
||||
if (_vm->getFeatures() & GF_DEMO) {
|
||||
uint32 dataSize = stream->size();
|
||||
int8 *data = (int8 *)malloc(dataSize);
|
||||
if (stream->read(data, dataSize) != dataSize)
|
||||
error("DosSoundMan_br::loadChannelData: Read failed");
|
||||
|
||||
// TODO: Confirm sound rate
|
||||
int rate = 11025;
|
||||
input = Audio::makeRawStream((byte *)data, dataSize, rate, 0);
|
||||
} else {
|
||||
input = Audio::make8SVXStream(*stream, looping);
|
||||
}
|
||||
|
||||
delete stream;
|
||||
|
||||
ch->stream = input;
|
||||
return input;
|
||||
}
|
||||
|
||||
void AmigaSoundMan_br::playSfx(const char *filename, uint channel, bool looping, int volume) {
|
||||
if (channel >= NUM_SFX_CHANNELS) {
|
||||
warning("unknown sfx channel");
|
||||
return;
|
||||
}
|
||||
|
||||
stopSfx(channel);
|
||||
|
||||
if (!_sfxEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
debugC(1, kDebugAudio, "AmigaSoundMan_ns::playSfx(%s, %i)", filename, channel);
|
||||
|
||||
Channel *ch = &_channels[channel];
|
||||
Audio::AudioStream *input = loadChannelData(filename, ch, looping);
|
||||
|
||||
if (volume == -1) {
|
||||
volume = ch->volume;
|
||||
}
|
||||
|
||||
_mixer->playStream(Audio::Mixer::kSFXSoundType, &ch->handle, input, -1, volume);
|
||||
}
|
||||
|
||||
void AmigaSoundMan_br::playMusic() {
|
||||
stopMusic();
|
||||
|
||||
if (!_musicEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
debugC(1, kDebugAudio, "AmigaSoundMan_ns::playMusic()");
|
||||
|
||||
Common::SeekableReadStream *stream = _vm->_disk->loadMusic(_musicFile.c_str());
|
||||
// NOTE: Music files don't always exist
|
||||
if (!stream)
|
||||
return;
|
||||
|
||||
_musicStream = Audio::makeProtrackerStream(stream);
|
||||
delete stream;
|
||||
|
||||
debugC(3, kDebugAudio, "AmigaSoundMan_ns::playMusic(): created new music stream");
|
||||
|
||||
_mixer->playStream(Audio::Mixer::kMusicSoundType, &_musicHandle, _musicStream, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, false);
|
||||
}
|
||||
|
||||
void AmigaSoundMan_br::stopMusic() {
|
||||
debugC(1, kDebugAudio, "AmigaSoundMan_ns::stopMusic()");
|
||||
|
||||
if (_mixer->isSoundHandleActive(_musicHandle)) {
|
||||
_mixer->stopHandle(_musicHandle);
|
||||
delete _musicStream;
|
||||
_musicStream = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void AmigaSoundMan_br::pause(bool p) {
|
||||
_mixer->pauseHandle(_musicHandle, p);
|
||||
}
|
||||
|
||||
SoundMan_br::SoundMan_br(Parallaction_br *vm) : _vm(vm) {
|
||||
_mixer = _vm->_mixer;
|
||||
|
||||
_musicEnabled = true;
|
||||
_sfxEnabled = true;
|
||||
|
||||
_sfxLooping = false;
|
||||
_sfxVolume = 0;
|
||||
_sfxRate = 0;
|
||||
_sfxChannel = 0;
|
||||
}
|
||||
|
||||
SoundMan_br::~SoundMan_br() {
|
||||
stopAllSfx();
|
||||
}
|
||||
|
||||
void SoundMan_br::stopAllSfx() {
|
||||
stopSfx(0);
|
||||
stopSfx(1);
|
||||
stopSfx(2);
|
||||
stopSfx(3);
|
||||
}
|
||||
|
||||
void SoundMan_br::setMusicFile(const char *name) {
|
||||
stopMusic();
|
||||
_musicFile = name;
|
||||
}
|
||||
|
||||
void SoundMan_br::stopSfx(uint channel) {
|
||||
if (channel >= NUM_SFX_CHANNELS) {
|
||||
warning("unknown sfx channel");
|
||||
return;
|
||||
}
|
||||
|
||||
debugC(1, kDebugAudio, "SoundMan_br::stopSfx(%i)", channel);
|
||||
_mixer->stopHandle(_channels[channel].handle);
|
||||
_channels[channel].stream = nullptr;
|
||||
}
|
||||
|
||||
void SoundMan_br::execute(int command, const char *parm) {
|
||||
uint32 n = parm ? strtoul(parm, nullptr, 10) : 0;
|
||||
bool b = (n == 1) ? true : false;
|
||||
|
||||
switch (command) {
|
||||
case SC_PLAYMUSIC:
|
||||
playMusic();
|
||||
break;
|
||||
case SC_STOPMUSIC:
|
||||
stopMusic();
|
||||
break;
|
||||
case SC_SETMUSICFILE:
|
||||
if (!parm)
|
||||
error("no parameter passed to SC_SETMUSICFILE");
|
||||
setMusicFile(parm);
|
||||
break;
|
||||
|
||||
case SC_PLAYSFX:
|
||||
if (!parm)
|
||||
error("no parameter passed to SC_PLAYSFX");
|
||||
playSfx(parm, _sfxChannel, _sfxLooping, _sfxVolume);
|
||||
break;
|
||||
case SC_STOPSFX:
|
||||
stopSfx(n);
|
||||
break;
|
||||
|
||||
case SC_SETSFXCHANNEL:
|
||||
_sfxChannel = n;
|
||||
break;
|
||||
case SC_SETSFXLOOPING:
|
||||
_sfxLooping = b;
|
||||
break;
|
||||
case SC_SETSFXVOLUME:
|
||||
_sfxVolume = n;
|
||||
break;
|
||||
|
||||
case SC_PAUSE:
|
||||
pause(b);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SoundMan_br::enableSfx(bool enable) {
|
||||
if (!enable) {
|
||||
stopAllSfx();
|
||||
}
|
||||
_sfxEnabled = enable;
|
||||
}
|
||||
|
||||
void SoundMan_br::enableMusic(bool enable) {
|
||||
if (enable) {
|
||||
playMusic();
|
||||
} else {
|
||||
stopMusic();
|
||||
}
|
||||
_musicEnabled = enable;
|
||||
}
|
||||
|
||||
bool SoundMan_br::isSfxEnabled() const {
|
||||
return _sfxEnabled;
|
||||
}
|
||||
|
||||
bool SoundMan_br::isMusicEnabled() const {
|
||||
return _musicEnabled;
|
||||
}
|
||||
|
||||
} // namespace Parallaction
|
||||
386
engines/parallaction/sound_ns.cpp
Normal file
386
engines/parallaction/sound_ns.cpp
Normal file
@@ -0,0 +1,386 @@
|
||||
/* 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 "common/stream.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
#include "audio/mixer.h"
|
||||
#include "audio/midiparser.h"
|
||||
#include "audio/midiplayer.h"
|
||||
#include "audio/mods/protracker.h"
|
||||
#include "audio/decoders/raw.h"
|
||||
|
||||
#include "parallaction/sound.h"
|
||||
#include "parallaction/parallaction.h"
|
||||
|
||||
|
||||
namespace Parallaction {
|
||||
|
||||
class MidiPlayer : public Audio::MidiPlayer {
|
||||
public:
|
||||
|
||||
MidiPlayer();
|
||||
|
||||
void play(Common::SeekableReadStream *stream);
|
||||
void pause(bool p);
|
||||
void pause() override { assert(0); } // overridden
|
||||
void onTimer() override;
|
||||
|
||||
private:
|
||||
bool _paused;
|
||||
};
|
||||
|
||||
MidiPlayer::MidiPlayer()
|
||||
: _paused(false) {
|
||||
|
||||
MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
|
||||
_driver = MidiDriver::createMidi(dev);
|
||||
assert(_driver);
|
||||
|
||||
int ret = _driver->open();
|
||||
if (ret == 0) {
|
||||
_driver->setTimerCallback(this, &timerCallback);
|
||||
}
|
||||
}
|
||||
|
||||
void MidiPlayer::play(Common::SeekableReadStream *stream) {
|
||||
Common::StackLock lock(_mutex);
|
||||
|
||||
stop();
|
||||
if (!stream)
|
||||
return;
|
||||
|
||||
int size = stream->size();
|
||||
_midiData = (uint8 *)malloc(size);
|
||||
if (_midiData) {
|
||||
stream->read(_midiData, size);
|
||||
delete stream;
|
||||
|
||||
_parser = MidiParser::createParser_SMF();
|
||||
_parser->loadMusic(_midiData, size);
|
||||
_parser->setTrack(0);
|
||||
_parser->setMidiDriver(this);
|
||||
_parser->setTimerRate(_driver->getBaseTempo());
|
||||
_isLooping = true;
|
||||
_isPlaying = true;
|
||||
}
|
||||
}
|
||||
|
||||
void MidiPlayer::pause(bool p) {
|
||||
_paused = p;
|
||||
|
||||
for (int i = 0; i < kNumChannels; ++i) {
|
||||
if (_channelsTable[i]) {
|
||||
_channelsTable[i]->volume(_paused ? 0 : _channelsVolume[i] * _masterVolume / 255);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MidiPlayer::onTimer() {
|
||||
Common::StackLock lock(_mutex);
|
||||
|
||||
if (!_paused && _isPlaying && _parser) {
|
||||
_parser->onTimer();
|
||||
}
|
||||
}
|
||||
|
||||
DosSoundMan_ns::DosSoundMan_ns(Parallaction_ns *vm) : SoundMan_ns(vm), _playing(false) {
|
||||
_midiPlayer = new MidiPlayer();
|
||||
}
|
||||
|
||||
DosSoundMan_ns::~DosSoundMan_ns() {
|
||||
debugC(1, kDebugAudio, "DosSoundMan_ns_ns::playMusic()");
|
||||
|
||||
delete _midiPlayer;
|
||||
}
|
||||
|
||||
bool DosSoundMan_ns::isLocationSilent(const char *locationName) {
|
||||
|
||||
// these are the prefixes for location names with no background midi music
|
||||
const char *noMusicPrefix[] = { "museo", "intgrottadopo", "caveau", "estgrotta", "plaza1", "endtgz", "common", nullptr };
|
||||
Common::String s(locationName);
|
||||
|
||||
for (int i = 0; noMusicPrefix[i]; i++) {
|
||||
if (s.hasPrefix(noMusicPrefix[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void DosSoundMan_ns::playMusic() {
|
||||
debugC(1, kDebugAudio, "DosSoundMan_ns_ns::playMusic()");
|
||||
|
||||
if (isLocationSilent(_vm->_location._name)) {
|
||||
// just stop the music if this location is silent
|
||||
_midiPlayer->stop();
|
||||
return;
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *stream = _vm->_disk->loadMusic(_musicFile);
|
||||
_midiPlayer->play(stream);
|
||||
_midiPlayer->setVolume(255);
|
||||
|
||||
_playing = true;
|
||||
}
|
||||
|
||||
void DosSoundMan_ns::stopMusic() {
|
||||
_musicFile[0] = 0;
|
||||
_midiPlayer->stop();
|
||||
|
||||
_playing = false;
|
||||
}
|
||||
|
||||
void DosSoundMan_ns::pause(bool p) {
|
||||
SoundMan_ns::pause(p);
|
||||
_midiPlayer->pause(p);
|
||||
}
|
||||
|
||||
bool DosSoundMan_ns::locationHasOwnSoftMusic(const char *locationName) {
|
||||
return !scumm_stricmp(locationName, "night") || !scumm_stricmp(locationName, "intsushi");
|
||||
}
|
||||
|
||||
void DosSoundMan_ns::playCharacterMusic(const char *character) {
|
||||
if (!character || locationHasOwnSoftMusic(_vm->_location._name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
char *name = const_cast<char *>(character);
|
||||
const char *newMusicFile = nullptr;
|
||||
|
||||
if (!scumm_stricmp(name, g_dinoName)) {
|
||||
newMusicFile = "dino";
|
||||
} else
|
||||
if (!scumm_stricmp(name, g_donnaName)) {
|
||||
newMusicFile = "donna";
|
||||
} else
|
||||
if (!scumm_stricmp(name, g_doughName)) {
|
||||
newMusicFile = "nuts";
|
||||
} else {
|
||||
warning("unknown character '%s' in DosSoundMan_ns_ns::playCharacterMusic", character);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_playing || (newMusicFile && scumm_stricmp(newMusicFile, _musicFile))) {
|
||||
// avoid restarting the same piece
|
||||
setMusicFile(newMusicFile);
|
||||
playMusic();
|
||||
debugC(2, kDebugExec, "changeLocation: started character specific music (%s)", newMusicFile);
|
||||
}
|
||||
}
|
||||
|
||||
void DosSoundMan_ns::playLocationMusic(const char *location) {
|
||||
if (locationHasOwnSoftMusic(location)) {
|
||||
setMusicFile("soft");
|
||||
playMusic();
|
||||
debugC(2, kDebugExec, "changeLocation: started music 'soft'");
|
||||
} else
|
||||
if (isLocationSilent(location)) {
|
||||
stopMusic();
|
||||
debugC(2, kDebugExec, "changeLocation: music stopped");
|
||||
} else {
|
||||
playCharacterMusic(_vm->_char.getBaseName());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma mark Amiga sound manager code
|
||||
|
||||
|
||||
#define AMIGABEEP_SIZE 16
|
||||
#define NUM_REPEATS 60
|
||||
|
||||
static int8 res_amigaBeep[AMIGABEEP_SIZE] = {
|
||||
0, 20, 40, 60, 80, 60, 40, 20, 0, -20, -40, -60, -80, -60, -40, -20
|
||||
};
|
||||
|
||||
AmigaSoundMan_ns::AmigaSoundMan_ns(Parallaction_ns *vm) : SoundMan_ns(vm) {
|
||||
_musicStream = nullptr;
|
||||
|
||||
// initialize the waveform for the 'beep' sound
|
||||
beepSoundBufferSize = AMIGABEEP_SIZE * NUM_REPEATS;
|
||||
beepSoundBuffer = new int8[beepSoundBufferSize];
|
||||
int8 *odata = beepSoundBuffer;
|
||||
for (int i = 0; i < NUM_REPEATS; i++) {
|
||||
memcpy(odata, res_amigaBeep, AMIGABEEP_SIZE);
|
||||
odata += AMIGABEEP_SIZE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
AmigaSoundMan_ns::~AmigaSoundMan_ns() {
|
||||
stopMusic();
|
||||
stopSfx(0);
|
||||
stopSfx(1);
|
||||
stopSfx(2);
|
||||
stopSfx(3);
|
||||
|
||||
delete[] beepSoundBuffer;
|
||||
}
|
||||
|
||||
Audio::AudioStream *AmigaSoundMan_ns::loadChannelData(const char *filename, Channel *ch, bool looping) {
|
||||
Audio::AudioStream *input = nullptr;
|
||||
|
||||
if (!scumm_stricmp("beep", filename)) {
|
||||
int rate = 11934;
|
||||
ch->volume = 160;
|
||||
input = Audio::makeRawStream((byte *)beepSoundBuffer, beepSoundBufferSize, rate, 0, DisposeAfterUse::NO);
|
||||
} else {
|
||||
Common::SeekableReadStream *stream = _vm->_disk->loadSound(filename);
|
||||
input = Audio::make8SVXStream(*stream, looping);
|
||||
delete stream;
|
||||
}
|
||||
|
||||
ch->stream = input;
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
void AmigaSoundMan_ns::playSfx(const char *filename, uint channel, bool looping, int volume) {
|
||||
if (channel >= NUM_SFX_CHANNELS) {
|
||||
warning("unknown sfx channel");
|
||||
return;
|
||||
}
|
||||
|
||||
stopSfx(channel);
|
||||
|
||||
debugC(1, kDebugAudio, "AmigaSoundMan_ns::playSfx(%s, %i)", filename, channel);
|
||||
|
||||
Channel *ch = &_channels[channel];
|
||||
Audio::AudioStream *input = loadChannelData(filename, ch, looping);
|
||||
|
||||
if (volume == -1) {
|
||||
volume = ch->volume;
|
||||
}
|
||||
|
||||
_mixer->playStream(Audio::Mixer::kSFXSoundType, &ch->handle, input, -1, volume);
|
||||
}
|
||||
|
||||
void AmigaSoundMan_ns::stopSfx(uint channel) {
|
||||
if (channel >= NUM_SFX_CHANNELS) {
|
||||
warning("unknown sfx channel");
|
||||
return;
|
||||
}
|
||||
|
||||
debugC(1, kDebugAudio, "AmigaSoundMan_ns::stopSfx(%i)", channel);
|
||||
_mixer->stopHandle(_channels[channel].handle);
|
||||
_channels[channel].stream = nullptr;
|
||||
}
|
||||
|
||||
void AmigaSoundMan_ns::playMusic() {
|
||||
stopMusic();
|
||||
|
||||
debugC(1, kDebugAudio, "AmigaSoundMan_ns::playMusic()");
|
||||
|
||||
Common::SeekableReadStream *stream = _vm->_disk->loadMusic(_musicFile);
|
||||
_musicStream = Audio::makeProtrackerStream(stream);
|
||||
delete stream;
|
||||
|
||||
debugC(3, kDebugAudio, "AmigaSoundMan_ns::playMusic(): created new music stream");
|
||||
|
||||
_mixer->playStream(Audio::Mixer::kMusicSoundType, &_musicHandle, _musicStream, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, false);
|
||||
}
|
||||
|
||||
void AmigaSoundMan_ns::stopMusic() {
|
||||
debugC(1, kDebugAudio, "AmigaSoundMan_ns::stopMusic()");
|
||||
|
||||
if (_mixer->isSoundHandleActive(_musicHandle)) {
|
||||
_mixer->stopHandle(_musicHandle);
|
||||
delete _musicStream;
|
||||
_musicStream = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void AmigaSoundMan_ns::playCharacterMusic(const char *character) {
|
||||
}
|
||||
|
||||
void AmigaSoundMan_ns::playLocationMusic(const char *location) {
|
||||
}
|
||||
|
||||
|
||||
SoundMan_ns::SoundMan_ns(Parallaction_ns *vm) : _vm(vm) {
|
||||
_mixer = _vm->_mixer;
|
||||
_sfxLooping = false;
|
||||
_sfxVolume = 0;
|
||||
_sfxRate = 0;
|
||||
_sfxChannel = 0;
|
||||
_musicType = 0;
|
||||
}
|
||||
|
||||
void SoundMan_ns::setMusicVolume(int value) {
|
||||
_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, value);
|
||||
}
|
||||
|
||||
void SoundMan_ns::setMusicFile(const char *filename) {
|
||||
Common::strlcpy(_musicFile, filename, PATH_LEN);
|
||||
}
|
||||
|
||||
void SoundMan_ns::execute(int command, const char *parm = nullptr) {
|
||||
uint32 n = strtoul(parm, nullptr, 10);
|
||||
bool b = (n == 1) ? true : false;
|
||||
|
||||
switch (command) {
|
||||
case SC_PLAYMUSIC:
|
||||
if (_musicType == MUSIC_CHARACTER) playCharacterMusic(parm);
|
||||
else if (_musicType == MUSIC_LOCATION) playLocationMusic(parm);
|
||||
else playMusic();
|
||||
break;
|
||||
case SC_STOPMUSIC:
|
||||
stopMusic();
|
||||
break;
|
||||
case SC_SETMUSICTYPE:
|
||||
_musicType = n;
|
||||
break;
|
||||
case SC_SETMUSICFILE:
|
||||
setMusicFile(parm);
|
||||
break;
|
||||
|
||||
case SC_PLAYSFX:
|
||||
playSfx(parm, _sfxChannel, _sfxLooping, _sfxVolume);
|
||||
break;
|
||||
case SC_STOPSFX:
|
||||
stopSfx(n);
|
||||
break;
|
||||
|
||||
case SC_SETSFXCHANNEL:
|
||||
_sfxChannel = n;
|
||||
break;
|
||||
case SC_SETSFXLOOPING:
|
||||
_sfxLooping = b;
|
||||
break;
|
||||
case SC_SETSFXVOLUME:
|
||||
_sfxVolume = n;
|
||||
break;
|
||||
|
||||
case SC_PAUSE:
|
||||
pause(b);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Parallaction
|
||||
255
engines/parallaction/staticres.cpp
Normal file
255
engines/parallaction/staticres.cpp
Normal file
@@ -0,0 +1,255 @@
|
||||
/* 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 "parallaction/parallaction.h"
|
||||
|
||||
|
||||
namespace Parallaction {
|
||||
|
||||
byte Input::_resMouseArrow_NS[256] = {
|
||||
0x12, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x00, 0x00,
|
||||
0x11, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x13, 0x00, 0x00, 0x00,
|
||||
0x11, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x13, 0x00, 0x00, 0x00, 0x00,
|
||||
0x11, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x11, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x13, 0x00, 0x00, 0x00, 0x00,
|
||||
0x11, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x13, 0x00, 0x00, 0x00,
|
||||
0x11, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x13, 0x00, 0x00,
|
||||
0x11, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x13, 0x00,
|
||||
0x11, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x13, 0x00,
|
||||
0x11, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x13, 0x00, 0x00,
|
||||
0x11, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x13, 0x00, 0x00, 0x00,
|
||||
0x11, 0x12, 0x12, 0x11, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x13, 0x00, 0x00, 0x00, 0x00,
|
||||
0x11, 0x12, 0x11, 0x00, 0x11, 0x12, 0x12, 0x12, 0x12, 0x12, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x11, 0x11, 0x00, 0x00, 0x00, 0x11, 0x12, 0x12, 0x12, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x12, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
/* TODO: Scale this bitmap. Its 32x16, but the original scales it twice horizontally and 6 times
|
||||
vertically (64x96).
|
||||
|
||||
TODO: The cursor data should be adjusted by adding 0x10 to each byte, because the bitmap
|
||||
must be drawn using the background palette.
|
||||
*/
|
||||
byte Input::_resMouseArrow_BR_Amiga[512] = {
|
||||
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
|
||||
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02,
|
||||
0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00,
|
||||
0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
|
||||
0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
|
||||
0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00,
|
||||
0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02,
|
||||
0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00,
|
||||
0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
|
||||
0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x03, 0x03, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x03, 0x03, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
|
||||
0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x03, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x01, 0x02, 0x02, 0x02,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x03, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
/*
|
||||
This palette snippet is used for animations in Big Red Adventure.
|
||||
*/
|
||||
byte braAmigaFramesDefaultPalette[48] = {
|
||||
0x00, 0x00, 0x00, 0x14, 0x14, 0x14, 0xFF, 0xE0, 0xCF, 0x7F, 0x7F, 0x7F, 0xD9, 0x9C, 0x84, 0x00,
|
||||
0x9E, 0xF0, 0x91, 0xCC, 0x36, 0xFF, 0x6A, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xDC, 0x11, 0xB0, 0xEE,
|
||||
0xF0, 0xFF, 0x17, 0x3D, 0x18, 0xAC, 0x3A, 0xB0, 0x00, 0x00, 0x7D, 0x00, 0x00, 0xFF, 0xA8, 0xFF,
|
||||
};
|
||||
|
||||
|
||||
const char *callableNamesRes_ns[] = {
|
||||
"Projector",
|
||||
"HBOff",
|
||||
"StartIntro",
|
||||
"EndIntro",
|
||||
"MoveSheet",
|
||||
"Sketch",
|
||||
"Shade",
|
||||
"Score",
|
||||
"OffSound",
|
||||
"StartMusic",
|
||||
"CloseMusic",
|
||||
"Fade",
|
||||
"HBOn",
|
||||
"MoveSarc",
|
||||
"ContaFoglie",
|
||||
"ZeroFoglie",
|
||||
"Trasformata",
|
||||
"OffMouse",
|
||||
"OnMouse",
|
||||
"SetMask",
|
||||
"EndComment",
|
||||
"Frankenstain",
|
||||
"Finito",
|
||||
"Ridux",
|
||||
"TestResult"
|
||||
};
|
||||
|
||||
|
||||
const char *callableNamesRes_br[] = {
|
||||
"blufade",
|
||||
"resetpalette",
|
||||
"ferrcycle",
|
||||
"lipsinc",
|
||||
"albycle",
|
||||
"password"
|
||||
};
|
||||
|
||||
const char *g_dinoName = "dino";
|
||||
const char *g_donnaName = "donna";
|
||||
const char *g_doughName = "dough";
|
||||
const char *g_drkiName = "drki";
|
||||
|
||||
const char *g_minidinoName = "minidino";
|
||||
const char *g_minidonnaName = "minidonna";
|
||||
const char *g_minidoughName = "minidough";
|
||||
const char *g_minidrkiName = "minidrki";
|
||||
|
||||
#define CALLABLE_NS(x) &Parallaction_ns::x
|
||||
|
||||
const Parallaction_ns::Callable Parallaction_ns::_dosCallables[] = {
|
||||
CALLABLE_NS(_c_play_boogie),
|
||||
CALLABLE_NS(_c_play_boogie),
|
||||
CALLABLE_NS(_c_startIntro),
|
||||
CALLABLE_NS(_c_endIntro),
|
||||
CALLABLE_NS(_c_moveSheet),
|
||||
CALLABLE_NS(_c_sketch),
|
||||
CALLABLE_NS(_c_shade),
|
||||
CALLABLE_NS(_c_score),
|
||||
CALLABLE_NS(_c_null),
|
||||
CALLABLE_NS(_c_null),
|
||||
CALLABLE_NS(_c_null),
|
||||
CALLABLE_NS(_c_fade),
|
||||
CALLABLE_NS(_c_play_boogie),
|
||||
CALLABLE_NS(_c_moveSarc),
|
||||
CALLABLE_NS(_c_contaFoglie),
|
||||
CALLABLE_NS(_c_zeroFoglie),
|
||||
CALLABLE_NS(_c_trasformata),
|
||||
CALLABLE_NS(_c_offMouse),
|
||||
CALLABLE_NS(_c_onMouse),
|
||||
CALLABLE_NS(_c_setMask),
|
||||
CALLABLE_NS(_c_endComment),
|
||||
CALLABLE_NS(_c_frankenstein),
|
||||
CALLABLE_NS(_c_finito),
|
||||
CALLABLE_NS(_c_ridux),
|
||||
CALLABLE_NS(_c_testResult)
|
||||
};
|
||||
|
||||
const Parallaction_ns::Callable Parallaction_ns::_amigaCallables[] = {
|
||||
CALLABLE_NS(_c_projector),
|
||||
CALLABLE_NS(_c_HBOff),
|
||||
CALLABLE_NS(_c_startIntro),
|
||||
CALLABLE_NS(_c_endIntro),
|
||||
CALLABLE_NS(_c_moveSheet),
|
||||
CALLABLE_NS(_c_sketch),
|
||||
CALLABLE_NS(_c_shade),
|
||||
CALLABLE_NS(_c_score),
|
||||
CALLABLE_NS(_c_offSound),
|
||||
CALLABLE_NS(_c_startMusic),
|
||||
CALLABLE_NS(_c_closeMusic),
|
||||
CALLABLE_NS(_c_fade),
|
||||
CALLABLE_NS(_c_HBOn),
|
||||
CALLABLE_NS(_c_moveSarc),
|
||||
CALLABLE_NS(_c_contaFoglie),
|
||||
CALLABLE_NS(_c_zeroFoglie),
|
||||
CALLABLE_NS(_c_trasformata),
|
||||
CALLABLE_NS(_c_offMouse),
|
||||
CALLABLE_NS(_c_onMouse),
|
||||
CALLABLE_NS(_c_setMask),
|
||||
CALLABLE_NS(_c_endComment),
|
||||
CALLABLE_NS(_c_frankenstein),
|
||||
CALLABLE_NS(_c_finito),
|
||||
CALLABLE_NS(_c_ridux),
|
||||
CALLABLE_NS(_c_testResult)
|
||||
};
|
||||
|
||||
#define CALLABLE_BR(x) &Parallaction_br::x
|
||||
|
||||
const Parallaction_br::Callable Parallaction_br::_dosCallables[] = {
|
||||
CALLABLE_BR(_c_blufade),
|
||||
CALLABLE_BR(_c_resetpalette),
|
||||
CALLABLE_BR(_c_ferrcycle),
|
||||
CALLABLE_BR(_c_lipsinc),
|
||||
CALLABLE_BR(_c_albcycle),
|
||||
CALLABLE_BR(_c_password)
|
||||
};
|
||||
|
||||
const Parallaction_br::Callable Parallaction_br::_amigaCallables[] = {
|
||||
CALLABLE_BR(_c_blufade),
|
||||
CALLABLE_BR(_c_resetpalette),
|
||||
CALLABLE_BR(_c_null),
|
||||
CALLABLE_BR(_c_null),
|
||||
CALLABLE_BR(_c_null),
|
||||
CALLABLE_BR(_c_null)
|
||||
};
|
||||
|
||||
void Parallaction_ns::initResources() {
|
||||
|
||||
_callableNames = new Table(ARRAYSIZE(callableNamesRes_ns), callableNamesRes_ns);
|
||||
|
||||
_localFlagNames = new FixedTable(NUM_LOCATIONS, 1);
|
||||
_localFlagNames->addData("visited");
|
||||
|
||||
if (getPlatform() == Common::kPlatformDOS) {
|
||||
_callables = _dosCallables;
|
||||
} else {
|
||||
_callables = _amigaCallables;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Parallaction_br::initResources() {
|
||||
|
||||
_callableNames = new Table(ARRAYSIZE(callableNamesRes_br), callableNamesRes_br);
|
||||
|
||||
_localFlagNames = new FixedTable(NUM_LOCATIONS, 2);
|
||||
_localFlagNames->addData("visited");
|
||||
_localFlagNames->addData("testtrue");
|
||||
|
||||
if (getPlatform() == Common::kPlatformDOS) {
|
||||
_callables = _dosCallables;
|
||||
} else {
|
||||
_callables = _amigaCallables;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Parallaction
|
||||
723
engines/parallaction/walk.cpp
Normal file
723
engines/parallaction/walk.cpp
Normal file
@@ -0,0 +1,723 @@
|
||||
/* 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 "parallaction/exec.h"
|
||||
#include "parallaction/parallaction.h"
|
||||
#include "parallaction/walk.h"
|
||||
|
||||
namespace Parallaction {
|
||||
|
||||
enum {
|
||||
WALK_LEFT = 0,
|
||||
WALK_RIGHT = 1,
|
||||
WALK_DOWN = 2,
|
||||
WALK_UP = 3
|
||||
};
|
||||
|
||||
struct WalkFrames {
|
||||
int16 stillFrame[4];
|
||||
int16 firstWalkFrame[4];
|
||||
int16 numWalkFrames[4];
|
||||
int16 frameRepeat[4];
|
||||
};
|
||||
|
||||
WalkFrames _char20WalkFrames_NS = {
|
||||
{ 0, 7, 14, 17 },
|
||||
{ 1, 8, 15, 18 },
|
||||
{ 6, 6, 2, 2 },
|
||||
{ 2, 2, 4, 4 }
|
||||
};
|
||||
|
||||
WalkFrames _char24WalkFrames_NS = {
|
||||
{ 0, 9, 18, 21 },
|
||||
{ 1, 10, 19, 22 },
|
||||
{ 8, 8, 2, 2 },
|
||||
{ 2, 2, 4, 4 }
|
||||
};
|
||||
|
||||
static int getPathWidth() {
|
||||
if (!g_vm->_gfx->_backgroundInfo->_path) {
|
||||
warning("getPathWidth() _path is NULL!");
|
||||
return 0;
|
||||
} else
|
||||
return g_vm->_gfx->_backgroundInfo->_path->w;
|
||||
}
|
||||
|
||||
static int getPathHeight() {
|
||||
if (!g_vm->_gfx->_backgroundInfo->_path) {
|
||||
warning("getPathHeight() _path is NULL!");
|
||||
return 0;
|
||||
} else
|
||||
return g_vm->_gfx->_backgroundInfo->_path->h;
|
||||
}
|
||||
|
||||
static bool isPathClear(uint16 x, uint16 y) {
|
||||
if (!g_vm->_gfx->_backgroundInfo->_path) {
|
||||
warning("isPathClear() _path is NULL!");
|
||||
return false;
|
||||
} else
|
||||
return (g_vm->_gfx->_backgroundInfo->_path->getValue(x, y) ? true : false);
|
||||
}
|
||||
|
||||
// adjusts position towards nearest walkable point
|
||||
void PathWalker_NS::correctPathPoint(Common::Point &to) {
|
||||
if (isPathClear(to.x, to.y)) return;
|
||||
|
||||
int maxX = getPathWidth();
|
||||
int maxY = getPathHeight();
|
||||
|
||||
int16 right = to.x;
|
||||
int16 left = to.x;
|
||||
do {
|
||||
right++;
|
||||
} while ((right < maxX) && !isPathClear(right, to.y));
|
||||
do {
|
||||
left--;
|
||||
} while ((left > 0) && !isPathClear(left, to.y));
|
||||
right = (right == maxX) ? 1000 : right - to.x;
|
||||
left = (left == 0) ? 1000 : to.x - left;
|
||||
|
||||
int16 top = to.y;
|
||||
int16 bottom = to.y;
|
||||
do {
|
||||
top--;
|
||||
} while ((top > 0) && !isPathClear(to.x, top));
|
||||
do {
|
||||
bottom++;
|
||||
} while ((bottom < maxY) && !isPathClear(to.x, bottom));
|
||||
top = (top == 0) ? 1000 : to.y - top;
|
||||
bottom = (bottom == maxY) ? 1000 : bottom - to.y;
|
||||
|
||||
int16 closeX = (right >= left) ? left : right;
|
||||
int16 closeY = (top >= bottom) ? bottom : top;
|
||||
int16 close = (closeX >= closeY) ? closeY : closeX;
|
||||
if (close == right) {
|
||||
to.x += right;
|
||||
} else
|
||||
if (close == left) {
|
||||
to.x -= left;
|
||||
} else
|
||||
if (close == top) {
|
||||
to.y -= top;
|
||||
} else
|
||||
if (close == bottom) {
|
||||
to.y += bottom;
|
||||
}
|
||||
}
|
||||
|
||||
uint32 PathWalker_NS::buildSubPath(const Common::Point& pos, const Common::Point& stop) {
|
||||
uint32 v28 = 0;
|
||||
uint32 v2C = 0;
|
||||
uint32 v34 = pos.sqrDist(stop); // square distance from current position and target
|
||||
uint32 v30 = v34;
|
||||
|
||||
_subPath.clear();
|
||||
|
||||
Common::Point v20(pos);
|
||||
|
||||
while (true) {
|
||||
|
||||
PointList::iterator nearest = _vm->_location._walkPoints.end();
|
||||
PointList::iterator locNode = _vm->_location._walkPoints.begin();
|
||||
|
||||
// scans location path nodes searching for the nearest Node
|
||||
// which can't be farther than the target position
|
||||
// otherwise no _closest_node is selected
|
||||
while (locNode != _vm->_location._walkPoints.end()) {
|
||||
|
||||
Common::Point v8 = *locNode;
|
||||
v2C = v8.sqrDist(stop);
|
||||
v28 = v8.sqrDist(v20);
|
||||
|
||||
if (v2C < v34 && v28 < v30) {
|
||||
v30 = v28;
|
||||
nearest = locNode;
|
||||
}
|
||||
|
||||
locNode++;
|
||||
}
|
||||
|
||||
if (nearest == _vm->_location._walkPoints.end()) break;
|
||||
|
||||
v20 = *nearest;
|
||||
v34 = v30 = v20.sqrDist(stop);
|
||||
|
||||
_subPath.push_back(*nearest);
|
||||
}
|
||||
|
||||
return v34;
|
||||
}
|
||||
|
||||
// x, y: mouse click (foot) coordinates
|
||||
void PathWalker_NS::buildPath(AnimationPtr a, uint16 x, uint16 y) {
|
||||
debugC(1, kDebugWalk, "PathBuilder::buildPath to (%i, %i)", x, y);
|
||||
|
||||
_a = a;
|
||||
|
||||
_walkPath.clear();
|
||||
|
||||
Common::Point to(x, y);
|
||||
correctPathPoint(to);
|
||||
debugC(1, kDebugWalk, "found closest path point at (%i, %i)", to.x, to.y);
|
||||
|
||||
Common::Point v48(to);
|
||||
Common::Point v44(to);
|
||||
|
||||
uint16 v38 = walkFunc1(to, v44);
|
||||
if (v38 == 1) {
|
||||
// destination directly reachable
|
||||
debugC(1, kDebugWalk, "direct move to (%i, %i)", to.x, to.y);
|
||||
_walkPath.push_back(v48);
|
||||
return;
|
||||
}
|
||||
|
||||
// path is obstructed: look for alternative
|
||||
_walkPath.push_back(v48);
|
||||
Common::Point pos;
|
||||
_a->getFoot(pos);
|
||||
|
||||
uint32 v34 = buildSubPath(pos, v48);
|
||||
if (v38 != 0 && v34 > v38) {
|
||||
// no alternative path (gap?)
|
||||
_walkPath.clear();
|
||||
_walkPath.push_back(v44);
|
||||
return;
|
||||
}
|
||||
_walkPath.insert(_walkPath.begin(), _subPath.begin(), _subPath.end());
|
||||
|
||||
buildSubPath(pos, *_walkPath.begin());
|
||||
_walkPath.insert(_walkPath.begin(), _subPath.begin(), _subPath.end());
|
||||
}
|
||||
|
||||
// x,y : top left coordinates
|
||||
//
|
||||
// 0 : Point not reachable
|
||||
// 1 : Point reachable in a straight line
|
||||
// other values: square distance to target (point not reachable in a straight line)
|
||||
uint16 PathWalker_NS::walkFunc1(const Common::Point &to, Common::Point& node) {
|
||||
Common::Point arg(to);
|
||||
|
||||
Common::Point v4;
|
||||
|
||||
Common::Point foot;
|
||||
_a->getFoot(foot);
|
||||
|
||||
Common::Point v8(foot);
|
||||
|
||||
while (foot != arg) {
|
||||
|
||||
if (foot.x < to.x && isPathClear(foot.x + 1, foot.y)) foot.x++;
|
||||
if (foot.x > to.x && isPathClear(foot.x - 1, foot.y)) foot.x--;
|
||||
if (foot.y < to.y && isPathClear(foot.x, foot.y + 1)) foot.y++;
|
||||
if (foot.y > to.y && isPathClear(foot.x, foot.y - 1)) foot.y--;
|
||||
|
||||
if (foot == v8 && foot != arg) {
|
||||
// foot couldn't move and still away from target
|
||||
|
||||
v4 = foot;
|
||||
|
||||
while (foot != arg) {
|
||||
|
||||
if (foot.x < to.x && !isPathClear(foot.x + 1, foot.y)) foot.x++;
|
||||
if (foot.x > to.x && !isPathClear(foot.x - 1, foot.y)) foot.x--;
|
||||
if (foot.y < to.y && !isPathClear(foot.x, foot.y + 1)) foot.y++;
|
||||
if (foot.y > to.y && !isPathClear(foot.x, foot.y - 1)) foot.y--;
|
||||
|
||||
if (foot == v8 && foot != arg)
|
||||
return 0;
|
||||
|
||||
v8 = foot;
|
||||
}
|
||||
|
||||
node = v4;
|
||||
return v4.sqrDist(to);
|
||||
}
|
||||
|
||||
v8 = foot;
|
||||
}
|
||||
|
||||
// there exists an unobstructed path
|
||||
return 1;
|
||||
}
|
||||
|
||||
void PathWalker_NS::clipMove(Common::Point& pos, const Common::Point& to) {
|
||||
if ((pos.x < to.x) && (pos.x < getPathWidth()) && isPathClear(pos.x + 2, pos.y)) {
|
||||
pos.x = (pos.x + 2 < to.x) ? pos.x + 2 : to.x;
|
||||
}
|
||||
|
||||
if ((pos.x > to.x) && (pos.x > 0) && isPathClear(pos.x - 2, pos.y)) {
|
||||
pos.x = (pos.x - 2 > to.x) ? pos.x - 2 : to.x;
|
||||
}
|
||||
|
||||
if ((pos.y < to.y) && (pos.y < getPathHeight()) && isPathClear(pos.x, pos.y + 2)) {
|
||||
pos.y = (pos.y + 2 <= to.y) ? pos.y + 2 : to.y;
|
||||
}
|
||||
|
||||
if ((pos.y > to.y) && (pos.y > 0) && isPathClear(pos.x, pos.y - 2)) {
|
||||
pos.y = (pos.y - 2 >= to.y) ? pos.y - 2 : to.y;
|
||||
}
|
||||
}
|
||||
|
||||
void PathWalker_NS::checkDoor(const Common::Point &foot) {
|
||||
ZonePtr z = _vm->hitZone(kZoneDoor, foot.x, foot.y);
|
||||
if (z) {
|
||||
if ((z->_flags & kFlagsClosed) == 0) {
|
||||
_vm->_location._startPosition = z->u._doorStartPos;
|
||||
_vm->_location._startFrame = z->u._doorStartFrame;
|
||||
_vm->scheduleLocationSwitch(z->u._doorLocation.c_str());
|
||||
_vm->_zoneTrap.reset();
|
||||
} else {
|
||||
_vm->_cmdExec->run(z->_commands, z);
|
||||
}
|
||||
}
|
||||
|
||||
z = _vm->hitZone(kZoneTrap, foot.x, foot.y);
|
||||
if (z) {
|
||||
_vm->setLocationFlags(kFlagsEnter);
|
||||
_vm->_cmdExec->run(z->_commands, z);
|
||||
_vm->clearLocationFlags(kFlagsEnter);
|
||||
_vm->_zoneTrap = z;
|
||||
} else
|
||||
if (_vm->_zoneTrap) {
|
||||
_vm->setLocationFlags(kFlagsExit);
|
||||
_vm->_cmdExec->run(_vm->_zoneTrap->_commands, _vm->_zoneTrap);
|
||||
_vm->clearLocationFlags(kFlagsExit);
|
||||
_vm->_zoneTrap.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void PathWalker_NS::finalizeWalk() {
|
||||
g_engineFlags &= ~kEngineWalking;
|
||||
|
||||
Common::Point foot;
|
||||
_a->getFoot(foot);
|
||||
checkDoor(foot);
|
||||
|
||||
_walkPath.clear();
|
||||
}
|
||||
|
||||
void PathWalker_NS::walk() {
|
||||
if ((g_engineFlags & kEngineWalking) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Common::Point curPos;
|
||||
_a->getFoot(curPos);
|
||||
|
||||
// update target, if previous was reached
|
||||
PointList::iterator it = _walkPath.begin();
|
||||
if (it != _walkPath.end()) {
|
||||
if (*it == curPos) {
|
||||
debugC(1, kDebugWalk, "walk reached node (%i, %i)", (*it).x, (*it).y);
|
||||
it = _walkPath.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
// advance character towards the target
|
||||
Common::Point targetPos;
|
||||
if (it == _walkPath.end()) {
|
||||
debugC(1, kDebugWalk, "walk reached last node");
|
||||
finalizeWalk();
|
||||
targetPos = curPos;
|
||||
} else {
|
||||
// targetPos is saved to help setting character direction
|
||||
targetPos = *it;
|
||||
|
||||
Common::Point newPos(curPos);
|
||||
clipMove(newPos, targetPos);
|
||||
_a->setFoot(newPos);
|
||||
|
||||
if (newPos == curPos) {
|
||||
debugC(1, kDebugWalk, "walk was blocked by an unforeseen obstacle");
|
||||
finalizeWalk();
|
||||
targetPos = newPos; // when walking is interrupted, targetPos must be hacked so that a still frame can be selected
|
||||
}
|
||||
}
|
||||
|
||||
// targetPos is used to select the direction (and the walkFrame) of a character,
|
||||
// since it doesn't cause the sudden changes in orientation that newPos would.
|
||||
// Since newPos is 'adjusted' according to walkable areas, an imaginary line drawn
|
||||
// from curPos to newPos is prone to abrutply change in direction, thus making the
|
||||
// code select 'too different' frames when walking diagonally against obstacles,
|
||||
// and yielding an annoying shaking effect in the character.
|
||||
updateDirection(curPos, targetPos);
|
||||
}
|
||||
|
||||
void PathWalker_NS::updateDirection(const Common::Point& pos, const Common::Point& to) {
|
||||
Common::Point dist(to.x - pos.x, to.y - pos.y);
|
||||
WalkFrames *frames = (_a->getFrameNum() == 20) ? &_char20WalkFrames_NS : &_char24WalkFrames_NS;
|
||||
|
||||
_step++;
|
||||
|
||||
if (dist.x == 0 && dist.y == 0) {
|
||||
_a->setF(frames->stillFrame[_direction]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (dist.x < 0)
|
||||
dist.x = -dist.x;
|
||||
if (dist.y < 0)
|
||||
dist.y = -dist.y;
|
||||
|
||||
_direction = (dist.x > dist.y) ? ((to.x > pos.x) ? WALK_LEFT : WALK_RIGHT) : ((to.y > pos.y) ? WALK_DOWN : WALK_UP);
|
||||
_a->setF(frames->firstWalkFrame[_direction] + (_step / frames->frameRepeat[_direction]) % frames->numWalkFrames[_direction]);
|
||||
}
|
||||
|
||||
PathWalker_NS::PathWalker_NS(Parallaction *vm) : _direction(WALK_DOWN), _step(0), _vm(vm) {
|
||||
}
|
||||
|
||||
bool PathWalker_BR::directPathExists(const Common::Point &from, const Common::Point &to) {
|
||||
Common::Point copy(from);
|
||||
Common::Point p(copy);
|
||||
|
||||
while (p != to) {
|
||||
if (p.x < to.x && isPathClear(p.x + 1, p.y)) p.x++;
|
||||
if (p.x > to.x && isPathClear(p.x - 1, p.y)) p.x--;
|
||||
if (p.y < to.y && isPathClear(p.x, p.y + 1)) p.y++;
|
||||
if (p.y > to.y && isPathClear(p.x, p.y - 1)) p.y--;
|
||||
|
||||
if (p == copy && p != to)
|
||||
return false;
|
||||
|
||||
copy = p;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PathWalker_BR::setCharacterPath(AnimationPtr a, uint16 x, uint16 y) {
|
||||
_character._a = a;
|
||||
_character._first = true;
|
||||
_character._stillWalkingTowardsNode = true;
|
||||
_character._walkDelay = 0;
|
||||
buildPath(_character, x, y);
|
||||
_character._active = true;
|
||||
}
|
||||
|
||||
void PathWalker_BR::setFollowerPath(AnimationPtr a, uint16 x, uint16 y) {
|
||||
_follower._a = a;
|
||||
_follower._first = true;
|
||||
_follower._stillWalkingTowardsNode = true;
|
||||
_follower._walkDelay = 5;
|
||||
buildPath(_follower, x - 50, y);
|
||||
_follower._active = true;
|
||||
}
|
||||
|
||||
void PathWalker_BR::stopFollower() {
|
||||
if (_follower._active) {
|
||||
uint32 frame = _follower._a->getF();
|
||||
_follower._a->setF((frame/9) * 9);
|
||||
}
|
||||
_follower._a.reset();
|
||||
_follower._active = false;
|
||||
}
|
||||
|
||||
void PathWalker_BR::buildPath(State &s, uint16 x, uint16 y) {
|
||||
Common::Point foot;
|
||||
s._a->getFoot(foot);
|
||||
|
||||
debugC(1, kDebugWalk, "buildPath: try to build path from (%i, %i) to (%i, %i)", foot.x, foot.y, x, y);
|
||||
s._walkPath.clear();
|
||||
|
||||
// look for easy path first
|
||||
Common::Point dest(x, y);
|
||||
if (directPathExists(foot, dest)) {
|
||||
s._walkPath.push_back(dest);
|
||||
debugC(3, kDebugWalk, "buildPath: direct path found");
|
||||
return;
|
||||
}
|
||||
|
||||
// look for short circuit cases
|
||||
ZonePtr z0 = _vm->hitZone(kZonePath, x, y);
|
||||
if (!z0) {
|
||||
s._walkPath.push_back(dest);
|
||||
debugC(3, kDebugWalk, "buildPath: corner case 0 (%i nodes)", s._walkPath.size());
|
||||
return;
|
||||
}
|
||||
ZonePtr z1 = _vm->hitZone(kZonePath, foot.x, foot.y);
|
||||
if (!z1 || z1 == z0) {
|
||||
s._walkPath.push_back(dest);
|
||||
debugC(3, kDebugWalk, "buildPath: corner case 1 (%i nodes)", s._walkPath.size());
|
||||
return;
|
||||
}
|
||||
|
||||
// build complex path
|
||||
int id = atoi(z0->_name);
|
||||
|
||||
if (z1->u._pathLists[id].empty()) {
|
||||
s._walkPath.clear();
|
||||
debugC(3, kDebugWalk, "buildPath: no path found");
|
||||
// If no path, trigger finalize and stop of walking...
|
||||
s._stillWalkingTowardsNode = false;
|
||||
return;
|
||||
}
|
||||
|
||||
PointList::iterator b = z1->u._pathLists[id].begin();
|
||||
PointList::iterator e = z1->u._pathLists[id].end();
|
||||
for ( ; b != e; ++b) {
|
||||
s._walkPath.push_front(*b);
|
||||
}
|
||||
s._walkPath.push_back(dest);
|
||||
debugC(3, kDebugWalk, "buildPath: complex path (%i nodes)", s._walkPath.size());
|
||||
}
|
||||
|
||||
void PathWalker_BR::finalizeWalk(State &s) {
|
||||
g_engineFlags &= ~kEngineWalking;
|
||||
|
||||
Common::Point foot;
|
||||
_character._a->getFoot(foot);
|
||||
|
||||
ZonePtr z = _vm->hitZone(kZoneDoor, foot.x, foot.y);
|
||||
if (z && ((z->_flags & kFlagsClosed) == 0)) {
|
||||
_vm->_location._startPosition = z->u._doorStartPos; // foot pos
|
||||
_vm->_location._startFrame = z->u._doorStartFrame;
|
||||
|
||||
// TODO: implement working follower. Must find out a location in which the code is
|
||||
// used and which is stable enough.
|
||||
if (_follower._active) {
|
||||
_vm->_location._followerStartPosition = z->u._doorStartPos2_br; // foot pos
|
||||
_vm->_location._followerStartFrame = z->u._doorStartFrame2_br;
|
||||
} else {
|
||||
_vm->_location._followerStartPosition.x = -1000;
|
||||
_vm->_location._followerStartPosition.y = -1000;
|
||||
_vm->_location._followerStartFrame = 0;
|
||||
}
|
||||
|
||||
_vm->scheduleLocationSwitch(z->u._doorLocation.c_str());
|
||||
_vm->_cmdExec->run(z->_commands, z);
|
||||
}
|
||||
|
||||
#if 0
|
||||
// TODO: Input::walkTo must be extended to support destination frame in addition to coordinates
|
||||
if (g_engineFlags & FINAL_WALK_FRAME) { // this flag is set in readInput()
|
||||
g_engineFlags &= ~FINAL_WALK_FRAME;
|
||||
_ch._a->_frame = _moveToF; // from readInput()...
|
||||
} else {
|
||||
_ch._a->_frame = _dirFrame; // from walk()
|
||||
}
|
||||
_ch._a->setFoot(foot);
|
||||
#endif
|
||||
|
||||
s._a->setF(s._dirFrame); // temporary solution
|
||||
|
||||
s._active = false;
|
||||
}
|
||||
|
||||
void PathWalker_BR::walk() {
|
||||
if ((g_engineFlags & kEngineWalking) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
doWalk(_character);
|
||||
doWalk(_follower);
|
||||
|
||||
Common::Point pos, foot;
|
||||
_vm->_gfx->getScrollPos(pos);
|
||||
_character._a->getFoot(foot);
|
||||
|
||||
int32 dx = 0, dy = 0;
|
||||
if (foot.x > pos.x + 600) {
|
||||
dx = 78*4;
|
||||
} else
|
||||
if (foot.x < pos.x + 40) {
|
||||
dx = -78*4;
|
||||
}
|
||||
|
||||
if (foot.y > pos.y + 350) {
|
||||
dy = 100;
|
||||
} else
|
||||
if (foot.y < pos.y + 80) {
|
||||
dy = -100;
|
||||
}
|
||||
|
||||
_vm->_gfx->initiateScroll(dx, dy);
|
||||
}
|
||||
|
||||
void PathWalker_BR::checkTrap(const Common::Point &p) {
|
||||
ZonePtr z = _vm->hitZone(kZoneTrap, p.x, p.y);
|
||||
|
||||
if (z && z != _vm->_zoneTrap) {
|
||||
if (z->_flags & kFlagsIsAnimation) {
|
||||
z->_flags |= kFlagsActing;
|
||||
} else {
|
||||
_vm->setLocationFlags(kFlagsExit);
|
||||
_vm->_cmdExec->run(z->_commands, z);
|
||||
_vm->clearLocationFlags(kFlagsExit);
|
||||
}
|
||||
}
|
||||
|
||||
if (_vm->_zoneTrap && _vm->_zoneTrap != z) {
|
||||
if (_vm->_zoneTrap->_flags & kFlagsIsAnimation) {
|
||||
_vm->_zoneTrap->_flags &= ~kFlagsActing;
|
||||
} else {
|
||||
_vm->setLocationFlags(kFlagsEnter);
|
||||
_vm->_cmdExec->run(_vm->_zoneTrap->_commands, _vm->_zoneTrap);
|
||||
_vm->clearLocationFlags(kFlagsEnter);
|
||||
}
|
||||
}
|
||||
|
||||
_vm->_zoneTrap = z;
|
||||
}
|
||||
|
||||
void PathWalker_BR::doWalk(State &s) {
|
||||
if (!s._active) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (s._walkDelay > 0) {
|
||||
s._walkDelay--;
|
||||
if (s._walkDelay == 0 && !s._a->_scriptName.empty()) {
|
||||
// stop script and reset
|
||||
s._a->_flags &= ~kFlagsActing;
|
||||
// _vm->_programExec->resetProgram(s._a->_scriptName);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!s._stillWalkingTowardsNode) {
|
||||
if (!s._walkPath.empty())
|
||||
s._walkPath.erase(s._walkPath.begin());
|
||||
|
||||
if (s._walkPath.empty()) {
|
||||
finalizeWalk(s);
|
||||
debugC(3, kDebugWalk, "PathWalker_BR::doWalk, walk completed (no more nodes)");
|
||||
return;
|
||||
} else {
|
||||
debugC(3, kDebugWalk, "PathWalker_BR::doWalk, reached a walkpath node, %i left", s._walkPath.size());
|
||||
}
|
||||
}
|
||||
|
||||
s._a->getFoot(s._startFoot);
|
||||
|
||||
uint scale = _vm->_location.getScale(s._startFoot.y);
|
||||
int xStep = (scale * 16) / 100 + 1;
|
||||
int yStep = (scale * 10) / 100 + 1;
|
||||
|
||||
/* WORKAROUND: in the balloon scene, the position of the balloon (which is implemented as a
|
||||
Character) is controlled by the user (for movement, via this walking code) and by the scripts
|
||||
(to simulate the balloon floating in the air, in a neverending loop that alters the position
|
||||
coordinates).
|
||||
When the two step sizes are equal in magnitude and opposite in direction, then the walk code
|
||||
enters an infinite loop without giving control back to the user (this happens quite frequently
|
||||
when navigating the balloon near the borders of the screen, where the calculated step is
|
||||
forcibly small because of clipping). Since the "floating" script (part1/scripts/mongolo.scr)
|
||||
uses increments of 3 for both x and y, we tweak the calculated steps accordingly here. */
|
||||
if (xStep == 3) xStep--;
|
||||
if (yStep == 3) yStep--;
|
||||
|
||||
debugC(9, kDebugWalk, "calculated step: (%i, %i)", xStep, yStep);
|
||||
|
||||
s._stillWalkingTowardsNode = false;
|
||||
s._step++;
|
||||
s._step %= 8;
|
||||
|
||||
int maxX = _vm->_gfx->_backgroundInfo->width;
|
||||
int minX = 0;
|
||||
int maxY = _vm->_gfx->_backgroundInfo->height;
|
||||
int minY = 0;
|
||||
|
||||
int walkFrame = s._step;
|
||||
s._dirFrame = 0;
|
||||
Common::Point newpos(s._startFoot), delta;
|
||||
|
||||
assert (!s._walkPath.empty());
|
||||
Common::Point p(*s._walkPath.begin());
|
||||
|
||||
if (s._startFoot.y < p.y && (s._startFoot.y + yStep) < maxY && isPathClear(s._startFoot.x, s._startFoot.y + yStep)) {
|
||||
if (yStep + s._startFoot.y <= p.y) {
|
||||
s._stillWalkingTowardsNode = true;
|
||||
delta.y = yStep;
|
||||
newpos.y = yStep + s._startFoot.y;
|
||||
} else {
|
||||
delta.y = p.y - s._startFoot.y;
|
||||
newpos.y = p.y;
|
||||
}
|
||||
s._dirFrame = 9;
|
||||
} else
|
||||
if (s._startFoot.y > p.y && (s._startFoot.y - yStep) > minY && isPathClear(s._startFoot.x, s._startFoot.y - yStep)) {
|
||||
if (s._startFoot.y - yStep >= p.y) {
|
||||
s._stillWalkingTowardsNode = true;
|
||||
delta.y = yStep;
|
||||
newpos.y = s._startFoot.y - yStep;
|
||||
} else {
|
||||
delta.y = s._startFoot.y - p.y;
|
||||
newpos.y = p.y;
|
||||
}
|
||||
s._dirFrame = 0;
|
||||
}
|
||||
|
||||
if (s._startFoot.x < p.x && (s._startFoot.x + xStep) < maxX && isPathClear(s._startFoot.x + xStep, s._startFoot.y)) {
|
||||
if (s._startFoot.x + xStep <= p.x) {
|
||||
s._stillWalkingTowardsNode = true;
|
||||
delta.x = xStep;
|
||||
newpos.x = xStep + s._startFoot.x;
|
||||
} else {
|
||||
delta.x = p.x - s._startFoot.x;
|
||||
newpos.x = p.x;
|
||||
}
|
||||
if (delta.y < delta.x) {
|
||||
s._dirFrame = 18; // right
|
||||
}
|
||||
} else
|
||||
if (s._startFoot.x > p.x && (s._startFoot.x - xStep) > minX && isPathClear(s._startFoot.x - xStep, s._startFoot.y)) {
|
||||
if (s._startFoot.x - xStep >= p.x) {
|
||||
s._stillWalkingTowardsNode = true;
|
||||
delta.x = xStep;
|
||||
newpos.x = s._startFoot.x - xStep;
|
||||
} else {
|
||||
delta.x = s._startFoot.x - p.x;
|
||||
newpos.x = p.x;
|
||||
}
|
||||
if (delta.y < delta.x) {
|
||||
s._dirFrame = 27; // left
|
||||
}
|
||||
}
|
||||
|
||||
debugC(9, kDebugWalk, "foot (%i, %i) dest (%i, %i) deltas = %i/%i ", s._startFoot.x, s._startFoot.y, p.x, p.y, delta.x, delta.y);
|
||||
|
||||
if (s._stillWalkingTowardsNode) {
|
||||
debugC(9, kDebugWalk, "PathWalker_BR::doWalk, foot moved from (%i, %i) to (%i, %i)", s._startFoot.x, s._startFoot.y, newpos.x, newpos.y);
|
||||
s._a->setF(walkFrame + s._dirFrame + 1);
|
||||
s._startFoot.x = newpos.x;
|
||||
s._startFoot.y = newpos.y;
|
||||
s._a->setFoot(s._startFoot);
|
||||
s._a->setZ(newpos.y);
|
||||
}
|
||||
|
||||
if (s._stillWalkingTowardsNode || !s._walkPath.empty()) {
|
||||
Common::Point p2;
|
||||
s._a->getFoot(p2);
|
||||
checkTrap(p2);
|
||||
debugC(3, kDebugWalk, "PathWalker_BR::doWalk, stepped to (%i, %i)", p2.x, p2.y);
|
||||
} else {
|
||||
debugC(3, kDebugWalk, "PathWalker_BR::doWalk, case 2");
|
||||
finalizeWalk(s);
|
||||
}
|
||||
}
|
||||
|
||||
PathWalker_BR::PathWalker_BR(Parallaction *vm) : _vm(vm) {
|
||||
_character._active = false;
|
||||
_character._step = 0;
|
||||
_follower._active = false;
|
||||
_follower._step = 0;
|
||||
}
|
||||
|
||||
} // namespace Parallaction
|
||||
98
engines/parallaction/walk.h
Normal file
98
engines/parallaction/walk.h
Normal 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 PARALLACTION_WALK_H
|
||||
#define PARALLACTION_WALK_H
|
||||
|
||||
#include "common/ptr.h"
|
||||
#include "common/list.h"
|
||||
|
||||
#include "parallaction/objects.h"
|
||||
|
||||
namespace Parallaction {
|
||||
|
||||
struct Character;
|
||||
|
||||
class PathWalker_NS {
|
||||
AnimationPtr _a;
|
||||
PointList _walkPath;
|
||||
int16 _direction, _step;
|
||||
|
||||
// builder routines
|
||||
PointList _subPath;
|
||||
void correctPathPoint(Common::Point &to);
|
||||
uint32 buildSubPath(const Common::Point& pos, const Common::Point& stop);
|
||||
uint16 walkFunc1(const Common::Point &to, Common::Point& node);
|
||||
|
||||
// walker routines
|
||||
void finalizeWalk();
|
||||
void clipMove(Common::Point& pos, const Common::Point& to);
|
||||
void checkDoor(const Common::Point &foot);
|
||||
void updateDirection(const Common::Point& pos, const Common::Point& to);
|
||||
|
||||
Parallaction *_vm;
|
||||
|
||||
public:
|
||||
PathWalker_NS(Parallaction *vm);
|
||||
|
||||
void buildPath(AnimationPtr a, uint16 x, uint16 y);
|
||||
void walk();
|
||||
};
|
||||
|
||||
class PathWalker_BR {
|
||||
|
||||
struct State {
|
||||
bool _active;
|
||||
AnimationPtr _a;
|
||||
int _walkDelay;
|
||||
bool _stillWalkingTowardsNode;
|
||||
Common::Point _startFoot;
|
||||
bool _first;
|
||||
int _step;
|
||||
int _dirFrame;
|
||||
PointList _walkPath;
|
||||
};
|
||||
|
||||
State _character;
|
||||
State _follower;
|
||||
|
||||
void finalizeWalk(State &s);
|
||||
bool directPathExists(const Common::Point &from, const Common::Point &to);
|
||||
void buildPath(State &s, uint16 x, uint16 y);
|
||||
void doWalk(State &s);
|
||||
void checkTrap(const Common::Point &p);
|
||||
|
||||
Parallaction *_vm;
|
||||
|
||||
public:
|
||||
PathWalker_BR(Parallaction *vm);
|
||||
~PathWalker_BR() { }
|
||||
|
||||
void setCharacterPath(AnimationPtr a, uint16 x, uint16 y);
|
||||
void setFollowerPath(AnimationPtr a, uint16 x, uint16 y);
|
||||
void stopFollower();
|
||||
|
||||
void walk();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user