Initial commit
This commit is contained in:
1271
engines/zvision/scripting/actions.cpp
Normal file
1271
engines/zvision/scripting/actions.cpp
Normal file
File diff suppressed because it is too large
Load Diff
446
engines/zvision/scripting/actions.h
Normal file
446
engines/zvision/scripting/actions.h
Normal file
@@ -0,0 +1,446 @@
|
||||
/* 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 ZVISION_ACTIONS_H
|
||||
#define ZVISION_ACTIONS_H
|
||||
|
||||
#include "common/path.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/str.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
// Forward declaration of ZVision. This file is included before ZVision is declared
|
||||
class ZVision;
|
||||
class ScriptManager;
|
||||
class ValueSlot;
|
||||
|
||||
/**
|
||||
* The base class that represents any action that a Puzzle can take.
|
||||
* This class is purely virtual.
|
||||
*/
|
||||
class ResultAction {
|
||||
public:
|
||||
ResultAction(ZVision *engine, int32 slotkey);
|
||||
virtual ~ResultAction() {}
|
||||
/**
|
||||
* This is called by the script system whenever a Puzzle's criteria are found to be true.
|
||||
* It should execute any necessary actions and return a value indicating whether the script
|
||||
* system should continue to test puzzles. In 99% of cases this will be 'true'.
|
||||
*
|
||||
* @param engine A pointer to the base engine so the ResultAction can access all the necessary methods
|
||||
* @return Should the script system continue to test any remaining puzzles (true) or immediately break and go on to the next frame (false)
|
||||
*/
|
||||
virtual bool execute() = 0;
|
||||
protected:
|
||||
ZVision *_engine;
|
||||
ScriptManager *_scriptManager;
|
||||
int32 _slotKey;
|
||||
};
|
||||
|
||||
class ActionAdd : public ResultAction {
|
||||
public:
|
||||
ActionAdd(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
~ActionAdd();
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
uint32 _key;
|
||||
ValueSlot *_value = NULL;
|
||||
};
|
||||
|
||||
class ActionAssign : public ResultAction {
|
||||
public:
|
||||
ActionAssign(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
~ActionAssign() override;
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
uint32 _key;
|
||||
ValueSlot *_value = NULL;
|
||||
};
|
||||
|
||||
class ActionAttenuate : public ResultAction {
|
||||
public:
|
||||
ActionAttenuate(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
uint32 _key;
|
||||
int32 _attenuation;
|
||||
};
|
||||
|
||||
class ActionChangeLocation : public ResultAction {
|
||||
public:
|
||||
ActionChangeLocation(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
char _world;
|
||||
char _room;
|
||||
char _node;
|
||||
char _view;
|
||||
uint32 _offset;
|
||||
};
|
||||
|
||||
class ActionCrossfade : public ResultAction {
|
||||
public:
|
||||
ActionCrossfade(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
uint32 _keyOne;
|
||||
uint32 _keyTwo;
|
||||
int32 _oneStartVolume;
|
||||
int32 _twoStartVolume;
|
||||
int32 _oneEndVolume;
|
||||
int32 _twoEndVolume;
|
||||
int32 _timeInMillis;
|
||||
};
|
||||
|
||||
class ActionCursor : public ResultAction {
|
||||
public:
|
||||
ActionCursor(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
uint8 _action;
|
||||
};
|
||||
|
||||
class ActionDelayRender : public ResultAction {
|
||||
public:
|
||||
ActionDelayRender(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
uint32 _framesToDelay;
|
||||
};
|
||||
|
||||
class ActionDisableControl : public ResultAction {
|
||||
public:
|
||||
ActionDisableControl(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
uint32 _key;
|
||||
};
|
||||
|
||||
class ActionDisplayMessage : public ResultAction {
|
||||
public:
|
||||
ActionDisplayMessage(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
int16 _control;
|
||||
int16 _msgid;
|
||||
};
|
||||
|
||||
class ActionDissolve : public ResultAction {
|
||||
public:
|
||||
ActionDissolve(ZVision *engine);
|
||||
bool execute() override;
|
||||
};
|
||||
|
||||
class ActionDistort : public ResultAction {
|
||||
public:
|
||||
ActionDistort(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
~ActionDistort() override;
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
int16 _distSlot;
|
||||
int16 _speed;
|
||||
float _startAngle;
|
||||
float _endAngle;
|
||||
float _startLineScale;
|
||||
float _endLineScale;
|
||||
};
|
||||
|
||||
class ActionEnableControl : public ResultAction {
|
||||
public:
|
||||
ActionEnableControl(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
uint32 _key;
|
||||
};
|
||||
|
||||
class ActionFlushMouseEvents : public ResultAction {
|
||||
public:
|
||||
ActionFlushMouseEvents(ZVision *engine, int32 slotkey);
|
||||
bool execute() override;
|
||||
};
|
||||
|
||||
class ActionInventory : public ResultAction {
|
||||
public:
|
||||
ActionInventory(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
bool execute() override;
|
||||
private:
|
||||
int8 _type;
|
||||
int32 _key;
|
||||
};
|
||||
|
||||
class ActionKill : public ResultAction {
|
||||
public:
|
||||
ActionKill(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
uint32 _key;
|
||||
uint32 _type;
|
||||
};
|
||||
|
||||
class ActionMenuBarEnable : public ResultAction {
|
||||
public:
|
||||
ActionMenuBarEnable(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
bool execute() override;
|
||||
private:
|
||||
uint16 _menus;
|
||||
};
|
||||
|
||||
class ActionMusic : public ResultAction {
|
||||
public:
|
||||
ActionMusic(ZVision *engine, int32 slotkey, const Common::String &line, bool global);
|
||||
~ActionMusic() override;
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
Common::Path _fileName;
|
||||
bool _loop;
|
||||
ValueSlot *_volume;
|
||||
bool _universe;
|
||||
bool _midi;
|
||||
int8 _note;
|
||||
int8 _prog;
|
||||
};
|
||||
|
||||
class ActionPanTrack : public ResultAction {
|
||||
public:
|
||||
ActionPanTrack(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
~ActionPanTrack() override;
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
int32 _pos; // Sound source position; NB in panoramas (all original game scripts), this is specified as the X background coordinate; otherwise it is specified in azimuth degrees.
|
||||
uint8 _mag; // Magnitude of effect (not used by original game scripts); 255 for fully directional sound, 0 for fully ambient
|
||||
bool _resetMusicNode; // If true (default, original game scripts have no concept of this), associated music slot value is reset to a value of 2 upon creation of this object.
|
||||
// This seems necessary to ensure all original game pan-track effects load correctly, though it is still unclear exactly what the original intent of these values was.
|
||||
// So far, best guess for music slotkey values is: 0 = has never been loaded, 1 = loaded and actively playing now, 2 = has loaded & played & then subsequently been killed.
|
||||
// Since there is literally nothing in the game scripts that sets some of these values to 2, and certain pan_tracks require it to be 2 for the puzzle that creates them to trigger, the original game engine code must have set these values to 2 manually somehow upon conditions being met to allow a pan_track to be created?
|
||||
bool _staticScreen; // Used by auxiliary scripts to apply directionality to audio in static screens; not used in original game scripts.
|
||||
bool _resetMixerOnDelete; // Unnecessary and should be set false for original scripts; useful in some cases in extra scripts to avoid brief volume spikes on location changes
|
||||
uint32 _musicSlot;
|
||||
};
|
||||
|
||||
class ActionPlayAnimation : public ResultAction {
|
||||
public:
|
||||
ActionPlayAnimation(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
~ActionPlayAnimation() override;
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
Common::Path _fileName;
|
||||
uint32 _x;
|
||||
uint32 _y;
|
||||
uint32 _x2;
|
||||
uint32 _y2;
|
||||
uint32 _start;
|
||||
uint32 _end;
|
||||
int32 _mask;
|
||||
int32 _framerate;
|
||||
int32 _loopCount;
|
||||
};
|
||||
|
||||
class ActionPlayPreloadAnimation : public ResultAction {
|
||||
public:
|
||||
ActionPlayPreloadAnimation(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
uint32 _controlKey;
|
||||
uint32 _x1;
|
||||
uint32 _y1;
|
||||
uint32 _x2;
|
||||
uint32 _y2;
|
||||
uint _startFrame;
|
||||
uint _endFrame;
|
||||
uint _loopCount;
|
||||
};
|
||||
|
||||
class ActionPreloadAnimation : public ResultAction {
|
||||
public:
|
||||
ActionPreloadAnimation(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
~ActionPreloadAnimation() override;
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
Common::Path _fileName;
|
||||
int32 _mask;
|
||||
int32 _framerate;
|
||||
};
|
||||
|
||||
class ActionPreferences : public ResultAction {
|
||||
public:
|
||||
ActionPreferences(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
bool _save;
|
||||
};
|
||||
|
||||
class ActionQuit : public ResultAction {
|
||||
public:
|
||||
ActionQuit(ZVision *engine, int32 slotkey) : ResultAction(engine, slotkey) {}
|
||||
bool execute() override;
|
||||
};
|
||||
|
||||
class ActionRegion : public ResultAction {
|
||||
public:
|
||||
ActionRegion(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
~ActionRegion() override;
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
Common::String _art;
|
||||
Common::String _custom;
|
||||
Common::Rect _rect;
|
||||
uint16 _delay;
|
||||
uint16 _type;
|
||||
uint16 _unk1;
|
||||
uint16 _unk2;
|
||||
};
|
||||
|
||||
// Only used by ZGI (locations cd6e, cd6k, dg2f, dg4e, dv1j)
|
||||
class ActionUnloadAnimation : public ResultAction {
|
||||
public:
|
||||
ActionUnloadAnimation(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
bool execute() override;
|
||||
private:
|
||||
uint32 _key;
|
||||
};
|
||||
|
||||
class ActionRandom : public ResultAction {
|
||||
public:
|
||||
ActionRandom(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
~ActionRandom() override;
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
ValueSlot *_max;
|
||||
};
|
||||
|
||||
class ActionRestoreGame : public ResultAction {
|
||||
public:
|
||||
ActionRestoreGame(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
Common::Path _fileName;
|
||||
};
|
||||
|
||||
class ActionRotateTo : public ResultAction {
|
||||
public:
|
||||
ActionRotateTo(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
int32 _toPos;
|
||||
int32 _time;
|
||||
};
|
||||
|
||||
class ActionSetPartialScreen : public ResultAction {
|
||||
public:
|
||||
ActionSetPartialScreen(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
uint _x;
|
||||
uint _y;
|
||||
Common::Path _fileName;
|
||||
int32 _backgroundColor;
|
||||
};
|
||||
|
||||
class ActionSetScreen : public ResultAction {
|
||||
public:
|
||||
ActionSetScreen(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
Common::Path _fileName;
|
||||
};
|
||||
|
||||
class ActionStop : public ResultAction {
|
||||
public:
|
||||
ActionStop(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
uint32 _key;
|
||||
};
|
||||
|
||||
class ActionStreamVideo : public ResultAction {
|
||||
public:
|
||||
ActionStreamVideo(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
Common::Path _fileName;
|
||||
uint _x1;
|
||||
uint _y1;
|
||||
uint _x2;
|
||||
uint _y2;
|
||||
uint _flags;
|
||||
bool _skippable;
|
||||
};
|
||||
|
||||
class ActionSyncSound : public ResultAction {
|
||||
public:
|
||||
ActionSyncSound(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
int _syncto;
|
||||
Common::Path _fileName;
|
||||
};
|
||||
|
||||
class ActionTimer : public ResultAction {
|
||||
public:
|
||||
ActionTimer(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
~ActionTimer() override;
|
||||
bool execute() override;
|
||||
private:
|
||||
ValueSlot *_time;
|
||||
};
|
||||
|
||||
class ActionTtyText : public ResultAction {
|
||||
public:
|
||||
ActionTtyText(ZVision *engine, int32 slotkey, const Common::String &line);
|
||||
~ActionTtyText() override;
|
||||
bool execute() override;
|
||||
|
||||
private:
|
||||
Common::Path _filename;
|
||||
uint32 _delay;
|
||||
Common::Rect _r;
|
||||
};
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
140
engines/zvision/scripting/control.cpp
Normal file
140
engines/zvision/scripting/control.cpp
Normal file
@@ -0,0 +1,140 @@
|
||||
|
||||
/* 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/scummsys.h"
|
||||
#include "common/stream.h"
|
||||
#include "zvision/detection.h"
|
||||
#include "zvision/zvision.h"
|
||||
#include "zvision/graphics/render_manager.h"
|
||||
#include "zvision/scripting/control.h"
|
||||
#include "zvision/scripting/script_manager.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
void Control::parseFlatControl(ZVision *engine) {
|
||||
debugC(1, kDebugGraphics, "Setting render state to FLAT");
|
||||
engine->getRenderManager()->getRenderTable()->setRenderState(RenderTable::FLAT);
|
||||
}
|
||||
|
||||
void Control::parsePanoramaControl(ZVision *engine, Common::SeekableReadStream &stream) {
|
||||
debugC(1, kDebugGraphics, "Setting render state to PANORAMA");
|
||||
RenderTable *renderTable = engine->getRenderManager()->getRenderTable();
|
||||
renderTable->setRenderState(RenderTable::PANORAMA);
|
||||
|
||||
// Loop until we find the closing brace
|
||||
Common::String line = stream.readLine();
|
||||
engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
|
||||
|
||||
while (!stream.eos() && !line.contains('}')) {
|
||||
if (line.matchString("angle*", true)) {
|
||||
float fov;
|
||||
if (sscanf(line.c_str(), "angle(%f)", &fov) == 1)
|
||||
renderTable->setPanoramaFoV(fov);
|
||||
} else if (line.matchString("linscale*", true)) {
|
||||
float scale;
|
||||
if (sscanf(line.c_str(), "linscale(%f)", &scale) == 1)
|
||||
renderTable->setPanoramaScale(scale);
|
||||
} else if (line.matchString("reversepana*", true)) {
|
||||
uint reverse = 0;
|
||||
sscanf(line.c_str(), "reversepana(%u)", &reverse);
|
||||
if (reverse == 1) {
|
||||
renderTable->setPanoramaReverse(true);
|
||||
}
|
||||
} else if (line.matchString("zeropoint*", true)) {
|
||||
uint point;
|
||||
if (sscanf(line.c_str(), "zeropoint(%u)", &point) == 1)
|
||||
renderTable->setPanoramaZeroPoint(point);
|
||||
}
|
||||
|
||||
line = stream.readLine();
|
||||
engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
|
||||
}
|
||||
|
||||
renderTable->generateRenderTable();
|
||||
}
|
||||
|
||||
// Only used in Zork Nemesis, handles tilt controls (ZGI doesn't have a tilt view)
|
||||
void Control::parseTiltControl(ZVision *engine, Common::SeekableReadStream &stream) {
|
||||
debugC(1, kDebugGraphics, "Setting render state to TILT");
|
||||
RenderTable *renderTable = engine->getRenderManager()->getRenderTable();
|
||||
renderTable->setRenderState(RenderTable::TILT);
|
||||
|
||||
// Loop until we find the closing brace
|
||||
Common::String line = stream.readLine();
|
||||
engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
|
||||
|
||||
while (!stream.eos() && !line.contains('}')) {
|
||||
if (line.matchString("angle*", true)) {
|
||||
float fov;
|
||||
if (sscanf(line.c_str(), "angle(%f)", &fov) == 1)
|
||||
renderTable->setTiltFoV(fov);
|
||||
} else if (line.matchString("linscale*", true)) {
|
||||
float scale;
|
||||
if (sscanf(line.c_str(), "linscale(%f)", &scale) == 1)
|
||||
renderTable->setTiltScale(scale);
|
||||
} else if (line.matchString("reversepana*", true)) {
|
||||
uint reverse = 0;
|
||||
sscanf(line.c_str(), "reversepana(%u)", &reverse);
|
||||
if (reverse == 1) {
|
||||
renderTable->setTiltReverse(true);
|
||||
}
|
||||
}
|
||||
|
||||
line = stream.readLine();
|
||||
engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
|
||||
}
|
||||
|
||||
renderTable->generateRenderTable();
|
||||
}
|
||||
|
||||
void Control::getParams(const Common::String &inputStr, Common::String ¶meter, Common::String &values) {
|
||||
const char *chrs = inputStr.c_str();
|
||||
uint lbr;
|
||||
|
||||
for (lbr = 0; lbr < inputStr.size(); lbr++)
|
||||
if (chrs[lbr] == '(')
|
||||
break;
|
||||
|
||||
if (lbr >= inputStr.size())
|
||||
return;
|
||||
|
||||
uint rbr;
|
||||
|
||||
for (rbr = lbr + 1; rbr < inputStr.size(); rbr++)
|
||||
if (chrs[rbr] == ')')
|
||||
break;
|
||||
|
||||
if (rbr >= inputStr.size())
|
||||
return;
|
||||
|
||||
parameter = Common::String(chrs, chrs + lbr);
|
||||
values = Common::String(chrs + lbr + 1, chrs + rbr);
|
||||
}
|
||||
|
||||
void Control::setVenus() {
|
||||
if (_venusId >= 0)
|
||||
if (_engine->getScriptManager()->getStateValue(_venusId) > 0)
|
||||
_engine->getScriptManager()->setStateValue(StateKey_Venus, _venusId);
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
146
engines/zvision/scripting/control.h
Normal file
146
engines/zvision/scripting/control.h
Normal file
@@ -0,0 +1,146 @@
|
||||
/* 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 ZVISION_CONTROL_H
|
||||
#define ZVISION_CONTROL_H
|
||||
|
||||
#include "common/keyboard.h"
|
||||
#include "common/str.h"
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
struct Point;
|
||||
class WriteStream;
|
||||
}
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
class ZVision;
|
||||
|
||||
/**
|
||||
* The base class for all Controls.
|
||||
*
|
||||
* Controls are the things that the user interacts with. Ex: A lever on the door
|
||||
*/
|
||||
class Control {
|
||||
public:
|
||||
|
||||
enum ControlType {
|
||||
CONTROL_UNKNOW,
|
||||
CONTROL_INPUT,
|
||||
CONTROL_PUSHTGL,
|
||||
CONTROL_SLOT,
|
||||
CONTROL_LEVER,
|
||||
CONTROL_SAVE,
|
||||
CONTROL_SAFE,
|
||||
CONTROL_FIST,
|
||||
CONTROL_TITLER,
|
||||
CONTROL_HOTMOV,
|
||||
CONTROL_PAINT
|
||||
};
|
||||
|
||||
Control(ZVision *engine, uint32 key, ControlType type) : _engine(engine), _key(key), _type(type), _venusId(-1) {}
|
||||
virtual ~Control() {}
|
||||
|
||||
uint32 getKey() {
|
||||
return _key;
|
||||
}
|
||||
|
||||
ControlType getType() {
|
||||
return _type;
|
||||
}
|
||||
|
||||
virtual void focus() {}
|
||||
virtual void unfocus() {}
|
||||
/**
|
||||
* Called when LeftMouse is pushed. Default is NOP.
|
||||
*
|
||||
* @param screenSpacePos The position of the mouse in screen space
|
||||
* @param backgroundImageSpacePos The position of the mouse in background image space
|
||||
*/
|
||||
virtual bool onMouseDown(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Called when LeftMouse is lifted. Default is NOP.
|
||||
*
|
||||
* @param screenSpacePos The position of the mouse in screen space
|
||||
* @param backgroundImageSpacePos The position of the mouse in background image space
|
||||
*/
|
||||
virtual bool onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Called on every MouseMove. Default is NOP.
|
||||
*
|
||||
* @param screenSpacePos The position of the mouse in screen space
|
||||
* @param backgroundImageSpacePos The position of the mouse in background image space
|
||||
* @return Was the cursor changed?
|
||||
*/
|
||||
virtual bool onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Called when a key is pressed. Default is NOP.
|
||||
*
|
||||
* @param keycode The key that was pressed
|
||||
*/
|
||||
virtual bool onKeyDown(Common::KeyState keyState) {
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Called when a key is released. Default is NOP.
|
||||
*
|
||||
* @param keycode The key that was pressed
|
||||
*/
|
||||
virtual bool onKeyUp(Common::KeyState keyState) {
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Processes the node given the deltaTime since last frame. Default is NOP.
|
||||
*
|
||||
* @param deltaTimeInMillis The number of milliseconds that have passed since last frame
|
||||
* @return If true, the node can be deleted after process() finishes
|
||||
*/
|
||||
virtual bool process(uint32 deltaTimeInMillis) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void setVenus();
|
||||
|
||||
protected:
|
||||
ZVision *_engine;
|
||||
uint32 _key;
|
||||
int32 _venusId;
|
||||
|
||||
void getParams(const Common::String &inputStr, Common::String ¶meter, Common::String &values);
|
||||
// Static member functions
|
||||
public:
|
||||
static void parseFlatControl(ZVision *engine);
|
||||
static void parsePanoramaControl(ZVision *engine, Common::SeekableReadStream &stream);
|
||||
static void parseTiltControl(ZVision *engine, Common::SeekableReadStream &stream);
|
||||
private:
|
||||
ControlType _type;
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
304
engines/zvision/scripting/controls/fist_control.cpp
Normal file
304
engines/zvision/scripting/controls/fist_control.cpp
Normal file
@@ -0,0 +1,304 @@
|
||||
/* 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/scummsys.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/system.h"
|
||||
#include "graphics/surface.h"
|
||||
#include "video/video_decoder.h"
|
||||
#include "zvision/zvision.h"
|
||||
#include "zvision/graphics/render_manager.h"
|
||||
#include "zvision/graphics/cursors/cursor_manager.h"
|
||||
#include "zvision/scripting/script_manager.h"
|
||||
#include "zvision/scripting/controls/fist_control.h"
|
||||
#include "zvision/video/rlf_decoder.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
FistControl::FistControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream)
|
||||
: Control(engine, key, CONTROL_FIST) {
|
||||
_cursor = CursorIndex_Idle;
|
||||
_animation = NULL;
|
||||
_soundKey = 0;
|
||||
_fiststatus = 0;
|
||||
_order = 0;
|
||||
_fistnum = 0;
|
||||
|
||||
_animationId = 0;
|
||||
|
||||
clearFistArray(_fistsUp);
|
||||
clearFistArray(_fistsDwn);
|
||||
|
||||
_numEntries = 0;
|
||||
_entries.clear();
|
||||
|
||||
_anmRect = Common::Rect();
|
||||
|
||||
// Loop until we find the closing brace
|
||||
Common::String line = stream.readLine();
|
||||
_engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
|
||||
Common::String param;
|
||||
Common::String values;
|
||||
getParams(line, param, values);
|
||||
|
||||
while (!stream.eos() && !line.contains('}')) {
|
||||
if (param.matchString("sound_key", true)) {
|
||||
_soundKey = atoi(values.c_str());
|
||||
} else if (param.matchString("cursor", true)) {
|
||||
_cursor = _engine->getCursorManager()->getCursorId(values);
|
||||
} else if (param.matchString("descfile", true)) {
|
||||
readDescFile(Common::Path(values));
|
||||
} else if (param.matchString("animation_id", true)) {
|
||||
_animationId = atoi(values.c_str());
|
||||
} else if (param.matchString("venus_id", true)) {
|
||||
_venusId = atoi(values.c_str());
|
||||
}
|
||||
|
||||
line = stream.readLine();
|
||||
_engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
|
||||
getParams(line, param, values);
|
||||
}
|
||||
}
|
||||
|
||||
FistControl::~FistControl() {
|
||||
if (_animation)
|
||||
delete _animation;
|
||||
|
||||
clearFistArray(_fistsUp);
|
||||
clearFistArray(_fistsDwn);
|
||||
_entries.clear();
|
||||
}
|
||||
|
||||
bool FistControl::process(uint32 deltaTimeInMillis) {
|
||||
if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
|
||||
return false;
|
||||
|
||||
if (_animation && _animation->isPlaying()) {
|
||||
if (_animation->endOfVideo()) {
|
||||
_animation->stop();
|
||||
_engine->getScriptManager()->setStateValue(_animationId, 2);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_animation->needsUpdate()) {
|
||||
const Graphics::Surface *frameData = _animation->decodeNextFrame();
|
||||
if (frameData)
|
||||
// WORKAROUND: Ignore the target frame dimensions for the finger animations.
|
||||
// The target dimensions specify an area smaller than expected, thus if we
|
||||
// scale the finger videos to fit these dimensions, they are not aligned
|
||||
// correctly. Not scaling these videos yields a result identical to the
|
||||
// original. Fixes bug #6784.
|
||||
_engine->getRenderManager()->blitSurfaceToBkg(*frameData, _anmRect.left, _anmRect.top);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FistControl::onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
|
||||
if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
|
||||
return false;
|
||||
|
||||
if (mouseIn(screenSpacePos, backgroundImageSpacePos) >= 0) {
|
||||
_engine->getCursorManager()->changeCursor(_cursor);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FistControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
|
||||
if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
|
||||
return false;
|
||||
|
||||
int fistNumber = mouseIn(screenSpacePos, backgroundImageSpacePos);
|
||||
|
||||
if (fistNumber >= 0) {
|
||||
setVenus();
|
||||
|
||||
uint32 oldStatus = _fiststatus;
|
||||
_fiststatus ^= (1 << fistNumber);
|
||||
|
||||
for (int i = 0; i < _numEntries; i++)
|
||||
if (_entries[i]._bitsStrt == oldStatus && _entries[i]._bitsEnd == _fiststatus) {
|
||||
if (_animation) {
|
||||
_animation->stop();
|
||||
_animation->seekToFrame(_entries[i]._anmStrt);
|
||||
_animation->setEndFrame(_entries[i]._anmEnd);
|
||||
_animation->start();
|
||||
}
|
||||
|
||||
_engine->getScriptManager()->setStateValue(_animationId, 1);
|
||||
_engine->getScriptManager()->setStateValue(_soundKey, _entries[i]._sound);
|
||||
break;
|
||||
}
|
||||
|
||||
_engine->getScriptManager()->setStateValue(_key, _fiststatus);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void FistControl::readDescFile(const Common::Path &fileName) {
|
||||
Common::File file;
|
||||
if (!file.open(fileName)) {
|
||||
warning("Desc file %s could could be opened", fileName.toString().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
Common::String line;
|
||||
Common::String param;
|
||||
Common::String values;
|
||||
|
||||
while (!file.eos()) {
|
||||
line = file.readLine();
|
||||
getFistParams(line, param, values);
|
||||
|
||||
if (param.matchString("animation_id", true)) {
|
||||
// Not used
|
||||
} else if (param.matchString("animation", true)) {
|
||||
_animation = _engine->loadAnimation(Common::Path(values));
|
||||
} else if (param.matchString("anim_rect", true)) {
|
||||
int left, top, right, bottom;
|
||||
if (sscanf(values.c_str(), "%d %d %d %d", &left, &top, &right, &bottom) == 4)
|
||||
_anmRect = Common::Rect(left, top, right, bottom);
|
||||
} else if (param.matchString("num_fingers", true)) {
|
||||
if (sscanf(values.c_str(), "%d", &_fistnum) == 1) {
|
||||
_fistsUp.resize(_fistnum);
|
||||
_fistsDwn.resize(_fistnum);
|
||||
}
|
||||
} else if (param.matchString("entries", true)) {
|
||||
if (sscanf(values.c_str(), "%d", &_numEntries) == 1)
|
||||
_entries.resize(_numEntries);
|
||||
} else if (param.matchString("eval_order_ascending", true)) {
|
||||
sscanf(values.c_str(), "%d", &_order);
|
||||
} else if (param.matchString("up_hs_num_*", true)) {
|
||||
int fist, num;
|
||||
num = atoi(values.c_str());
|
||||
|
||||
if (sscanf(param.c_str(), "up_hs_num_%d", &fist) == 1)
|
||||
_fistsUp[fist].resize(num);
|
||||
} else if (param.matchString("up_hs_*", true)) {
|
||||
int16 fist, box, x1, y1, x2, y2;
|
||||
if (sscanf(param.c_str(), "up_hs_%hd_%hd", &fist, &box) == 2) {
|
||||
if (sscanf(values.c_str(), "%hd %hd %hd %hd", &x1, &y1, &x2, &y2) == 4)
|
||||
(_fistsUp[fist])[box] = Common::Rect(x1, y1, x2, y2);
|
||||
}
|
||||
} else if (param.matchString("down_hs_num_*", true)) {
|
||||
int fist, num;
|
||||
num = atoi(values.c_str());
|
||||
|
||||
if (sscanf(param.c_str(), "down_hs_num_%d", &fist) == 1)
|
||||
_fistsDwn[fist].resize(num);
|
||||
} else if (param.matchString("down_hs_*", true)) {
|
||||
int16 fist, box, x1, y1, x2, y2;
|
||||
if (sscanf(param.c_str(), "down_hs_%hd_%hd", &fist, &box) == 2) {
|
||||
if (sscanf(values.c_str(), "%hd %hd %hd %hd", &x1, &y1, &x2, &y2) == 4)
|
||||
(_fistsDwn[fist])[box] = Common::Rect(x1, y1, x2, y2);
|
||||
}
|
||||
} else {
|
||||
int entry, start, end, sound;
|
||||
char bitsStart[33];
|
||||
char bitsEnd[33];
|
||||
entry = atoi(param.c_str());
|
||||
if (sscanf(values.c_str(), "%s %s %d %d (%d)", bitsStart, bitsEnd, &start, &end, &sound) == 5) {
|
||||
_entries[entry]._bitsStrt = readBits(bitsStart);
|
||||
_entries[entry]._bitsEnd = readBits(bitsEnd);
|
||||
_entries[entry]._anmStrt = start;
|
||||
_entries[entry]._anmEnd = end;
|
||||
_entries[entry]._sound = sound;
|
||||
}
|
||||
}
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
|
||||
void FistControl::clearFistArray(Common::Array< Common::Array<Common::Rect> > &arr) {
|
||||
for (uint i = 0; i < arr.size(); i++)
|
||||
arr[i].clear();
|
||||
|
||||
arr.clear();
|
||||
}
|
||||
|
||||
uint32 FistControl::readBits(const char *str) {
|
||||
uint32 bfield = 0;
|
||||
int len = strlen(str);
|
||||
for (int i = 0; i < len; i++)
|
||||
if (str[i] != '0')
|
||||
bfield |= (1 << i);
|
||||
return bfield;
|
||||
}
|
||||
|
||||
int FistControl::mouseIn(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
|
||||
if (_order) {
|
||||
for (int i = 0; i < _fistnum; i++) {
|
||||
if (((_fiststatus >> i) & 1) == 1) {
|
||||
for (uint j = 0; j < _fistsDwn[i].size(); j++)
|
||||
if ((_fistsDwn[i])[j].contains(backgroundImageSpacePos))
|
||||
return i;
|
||||
} else {
|
||||
for (uint j = 0; j < _fistsUp[i].size(); j++)
|
||||
if ((_fistsUp[i])[j].contains(backgroundImageSpacePos))
|
||||
return i;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int i = _fistnum - 1; i >= 0; i--) {
|
||||
if (((_fiststatus >> i) & 1) == 1) {
|
||||
for (uint j = 0; j < _fistsDwn[i].size(); j++)
|
||||
if ((_fistsDwn[i])[j].contains(backgroundImageSpacePos))
|
||||
return i;
|
||||
} else {
|
||||
for (uint j = 0; j < _fistsUp[i].size(); j++)
|
||||
if ((_fistsUp[i])[j].contains(backgroundImageSpacePos))
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void FistControl::getFistParams(const Common::String &inputStr, Common::String ¶meter, Common::String &values) {
|
||||
const char *chrs = inputStr.c_str();
|
||||
uint lbr;
|
||||
|
||||
for (lbr = 0; lbr < inputStr.size(); lbr++)
|
||||
if (chrs[lbr] == ':')
|
||||
break;
|
||||
|
||||
if (lbr >= inputStr.size())
|
||||
return;
|
||||
|
||||
uint rbr;
|
||||
|
||||
for (rbr = lbr + 1; rbr < inputStr.size(); rbr++)
|
||||
if (chrs[rbr] == '~')
|
||||
break;
|
||||
|
||||
if (rbr >= inputStr.size())
|
||||
return;
|
||||
|
||||
parameter = Common::String(chrs, chrs + lbr);
|
||||
values = Common::String(chrs + lbr + 1, chrs + rbr);
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
82
engines/zvision/scripting/controls/fist_control.h
Normal file
82
engines/zvision/scripting/controls/fist_control.h
Normal file
@@ -0,0 +1,82 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ZVISION_FIST_CONTROL_H
|
||||
#define ZVISION_FIST_CONTROL_H
|
||||
|
||||
#include "common/array.h"
|
||||
#include "common/rect.h"
|
||||
#include "zvision/scripting/control.h"
|
||||
|
||||
namespace Video {
|
||||
class VideoDecoder;
|
||||
}
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
// Only used in Zork Nemesis, handles the door lock puzzle with the skeletal fingers (td9e)
|
||||
class FistControl : public Control {
|
||||
public:
|
||||
FistControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream);
|
||||
~FistControl() override;
|
||||
|
||||
private:
|
||||
uint32 _fiststatus;
|
||||
int _fistnum;
|
||||
int16 _cursor;
|
||||
int _order;
|
||||
|
||||
Common::Array< Common::Array<Common::Rect> > _fistsUp;
|
||||
Common::Array< Common::Array<Common::Rect> > _fistsDwn;
|
||||
|
||||
int32 _numEntries;
|
||||
|
||||
struct entries {
|
||||
uint32 _bitsStrt;
|
||||
uint32 _bitsEnd;
|
||||
int32 _anmStrt;
|
||||
int32 _anmEnd;
|
||||
int32 _sound;
|
||||
};
|
||||
|
||||
Common::Array<entries> _entries;
|
||||
|
||||
Video::VideoDecoder *_animation;
|
||||
Common::Rect _anmRect;
|
||||
int32 _soundKey;
|
||||
int32 _animationId;
|
||||
|
||||
public:
|
||||
bool onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) override;
|
||||
bool onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) override;
|
||||
bool process(uint32 deltaTimeInMillis) override;
|
||||
|
||||
private:
|
||||
void readDescFile(const Common::Path &fileName);
|
||||
void clearFistArray(Common::Array< Common::Array<Common::Rect> > &arr);
|
||||
uint32 readBits(const char *str);
|
||||
int mouseIn(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos);
|
||||
void getFistParams(const Common::String &inputStr, Common::String ¶meter, Common::String &values);
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
184
engines/zvision/scripting/controls/hotmov_control.cpp
Normal file
184
engines/zvision/scripting/controls/hotmov_control.cpp
Normal file
@@ -0,0 +1,184 @@
|
||||
/* 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/scummsys.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/system.h"
|
||||
#include "graphics/surface.h"
|
||||
#include "video/video_decoder.h"
|
||||
#include "zvision/zvision.h"
|
||||
#include "zvision/graphics/render_manager.h"
|
||||
#include "zvision/graphics/cursors/cursor_manager.h"
|
||||
#include "zvision/scripting/script_manager.h"
|
||||
#include "zvision/scripting/controls/hotmov_control.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
HotMovControl::HotMovControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream)
|
||||
: Control(engine, key, CONTROL_HOTMOV) {
|
||||
_animation = NULL;
|
||||
_cycle = 0;
|
||||
_frames.clear();
|
||||
_cyclesCount = 0;
|
||||
_framesCount = 0;
|
||||
|
||||
_engine->getScriptManager()->setStateValue(_key, 0);
|
||||
|
||||
// Loop until we find the closing brace
|
||||
Common::String line = stream.readLine();
|
||||
_engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
|
||||
Common::String param;
|
||||
Common::String values;
|
||||
getParams(line, param, values);
|
||||
|
||||
while (!stream.eos() && !line.contains('}')) {
|
||||
if (param.matchString("hs_frame_list", true)) {
|
||||
readHsFile(Common::Path(values));
|
||||
} else if (param.matchString("rectangle", true)) {
|
||||
int x;
|
||||
int y;
|
||||
int width;
|
||||
int height;
|
||||
|
||||
if (sscanf(values.c_str(), "%d %d %d %d", &x, &y, &width, &height) == 4)
|
||||
_rectangle = Common::Rect(x, y, width, height);
|
||||
} else if (param.matchString("num_frames", true)) {
|
||||
_framesCount = atoi(values.c_str());
|
||||
} else if (param.matchString("num_cycles", true)) {
|
||||
_cyclesCount = atoi(values.c_str());
|
||||
} else if (param.matchString("animation", true)) {
|
||||
char filename[64];
|
||||
if (sscanf(values.c_str(), "%s", filename) == 1) {
|
||||
values = Common::String(filename);
|
||||
_animation = _engine->loadAnimation(Common::Path(values));
|
||||
_animation->start();
|
||||
}
|
||||
} else if (param.matchString("venus_id", true)) {
|
||||
_venusId = atoi(values.c_str());
|
||||
}
|
||||
|
||||
line = stream.readLine();
|
||||
_engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
|
||||
getParams(line, param, values);
|
||||
}
|
||||
}
|
||||
|
||||
HotMovControl::~HotMovControl() {
|
||||
if (_animation)
|
||||
delete _animation;
|
||||
|
||||
_frames.clear();
|
||||
}
|
||||
|
||||
bool HotMovControl::process(uint32 deltaTimeInMillis) {
|
||||
if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
|
||||
return false;
|
||||
|
||||
if (_cycle < _cyclesCount) {
|
||||
if (_animation && _animation->endOfVideo()) {
|
||||
_cycle++;
|
||||
|
||||
if (_cycle == _cyclesCount) {
|
||||
_engine->getScriptManager()->setStateValue(_key, 2);
|
||||
return false;
|
||||
}
|
||||
|
||||
_animation->rewind();
|
||||
}
|
||||
|
||||
if (_animation && _animation->needsUpdate()) {
|
||||
const Graphics::Surface *frameData = _animation->decodeNextFrame();
|
||||
if (frameData)
|
||||
_engine->getRenderManager()->blitSurfaceToBkgScaled(*frameData, _rectangle);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HotMovControl::onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
|
||||
if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
|
||||
return false;
|
||||
|
||||
if (!_animation)
|
||||
return false;
|
||||
|
||||
if (_cycle < _cyclesCount) {
|
||||
if (_frames[_animation->getCurFrame()].contains(backgroundImageSpacePos)) {
|
||||
_engine->getCursorManager()->changeCursor(CursorIndex_Active);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HotMovControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
|
||||
if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
|
||||
return false;
|
||||
|
||||
if (!_animation)
|
||||
return false;
|
||||
|
||||
if (_cycle < _cyclesCount) {
|
||||
if (_frames[_animation->getCurFrame()].contains(backgroundImageSpacePos)) {
|
||||
setVenus();
|
||||
_engine->getScriptManager()->setStateValue(_key, 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void HotMovControl::readHsFile(const Common::Path &fileName) {
|
||||
if (_framesCount == 0)
|
||||
return;
|
||||
|
||||
Common::File file;
|
||||
if (!file.open(fileName)) {
|
||||
warning("HS file %s could could be opened", fileName.toString().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
Common::String line;
|
||||
|
||||
_frames.resize(_framesCount);
|
||||
|
||||
while (!file.eos()) {
|
||||
line = file.readLine();
|
||||
|
||||
int frame;
|
||||
int x;
|
||||
int y;
|
||||
int width;
|
||||
int height;
|
||||
|
||||
if (sscanf(line.c_str(), "%d:%d %d %d %d~", &frame, &x, &y, &width, &height) == 5) {
|
||||
if (frame >= 0 && frame < _framesCount)
|
||||
_frames[frame] = Common::Rect(x, y, width, height);
|
||||
}
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
60
engines/zvision/scripting/controls/hotmov_control.h
Normal file
60
engines/zvision/scripting/controls/hotmov_control.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ZVISION_HOTMOV_CONTROL_H
|
||||
#define ZVISION_HOTMOV_CONTROL_H
|
||||
|
||||
#include "common/array.h"
|
||||
#include "common/path.h"
|
||||
#include "common/rect.h"
|
||||
#include "zvision/scripting/control.h"
|
||||
|
||||
namespace Video {
|
||||
class VideoDecoder;
|
||||
}
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
// Only used in Zork Nemesis, handles movies where the player needs to click on something (mj7g, vw3g)
|
||||
class HotMovControl : public Control {
|
||||
public:
|
||||
HotMovControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream);
|
||||
~HotMovControl() override;
|
||||
|
||||
private:
|
||||
int32 _framesCount;
|
||||
int32 _cycle;
|
||||
int32 _cyclesCount;
|
||||
Video::VideoDecoder *_animation;
|
||||
Common::Rect _rectangle;
|
||||
Common::Array<Common::Rect> _frames;
|
||||
public:
|
||||
bool onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) override;
|
||||
bool onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) override;
|
||||
bool process(uint32 deltaTimeInMillis) override;
|
||||
|
||||
private:
|
||||
void readHsFile(const Common::Path &fileName);
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
271
engines/zvision/scripting/controls/input_control.cpp
Normal file
271
engines/zvision/scripting/controls/input_control.cpp
Normal file
@@ -0,0 +1,271 @@
|
||||
/* 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 "backends/keymapper/keymap.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/str.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/system.h"
|
||||
#include "video/video_decoder.h"
|
||||
#include "zvision/zvision.h"
|
||||
#include "zvision/graphics/render_manager.h"
|
||||
#include "zvision/graphics/cursors/cursor_manager.h"
|
||||
#include "zvision/scripting/script_manager.h"
|
||||
#include "zvision/scripting/controls/input_control.h"
|
||||
#include "zvision/text/string_manager.h"
|
||||
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
InputControl::InputControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream)
|
||||
: Control(engine, key, CONTROL_INPUT),
|
||||
_background(0),
|
||||
_nextTabstop(0),
|
||||
_focused(false),
|
||||
_textChanged(false),
|
||||
_enterPressed(false),
|
||||
_readOnly(false),
|
||||
_txtWidth(0),
|
||||
_animation(NULL) {
|
||||
// Loop until we find the closing brace
|
||||
Common::String line = stream.readLine();
|
||||
_engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
|
||||
Common::String param;
|
||||
Common::String values;
|
||||
getParams(line, param, values);
|
||||
|
||||
while (!stream.eos() && !line.contains('}')) {
|
||||
if (param.matchString("rectangle", true)) {
|
||||
int x1, y1, x2, y2;
|
||||
if (sscanf(values.c_str(), "%d %d %d %d", &x1, &y1, &x2, &y2) == 4)
|
||||
_textRectangle = Common::Rect(x1, y1, x2, y2);
|
||||
} else if (param.matchString("aux_hotspot", true)) {
|
||||
int x1, y1, x2, y2;
|
||||
if (sscanf(values.c_str(), "%d %d %d %d", &x1, &y1, &x2, &y2) == 4)
|
||||
_headerRectangle = Common::Rect(x1, y1, x2, y2);
|
||||
} else if (param.matchString("string_init", true)) {
|
||||
uint fontFormatNumber;
|
||||
if (sscanf(values.c_str(), "%u", &fontFormatNumber) == 1)
|
||||
_stringInit.readAllStyles(_engine->getStringManager()->getTextLine(fontFormatNumber));
|
||||
} else if (param.matchString("chooser_init_string", true)) {
|
||||
uint fontFormatNumber;
|
||||
if (sscanf(values.c_str(), "%u", &fontFormatNumber) == 1)
|
||||
_stringChooserInit.readAllStyles(_engine->getStringManager()->getTextLine(fontFormatNumber));
|
||||
} else if (param.matchString("next_tabstop", true)) {
|
||||
sscanf(values.c_str(), "%u", &_nextTabstop);
|
||||
} else if (param.matchString("cursor_dimensions", true)) {
|
||||
// Ignore, use the dimensions in the animation file
|
||||
} else if (param.matchString("cursor_animation_frames", true)) {
|
||||
// Ignore, use the frame count in the animation file
|
||||
} else if (param.matchString("cursor_animation", true)) {
|
||||
char fileName[25];
|
||||
if (sscanf(values.c_str(), "%24s %*u", fileName) == 1) {
|
||||
_animation = _engine->loadAnimation(fileName);
|
||||
_animation->start();
|
||||
}
|
||||
} else if (param.matchString("focus", true)) {
|
||||
_focused = true;
|
||||
_engine->getScriptManager()->setFocusControlKey(_key);
|
||||
} else if (param.matchString("venus_id", true)) {
|
||||
_venusId = atoi(values.c_str());
|
||||
}
|
||||
|
||||
line = stream.readLine();
|
||||
_engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
|
||||
getParams(line, param, values);
|
||||
}
|
||||
|
||||
_maxTxtWidth = _textRectangle.width();
|
||||
if (_animation)
|
||||
_maxTxtWidth -= _animation->getWidth();
|
||||
}
|
||||
|
||||
InputControl::~InputControl() {
|
||||
_background->free();
|
||||
delete _background;
|
||||
unfocus();
|
||||
}
|
||||
|
||||
void InputControl::focus() {
|
||||
if (!_readOnly) {
|
||||
_engine->getGameKeymap()->setEnabled(false);
|
||||
}
|
||||
_focused = true;
|
||||
_textChanged = true;
|
||||
}
|
||||
|
||||
void InputControl::unfocus() {
|
||||
if (!_readOnly) {
|
||||
_engine->getGameKeymap()->setEnabled(true);
|
||||
}
|
||||
_focused = false;
|
||||
_textChanged = true;
|
||||
}
|
||||
|
||||
bool InputControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
|
||||
if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
|
||||
return false;
|
||||
|
||||
if (_textRectangle.contains(backgroundImageSpacePos)) {
|
||||
if (!_readOnly) {
|
||||
// Save
|
||||
_engine->getScriptManager()->focusControl(_key);
|
||||
setVenus();
|
||||
} else {
|
||||
// Restore
|
||||
if (_currentInputText.size()) {
|
||||
setVenus();
|
||||
_enterPressed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool InputControl::onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
|
||||
if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
|
||||
return false;
|
||||
|
||||
if (_textRectangle.contains(backgroundImageSpacePos)) {
|
||||
if (!_readOnly) {
|
||||
// Save
|
||||
_engine->getCursorManager()->changeCursor(CursorIndex_Active);
|
||||
return true;
|
||||
} else {
|
||||
// Restore
|
||||
if (_currentInputText.size()) {
|
||||
_engine->getCursorManager()->changeCursor(CursorIndex_Active);
|
||||
_engine->getScriptManager()->focusControl(_key);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool InputControl::onKeyDown(Common::KeyState keyState) {
|
||||
if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
|
||||
return false;
|
||||
|
||||
if (!_focused) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (keyState.keycode == Common::KEYCODE_BACKSPACE) {
|
||||
if (!_readOnly) {
|
||||
_currentInputText.deleteLastChar();
|
||||
_textChanged = true;
|
||||
}
|
||||
} else if (keyState.keycode == Common::KEYCODE_RETURN) {
|
||||
_enterPressed = true;
|
||||
} else if (keyState.keycode == Common::KEYCODE_TAB) {
|
||||
unfocus();
|
||||
// Focus the next input control
|
||||
_engine->getScriptManager()->focusControl(_nextTabstop);
|
||||
// Don't process this event for other controls
|
||||
return true;
|
||||
} else {
|
||||
if (!_readOnly) {
|
||||
// Otherwise, append the new character to the end of the current text
|
||||
uint16 asciiValue = keyState.ascii;
|
||||
// We only care about text values
|
||||
if (asciiValue >= 32 && asciiValue <= 126) {
|
||||
_currentInputText += (char)asciiValue;
|
||||
_textChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool InputControl::process(uint32 deltaTimeInMillis) {
|
||||
if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
|
||||
return false;
|
||||
|
||||
if (!_background) {
|
||||
_background = _engine->getRenderManager()->getBkgRect(_textRectangle);
|
||||
}
|
||||
|
||||
// First see if we need to render the text
|
||||
if (_textChanged) {
|
||||
// Blit the text using the RenderManager
|
||||
|
||||
Graphics::Surface txt;
|
||||
txt.copyFrom(*_background);
|
||||
|
||||
int32 oldTxtWidth = _txtWidth;
|
||||
|
||||
if (!_readOnly || !_focused)
|
||||
_txtWidth = _engine->getTextRenderer()->drawText(_currentInputText, _stringInit, txt);
|
||||
else
|
||||
_txtWidth = _engine->getTextRenderer()->drawText(_currentInputText, _stringChooserInit, txt);
|
||||
|
||||
if (_readOnly || _txtWidth <= _maxTxtWidth)
|
||||
_engine->getRenderManager()->blitSurfaceToBkg(txt, _textRectangle.left, _textRectangle.top);
|
||||
else {
|
||||
// Assume the last character caused the overflow.
|
||||
_currentInputText.deleteLastChar();
|
||||
_txtWidth = oldTxtWidth;
|
||||
}
|
||||
|
||||
txt.free();
|
||||
}
|
||||
|
||||
if (_animation && !_readOnly && _focused) {
|
||||
if (_animation->endOfVideo())
|
||||
_animation->rewind();
|
||||
|
||||
if (_animation->needsUpdate()) {
|
||||
const Graphics::Surface *srf = _animation->decodeNextFrame();
|
||||
int16 xx = _textRectangle.left + _txtWidth;
|
||||
if (xx >= _textRectangle.left + (_textRectangle.width() - (int16)_animation->getWidth()))
|
||||
xx = _textRectangle.left + _textRectangle.width() - (int16)_animation->getWidth();
|
||||
_engine->getRenderManager()->blitSurfaceToBkg(*srf, xx, _textRectangle.top);
|
||||
}
|
||||
}
|
||||
|
||||
_textChanged = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool InputControl::enterPress() {
|
||||
if (_enterPressed) {
|
||||
_enterPressed = false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void InputControl::setText(const Common::String &_str) {
|
||||
_currentInputText = _str;
|
||||
_textChanged = true;
|
||||
}
|
||||
|
||||
const Common::String InputControl::getText() {
|
||||
return _currentInputText;
|
||||
}
|
||||
|
||||
void InputControl::setReadOnly(bool readonly) {
|
||||
_readOnly = readonly;
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
74
engines/zvision/scripting/controls/input_control.h
Normal file
74
engines/zvision/scripting/controls/input_control.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ZVISION_INPUT_CONTROL_H
|
||||
#define ZVISION_INPUT_CONTROL_H
|
||||
|
||||
#include "common/rect.h"
|
||||
#include "zvision/scripting/control.h"
|
||||
#include "zvision/text/string_manager.h"
|
||||
#include "zvision/text/text.h"
|
||||
|
||||
namespace Video {
|
||||
class VideoDecoder;
|
||||
}
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
class InputControl : public Control {
|
||||
public:
|
||||
InputControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream);
|
||||
~InputControl() override;
|
||||
|
||||
private:
|
||||
Graphics::Surface *_background;
|
||||
Common::Rect _textRectangle;
|
||||
Common::Rect _headerRectangle;
|
||||
TextStyleState _stringInit;
|
||||
TextStyleState _stringChooserInit;
|
||||
uint32 _nextTabstop;
|
||||
bool _focused;
|
||||
|
||||
Common::String _currentInputText;
|
||||
bool _textChanged;
|
||||
bool _enterPressed;
|
||||
bool _readOnly;
|
||||
|
||||
int16 _txtWidth;
|
||||
int16 _maxTxtWidth;
|
||||
Video::VideoDecoder *_animation;
|
||||
|
||||
public:
|
||||
void focus() override;
|
||||
void unfocus() override;
|
||||
bool onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) override;
|
||||
bool onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) override;
|
||||
bool onKeyDown(Common::KeyState keyState) override;
|
||||
bool process(uint32 deltaTimeInMillis) override;
|
||||
void setText(const Common::String &_str);
|
||||
const Common::String getText();
|
||||
bool enterPress();
|
||||
void setReadOnly(bool);
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
352
engines/zvision/scripting/controls/lever_control.cpp
Normal file
352
engines/zvision/scripting/controls/lever_control.cpp
Normal file
@@ -0,0 +1,352 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/file.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/system.h"
|
||||
#include "common/tokenizer.h"
|
||||
#include "graphics/surface.h"
|
||||
#include "video/video_decoder.h"
|
||||
#include "zvision/zvision.h"
|
||||
#include "zvision/graphics/render_manager.h"
|
||||
#include "zvision/graphics/cursors/cursor_manager.h"
|
||||
#include "zvision/scripting/script_manager.h"
|
||||
#include "zvision/scripting/controls/lever_control.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
LeverControl::LeverControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream)
|
||||
: Control(engine, key, CONTROL_LEVER),
|
||||
_frameInfo(0),
|
||||
_frameCount(0),
|
||||
_startFrame(0),
|
||||
_currentFrame(0),
|
||||
_lastRenderedFrame(0),
|
||||
_mouseIsCaptured(false),
|
||||
_isReturning(false),
|
||||
_accumulatedTime(0),
|
||||
_returnRoutesCurrentFrame(0),
|
||||
_animation(NULL),
|
||||
_cursor(CursorIndex_Active),
|
||||
_mirrored(false) {
|
||||
|
||||
// Loop until we find the closing brace
|
||||
Common::String line = stream.readLine();
|
||||
_engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
|
||||
|
||||
Common::String param;
|
||||
Common::String values;
|
||||
getParams(line, param, values);
|
||||
|
||||
while (!stream.eos() && !line.contains('}')) {
|
||||
if (param.matchString("descfile", true)) {
|
||||
char levFileName[25];
|
||||
if (sscanf(values.c_str(), "%24s", levFileName) == 1)
|
||||
parseLevFile(levFileName);
|
||||
} else if (param.matchString("cursor", true)) {
|
||||
char cursorName[25];
|
||||
if (sscanf(values.c_str(), "%24s", cursorName) == 1)
|
||||
_cursor = _engine->getCursorManager()->getCursorId(Common::String(cursorName));
|
||||
}
|
||||
|
||||
line = stream.readLine();
|
||||
_engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
|
||||
getParams(line, param, values);
|
||||
}
|
||||
|
||||
renderFrame(_currentFrame);
|
||||
}
|
||||
|
||||
LeverControl::~LeverControl() {
|
||||
if (_animation)
|
||||
delete _animation;
|
||||
|
||||
delete[] _frameInfo;
|
||||
}
|
||||
|
||||
void LeverControl::parseLevFile(const Common::Path &fileName) {
|
||||
debugC(2, kDebugControl, "LeverControl::parseLevFile(%s)", fileName.toString().c_str());
|
||||
Common::File file;
|
||||
if (!file.open(fileName)) {
|
||||
warning("LEV file %s could could be opened", fileName.toString().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
Common::String line;
|
||||
Common::String param;
|
||||
Common::String values;
|
||||
|
||||
int id = 0;
|
||||
|
||||
while (!file.eos()) {
|
||||
line = file.readLine();
|
||||
getLevParams(line, param, values);
|
||||
|
||||
if (param.matchString("animation_id", true)) {
|
||||
sscanf(values.c_str(), "%d", &id);
|
||||
debugC(2, kDebugControl, "Lever animation ID: %d", id);
|
||||
} else if (param.matchString("filename", true)) {
|
||||
_animation = _engine->loadAnimation(Common::Path(values));
|
||||
} else if (param.matchString("skipcolor", true)) {
|
||||
// Not used
|
||||
} else if (param.matchString("anim_coords", true)) {
|
||||
int left, top, right, bottom;
|
||||
if (sscanf(values.c_str(), "%d %d %d %d", &left, &top, &right, &bottom) == 4) {
|
||||
_animationCoords.left = left;
|
||||
_animationCoords.top = top;
|
||||
_animationCoords.right = right;
|
||||
_animationCoords.bottom = bottom;
|
||||
}
|
||||
} else if (param.matchString("mirrored", true)) {
|
||||
uint mirrored;
|
||||
if (sscanf(values.c_str(), "%u", &mirrored) == 1)
|
||||
_mirrored = mirrored == 0 ? false : true;
|
||||
} else if (param.matchString("frames", true)) {
|
||||
if (sscanf(values.c_str(), "%u", &_frameCount) == 1)
|
||||
_frameInfo = new FrameInfo[_frameCount];
|
||||
} else if (param.matchString("elsewhere", true)) {
|
||||
// Not used
|
||||
} else if (param.matchString("out_of_control", true)) {
|
||||
// Not used
|
||||
} else if (param.matchString("start_pos", true)) {
|
||||
if (sscanf(values.c_str(), "%u", &_startFrame) == 1)
|
||||
_currentFrame = _startFrame;
|
||||
} else if (param.matchString("hotspot_deltas", true)) {
|
||||
uint x;
|
||||
uint y;
|
||||
if (sscanf(values.c_str(), "%u %u", &x, &y) == 2) {
|
||||
_hotspotDelta.x = x;
|
||||
_hotspotDelta.y = y;
|
||||
}
|
||||
} else if (param.matchString("venus_id", true)) {
|
||||
_venusId = atoi(values.c_str());
|
||||
} else {
|
||||
uint frameNumber;
|
||||
uint x, y;
|
||||
|
||||
line.toLowercase();
|
||||
|
||||
if (sscanf(line.c_str(), "%u:%u %u", &frameNumber, &x, &y) == 3) {
|
||||
_frameInfo[frameNumber].hotspot.left = x;
|
||||
_frameInfo[frameNumber].hotspot.top = y;
|
||||
_frameInfo[frameNumber].hotspot.right = x + _hotspotDelta.x;
|
||||
_frameInfo[frameNumber].hotspot.bottom = y + _hotspotDelta.y;
|
||||
}
|
||||
|
||||
Common::StringTokenizer tokenizer(line, " ^=()~");
|
||||
tokenizer.nextToken();
|
||||
tokenizer.nextToken();
|
||||
|
||||
Common::String token = tokenizer.nextToken();
|
||||
while (!tokenizer.empty()) {
|
||||
if (token == "d") {
|
||||
token = tokenizer.nextToken();
|
||||
|
||||
uint angle;
|
||||
uint toFrame;
|
||||
if (sscanf(token.c_str(), "%u,%u", &toFrame, &angle) == 2)
|
||||
_frameInfo[frameNumber].paths.push_back(PathSegment(angle, toFrame));
|
||||
} else if (token.hasPrefix("p")) {
|
||||
// Format: P(<from> to <to>)
|
||||
tokenizer.nextToken();
|
||||
tokenizer.nextToken();
|
||||
token = tokenizer.nextToken();
|
||||
uint to = atoi(token.c_str());
|
||||
|
||||
_frameInfo[frameNumber].returnRoute.push_back(to);
|
||||
}
|
||||
|
||||
token = tokenizer.nextToken();
|
||||
}
|
||||
}
|
||||
|
||||
// Don't read lines in this place because last will not be parsed.
|
||||
}
|
||||
// WORKAROUND for a script bug in Zork: Nemesis, room tz2e (orrery)
|
||||
// Animation coordinates for left hand lever do not properly align with background image
|
||||
switch (id) {
|
||||
case 2926:
|
||||
_animationCoords.bottom -= 4;
|
||||
_animationCoords.right += 1;
|
||||
_animationCoords.left -= 1;
|
||||
_animationCoords.top += 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Cycle through all unit direction vectors in path segments & determine step distance
|
||||
debugC(3, kDebugControl, "Setting step distances");
|
||||
for (uint frame=0; frame < _frameCount; frame++) {
|
||||
debugC(3, kDebugControl, "Frame %d", frame);
|
||||
for (auto &iter : _frameInfo[frame].paths) {
|
||||
uint destFrame = iter.toFrame;
|
||||
Common::Point deltaPos = _frameInfo[destFrame].hotspot.origin() - _frameInfo[frame].hotspot.origin();
|
||||
Math::Vector2d deltaPosVector((float)deltaPos.x, (float)deltaPos.y);
|
||||
iter.distance *= deltaPosVector.getMagnitude();
|
||||
debugC(3, kDebugControl, "\tdeltaPos = %d,%d, Distance %f", deltaPos.x, deltaPos.y, iter.distance);
|
||||
}
|
||||
}
|
||||
debugC(2, kDebugControl, "LeverControl::~parseLevFile()");
|
||||
}
|
||||
|
||||
bool LeverControl::onMouseDown(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
|
||||
if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
|
||||
return false;
|
||||
|
||||
if (_frameInfo[_currentFrame].hotspot.contains(backgroundImageSpacePos)) {
|
||||
setVenus();
|
||||
_mouseIsCaptured = true;
|
||||
_gripOffset = backgroundImageSpacePos - _frameInfo[_currentFrame].hotspot.origin();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LeverControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
|
||||
if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
|
||||
return false;
|
||||
|
||||
if (_mouseIsCaptured) {
|
||||
_mouseIsCaptured = false;
|
||||
_engine->getScriptManager()->setStateValue(_key, _currentFrame);
|
||||
|
||||
_isReturning = true;
|
||||
_returnRoutesCurrentProgress = _frameInfo[_currentFrame].returnRoute.begin();
|
||||
_returnRoutesCurrentFrame = _currentFrame;
|
||||
}
|
||||
_gripOffset = Common::Point(0,0);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LeverControl::onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
|
||||
if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
|
||||
return false;
|
||||
|
||||
bool cursorWasChanged = false;
|
||||
|
||||
if (_mouseIsCaptured) {
|
||||
uint nextFrame = _currentFrame;
|
||||
do {
|
||||
Common::Point gripOrigin = _frameInfo[_currentFrame].hotspot.origin() + _gripOffset;
|
||||
debugC(1, kDebugControl, "LeverControl::onMouseMove() screenPos = %d,%d, imagePos = %d,%d, gripOrigin = %d,%d", screenSpacePos.x, screenSpacePos.y, backgroundImageSpacePos.x, backgroundImageSpacePos.y, gripOrigin.x, gripOrigin.y);
|
||||
Common::Point deltaPos = backgroundImageSpacePos - gripOrigin;
|
||||
nextFrame = getNextFrame(deltaPos);
|
||||
if (nextFrame != _currentFrame) {
|
||||
_currentFrame = nextFrame;
|
||||
_engine->getScriptManager()->setStateValue(_key, _currentFrame);
|
||||
}
|
||||
} while (nextFrame != _currentFrame);
|
||||
|
||||
if (_lastRenderedFrame != _currentFrame)
|
||||
renderFrame(_currentFrame);
|
||||
|
||||
_engine->getCursorManager()->changeCursor(_cursor);
|
||||
cursorWasChanged = true;
|
||||
} else if (_frameInfo[_currentFrame].hotspot.contains(backgroundImageSpacePos)) {
|
||||
_engine->getCursorManager()->changeCursor(_cursor);
|
||||
cursorWasChanged = true;
|
||||
}
|
||||
return cursorWasChanged;
|
||||
}
|
||||
|
||||
uint LeverControl::getNextFrame(Common::Point &deltaPos) {
|
||||
Math::Vector2d movement((float)deltaPos.x, (float)deltaPos.y);
|
||||
for (auto &iter : _frameInfo[_currentFrame].paths) {
|
||||
debugC(1, kDebugControl, "\tPossible step = %f,%f, angle = %d, distance %f", iter.direction.getX(), iter.direction.getY(), (uint)Math::rad2deg(iter.angle), iter.distance);
|
||||
if (movement.dotProduct(iter.direction) >= iter.distance/2) {
|
||||
return iter.toFrame;
|
||||
}
|
||||
}
|
||||
return _currentFrame;
|
||||
}
|
||||
|
||||
bool LeverControl::process(uint32 deltaTimeInMillis) {
|
||||
if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
|
||||
return false;
|
||||
|
||||
if (_isReturning) {
|
||||
_accumulatedTime += deltaTimeInMillis;
|
||||
while (_accumulatedTime >= _returnFramePeriod) {
|
||||
_accumulatedTime -= _returnFramePeriod;
|
||||
if (_returnRoutesCurrentFrame == *_returnRoutesCurrentProgress) {
|
||||
_returnRoutesCurrentProgress++;
|
||||
}
|
||||
if (_returnRoutesCurrentProgress == _frameInfo[_currentFrame].returnRoute.end()) {
|
||||
_isReturning = false;
|
||||
_currentFrame = _returnRoutesCurrentFrame;
|
||||
return false;
|
||||
}
|
||||
|
||||
uint toFrame = *_returnRoutesCurrentProgress;
|
||||
if (_returnRoutesCurrentFrame < toFrame) {
|
||||
_returnRoutesCurrentFrame++;
|
||||
} else if (_returnRoutesCurrentFrame > toFrame) {
|
||||
_returnRoutesCurrentFrame--;
|
||||
}
|
||||
|
||||
_engine->getScriptManager()->setStateValue(_key, _returnRoutesCurrentFrame);
|
||||
renderFrame(_returnRoutesCurrentFrame);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void LeverControl::renderFrame(uint frameNumber) {
|
||||
_lastRenderedFrame = frameNumber;
|
||||
if (frameNumber != 0 && frameNumber < _lastRenderedFrame && _mirrored)
|
||||
frameNumber = (_frameCount * 2) - frameNumber - 1;
|
||||
|
||||
const Graphics::Surface *frameData;
|
||||
|
||||
_animation->seekToFrame(frameNumber);
|
||||
frameData = _animation->decodeNextFrame();
|
||||
if (frameData)
|
||||
_engine->getRenderManager()->blitSurfaceToBkgScaled(*frameData, _animationCoords);
|
||||
}
|
||||
|
||||
void LeverControl::getLevParams(const Common::String &inputStr, Common::String ¶meter, Common::String &values) {
|
||||
const char *chrs = inputStr.c_str();
|
||||
uint lbr;
|
||||
|
||||
for (lbr = 0; lbr < inputStr.size(); lbr++)
|
||||
if (chrs[lbr] == ':')
|
||||
break;
|
||||
|
||||
if (lbr >= inputStr.size())
|
||||
return;
|
||||
|
||||
uint rbr;
|
||||
|
||||
for (rbr = lbr + 1; rbr < inputStr.size(); rbr++)
|
||||
if (chrs[rbr] == '~')
|
||||
break;
|
||||
|
||||
if (rbr >= inputStr.size())
|
||||
return;
|
||||
|
||||
parameter = Common::String(chrs, chrs + lbr);
|
||||
values = Common::String(chrs + lbr + 1, chrs + rbr);
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
96
engines/zvision/scripting/controls/lever_control.h
Normal file
96
engines/zvision/scripting/controls/lever_control.h
Normal file
@@ -0,0 +1,96 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ZVISION_LEVER_CONTROL_H
|
||||
#define ZVISION_LEVER_CONTROL_H
|
||||
|
||||
#include "common/list.h"
|
||||
#include "common/path.h"
|
||||
#include "common/rect.h"
|
||||
#include "math/vector2d.h"
|
||||
#include "zvision/scripting/control.h"
|
||||
|
||||
namespace Video {
|
||||
class VideoDecoder;
|
||||
}
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
// Only used in Zork Nemesis, handles draggable levers (te2e, tm7e, tp2e, tt2e, tz2e)
|
||||
class LeverControl : public Control {
|
||||
public:
|
||||
LeverControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream);
|
||||
~LeverControl() override;
|
||||
|
||||
private:
|
||||
|
||||
struct PathSegment {
|
||||
PathSegment(uint a, uint t) : angle(Math::deg2rad<float>(a)), toFrame(t), direction(cos(angle), -sin(angle)) {}
|
||||
|
||||
float angle; // Radians
|
||||
uint toFrame;
|
||||
Math::Vector2d direction; // NB unit vector upon initialisation
|
||||
float distance = 1.0f;
|
||||
};
|
||||
|
||||
struct FrameInfo {
|
||||
Common::Rect hotspot;
|
||||
Common::List<PathSegment> paths;
|
||||
Common::List<uint> returnRoute;
|
||||
};
|
||||
|
||||
private:
|
||||
Video::VideoDecoder *_animation;
|
||||
|
||||
int _cursor;
|
||||
Common::Rect _animationCoords;
|
||||
bool _mirrored;
|
||||
uint _frameCount;
|
||||
uint _startFrame;
|
||||
Common::Point _hotspotDelta;
|
||||
FrameInfo *_frameInfo;
|
||||
|
||||
uint _currentFrame;
|
||||
uint _lastRenderedFrame;
|
||||
bool _mouseIsCaptured;
|
||||
bool _isReturning;
|
||||
Common::Point _gripOffset;
|
||||
Common::List<uint>::iterator _returnRoutesCurrentProgress;
|
||||
uint _returnRoutesCurrentFrame;
|
||||
const uint8 _returnFramePeriod = 60; // milliseconds
|
||||
uint32 _accumulatedTime;
|
||||
|
||||
public:
|
||||
bool onMouseDown(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) override;
|
||||
bool onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) override;
|
||||
bool onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) override;
|
||||
bool process(uint32 deltaTimeInMillis) override;
|
||||
|
||||
private:
|
||||
void parseLevFile(const Common::Path &fileName);
|
||||
void renderFrame(uint frameNumber);
|
||||
void getLevParams(const Common::String &inputStr, Common::String ¶meter, Common::String &values);
|
||||
uint getNextFrame(Common::Point &deltaPos);
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
216
engines/zvision/scripting/controls/paint_control.cpp
Normal file
216
engines/zvision/scripting/controls/paint_control.cpp
Normal file
@@ -0,0 +1,216 @@
|
||||
/* 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/scummsys.h"
|
||||
#include "common/stream.h"
|
||||
#include "zvision/zvision.h"
|
||||
#include "zvision/graphics/cursors/cursor_manager.h"
|
||||
#include "zvision/graphics/render_manager.h"
|
||||
#include "zvision/scripting/script_manager.h"
|
||||
#include "zvision/scripting/controls/paint_control.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
PaintControl::PaintControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream)
|
||||
: Control(engine, key, CONTROL_PAINT) {
|
||||
|
||||
_cursor = CursorIndex_Active;
|
||||
_paint = NULL;
|
||||
_bkg = NULL;
|
||||
_brush = NULL;
|
||||
_colorKey = 0;
|
||||
_mouseDown = false;
|
||||
|
||||
// Loop until we find the closing brace
|
||||
Common::String line = stream.readLine();
|
||||
_engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
|
||||
Common::String param;
|
||||
Common::String values;
|
||||
getParams(line, param, values);
|
||||
|
||||
while (!stream.eos() && !line.contains('}')) {
|
||||
if (param.matchString("rectangle", true)) {
|
||||
int x;
|
||||
int y;
|
||||
int width;
|
||||
int height;
|
||||
|
||||
if (sscanf(values.c_str(), "%d %d %d %d", &x, &y, &width, &height) == 4)
|
||||
_rectangle = Common::Rect(x, y, width + x, height + y);
|
||||
} else if (param.matchString("cursor", true)) {
|
||||
_cursor = _engine->getCursorManager()->getCursorId(values);
|
||||
} else if (param.matchString("brush_file", true)) {
|
||||
_brush = _engine->getRenderManager()->loadImage(Common::Path(values), false);
|
||||
} else if (param.matchString("venus_id", true)) {
|
||||
_venusId = atoi(values.c_str());
|
||||
} else if (param.matchString("paint_file", true)) {
|
||||
_paint = _engine->getRenderManager()->loadImage(Common::Path(values), false);
|
||||
} else if (param.matchString("eligible_objects", true)) {
|
||||
char buf[256];
|
||||
memset(buf, 0, 256);
|
||||
strncpy(buf, values.c_str(), 255);
|
||||
|
||||
char *curpos = buf;
|
||||
char *strend = buf + strlen(buf);
|
||||
while (true) {
|
||||
char *st = curpos;
|
||||
|
||||
if (st >= strend)
|
||||
break;
|
||||
|
||||
while (*curpos != ' ' && curpos < strend)
|
||||
curpos++;
|
||||
|
||||
*curpos = 0;
|
||||
curpos++;
|
||||
|
||||
int obj = atoi(st);
|
||||
|
||||
_eligibleObjects.push_back(obj);
|
||||
}
|
||||
}
|
||||
|
||||
line = stream.readLine();
|
||||
_engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
|
||||
getParams(line, param, values);
|
||||
}
|
||||
|
||||
if (_paint) {
|
||||
_colorKey = _paint->format.RGBToColor(255, 0, 255);
|
||||
_bkg = new Graphics::Surface;
|
||||
_bkg->create(_rectangle.width(), _rectangle.height(), _paint->format);
|
||||
_bkg->fillRect(Common::Rect(_rectangle.width(), _rectangle.height()), _colorKey);
|
||||
|
||||
Graphics::Surface *tmp = new Graphics::Surface;
|
||||
tmp->create(_rectangle.width(), _rectangle.height(), _paint->format);
|
||||
_engine->getRenderManager()->blitSurfaceToSurface(*_paint, _rectangle, *tmp, 0, 0);
|
||||
_paint->free();
|
||||
delete _paint;
|
||||
_paint = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
PaintControl::~PaintControl() {
|
||||
// Clear the state value back to 0
|
||||
//_engine->getScriptManager()->setStateValue(_key, 0);
|
||||
if (_paint) {
|
||||
_paint->free();
|
||||
delete _paint;
|
||||
}
|
||||
if (_brush) {
|
||||
_brush->free();
|
||||
delete _brush;
|
||||
}
|
||||
if (_bkg) {
|
||||
_bkg->free();
|
||||
delete _bkg;
|
||||
}
|
||||
}
|
||||
|
||||
bool PaintControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
|
||||
if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
|
||||
return false;
|
||||
|
||||
_mouseDown = false;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PaintControl::onMouseDown(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
|
||||
if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
|
||||
return false;
|
||||
|
||||
if (_rectangle.contains(backgroundImageSpacePos)) {
|
||||
int mouseItem = _engine->getScriptManager()->getStateValue(StateKey_InventoryItem);
|
||||
|
||||
if (eligeblity(mouseItem)) {
|
||||
setVenus();
|
||||
_mouseDown = true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PaintControl::onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
|
||||
if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
|
||||
return false;
|
||||
|
||||
if (_rectangle.contains(backgroundImageSpacePos)) {
|
||||
int mouseItem = _engine->getScriptManager()->getStateValue(StateKey_InventoryItem);
|
||||
|
||||
if (eligeblity(mouseItem)) {
|
||||
_engine->getCursorManager()->changeCursor(_cursor);
|
||||
|
||||
if (_mouseDown) {
|
||||
Common::Rect bkgRect = paint(backgroundImageSpacePos);
|
||||
if (!bkgRect.isEmpty()) {
|
||||
Common::Rect imgRect = bkgRect;
|
||||
imgRect.translate(-_rectangle.left, -_rectangle.top);
|
||||
|
||||
Graphics::Surface imgUpdate = _bkg->getSubArea(imgRect);
|
||||
|
||||
_engine->getRenderManager()->blitSurfaceToBkg(imgUpdate, bkgRect.left, bkgRect.top, _colorKey);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PaintControl::eligeblity(int itemId) {
|
||||
for (Common::List<int>::iterator it = _eligibleObjects.begin(); it != _eligibleObjects.end(); it++)
|
||||
if (*it == itemId)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
Common::Rect PaintControl::paint(const Common::Point &point) {
|
||||
Common::Rect paintRect = Common::Rect(_brush->w, _brush->h);
|
||||
paintRect.moveTo(point);
|
||||
paintRect.clip(_rectangle);
|
||||
|
||||
if (!paintRect.isEmpty()) {
|
||||
Common::Rect brushRect = paintRect;
|
||||
brushRect.translate(-point.x, -point.y);
|
||||
|
||||
Common::Rect bkgRect = paintRect;
|
||||
bkgRect.translate(-_rectangle.left, -_rectangle.top);
|
||||
|
||||
for (int yy = 0; yy < brushRect.height(); yy++) {
|
||||
uint16 *mask = (uint16 *)_brush->getBasePtr(brushRect.left, brushRect.top + yy);
|
||||
uint16 *from = (uint16 *)_paint->getBasePtr(bkgRect.left, bkgRect.top + yy);
|
||||
uint16 *to = (uint16 *)_bkg->getBasePtr(bkgRect.left, bkgRect.top + yy);
|
||||
for (int xx = 0; xx < brushRect.width(); xx++) {
|
||||
if (*mask != 0)
|
||||
*(to + xx) = *(from + xx);
|
||||
|
||||
mask++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return paintRect;
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
89
engines/zvision/scripting/controls/paint_control.h
Normal file
89
engines/zvision/scripting/controls/paint_control.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 ZVISION_PAINT_CONTROL_H
|
||||
#define ZVISION_PAINT_CONTROL_H
|
||||
|
||||
#include "common/list.h"
|
||||
#include "common/rect.h"
|
||||
#include "graphics/surface.h"
|
||||
#include "zvision/scripting/control.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
// Only used in Zork Nemesis, handles the painting puzzle screen in Lucien's room in Irondune (ch4g)
|
||||
class PaintControl : public Control {
|
||||
public:
|
||||
PaintControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream);
|
||||
~PaintControl() override;
|
||||
|
||||
/**
|
||||
* @param screenSpacePos The position of the mouse in screen space
|
||||
* @param backgroundImageSpacePos The position of the mouse in background image space
|
||||
*/
|
||||
bool onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) override;
|
||||
|
||||
/**
|
||||
* @param screenSpacePos The position of the mouse in screen space
|
||||
* @param backgroundImageSpacePos The position of the mouse in background image space
|
||||
*/
|
||||
bool onMouseDown(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) override;
|
||||
/**
|
||||
* Called on every MouseMove. Tests if the mouse is inside _hotspot, and if so, sets the cursor.
|
||||
*
|
||||
* @param engine The base engine
|
||||
* @param screenSpacePos The position of the mouse in screen space
|
||||
* @param backgroundImageSpacePos The position of the mouse in background image space
|
||||
* @return Was the cursor changed?
|
||||
*/
|
||||
bool onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) override;
|
||||
|
||||
bool process(uint32 deltaTimeInMillis) override {
|
||||
return false;
|
||||
};
|
||||
|
||||
private:
|
||||
/**
|
||||
* The area that will trigger the event
|
||||
* This is in image space coordinates, NOT screen space
|
||||
*/
|
||||
|
||||
uint32 _colorKey;
|
||||
|
||||
Graphics::Surface *_paint;
|
||||
Graphics::Surface *_bkg;
|
||||
Graphics::Surface *_brush;
|
||||
|
||||
Common::List<int> _eligibleObjects;
|
||||
|
||||
int _cursor;
|
||||
Common::Rect _rectangle;
|
||||
|
||||
bool _mouseDown;
|
||||
|
||||
bool eligeblity(int itemId);
|
||||
Common::Rect paint(const Common::Point &point);
|
||||
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
142
engines/zvision/scripting/controls/push_toggle_control.cpp
Normal file
142
engines/zvision/scripting/controls/push_toggle_control.cpp
Normal file
@@ -0,0 +1,142 @@
|
||||
/* 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/scummsys.h"
|
||||
#include "common/stream.h"
|
||||
#include "zvision/zvision.h"
|
||||
#include "zvision/graphics/cursors/cursor_manager.h"
|
||||
#include "zvision/scripting/script_manager.h"
|
||||
#include "zvision/scripting/controls/push_toggle_control.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
PushToggleControl::PushToggleControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream)
|
||||
: Control(engine, key, CONTROL_PUSHTGL),
|
||||
_countTo(2),
|
||||
_cursor(CursorIndex_Active),
|
||||
_event(Common::EVENT_LBUTTONUP) {
|
||||
|
||||
_hotspots.clear();
|
||||
|
||||
// Loop until we find the closing brace
|
||||
Common::String line = stream.readLine();
|
||||
_engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
|
||||
Common::String param;
|
||||
Common::String values;
|
||||
getParams(line, param, values);
|
||||
|
||||
while (!stream.eos() && !line.contains('}')) {
|
||||
if (param.matchString("*_hotspot", true)) {
|
||||
uint x;
|
||||
uint y;
|
||||
uint width;
|
||||
uint height;
|
||||
|
||||
if (sscanf(values.c_str(), "%u,%u,%u,%u", &x, &y, &width, &height) == 4)
|
||||
_hotspots.push_back(Common::Rect(x, y, x + width + 1, y + height + 1));
|
||||
} else if (param.matchString("cursor", true)) {
|
||||
_cursor = _engine->getCursorManager()->getCursorId(values);
|
||||
} else if (param.matchString("animation", true)) {
|
||||
// Not used
|
||||
} else if (param.matchString("sound", true)) {
|
||||
// Not used
|
||||
} else if (param.matchString("count_to", true)) {
|
||||
sscanf(values.c_str(), "%u", &_countTo);
|
||||
} else if (param.matchString("mouse_event", true)) {
|
||||
if (values.equalsIgnoreCase("up")) {
|
||||
_event = Common::EVENT_LBUTTONUP;
|
||||
} else if (values.equalsIgnoreCase("down")) {
|
||||
_event = Common::EVENT_LBUTTONDOWN;
|
||||
} else if (values.equalsIgnoreCase("double")) {
|
||||
// Not used
|
||||
}
|
||||
} else if (param.matchString("venus_id", true)) {
|
||||
_venusId = atoi(values.c_str());
|
||||
}
|
||||
|
||||
line = stream.readLine();
|
||||
_engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
|
||||
getParams(line, param, values);
|
||||
}
|
||||
|
||||
if (_hotspots.size() == 0) {
|
||||
warning("Push_toggle %u was parsed incorrectly", key);
|
||||
}
|
||||
}
|
||||
|
||||
PushToggleControl::~PushToggleControl() {
|
||||
_hotspots.clear();
|
||||
}
|
||||
|
||||
bool PushToggleControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
|
||||
if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
|
||||
return false;
|
||||
|
||||
if (_event != Common::EVENT_LBUTTONUP)
|
||||
return false;
|
||||
|
||||
if (contain(backgroundImageSpacePos)) {
|
||||
setVenus();
|
||||
int32 val = _engine->getScriptManager()->getStateValue(_key);
|
||||
val = (val + 1) % _countTo;
|
||||
_engine->getScriptManager()->setStateValue(_key, val);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PushToggleControl::onMouseDown(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
|
||||
if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
|
||||
return false;
|
||||
|
||||
if (_event != Common::EVENT_LBUTTONDOWN)
|
||||
return false;
|
||||
|
||||
if (contain(backgroundImageSpacePos)) {
|
||||
setVenus();
|
||||
int32 val = _engine->getScriptManager()->getStateValue(_key);
|
||||
val = (val + 1) % _countTo;
|
||||
_engine->getScriptManager()->setStateValue(_key, val);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PushToggleControl::onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
|
||||
if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
|
||||
return false;
|
||||
|
||||
if (contain(backgroundImageSpacePos)) {
|
||||
_engine->getCursorManager()->changeCursor(_cursor);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PushToggleControl::contain(const Common::Point &point) {
|
||||
for (uint i = 0; i < _hotspots.size(); i++)
|
||||
if (_hotspots[i].contains(point))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
79
engines/zvision/scripting/controls/push_toggle_control.h
Normal file
79
engines/zvision/scripting/controls/push_toggle_control.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ZVISION_PUSH_TOGGLE_CONTROL_H
|
||||
#define ZVISION_PUSH_TOGGLE_CONTROL_H
|
||||
|
||||
#include "common/array.h"
|
||||
#include "common/events.h"
|
||||
#include "common/rect.h"
|
||||
#include "zvision/scripting/control.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
class PushToggleControl : public Control {
|
||||
public:
|
||||
PushToggleControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream);
|
||||
~PushToggleControl() override;
|
||||
|
||||
/**
|
||||
* Called when LeftMouse is pushed. Default is NOP.
|
||||
*
|
||||
* @param screenSpacePos The position of the mouse in screen space
|
||||
* @param backgroundImageSpacePos The position of the mouse in background image space
|
||||
*/
|
||||
bool onMouseDown(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) override;
|
||||
/**
|
||||
* Called when LeftMouse is lifted. Calls ScriptManager::setStateValue(_key, 1);
|
||||
*
|
||||
* @param screenSpacePos The position of the mouse in screen space
|
||||
* @param backgroundImageSpacePos The position of the mouse in background image space
|
||||
*/
|
||||
bool onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) override;
|
||||
/**
|
||||
* Called on every MouseMove. Tests if the mouse is inside _hotspot, and if so, sets the cursor.
|
||||
*
|
||||
* @param engine The base engine
|
||||
* @param screenSpacePos The position of the mouse in screen space
|
||||
* @param backgroundImageSpacePos The position of the mouse in background image space
|
||||
* @return Was the cursor changed?
|
||||
*/
|
||||
bool onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* The area that will trigger the event
|
||||
* This is in image space coordinates, NOT screen space
|
||||
*/
|
||||
Common::Array<Common::Rect> _hotspots;
|
||||
/** The cursor to use when hovering over _hotspot */
|
||||
int _cursor;
|
||||
/** Button maximal values count */
|
||||
uint _countTo;
|
||||
|
||||
Common::EventType _event;
|
||||
|
||||
bool contain(const Common::Point &point);
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
177
engines/zvision/scripting/controls/safe_control.cpp
Normal file
177
engines/zvision/scripting/controls/safe_control.cpp
Normal file
@@ -0,0 +1,177 @@
|
||||
/* 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/scummsys.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/system.h"
|
||||
#include "common/tokenizer.h"
|
||||
#include "graphics/surface.h"
|
||||
#include "video/video_decoder.h"
|
||||
#include "zvision/zvision.h"
|
||||
#include "zvision/graphics/render_manager.h"
|
||||
#include "zvision/graphics/cursors/cursor_manager.h"
|
||||
#include "zvision/scripting/script_manager.h"
|
||||
#include "zvision/scripting/controls/safe_control.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
SafeControl::SafeControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream)
|
||||
: Control(engine, key, CONTROL_SAFE) {
|
||||
_statesCount = 0;
|
||||
_curState = 0;
|
||||
_animation = NULL;
|
||||
_innerRaduis = 0;
|
||||
_innerRadiusSqr = 0;
|
||||
_outerRadius = 0;
|
||||
_outerRadiusSqr = 0;
|
||||
_zeroPointer = 0;
|
||||
_startPointer = 0;
|
||||
_targetFrame = 0;
|
||||
|
||||
// Loop until we find the closing brace
|
||||
Common::String line = stream.readLine();
|
||||
_engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
|
||||
Common::String param;
|
||||
Common::String values;
|
||||
getParams(line, param, values);
|
||||
|
||||
while (!stream.eos() && !line.contains('}')) {
|
||||
if (param.matchString("animation", true)) {
|
||||
_animation = _engine->loadAnimation(Common::Path(values));
|
||||
_animation->start();
|
||||
} else if (param.matchString("rectangle", true)) {
|
||||
int x;
|
||||
int y;
|
||||
int width;
|
||||
int height;
|
||||
|
||||
if (sscanf(values.c_str(), "%d %d %d %d", &x, &y, &width, &height) == 4)
|
||||
_rectangle = Common::Rect(x, y, width, height);
|
||||
} else if (param.matchString("num_states", true)) {
|
||||
_statesCount = atoi(values.c_str());
|
||||
} else if (param.matchString("center", true)) {
|
||||
int x;
|
||||
int y;
|
||||
|
||||
if (sscanf(values.c_str(), "%d %d", &x, &y) == 2)
|
||||
_center = Common::Point(x, y);
|
||||
} else if (param.matchString("dial_inner_radius", true)) {
|
||||
_innerRaduis = atoi(values.c_str());
|
||||
_innerRadiusSqr = _innerRaduis * _innerRaduis;
|
||||
} else if (param.matchString("radius", true)) {
|
||||
_outerRadius = atoi(values.c_str());
|
||||
_outerRadiusSqr = _outerRadius * _outerRadius;
|
||||
} else if (param.matchString("zero_radians_offset", true)) {
|
||||
_zeroPointer = atoi(values.c_str());
|
||||
} else if (param.matchString("pointer_offset", true)) {
|
||||
_startPointer = atoi(values.c_str());
|
||||
_curState = _startPointer;
|
||||
} else if (param.matchString("cursor", true)) {
|
||||
// Not used
|
||||
} else if (param.matchString("mirrored", true)) {
|
||||
// Not used
|
||||
} else if (param.matchString("venus_id", true)) {
|
||||
_venusId = atoi(values.c_str());
|
||||
}
|
||||
|
||||
line = stream.readLine();
|
||||
_engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
|
||||
getParams(line, param, values);
|
||||
}
|
||||
|
||||
if (_animation)
|
||||
_animation->seekToFrame(_curState);
|
||||
}
|
||||
|
||||
SafeControl::~SafeControl() {
|
||||
if (_animation)
|
||||
delete _animation;
|
||||
|
||||
}
|
||||
|
||||
bool SafeControl::process(uint32 deltaTimeInMillis) {
|
||||
if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
|
||||
return false;
|
||||
|
||||
if (_animation && _animation->getCurFrame() != _targetFrame && _animation->needsUpdate()) {
|
||||
// If we're past the target frame, move back one
|
||||
if (_animation->getCurFrame() > _targetFrame)
|
||||
_animation->seekToFrame(_animation->getCurFrame() - 1);
|
||||
|
||||
const Graphics::Surface *frameData = _animation->decodeNextFrame();
|
||||
if (_animation->getCurFrame() == _targetFrame)
|
||||
_engine->getScriptManager()->setStateValue(_key, _curState);
|
||||
if (frameData)
|
||||
_engine->getRenderManager()->blitSurfaceToBkg(*frameData, _rectangle.left, _rectangle.top);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SafeControl::onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
|
||||
if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
|
||||
return false;
|
||||
|
||||
if (_rectangle.contains(backgroundImageSpacePos)) {
|
||||
int32 mR = backgroundImageSpacePos.sqrDist(_center);
|
||||
if (mR <= _outerRadiusSqr && mR >= _innerRadiusSqr) {
|
||||
_engine->getCursorManager()->changeCursor(CursorIndex_Active);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SafeControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
|
||||
if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
|
||||
return false;
|
||||
|
||||
if (_rectangle.contains(backgroundImageSpacePos)) {
|
||||
int32 mR = backgroundImageSpacePos.sqrDist(_center);
|
||||
if (mR <= _outerRadiusSqr && mR >= _innerRadiusSqr) {
|
||||
setVenus();
|
||||
|
||||
Common::Point tmp = backgroundImageSpacePos - _center;
|
||||
|
||||
// Coverity complains about the order of arguments here,
|
||||
// but changing that breaks the Zork Nemesis safe puzzle.
|
||||
float dd = atan2((float)tmp.x, (float)tmp.y) * 57.29578;
|
||||
|
||||
int16 dp_state = 360 / _statesCount;
|
||||
|
||||
int16 m_state = (_statesCount - ((((int16)dd + 540) % 360) / dp_state)) % _statesCount;
|
||||
|
||||
int16 tmp2 = (m_state + _curState - _zeroPointer + _statesCount - 1) % _statesCount;
|
||||
|
||||
if (_animation)
|
||||
_animation->seekToFrame((_curState + _statesCount - _startPointer) % _statesCount);
|
||||
|
||||
_curState = (_statesCount * 2 + tmp2) % _statesCount;
|
||||
|
||||
_targetFrame = (_curState + _statesCount - _startPointer) % _statesCount;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
63
engines/zvision/scripting/controls/safe_control.h
Normal file
63
engines/zvision/scripting/controls/safe_control.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ZVISION_SAFE_CONTROL_H
|
||||
#define ZVISION_SAFE_CONTROL_H
|
||||
|
||||
#include "common/list.h"
|
||||
#include "common/rect.h"
|
||||
#include "zvision/scripting/control.h"
|
||||
|
||||
namespace Video {
|
||||
class VideoDecoder;
|
||||
}
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
// Only used in Zork Nemesis, handles the safe in the Asylum (ac4g)
|
||||
class SafeControl : public Control {
|
||||
public:
|
||||
SafeControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream);
|
||||
~SafeControl() override;
|
||||
|
||||
private:
|
||||
int16 _statesCount;
|
||||
int16 _curState;
|
||||
Video::VideoDecoder *_animation;
|
||||
Common::Point _center;
|
||||
Common::Rect _rectangle;
|
||||
int16 _innerRaduis;
|
||||
int32 _innerRadiusSqr;
|
||||
int16 _outerRadius;
|
||||
int32 _outerRadiusSqr;
|
||||
int16 _zeroPointer;
|
||||
int16 _startPointer;
|
||||
int16 _targetFrame;
|
||||
|
||||
public:
|
||||
bool onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) override;
|
||||
bool onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) override;
|
||||
bool process(uint32 deltaTimeInMillis) override;
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
118
engines/zvision/scripting/controls/save_control.cpp
Normal file
118
engines/zvision/scripting/controls/save_control.cpp
Normal file
@@ -0,0 +1,118 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/str.h"
|
||||
#include "common/stream.h"
|
||||
#include "zvision/zvision.h"
|
||||
#include "zvision/file/save_manager.h"
|
||||
#include "zvision/scripting/script_manager.h"
|
||||
#include "zvision/scripting/controls/input_control.h"
|
||||
#include "zvision/scripting/controls/save_control.h"
|
||||
#include "zvision/text/string_manager.h"
|
||||
#include "zvision/text/subtitle_manager.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
SaveControl::SaveControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream)
|
||||
: Control(engine, key, CONTROL_SAVE),
|
||||
_saveControl(false) {
|
||||
// Loop until we find the closing brace
|
||||
Common::String line = stream.readLine();
|
||||
_engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
|
||||
Common::String param;
|
||||
Common::String values;
|
||||
getParams(line, param, values);
|
||||
|
||||
while (!stream.eos() && !line.contains('}')) {
|
||||
if (param.matchString("savebox", true)) {
|
||||
int saveId;
|
||||
int inputId;
|
||||
if (sscanf(values.c_str(), "%d %d", &saveId, &inputId) == 2) {
|
||||
saveElement elmnt;
|
||||
elmnt.inputKey = inputId;
|
||||
elmnt.saveId = saveId;
|
||||
elmnt.exist = false;
|
||||
_inputs.push_back(elmnt);
|
||||
}
|
||||
} else if (param.matchString("control_type", true)) {
|
||||
if (values.contains("save"))
|
||||
_saveControl = true;
|
||||
else
|
||||
_saveControl = false;
|
||||
}
|
||||
|
||||
line = stream.readLine();
|
||||
_engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
|
||||
getParams(line, param, values);
|
||||
}
|
||||
|
||||
for (saveElmntList::iterator iter = _inputs.begin(); iter != _inputs.end(); ++iter) {
|
||||
Control *ctrl = _engine->getScriptManager()->getControl(iter->inputKey);
|
||||
if (ctrl && ctrl->getType() == Control::CONTROL_INPUT) {
|
||||
InputControl *inp = (InputControl *)ctrl;
|
||||
inp->setReadOnly(!_saveControl);
|
||||
Common::SeekableReadStream *save = _engine->getSaveManager()->getSlotFile(iter->saveId);
|
||||
if (save) {
|
||||
SaveGameHeader header;
|
||||
if (_engine->getSaveManager()->readSaveGameHeader(save, header)) {
|
||||
inp->setText(header.saveName);
|
||||
iter->exist = true;
|
||||
}
|
||||
delete save;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool SaveControl::process(uint32 deltaTimeInMillis) {
|
||||
for (saveElmntList::iterator iter = _inputs.begin(); iter != _inputs.end(); ++iter) {
|
||||
Control *ctrl = _engine->getScriptManager()->getControl(iter->inputKey);
|
||||
if (ctrl && ctrl->getType() == Control::CONTROL_INPUT) {
|
||||
InputControl *inp = (InputControl *)ctrl;
|
||||
if (inp->enterPress()) {
|
||||
if (_saveControl) {
|
||||
if (inp->getText().size() > 0) {
|
||||
bool toSave = true;
|
||||
if (iter->exist)
|
||||
if (!_engine->getSubtitleManager()->askQuestion(_engine->getStringManager()->getTextLine(StringManager::ZVISION_STR_SAVEEXIST)))
|
||||
toSave = false;
|
||||
|
||||
if (toSave) {
|
||||
_engine->getSaveManager()->saveGame(iter->saveId, inp->getText(), true);
|
||||
_engine->getSubtitleManager()->delayedMessage(_engine->getStringManager()->getTextLine(StringManager::ZVISION_STR_SAVED), 2000);
|
||||
_engine->getScriptManager()->changeLocation(_engine->getScriptManager()->getLastMenuLocation());
|
||||
}
|
||||
} else {
|
||||
_engine->getSubtitleManager()->timedMessage(_engine->getStringManager()->getTextLine(StringManager::ZVISION_STR_SAVEEMPTY), 2000);
|
||||
}
|
||||
} else {
|
||||
_engine->getSaveManager()->loadGame(iter->saveId);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
53
engines/zvision/scripting/controls/save_control.h
Normal file
53
engines/zvision/scripting/controls/save_control.h
Normal file
@@ -0,0 +1,53 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ZVISION_SAVE_CONTROL_H
|
||||
#define ZVISION_SAVE_CONTROL_H
|
||||
|
||||
#include "common/list.h"
|
||||
#include "zvision/scripting/control.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
class SaveControl : public Control {
|
||||
public:
|
||||
SaveControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream);
|
||||
|
||||
private:
|
||||
struct saveElement {
|
||||
int saveId;
|
||||
int inputKey;
|
||||
bool exist;
|
||||
};
|
||||
typedef Common::List<saveElement> saveElmntList;
|
||||
saveElmntList _inputs;
|
||||
|
||||
bool _saveControl;
|
||||
|
||||
public:
|
||||
|
||||
bool process(uint32 deltaTimeInMillis) override;
|
||||
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
213
engines/zvision/scripting/controls/slot_control.cpp
Normal file
213
engines/zvision/scripting/controls/slot_control.cpp
Normal file
@@ -0,0 +1,213 @@
|
||||
/* 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/scummsys.h"
|
||||
#include "common/stream.h"
|
||||
#include "zvision/zvision.h"
|
||||
#include "zvision/graphics/cursors/cursor_manager.h"
|
||||
#include "zvision/graphics/render_manager.h"
|
||||
#include "zvision/scripting/script_manager.h"
|
||||
#include "zvision/scripting/controls/slot_control.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
SlotControl::SlotControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream)
|
||||
: Control(engine, key, CONTROL_SLOT),
|
||||
_cursor(CursorIndex_Active),
|
||||
_distanceId('0') {
|
||||
|
||||
_renderedItem = 0;
|
||||
_bkg = NULL;
|
||||
|
||||
// Loop until we find the closing brace
|
||||
Common::String line = stream.readLine();
|
||||
_engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
|
||||
Common::String param;
|
||||
Common::String values;
|
||||
getParams(line, param, values);
|
||||
|
||||
while (!stream.eos() && !line.contains('}')) {
|
||||
if (param.matchString("hotspot", true)) {
|
||||
int x;
|
||||
int y;
|
||||
int width;
|
||||
int height;
|
||||
|
||||
if (sscanf(values.c_str(), "%d %d %d %d", &x, &y, &width, &height) == 4)
|
||||
_hotspot = Common::Rect(x, y, width, height);
|
||||
} else if (param.matchString("rectangle", true)) {
|
||||
int x;
|
||||
int y;
|
||||
int width;
|
||||
int height;
|
||||
|
||||
if (sscanf(values.c_str(), "%d %d %d %d", &x, &y, &width, &height) == 4)
|
||||
_rectangle = Common::Rect(x, y, width, height);
|
||||
} else if (param.matchString("cursor", true)) {
|
||||
_cursor = _engine->getCursorManager()->getCursorId(values);
|
||||
} else if (param.matchString("distance_id", true)) {
|
||||
sscanf(values.c_str(), "%c", &_distanceId);
|
||||
} else if (param.matchString("venus_id", true)) {
|
||||
_venusId = atoi(values.c_str());
|
||||
} else if (param.matchString("eligible_objects", true)) {
|
||||
char buf[256];
|
||||
memset(buf, 0, 256);
|
||||
strncpy(buf, values.c_str(), 255);
|
||||
|
||||
char *curpos = buf;
|
||||
char *strend = buf + strlen(buf);
|
||||
while (true) {
|
||||
char *st = curpos;
|
||||
|
||||
if (st >= strend)
|
||||
break;
|
||||
|
||||
while (*curpos != ' ' && curpos < strend)
|
||||
curpos++;
|
||||
|
||||
*curpos = 0;
|
||||
curpos++;
|
||||
|
||||
int obj = atoi(st);
|
||||
|
||||
_eligibleObjects.push_back(obj);
|
||||
}
|
||||
}
|
||||
|
||||
line = stream.readLine();
|
||||
_engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
|
||||
getParams(line, param, values);
|
||||
}
|
||||
|
||||
if (_hotspot.isEmpty() || _rectangle.isEmpty()) {
|
||||
warning("Slot %u was parsed incorrectly", key);
|
||||
}
|
||||
}
|
||||
|
||||
SlotControl::~SlotControl() {
|
||||
// Clear the state value back to 0
|
||||
//_engine->getScriptManager()->setStateValue(_key, 0);
|
||||
|
||||
if (_bkg)
|
||||
delete _bkg;
|
||||
}
|
||||
|
||||
bool SlotControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
|
||||
if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
|
||||
return false;
|
||||
|
||||
if (_hotspot.contains(backgroundImageSpacePos)) {
|
||||
setVenus();
|
||||
|
||||
int item = _engine->getScriptManager()->getStateValue(_key);
|
||||
int mouseItem = _engine->getScriptManager()->getStateValue(StateKey_InventoryItem);
|
||||
if (item != 0) {
|
||||
if (mouseItem != 0) {
|
||||
if (eligeblity(mouseItem)) {
|
||||
_engine->getScriptManager()->inventoryDrop(mouseItem);
|
||||
_engine->getScriptManager()->inventoryAdd(item);
|
||||
_engine->getScriptManager()->setStateValue(_key, mouseItem);
|
||||
}
|
||||
} else {
|
||||
_engine->getScriptManager()->inventoryAdd(item);
|
||||
_engine->getScriptManager()->setStateValue(_key, 0);
|
||||
}
|
||||
} else if (mouseItem == 0) {
|
||||
if (eligeblity(0)) {
|
||||
_engine->getScriptManager()->inventoryDrop(0);
|
||||
_engine->getScriptManager()->setStateValue(_key, 0);
|
||||
}
|
||||
} else if (eligeblity(mouseItem)) {
|
||||
_engine->getScriptManager()->setStateValue(_key, mouseItem);
|
||||
_engine->getScriptManager()->inventoryDrop(mouseItem);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SlotControl::onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
|
||||
if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
|
||||
return false;
|
||||
|
||||
if (_hotspot.contains(backgroundImageSpacePos)) {
|
||||
_engine->getCursorManager()->changeCursor(_cursor);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SlotControl::process(uint32 deltaTimeInMillis) {
|
||||
if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED)
|
||||
return false;
|
||||
|
||||
if (_engine->canRender()) {
|
||||
int curItem = _engine->getScriptManager()->getStateValue(_key);
|
||||
if (curItem != _renderedItem) {
|
||||
if (_renderedItem != 0 && curItem == 0) {
|
||||
_engine->getRenderManager()->blitSurfaceToBkg(*_bkg, _rectangle.left, _rectangle.top);
|
||||
_renderedItem = curItem;
|
||||
} else {
|
||||
if (_renderedItem == 0) {
|
||||
if (_bkg)
|
||||
delete _bkg;
|
||||
|
||||
_bkg = _engine->getRenderManager()->getBkgRect(_rectangle);
|
||||
} else {
|
||||
_engine->getRenderManager()->blitSurfaceToBkg(*_bkg, _rectangle.left, _rectangle.top);
|
||||
}
|
||||
|
||||
char buf[16];
|
||||
if (_engine->getGameId() == GID_NEMESIS)
|
||||
Common::sprintf_s(buf, "%d%cobj.tga", curItem, _distanceId);
|
||||
else
|
||||
Common::sprintf_s(buf, "g0z%cu%2.2x1.tga", _distanceId, curItem);
|
||||
|
||||
Graphics::Surface *srf = _engine->getRenderManager()->loadImage(buf);
|
||||
|
||||
int16 drawx = _rectangle.left;
|
||||
int16 drawy = _rectangle.top;
|
||||
|
||||
if (_rectangle.width() > srf->w)
|
||||
drawx = _rectangle.left + (_rectangle.width() - srf->w) / 2;
|
||||
|
||||
if (_rectangle.height() > srf->h)
|
||||
drawy = _rectangle.top + (_rectangle.height() - srf->h) / 2;
|
||||
|
||||
_engine->getRenderManager()->blitSurfaceToBkg(*srf, drawx, drawy, 0);
|
||||
|
||||
delete srf;
|
||||
|
||||
_renderedItem = curItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SlotControl::eligeblity(int itemId) {
|
||||
for (Common::List<int>::iterator it = _eligibleObjects.begin(); it != _eligibleObjects.end(); it++)
|
||||
if (*it == itemId)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
81
engines/zvision/scripting/controls/slot_control.h
Normal file
81
engines/zvision/scripting/controls/slot_control.h
Normal file
@@ -0,0 +1,81 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ZVISION_SLOT_CONTROL_H
|
||||
#define ZVISION_SLOT_CONTROL_H
|
||||
|
||||
#include "common/list.h"
|
||||
#include "common/rect.h"
|
||||
#include "graphics/surface.h"
|
||||
#include "zvision/scripting/control.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
class SlotControl : public Control {
|
||||
public:
|
||||
SlotControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream);
|
||||
~SlotControl() override;
|
||||
|
||||
/**
|
||||
* Called when LeftMouse is lifted. Calls ScriptManager::setStateValue(_key, 1);
|
||||
*
|
||||
* @param screenSpacePos The position of the mouse in screen space
|
||||
* @param backgroundImageSpacePos The position of the mouse in background image space
|
||||
*/
|
||||
bool onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) override;
|
||||
/**
|
||||
* Called on every MouseMove. Tests if the mouse is inside _hotspot, and if so, sets the cursor.
|
||||
*
|
||||
* @param engine The base engine
|
||||
* @param screenSpacePos The position of the mouse in screen space
|
||||
* @param backgroundImageSpacePos The position of the mouse in background image space
|
||||
* @return Was the cursor changed?
|
||||
*/
|
||||
bool onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) override;
|
||||
|
||||
bool process(uint32 deltaTimeInMillis) override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* The area that will trigger the event
|
||||
* This is in image space coordinates, NOT screen space
|
||||
*/
|
||||
Common::Rect _rectangle;
|
||||
Common::Rect _hotspot;
|
||||
|
||||
int _cursor;
|
||||
char _distanceId;
|
||||
|
||||
int _renderedItem;
|
||||
|
||||
Common::List<int> _eligibleObjects;
|
||||
|
||||
bool eligeblity(int itemId);
|
||||
|
||||
Graphics::Surface *_bkg;
|
||||
|
||||
/** The cursor to use when hovering over _hotspot */
|
||||
Common::String _hoverCursor;
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
103
engines/zvision/scripting/controls/titler_control.cpp
Normal file
103
engines/zvision/scripting/controls/titler_control.cpp
Normal file
@@ -0,0 +1,103 @@
|
||||
/* 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/scummsys.h"
|
||||
#include "common/stream.h"
|
||||
#include "zvision/zvision.h"
|
||||
#include "zvision/graphics/render_manager.h"
|
||||
#include "zvision/scripting/script_manager.h"
|
||||
#include "zvision/scripting/controls/titler_control.h"
|
||||
#include "zvision/text/text.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
TitlerControl::TitlerControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream)
|
||||
: Control(engine, key, CONTROL_TITLER) {
|
||||
|
||||
_surface = NULL;
|
||||
_curString = -1;
|
||||
|
||||
// Loop until we find the closing brace
|
||||
Common::String line = stream.readLine();
|
||||
_engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
|
||||
Common::String param;
|
||||
Common::String values;
|
||||
getParams(line, param, values);
|
||||
|
||||
while (!stream.eos() && !line.contains('}')) {
|
||||
if (param.matchString("string_resource_file", true)) {
|
||||
readStringsFile(Common::Path(values));
|
||||
} else if (param.matchString("rectangle", true)) {
|
||||
int x;
|
||||
int y;
|
||||
int x2;
|
||||
int y2;
|
||||
|
||||
if (sscanf(values.c_str(), "%d %d %d %d", &x, &y, &x2, &y2) == 4)
|
||||
_rectangle = Common::Rect(x, y, x2, y2);
|
||||
}
|
||||
|
||||
line = stream.readLine();
|
||||
_engine->getScriptManager()->trimCommentsAndWhiteSpace(&line);
|
||||
getParams(line, param, values);
|
||||
}
|
||||
|
||||
if (!_rectangle.isEmpty()) {
|
||||
_surface = new Graphics::Surface;
|
||||
_surface->create(_rectangle.width(), _rectangle.height(), _engine->_resourcePixelFormat);
|
||||
_surface->fillRect(Common::Rect(_surface->w, _surface->h), 0);
|
||||
}
|
||||
}
|
||||
|
||||
TitlerControl::~TitlerControl() {
|
||||
if (_surface) {
|
||||
_surface->free();
|
||||
delete _surface;
|
||||
}
|
||||
}
|
||||
|
||||
void TitlerControl::setString(int strLine) {
|
||||
if (strLine != _curString && strLine >= 0 && strLine < (int)_strings.size()) {
|
||||
_surface->fillRect(Common::Rect(_surface->w, _surface->h), 0);
|
||||
_engine->getTextRenderer()->drawTextWithWordWrapping(_strings[strLine], *_surface);
|
||||
_engine->getRenderManager()->blitSurfaceToBkg(*_surface, _rectangle.left, _rectangle.top);
|
||||
_curString = strLine;
|
||||
}
|
||||
}
|
||||
|
||||
void TitlerControl::readStringsFile(const Common::Path &fileName) {
|
||||
Common::File file;
|
||||
if (!file.open(fileName)) {
|
||||
warning("String_resource_file %s could could be opened", fileName.toString().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
_strings.clear();
|
||||
|
||||
while (!file.eos()) {
|
||||
|
||||
Common::String line = readWideLine(file).encode();
|
||||
_strings.push_back(line);
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
55
engines/zvision/scripting/controls/titler_control.h
Normal file
55
engines/zvision/scripting/controls/titler_control.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/* 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 ZVISION_TITLER_CONTROL_H
|
||||
#define ZVISION_TITLER_CONTROL_H
|
||||
|
||||
#include "common/array.h"
|
||||
#include "common/file.h"
|
||||
#include "common/path.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/stream.h"
|
||||
#include "graphics/surface.h"
|
||||
#include "zvision/scripting/control.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
// Only used in Zork Nemesis, handles the death screen with the Restore/Exit buttons
|
||||
class TitlerControl : public Control {
|
||||
public:
|
||||
TitlerControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream);
|
||||
~TitlerControl() override;
|
||||
|
||||
void setString(int strLine);
|
||||
|
||||
private:
|
||||
|
||||
Common::Array< Common::String > _strings;
|
||||
Common::Rect _rectangle;
|
||||
int16 _curString;
|
||||
Graphics::Surface *_surface;
|
||||
|
||||
void readStringsFile(const Common::Path &fileName);
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
213
engines/zvision/scripting/effects/animation_effect.cpp
Normal file
213
engines/zvision/scripting/effects/animation_effect.cpp
Normal file
@@ -0,0 +1,213 @@
|
||||
/* 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/scummsys.h"
|
||||
#include "graphics/surface.h"
|
||||
#include "video/video_decoder.h"
|
||||
#include "zvision/zvision.h"
|
||||
#include "zvision/graphics/render_manager.h"
|
||||
#include "zvision/scripting/script_manager.h"
|
||||
#include "zvision/scripting/effects/animation_effect.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
AnimationEffect::AnimationEffect(ZVision *engine, uint32 controlKey, const Common::Path &fileName, int32 mask, int32 frate, bool disposeAfterUse)
|
||||
: ScriptingEffect(engine, controlKey, SCRIPTING_EFFECT_ANIM),
|
||||
_disposeAfterUse(disposeAfterUse),
|
||||
_mask(mask),
|
||||
_animation(NULL) {
|
||||
|
||||
_animation = engine->loadAnimation(fileName);
|
||||
|
||||
if (frate > 0) {
|
||||
_frmDelayOverride = (int32)(1000.0 / frate);
|
||||
|
||||
// WORKAROUND: We do not allow the engine to delay more than 66 msec
|
||||
// per frame (15fps max)
|
||||
if (_frmDelayOverride > 66)
|
||||
_frmDelayOverride = 66;
|
||||
} else {
|
||||
_frmDelayOverride = 0;
|
||||
}
|
||||
}
|
||||
|
||||
AnimationEffect::~AnimationEffect() {
|
||||
if (_animation)
|
||||
delete _animation;
|
||||
|
||||
_engine->getScriptManager()->setStateValue(_key, 2);
|
||||
|
||||
PlayNodes::iterator it = _playList.begin();
|
||||
if (it != _playList.end()) {
|
||||
_engine->getScriptManager()->setStateValue((*it).slot, 2);
|
||||
|
||||
if ((*it)._scaled) {
|
||||
(*it)._scaled->free();
|
||||
delete(*it)._scaled;
|
||||
}
|
||||
}
|
||||
|
||||
_playList.clear();
|
||||
}
|
||||
|
||||
bool AnimationEffect::process(uint32 deltaTimeInMillis) {
|
||||
ScriptManager *scriptManager = _engine->getScriptManager();
|
||||
RenderManager *renderManager = _engine->getRenderManager();
|
||||
RenderTable::RenderState renderState = renderManager->getRenderTable()->getRenderState();
|
||||
bool isPanorama = (renderState == RenderTable::PANORAMA);
|
||||
int16 velocity = _engine->getMouseVelocity() + _engine->getKeyboardVelocity();
|
||||
|
||||
// Do not update animation nodes in panoramic mode while turning, if the user
|
||||
// has set this option
|
||||
if (scriptManager->getStateValue(StateKey_NoTurnAnim) == 1 && isPanorama && velocity)
|
||||
return false;
|
||||
|
||||
PlayNodes::iterator it = _playList.begin();
|
||||
if (it != _playList.end()) {
|
||||
playnode *nod = &(*it);
|
||||
|
||||
if (nod->_curFrame == -1) {
|
||||
// The node is just beginning playback
|
||||
nod->_curFrame = nod->start;
|
||||
|
||||
_animation->start();
|
||||
_animation->seekToFrame(nod->start);
|
||||
_animation->setEndFrame(nod->stop);
|
||||
|
||||
nod->_delay = deltaTimeInMillis; // Force the frame to draw
|
||||
if (nod->slot)
|
||||
scriptManager->setStateValue(nod->slot, 1);
|
||||
} else if (_animation->endOfVideo()) {
|
||||
// The node has reached the end; check if we need to loop
|
||||
nod->loop--;
|
||||
|
||||
if (nod->loop == 0) {
|
||||
if (nod->slot >= 0)
|
||||
scriptManager->setStateValue(nod->slot, 2);
|
||||
if (nod->_scaled) {
|
||||
nod->_scaled->free();
|
||||
delete nod->_scaled;
|
||||
}
|
||||
_playList.erase(it);
|
||||
return _disposeAfterUse;
|
||||
}
|
||||
|
||||
nod->_curFrame = nod->start;
|
||||
_animation->seekToFrame(nod->start);
|
||||
}
|
||||
|
||||
// Check if we need to draw a frame
|
||||
bool needsUpdate = false;
|
||||
if (_frmDelayOverride == 0) {
|
||||
// If not overridden, use the VideoDecoder's check
|
||||
needsUpdate = _animation->needsUpdate();
|
||||
} else {
|
||||
// Otherwise, implement our own timing
|
||||
nod->_delay -= deltaTimeInMillis;
|
||||
|
||||
if (nod->_delay <= 0) {
|
||||
nod->_delay += _frmDelayOverride;
|
||||
needsUpdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (needsUpdate) {
|
||||
const Graphics::Surface *frame = _animation->decodeNextFrame();
|
||||
|
||||
if (frame) {
|
||||
int dstw;
|
||||
int dsth;
|
||||
if (isPanorama) {
|
||||
dstw = nod->pos.height();
|
||||
dsth = nod->pos.width();
|
||||
} else {
|
||||
dstw = nod->pos.width();
|
||||
dsth = nod->pos.height();
|
||||
}
|
||||
|
||||
// We only scale down the animation to fit its frame, not up, otherwise we
|
||||
// end up with distorted animations - e.g. the armor visor in location cz1e
|
||||
// in Nemesis (one of the armors inside Irondune), or the planet in location
|
||||
// aa10 in Nemesis (Juperon, outside the asylum). We do allow scaling up only
|
||||
// when a simple 2x filter is requested (e.g. the alchemists and cup sequence
|
||||
// in Nemesis)
|
||||
if (frame->w > dstw || frame->h > dsth || (frame->w == dstw / 2 && frame->h == dsth / 2)) {
|
||||
if (nod->_scaled)
|
||||
if (nod->_scaled->w != dstw || nod->_scaled->h != dsth) {
|
||||
nod->_scaled->free();
|
||||
delete nod->_scaled;
|
||||
nod->_scaled = NULL;
|
||||
}
|
||||
|
||||
if (!nod->_scaled) {
|
||||
nod->_scaled = new Graphics::Surface;
|
||||
nod->_scaled->create(dstw, dsth, frame->format);
|
||||
}
|
||||
|
||||
renderManager->scaleBuffer(frame->getPixels(), nod->_scaled->getPixels(), frame->w, frame->h, frame->format.bytesPerPixel, dstw, dsth);
|
||||
frame = nod->_scaled;
|
||||
}
|
||||
|
||||
if (isPanorama) {
|
||||
Graphics::Surface *transposed = RenderManager::tranposeSurface(frame);
|
||||
renderManager->blitSurfaceToBkg(*transposed, nod->pos.left, nod->pos.top, _mask);
|
||||
transposed->free();
|
||||
delete transposed;
|
||||
} else {
|
||||
renderManager->blitSurfaceToBkg(*frame, nod->pos.left, nod->pos.top, _mask);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void AnimationEffect::addPlayNode(int32 slot, int x, int y, int x2, int y2, int startFrame, int endFrame, int loops) {
|
||||
playnode nod;
|
||||
nod.loop = loops;
|
||||
nod.pos = Common::Rect(x, y, x2 + 1, y2 + 1);
|
||||
nod.start = startFrame;
|
||||
nod.stop = CLIP<int>(endFrame, 0, _animation->getFrameCount() - 1);
|
||||
nod.slot = slot;
|
||||
nod._curFrame = -1;
|
||||
nod._delay = 0;
|
||||
nod._scaled = NULL;
|
||||
_playList.push_back(nod);
|
||||
}
|
||||
|
||||
bool AnimationEffect::stop() {
|
||||
PlayNodes::iterator it = _playList.begin();
|
||||
if (it != _playList.end()) {
|
||||
_engine->getScriptManager()->setStateValue((*it).slot, 2);
|
||||
if ((*it)._scaled) {
|
||||
(*it)._scaled->free();
|
||||
delete(*it)._scaled;
|
||||
}
|
||||
}
|
||||
|
||||
_playList.clear();
|
||||
|
||||
// We don't need to delete, it's may be reused
|
||||
return false;
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
79
engines/zvision/scripting/effects/animation_effect.h
Normal file
79
engines/zvision/scripting/effects/animation_effect.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ZVISION_ANIMATION_NODE_H
|
||||
#define ZVISION_ANIMATION_NODE_H
|
||||
|
||||
#include "common/list.h"
|
||||
#include "common/path.h"
|
||||
#include "common/rect.h"
|
||||
#include "zvision/scripting/scripting_effect.h"
|
||||
|
||||
namespace Graphics {
|
||||
struct Surface;
|
||||
}
|
||||
|
||||
namespace Video {
|
||||
class VideoDecoder;
|
||||
}
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
class ZVision;
|
||||
|
||||
class AnimationEffect : public ScriptingEffect {
|
||||
public:
|
||||
AnimationEffect(ZVision *engine, uint32 controlKey, const Common::Path &fileName, int32 mask, int32 frate, bool disposeAfterUse = true);
|
||||
~AnimationEffect() override;
|
||||
|
||||
struct playnode {
|
||||
Common::Rect pos;
|
||||
int32 slot;
|
||||
int32 start;
|
||||
int32 stop;
|
||||
int32 loop;
|
||||
int32 _curFrame;
|
||||
int32 _delay;
|
||||
Graphics::Surface *_scaled;
|
||||
};
|
||||
|
||||
private:
|
||||
typedef Common::List<playnode> PlayNodes;
|
||||
|
||||
PlayNodes _playList;
|
||||
|
||||
int32 _mask;
|
||||
bool _disposeAfterUse;
|
||||
|
||||
Video::VideoDecoder *_animation;
|
||||
int32 _frmDelayOverride;
|
||||
|
||||
public:
|
||||
bool process(uint32 deltaTimeInMillis) override;
|
||||
|
||||
void addPlayNode(int32 slot, int x, int y, int x2, int y2, int startFrame, int endFrame, int loops = 1);
|
||||
|
||||
bool stop() override;
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
108
engines/zvision/scripting/effects/distort_effect.cpp
Normal file
108
engines/zvision/scripting/effects/distort_effect.cpp
Normal file
@@ -0,0 +1,108 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/stream.h"
|
||||
#include "zvision/zvision.h"
|
||||
#include "zvision/graphics/render_manager.h"
|
||||
#include "zvision/graphics/render_table.h"
|
||||
#include "zvision/scripting/script_manager.h"
|
||||
#include "zvision/scripting/effects/distort_effect.h"
|
||||
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
DistortNode::DistortNode(ZVision *engine, uint32 key, int16 speed, float startAngle, float endAngle, float startLineScale, float endLineScale)
|
||||
: ScriptingEffect(engine, key, SCRIPTING_EFFECT_DISTORT) {
|
||||
|
||||
_angle = _engine->getRenderManager()->getRenderTable()->getAngle();
|
||||
_linScale = _engine->getRenderManager()->getRenderTable()->getLinscale();
|
||||
|
||||
_speed = speed;
|
||||
_incr = true;
|
||||
_startAngle = startAngle;
|
||||
_endAngle = endAngle;
|
||||
_startLineScale = startLineScale;
|
||||
_endLineScale = endLineScale;
|
||||
|
||||
_curFrame = 1.0;
|
||||
|
||||
_diffAngle = endAngle - startAngle;
|
||||
_diffLinScale = endLineScale - startLineScale;
|
||||
|
||||
_frmSpeed = (float)speed / 15.0;
|
||||
_frames = (int)ceil((5.0 - _frmSpeed * 2.0) / _frmSpeed);
|
||||
if (_frames <= 0)
|
||||
_frames = 1;
|
||||
|
||||
if (_key != StateKey_NotSet)
|
||||
_engine->getScriptManager()->setStateValue(_key, 1);
|
||||
}
|
||||
|
||||
DistortNode::~DistortNode() {
|
||||
setParams(_angle, _linScale);
|
||||
}
|
||||
|
||||
bool DistortNode::process(uint32 deltaTimeInMillis) {
|
||||
float updTime = deltaTimeInMillis / (1000.0 / 60.0);
|
||||
|
||||
if (_incr)
|
||||
_curFrame += updTime;
|
||||
else
|
||||
_curFrame -= updTime;
|
||||
|
||||
if (_curFrame < 1.0) {
|
||||
_curFrame = 1.0;
|
||||
_incr = true;
|
||||
} else if (_curFrame > _frames) {
|
||||
_curFrame = _frames;
|
||||
_incr = false;
|
||||
}
|
||||
|
||||
float diff = (1.0 / (5.0 - (_curFrame * _frmSpeed))) / (5.0 - _frmSpeed);
|
||||
setParams(_startAngle + diff * _diffAngle, _startLineScale + diff * _diffLinScale);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void DistortNode::setParams(float angl, float linScale) {
|
||||
RenderTable *table = _engine->getRenderManager()->getRenderTable();
|
||||
switch (table->getRenderState()) {
|
||||
case RenderTable::PANORAMA: {
|
||||
table->setPanoramaFoV(angl);
|
||||
table->setPanoramaScale(linScale);
|
||||
table->generateRenderTable();
|
||||
_engine->getRenderManager()->markDirty();
|
||||
break;
|
||||
}
|
||||
case RenderTable::TILT: {
|
||||
table->setTiltFoV(angl);
|
||||
table->setTiltScale(linScale);
|
||||
table->generateRenderTable();
|
||||
_engine->getRenderManager()->markDirty();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
62
engines/zvision/scripting/effects/distort_effect.h
Normal file
62
engines/zvision/scripting/effects/distort_effect.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ZVISION_DISTORT_NODE_H
|
||||
#define ZVISION_DISTORT_NODE_H
|
||||
|
||||
#include "zvision/scripting/scripting_effect.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
class ZVision;
|
||||
|
||||
class DistortNode : public ScriptingEffect {
|
||||
public:
|
||||
DistortNode(ZVision *engine, uint32 key, int16 speed, float startAngle, float endAngle, float startLineScale, float endLineScale);
|
||||
~DistortNode() override;
|
||||
|
||||
bool process(uint32 deltaTimeInMillis) override;
|
||||
|
||||
private:
|
||||
int16 _speed;
|
||||
float _startAngle;
|
||||
float _endAngle;
|
||||
float _startLineScale;
|
||||
float _endLineScale;
|
||||
|
||||
float _frmSpeed;
|
||||
float _diffAngle;
|
||||
float _diffLinScale;
|
||||
bool _incr;
|
||||
int16 _frames;
|
||||
|
||||
float _curFrame;
|
||||
|
||||
float _angle;
|
||||
float _linScale;
|
||||
|
||||
private:
|
||||
void setParams(float angl, float linScale);
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
290
engines/zvision/scripting/effects/music_effect.cpp
Normal file
290
engines/zvision/scripting/effects/music_effect.cpp
Normal file
@@ -0,0 +1,290 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "audio/decoders/wave.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/file.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/stream.h"
|
||||
#include "math/utils.h"
|
||||
#include "math/angle.h"
|
||||
#include "zvision/zvision.h"
|
||||
#include "zvision/graphics/render_manager.h"
|
||||
#include "zvision/scripting/script_manager.h"
|
||||
#include "zvision/scripting/effects/music_effect.h"
|
||||
#include "zvision/sound/midi.h"
|
||||
#include "zvision/sound/volume_manager.h"
|
||||
#include "zvision/sound/zork_raw.h"
|
||||
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
void MusicNodeBASE::setDirection(Math::Angle azimuth, uint8 magnitude) {
|
||||
if (_engine->getScriptManager()->getStateValue(StateKey_Qsound) >= 1) {
|
||||
_azimuth = azimuth;
|
||||
_directionality = magnitude;
|
||||
_balance = ((int)(127 * _azimuth.getSine()) * _directionality) / 255;
|
||||
} else
|
||||
setBalance(0);
|
||||
updateMixer();
|
||||
}
|
||||
|
||||
void MusicNodeBASE::setBalance(int8 balance) {
|
||||
_balance = balance;
|
||||
_azimuth.setDegrees(0);
|
||||
_directionality = 255;
|
||||
updateMixer();
|
||||
}
|
||||
|
||||
void MusicNodeBASE::updateMixer() {
|
||||
if (_engine->getScriptManager()->getStateValue(StateKey_Qsound) >= 1)
|
||||
_volumeOut = _engine->getVolumeManager()->convert(_volume, _azimuth, _directionality); // Apply game-specific volume profile and then attenuate according to azimuth
|
||||
else
|
||||
_volumeOut = _engine->getVolumeManager()->convert(_volume); // Apply game-specific volume profile and ignore azimuth
|
||||
outputMixer();
|
||||
}
|
||||
|
||||
MusicNode::MusicNode(ZVision *engine, uint32 key, Common::Path &filename, bool loop, uint8 volume)
|
||||
: MusicNodeBASE(engine, key, SCRIPTING_EFFECT_AUDIO) {
|
||||
_loop = loop;
|
||||
_volume = volume;
|
||||
_balance = 0;
|
||||
_fade = false;
|
||||
_fadeStartVol = volume;
|
||||
_fadeEndVol = 0;
|
||||
_fadeTime = 0;
|
||||
_fadeElapsed = 0;
|
||||
_sub = 0;
|
||||
_stereo = false;
|
||||
_loaded = false;
|
||||
|
||||
Audio::RewindableAudioStream *audioStream = NULL;
|
||||
|
||||
if (filename.baseName().contains(".wav")) {
|
||||
Common::File *file = new Common::File();
|
||||
if (file->open(filename)) {
|
||||
audioStream = Audio::makeWAVStream(file, DisposeAfterUse::YES);
|
||||
}
|
||||
} else {
|
||||
audioStream = makeRawZorkStream(filename, _engine);
|
||||
}
|
||||
|
||||
if (audioStream) {
|
||||
_stereo = audioStream->isStereo();
|
||||
|
||||
if (_loop) {
|
||||
Audio::LoopingAudioStream *loopingAudioStream = new Audio::LoopingAudioStream(audioStream, 0, DisposeAfterUse::YES);
|
||||
_engine->_mixer->playStream(Audio::Mixer::kPlainSoundType, &_handle, loopingAudioStream, -1, _volume);
|
||||
} else {
|
||||
_engine->_mixer->playStream(Audio::Mixer::kPlainSoundType, &_handle, audioStream, -1, _volume);
|
||||
}
|
||||
|
||||
if (_key != StateKey_NotSet) {
|
||||
debugC(3, kDebugSound, "setting musicnode state value to 1");
|
||||
_engine->getScriptManager()->setStateValue(_key, 1);
|
||||
}
|
||||
|
||||
// Change filename.raw into filename.sub
|
||||
Common::String subname = filename.baseName();
|
||||
subname.setChar('s', subname.size() - 3);
|
||||
subname.setChar('u', subname.size() - 2);
|
||||
subname.setChar('b', subname.size() - 1);
|
||||
|
||||
Common::Path subpath(filename.getParent().appendComponent(subname));
|
||||
if (SearchMan.hasFile(subpath))
|
||||
_sub = _engine->getSubtitleManager()->create(subpath, _handle); // NB automatic subtitle!
|
||||
|
||||
_loaded = true;
|
||||
updateMixer();
|
||||
}
|
||||
debugC(3, kDebugSound, "MusicNode: %d created", _key);
|
||||
}
|
||||
|
||||
MusicNode::~MusicNode() {
|
||||
if (_loaded)
|
||||
_engine->_mixer->stopHandle(_handle);
|
||||
if (_key != StateKey_NotSet)
|
||||
_engine->getScriptManager()->setStateValue(_key, 2);
|
||||
if (_sub)
|
||||
_engine->getSubtitleManager()->destroy(_sub);
|
||||
debugC(3, kDebugSound, "MusicNode: %d destroyed", _key);
|
||||
}
|
||||
|
||||
void MusicNode::outputMixer() {
|
||||
_engine->_mixer->setChannelBalance(_handle, _balance);
|
||||
_engine->_mixer->setChannelVolume(_handle, _volumeOut);
|
||||
}
|
||||
|
||||
void MusicNode::setFade(int32 time, uint8 target) {
|
||||
_fadeStartVol = _volume;
|
||||
_fadeEndVol = target;
|
||||
_fadeElapsed = 0;
|
||||
_fadeTime = time <= 0 ? 0 : (uint32)time;
|
||||
_fade = true;
|
||||
}
|
||||
|
||||
bool MusicNode::process(uint32 deltaTimeInMillis) {
|
||||
if (!_loaded || ! _engine->_mixer->isSoundHandleActive(_handle))
|
||||
return stop();
|
||||
else {
|
||||
if (_fade) {
|
||||
debugC(3, kDebugSound, "Fading music, endVol %d, startVol %d, current %d, fade time %d, elapsed time %dms", _fadeEndVol, _fadeStartVol, _volume, _fadeTime, _fadeElapsed);
|
||||
uint8 _newvol = 0;
|
||||
_fadeElapsed += deltaTimeInMillis;
|
||||
if ((_fadeTime <= 0) | (_fadeElapsed >= _fadeTime)) {
|
||||
_newvol = _fadeEndVol;
|
||||
_fade = false;
|
||||
} else {
|
||||
if (_fadeEndVol > _fadeStartVol)
|
||||
_newvol = _fadeStartVol + (_fadeElapsed * (_fadeEndVol - _fadeStartVol)) / _fadeTime;
|
||||
else
|
||||
_newvol = _fadeStartVol - (_fadeElapsed * (_fadeStartVol - _fadeEndVol)) / _fadeTime;
|
||||
}
|
||||
if (_volume != _newvol)
|
||||
setVolume(_newvol);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void MusicNode::setVolume(uint8 newVolume) {
|
||||
if (_loaded) {
|
||||
debugC(4, kDebugSound, "Changing volume of music node %d from %d to %d", _key, _volume, newVolume);
|
||||
_volume = newVolume;
|
||||
updateMixer();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PanTrackNode::PanTrackNode(ZVision *engine, uint32 key, uint32 slot, int16 pos, uint8 mag, bool resetMixerOnDelete, bool staticScreen)
|
||||
: ScriptingEffect(engine, key, SCRIPTING_EFFECT_PANTRACK),
|
||||
_slot(slot),
|
||||
_sourcePos(0),
|
||||
_viewPos(0),
|
||||
_mag(mag),
|
||||
_width(0),
|
||||
_pos(pos),
|
||||
_staticScreen(staticScreen),
|
||||
_resetMixerOnDelete(resetMixerOnDelete) {
|
||||
debugC(3, kDebugSound, "Created PanTrackNode, key %d, slot %d", _key, _slot);
|
||||
process(0); // Try to set pan value for music node immediately
|
||||
}
|
||||
|
||||
PanTrackNode::~PanTrackNode() {
|
||||
debugC(1, kDebugSound, "Deleting PanTrackNode, key %d, slot %d", _key, _slot);
|
||||
ScriptManager *scriptManager = _engine->getScriptManager();
|
||||
ScriptingEffect *fx = scriptManager->getSideFX(_slot);
|
||||
if (fx && fx->getType() == SCRIPTING_EFFECT_AUDIO && _resetMixerOnDelete) {
|
||||
debugC(1, kDebugSound, "Resetting mixer, slot %d", _slot);
|
||||
MusicNodeBASE *mus = (MusicNodeBASE *)fx;
|
||||
mus->setBalance(0);
|
||||
} else
|
||||
debugC(1, kDebugSound, "NOT resetting mixer, slot %d", _slot);
|
||||
}
|
||||
|
||||
bool PanTrackNode::process(uint32 deltaTimeInMillis) {
|
||||
debugC(3, kDebugSound, "Processing PanTrackNode, key %d", _key);
|
||||
ScriptManager *scriptManager = _engine->getScriptManager();
|
||||
ScriptingEffect *fx = scriptManager->getSideFX(_slot);
|
||||
if (fx && fx->getType() == SCRIPTING_EFFECT_AUDIO) {
|
||||
MusicNodeBASE *mus = (MusicNodeBASE *)fx;
|
||||
if (!_staticScreen)
|
||||
// Original game scripted behaviour
|
||||
switch (_engine->getRenderManager()->getRenderTable()->getRenderState()) {
|
||||
case RenderTable::PANORAMA:
|
||||
debugC(3, kDebugSound, "PanTrackNode in panorama mode");
|
||||
_width = _engine->getRenderManager()->getBkgSize().x;
|
||||
if (_width) {
|
||||
_sourcePos.setDegrees(360 * _pos / _width);
|
||||
_viewPos.setDegrees(360 * scriptManager->getStateValue(StateKey_ViewPos) / _width);
|
||||
} else {
|
||||
warning("Encountered zero background width whilst processing PanTrackNode in panoramic mode!");
|
||||
}
|
||||
break;
|
||||
case RenderTable::FLAT:
|
||||
case RenderTable::TILT:
|
||||
default:
|
||||
debugC(3, kDebugSound, "PanTrackNode in FLAT/TILT mode");
|
||||
_sourcePos.setDegrees(0);
|
||||
_viewPos.setDegrees(0);
|
||||
break;
|
||||
} else {
|
||||
// Used for auxiliary scripts only
|
||||
_sourcePos.setDegrees(_pos);
|
||||
_viewPos.setDegrees(0);
|
||||
}
|
||||
Math::Angle azimuth;
|
||||
azimuth = _sourcePos - _viewPos;
|
||||
debugC(3, kDebugSound, "soundPos: %f, _viewPos: %f, azimuth: %f, width %d", _sourcePos.getDegrees(), _viewPos.getDegrees(), azimuth.getDegrees(), _width);
|
||||
// azimuth is sound source position relative to player, clockwise from centre of camera axis to front when viewed top-down
|
||||
mus->setDirection(azimuth, _mag);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
MusicMidiNode::MusicMidiNode(ZVision *engine, uint32 key, uint8 program, uint8 note, uint8 volume)
|
||||
: MusicNodeBASE(engine, key, SCRIPTING_EFFECT_AUDIO) {
|
||||
_volume = volume;
|
||||
_prog = program;
|
||||
_noteNumber = note;
|
||||
_pan = 0;
|
||||
|
||||
_chan = _engine->getMidiManager()->getFreeChannel();
|
||||
|
||||
if (_chan >= 0) {
|
||||
updateMixer();
|
||||
_engine->getMidiManager()->setProgram(_chan, _prog);
|
||||
_engine->getMidiManager()->noteOn(_chan, _noteNumber, _volume);
|
||||
}
|
||||
|
||||
if (_key != StateKey_NotSet)
|
||||
_engine->getScriptManager()->setStateValue(_key, 1);
|
||||
}
|
||||
|
||||
MusicMidiNode::~MusicMidiNode() {
|
||||
if (_chan >= 0) {
|
||||
_engine->getMidiManager()->noteOff(_chan);
|
||||
}
|
||||
if (_key != StateKey_NotSet)
|
||||
_engine->getScriptManager()->setStateValue(_key, 2);
|
||||
}
|
||||
|
||||
void MusicMidiNode::setFade(int32 time, uint8 target) {
|
||||
}
|
||||
|
||||
bool MusicMidiNode::process(uint32 deltaTimeInMillis) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void MusicMidiNode::setVolume(uint8 newVolume) {
|
||||
if (_chan >= 0) {
|
||||
_volume = newVolume;
|
||||
updateMixer();
|
||||
}
|
||||
}
|
||||
|
||||
void MusicMidiNode::outputMixer() {
|
||||
_engine->getMidiManager()->setBalance(_chan, _balance);
|
||||
_engine->getMidiManager()->setPan(_chan, _pan);
|
||||
_engine->getMidiManager()->setVolume(_chan, _volumeOut);
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
146
engines/zvision/scripting/effects/music_effect.h
Normal file
146
engines/zvision/scripting/effects/music_effect.h
Normal file
@@ -0,0 +1,146 @@
|
||||
/* 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 ZVISION_MUSIC_NODE_H
|
||||
#define ZVISION_MUSIC_NODE_H
|
||||
|
||||
#include "audio/mixer.h"
|
||||
#include "math/angle.h"
|
||||
#include "zvision/scripting/scripting_effect.h"
|
||||
#include "zvision/sound/volume_manager.h"
|
||||
#include "zvision/text/subtitle_manager.h"
|
||||
|
||||
namespace Common {
|
||||
class String;
|
||||
}
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
class MusicNodeBASE : public ScriptingEffect {
|
||||
public:
|
||||
MusicNodeBASE(ZVision *engine, uint32 key, ScriptingEffectType type) : ScriptingEffect(engine, key, type) {}
|
||||
~MusicNodeBASE() override {}
|
||||
|
||||
/**
|
||||
* Decrement the timer by the delta time. If the timer is finished, set the status
|
||||
* in _globalState and let this node be deleted
|
||||
*
|
||||
* @param deltaTimeInMillis The number of milliseconds that have passed since last frame
|
||||
* @return If true, the node can be deleted after process() finishes
|
||||
*/
|
||||
bool process(uint32 deltaTimeInMillis) override = 0;
|
||||
|
||||
virtual void setVolume(uint8 volume) = 0;
|
||||
uint8 getVolume() {
|
||||
return _volume;
|
||||
}
|
||||
virtual void setFade(int32 time, uint8 target) = 0;
|
||||
virtual void setBalance(int8 balance); // NB Overrides effects of setDirection()
|
||||
void setDirection(Math::Angle azimuth, uint8 magnitude = 255); // NB Overrides effects of setBalance()
|
||||
protected:
|
||||
void updateMixer();
|
||||
virtual void outputMixer() = 0;
|
||||
|
||||
uint8 _volume = 0;
|
||||
int8 _balance = 0;
|
||||
Math::Angle _azimuth;
|
||||
uint8 _directionality; // 0 = fully ambient, 255 = fully directional
|
||||
uint8 _volumeOut = 0;
|
||||
};
|
||||
|
||||
class MusicNode : public MusicNodeBASE {
|
||||
public:
|
||||
MusicNode(ZVision *engine, uint32 key, Common::Path &file, bool loop, uint8 volume);
|
||||
~MusicNode() override;
|
||||
|
||||
/**
|
||||
* Decrement the timer by the delta time. If the timer is finished, set the status
|
||||
* in _globalState and let this node be deleted
|
||||
*
|
||||
* @param deltaTimeInMillis The number of milliseconds that have passed since last frame
|
||||
* @return If true, the node can be deleted after process() finishes
|
||||
*/
|
||||
bool process(uint32 deltaTimeInMillis) override;
|
||||
|
||||
void setVolume(uint8 volume) override;
|
||||
|
||||
void setFade(int32 time, uint8 target) override;
|
||||
|
||||
private:
|
||||
void outputMixer() override;
|
||||
bool _loop;
|
||||
bool _fade;
|
||||
uint8 _fadeStartVol;
|
||||
uint8 _fadeEndVol;
|
||||
uint32 _fadeTime;
|
||||
uint32 _fadeElapsed; // Cumulative time since fade start
|
||||
bool _stereo;
|
||||
Audio::SoundHandle _handle;
|
||||
uint16 _sub;
|
||||
bool _loaded;
|
||||
};
|
||||
|
||||
// Only used by Zork: Nemesis, for the flute and piano puzzles (tj4e and ve6f, as well as vr)
|
||||
class MusicMidiNode : public MusicNodeBASE {
|
||||
public:
|
||||
MusicMidiNode(ZVision *engine, uint32 key, uint8 program, uint8 note, uint8 volume);
|
||||
~MusicMidiNode() override;
|
||||
|
||||
/**
|
||||
* Decrement the timer by the delta time. If the timer is finished, set the status
|
||||
* in _globalState and let this node be deleted
|
||||
*
|
||||
* @param deltaTimeInMillis The number of milliseconds that have passed since last frame
|
||||
* @return If true, the node can be deleted after process() finishes
|
||||
*/
|
||||
bool process(uint32 deltaTimeInMillis) override;
|
||||
|
||||
void setVolume(uint8 volume) override;
|
||||
|
||||
void setFade(int32 time, uint8 target) override;
|
||||
|
||||
private:
|
||||
void outputMixer() override;
|
||||
int8 _chan;
|
||||
uint8 _noteNumber;
|
||||
int8 _pan;
|
||||
uint8 _prog;
|
||||
};
|
||||
|
||||
class PanTrackNode : public ScriptingEffect {
|
||||
public:
|
||||
PanTrackNode(ZVision *engine, uint32 key, uint32 slot, int16 pos, uint8 mag = 255, bool resetMixerOnDelete = false, bool staticScreen = false);
|
||||
~PanTrackNode() override;
|
||||
|
||||
bool process(uint32 deltaTimeInMillis) override;
|
||||
|
||||
private:
|
||||
uint32 _slot;
|
||||
int16 _width, _pos;
|
||||
Math::Angle _sourcePos, _viewPos;
|
||||
uint8 _mag;
|
||||
bool _resetMixerOnDelete;
|
||||
bool _staticScreen;
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
53
engines/zvision/scripting/effects/region_effect.cpp
Normal file
53
engines/zvision/scripting/effects/region_effect.cpp
Normal file
@@ -0,0 +1,53 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "zvision/zvision.h"
|
||||
#include "zvision/graphics/render_manager.h"
|
||||
#include "zvision/scripting/script_manager.h"
|
||||
#include "zvision/scripting/effects/region_effect.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
RegionNode::RegionNode(ZVision *engine, uint32 key, GraphicsEffect *effect, uint32 delay)
|
||||
: ScriptingEffect(engine, key, SCRIPTING_EFFECT_REGION) {
|
||||
_effect = effect;
|
||||
_delay = delay;
|
||||
_timeLeft = 0;
|
||||
}
|
||||
|
||||
RegionNode::~RegionNode() {
|
||||
_engine->getRenderManager()->deleteEffect(_key);
|
||||
}
|
||||
|
||||
bool RegionNode::process(uint32 deltaTimeInMillis) {
|
||||
_timeLeft -= deltaTimeInMillis;
|
||||
|
||||
if (_timeLeft <= 0) {
|
||||
_timeLeft = _delay;
|
||||
if (_effect)
|
||||
_effect->update();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
55
engines/zvision/scripting/effects/region_effect.h
Normal file
55
engines/zvision/scripting/effects/region_effect.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/* 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 ZVISION_REGION_NODE_H
|
||||
#define ZVISION_REGION_NODE_H
|
||||
|
||||
#include "graphics/surface.h"
|
||||
#include "zvision/graphics/graphics_effect.h"
|
||||
#include "zvision/scripting/scripting_effect.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
class ZVision;
|
||||
|
||||
class RegionNode : public ScriptingEffect {
|
||||
public:
|
||||
RegionNode(ZVision *engine, uint32 key, GraphicsEffect *effect, uint32 delay);
|
||||
~RegionNode() override;
|
||||
|
||||
/**
|
||||
* Decrement the timer by the delta time. If the timer is finished, set the status
|
||||
* in _globalState and let this node be deleted
|
||||
*
|
||||
* @param deltaTimeInMillis The number of milliseconds that have passed since last frame
|
||||
* @return If true, the node can be deleted after process() finishes
|
||||
*/
|
||||
bool process(uint32 deltaTimeInMillis) override;
|
||||
|
||||
private:
|
||||
int32 _timeLeft;
|
||||
uint32 _delay;
|
||||
GraphicsEffect *_effect;
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
80
engines/zvision/scripting/effects/syncsound_effect.cpp
Normal file
80
engines/zvision/scripting/effects/syncsound_effect.cpp
Normal file
@@ -0,0 +1,80 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "audio/decoders/wave.h"
|
||||
#include "common/file.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/stream.h"
|
||||
#include "zvision/zvision.h"
|
||||
#include "zvision/graphics/render_manager.h"
|
||||
#include "zvision/scripting/script_manager.h"
|
||||
#include "zvision/scripting/effects/syncsound_effect.h"
|
||||
#include "zvision/sound/zork_raw.h"
|
||||
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
SyncSoundNode::SyncSoundNode(ZVision *engine, uint32 key, Common::Path &filename, int32 syncto)
|
||||
: ScriptingEffect(engine, key, SCRIPTING_EFFECT_AUDIO) {
|
||||
_syncto = syncto;
|
||||
_sub = 0;
|
||||
|
||||
Audio::RewindableAudioStream *audioStream = NULL;
|
||||
|
||||
if (filename.baseName().contains(".wav")) {
|
||||
Common::File *file = new Common::File();
|
||||
if (file->open(filename)) {
|
||||
audioStream = Audio::makeWAVStream(file, DisposeAfterUse::YES);
|
||||
}
|
||||
} else {
|
||||
audioStream = makeRawZorkStream(filename, _engine);
|
||||
}
|
||||
|
||||
_engine->_mixer->playStream(Audio::Mixer::kPlainSoundType, &_handle, audioStream);
|
||||
|
||||
Common::String subname = filename.baseName();
|
||||
subname.setChar('s', subname.size() - 3);
|
||||
subname.setChar('u', subname.size() - 2);
|
||||
subname.setChar('b', subname.size() - 1);
|
||||
|
||||
Common::Path subpath(filename.getParent().appendComponent(subname));
|
||||
if (SearchMan.hasFile(subpath))
|
||||
_sub = _engine->getSubtitleManager()->create(subpath, _handle); // NB automatic subtitle!
|
||||
}
|
||||
|
||||
SyncSoundNode::~SyncSoundNode() {
|
||||
_engine->_mixer->stopHandle(_handle);
|
||||
if (_sub)
|
||||
_engine->getSubtitleManager()->destroy(_sub);
|
||||
}
|
||||
|
||||
bool SyncSoundNode::process(uint32 deltaTimeInMillis) {
|
||||
if (! _engine->_mixer->isSoundHandleActive(_handle))
|
||||
return stop();
|
||||
else {
|
||||
|
||||
if (_engine->getScriptManager()->getSideFX(_syncto) == NULL)
|
||||
return stop();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
55
engines/zvision/scripting/effects/syncsound_effect.h
Normal file
55
engines/zvision/scripting/effects/syncsound_effect.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/* 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 ZVISION_SYNCSOUND_NODE_H
|
||||
#define ZVISION_SYNCSOUND_NODE_H
|
||||
|
||||
#include "audio/mixer.h"
|
||||
#include "zvision/scripting/scripting_effect.h"
|
||||
#include "zvision/text/subtitle_manager.h"
|
||||
|
||||
namespace Common {
|
||||
class String;
|
||||
}
|
||||
|
||||
namespace ZVision {
|
||||
class SyncSoundNode : public ScriptingEffect {
|
||||
public:
|
||||
SyncSoundNode(ZVision *engine, uint32 key, Common::Path &file, int32 syncto);
|
||||
~SyncSoundNode() override;
|
||||
|
||||
/**
|
||||
* Decrement the timer by the delta time. If the timer is finished, set the status
|
||||
* in _globalState and let this node be deleted
|
||||
*
|
||||
* @param deltaTimeInMillis The number of milliseconds that have passed since last frame
|
||||
* @return If true, the node can be deleted after process() finishes
|
||||
*/
|
||||
bool process(uint32 deltaTimeInMillis) override;
|
||||
private:
|
||||
int32 _syncto;
|
||||
Audio::SoundHandle _handle;
|
||||
uint16 _sub;
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
77
engines/zvision/scripting/effects/timer_effect.cpp
Normal file
77
engines/zvision/scripting/effects/timer_effect.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/stream.h"
|
||||
#include "zvision/zvision.h"
|
||||
#include "zvision/scripting/script_manager.h"
|
||||
#include "zvision/scripting/effects/timer_effect.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
TimerNode::TimerNode(ZVision *engine, uint32 key, uint timeInSeconds)
|
||||
: ScriptingEffect(engine, key, SCRIPTING_EFFECT_TIMER) {
|
||||
_timeLeft = 0;
|
||||
|
||||
if (_engine->getGameId() == GID_NEMESIS)
|
||||
_timeLeft = timeInSeconds * 1000;
|
||||
else if (_engine->getGameId() == GID_GRANDINQUISITOR)
|
||||
_timeLeft = timeInSeconds * 100;
|
||||
|
||||
if (_key != StateKey_NotSet)
|
||||
_engine->getScriptManager()->setStateValue(_key, 1);
|
||||
}
|
||||
|
||||
TimerNode::~TimerNode() {
|
||||
if (_key != StateKey_NotSet)
|
||||
_engine->getScriptManager()->setStateValue(_key, 2);
|
||||
int32 timeLeft = _timeLeft / (_engine->getGameId() == GID_NEMESIS ? 1000 : 100);
|
||||
if (timeLeft > 0)
|
||||
_engine->getScriptManager()->setStateValue(_key, timeLeft); // If timer was stopped by stop or kill
|
||||
}
|
||||
|
||||
bool TimerNode::process(uint32 deltaTimeInMillis) {
|
||||
_timeLeft -= deltaTimeInMillis;
|
||||
|
||||
if (_timeLeft <= 0)
|
||||
return stop();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TimerNode::stop() {
|
||||
if (_key != StateKey_NotSet)
|
||||
_engine->getScriptManager()->setStateValue(_key, 2);
|
||||
return true;
|
||||
}
|
||||
|
||||
void TimerNode::serialize(Common::WriteStream *stream) {
|
||||
stream->writeUint32BE(MKTAG('T', 'I', 'M', 'R'));
|
||||
stream->writeUint32LE(8); // size
|
||||
stream->writeUint32LE(_key);
|
||||
stream->writeUint32LE(_timeLeft);
|
||||
}
|
||||
|
||||
void TimerNode::deserialize(Common::SeekableReadStream *stream) {
|
||||
_timeLeft = stream->readUint32LE();
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
58
engines/zvision/scripting/effects/timer_effect.h
Normal file
58
engines/zvision/scripting/effects/timer_effect.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ZVISION_TIMER_NODE_H
|
||||
#define ZVISION_TIMER_NODE_H
|
||||
|
||||
#include "zvision/scripting/scripting_effect.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
class ZVision;
|
||||
|
||||
class TimerNode : public ScriptingEffect {
|
||||
public:
|
||||
TimerNode(ZVision *engine, uint32 key, uint timeInSeconds);
|
||||
~TimerNode() override;
|
||||
|
||||
/**
|
||||
* Decrement the timer by the delta time. If the timer is finished, set the status
|
||||
* in _globalState and let this node be deleted
|
||||
*
|
||||
* @param deltaTimeInMillis The number of milliseconds that have passed since last frame
|
||||
* @return If true, the node can be deleted after process() finishes
|
||||
*/
|
||||
bool process(uint32 deltaTimeInMillis) override;
|
||||
void serialize(Common::WriteStream *stream) override;
|
||||
void deserialize(Common::SeekableReadStream *stream) override;
|
||||
inline bool needsSerialization() override {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool stop() override;
|
||||
|
||||
private:
|
||||
int32 _timeLeft;
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
199
engines/zvision/scripting/effects/ttytext_effect.cpp
Normal file
199
engines/zvision/scripting/effects/ttytext_effect.cpp
Normal file
@@ -0,0 +1,199 @@
|
||||
/* 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/scummsys.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/unicode-bidi.h"
|
||||
#include "zvision/zvision.h"
|
||||
#include "zvision/graphics/render_manager.h"
|
||||
#include "zvision/scripting/script_manager.h"
|
||||
#include "zvision/scripting/effects/ttytext_effect.h"
|
||||
#include "zvision/text/text.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
ttyTextNode::ttyTextNode(ZVision *engine, uint32 key, const Common::Path &file, const Common::Rect &r, int32 delay) :
|
||||
ScriptingEffect(engine, key, SCRIPTING_EFFECT_TTYTXT),
|
||||
_fnt(engine) {
|
||||
_delay = delay;
|
||||
_r = r;
|
||||
_txtpos = 0;
|
||||
_nexttime = 0;
|
||||
_dx = 0;
|
||||
_dy = 0;
|
||||
_lineStartPos = 0;
|
||||
_startX = 0;
|
||||
|
||||
Common::File *infile = new Common::File;
|
||||
infile->open(file);
|
||||
if (infile) {
|
||||
while (!infile->eos()) {
|
||||
Common::U32String asciiLine = readWideLine(*infile);
|
||||
if (asciiLine.empty()) {
|
||||
continue;
|
||||
}
|
||||
_txtbuf += asciiLine;
|
||||
}
|
||||
|
||||
delete infile;
|
||||
}
|
||||
_isRTL = Common::convertBiDiU32String(_txtbuf).visual != _txtbuf;
|
||||
_img.create(_r.width(), _r.height(), _engine->_resourcePixelFormat);
|
||||
_state._sharp = true;
|
||||
_state.readAllStyles(_txtbuf.encode());
|
||||
_state.updateFontWithTextState(_fnt);
|
||||
_engine->getScriptManager()->setStateValue(_key, 1);
|
||||
}
|
||||
|
||||
ttyTextNode::~ttyTextNode() {
|
||||
_engine->getScriptManager()->setStateValue(_key, 2);
|
||||
_img.free();
|
||||
}
|
||||
|
||||
bool ttyTextNode::process(uint32 deltaTimeInMillis) {
|
||||
_nexttime -= deltaTimeInMillis;
|
||||
|
||||
if (_nexttime < 0) {
|
||||
if (_txtpos < _txtbuf.size()) {
|
||||
if (_txtbuf[_txtpos] == '<') {
|
||||
int32 start = _txtpos;
|
||||
int32 end = 0;
|
||||
int16 ret = 0;
|
||||
while (_txtbuf[_txtpos] != '>' && _txtpos < _txtbuf.size())
|
||||
_txtpos++;
|
||||
end = _txtpos;
|
||||
if (start != -1) {
|
||||
if ((end - start - 1) > 0) {
|
||||
Common::String buf = _txtbuf.substr(start + 1, end - start - 1);
|
||||
ret = _state.parseStyle(buf, buf.size());
|
||||
}
|
||||
}
|
||||
|
||||
if (ret & (TEXT_CHANGE_FONT_TYPE | TEXT_CHANGE_FONT_STYLE)) {
|
||||
_state.updateFontWithTextState(_fnt);
|
||||
} else if (ret & TEXT_CHANGE_NEWLINE) {
|
||||
newline();
|
||||
}
|
||||
|
||||
if (ret & TEXT_CHANGE_HAS_STATE_BOX) {
|
||||
Common::String buf;
|
||||
buf = Common::String::format("%d", _engine->getScriptManager()->getStateValue(_state._statebox));
|
||||
|
||||
if (_isRTL) {
|
||||
int16 currDx = _dx + _fnt.getStringWidth(buf);
|
||||
_dx = _r.width() - currDx;
|
||||
_isRTL = false;
|
||||
for (uint8 j = 0; j < buf.size(); j++)
|
||||
outchar(buf[j]);
|
||||
_isRTL = true;
|
||||
_dx = currDx;
|
||||
} else {
|
||||
for (uint8 j = 0; j < buf.size(); j++)
|
||||
outchar(buf[j]);
|
||||
}
|
||||
}
|
||||
|
||||
_txtpos++;
|
||||
_lineStartPos = _txtpos;
|
||||
_startX = _dx;
|
||||
} else {
|
||||
uint32 pos = _lineStartPos;
|
||||
int16 dx = _startX;
|
||||
|
||||
while (pos < _txtbuf.size() && _txtbuf[pos] != '<') {
|
||||
uint16 chr = _txtbuf[pos];
|
||||
|
||||
if (chr == ' ') {
|
||||
uint32 i = pos + 1;
|
||||
uint16 width = _fnt.getCharWidth(chr);
|
||||
|
||||
while (i < _txtbuf.size() && _txtbuf[i] != ' ' && _txtbuf[i] != '<') {
|
||||
|
||||
uint16 uchr = _txtbuf[i];
|
||||
|
||||
width += _fnt.getCharWidth(uchr);
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
if (dx + width > _r.width())
|
||||
break;
|
||||
}
|
||||
dx += _fnt.getCharWidth(chr);
|
||||
pos++;
|
||||
}
|
||||
|
||||
Common::U32String lineBuffer = Common::convertBiDiU32String(_txtbuf.substr(_lineStartPos, pos - _lineStartPos)).visual;
|
||||
if (pos == _txtpos)
|
||||
newline();
|
||||
else if (_isRTL)
|
||||
outchar(lineBuffer[pos - _txtpos - 1]);
|
||||
else
|
||||
outchar(lineBuffer[_txtpos - _lineStartPos]);
|
||||
|
||||
_txtpos++;
|
||||
}
|
||||
_nexttime = _delay;
|
||||
_engine->getRenderManager()->blitSurfaceToBkg(_img, _r.left, _r.top);
|
||||
} else
|
||||
return stop();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ttyTextNode::scroll() {
|
||||
int32 scrl = 0;
|
||||
while (_dy - scrl > _r.height() - _fnt.getFontHeight())
|
||||
scrl += _fnt.getFontHeight();
|
||||
int8 *pixels = (int8 *)_img.getPixels();
|
||||
for (uint16 h = scrl; h < _img.h; h++)
|
||||
memcpy(pixels + _img.pitch * (h - scrl), pixels + _img.pitch * h, _img.pitch);
|
||||
|
||||
_img.fillRect(Common::Rect(0, _img.h - scrl, _img.w, _img.h), 0);
|
||||
_dy -= scrl;
|
||||
}
|
||||
|
||||
void ttyTextNode::newline() {
|
||||
_dy += _fnt.getFontHeight();
|
||||
_dx = 0;
|
||||
_lineStartPos = _txtpos + 1;
|
||||
_startX = _dx;
|
||||
}
|
||||
|
||||
void ttyTextNode::outchar(uint16 chr) {
|
||||
uint32 clr = _engine->_resourcePixelFormat.RGBToColor(_state._red, _state._green, _state._blue);
|
||||
|
||||
if (_dx + _fnt.getCharWidth(chr) > _r.width())
|
||||
newline();
|
||||
|
||||
if (_dy + _fnt.getFontHeight() >= _r.height())
|
||||
scroll();
|
||||
|
||||
if (_isRTL)
|
||||
_fnt.drawChar(&_img, chr, _r.width() - _dx - _fnt.getCharWidth(chr), _dy, clr);
|
||||
else
|
||||
_fnt.drawChar(&_img, chr, _dx, _dy, clr);
|
||||
_dx += _fnt.getCharWidth(chr);
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
74
engines/zvision/scripting/effects/ttytext_effect.h
Normal file
74
engines/zvision/scripting/effects/ttytext_effect.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ZVISION_TTYTEXT_NODE_H
|
||||
#define ZVISION_TTYTEXT_NODE_H
|
||||
|
||||
#include "common/rect.h"
|
||||
#include "graphics/surface.h"
|
||||
#include "zvision/scripting/scripting_effect.h"
|
||||
#include "zvision/text/text.h"
|
||||
#include "zvision/text/truetype_font.h"
|
||||
|
||||
namespace Common {
|
||||
class String;
|
||||
}
|
||||
|
||||
namespace ZVision {
|
||||
class ttyTextNode : public ScriptingEffect {
|
||||
public:
|
||||
ttyTextNode(ZVision *engine, uint32 key, const Common::Path &file, const Common::Rect &r, int32 delay);
|
||||
~ttyTextNode() override;
|
||||
|
||||
/**
|
||||
* Decrement the timer by the delta time. If the timer is finished, set the status
|
||||
* in _globalState and let this node be deleted
|
||||
*
|
||||
* @param deltaTimeInMillis The number of milliseconds that have passed since last frame
|
||||
* @return If true, the node can be deleted after process() finishes
|
||||
*/
|
||||
bool process(uint32 deltaTimeInMillis) override;
|
||||
private:
|
||||
Common::Rect _r;
|
||||
|
||||
TextStyleState _state;
|
||||
StyledTTFont _fnt;
|
||||
Common::U32String _txtbuf;
|
||||
uint32 _txtpos;
|
||||
uint32 _lineStartPos;
|
||||
bool _isRTL;
|
||||
|
||||
int32 _delay;
|
||||
int32 _nexttime;
|
||||
Graphics::Surface _img;
|
||||
int16 _dx;
|
||||
int16 _startX;
|
||||
int16 _dy;
|
||||
private:
|
||||
|
||||
void newline();
|
||||
void scroll();
|
||||
void outchar(uint16 chr);
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
120
engines/zvision/scripting/inventory.cpp
Normal file
120
engines/zvision/scripting/inventory.cpp
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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "zvision/scripting/script_manager.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
int8 ScriptManager::inventoryGetCount() {
|
||||
return getStateValue(StateKey_Inv_Cnt_Slot);
|
||||
}
|
||||
|
||||
void ScriptManager::inventorySetCount(int8 cnt) {
|
||||
setStateValue(StateKey_Inv_Cnt_Slot, cnt);
|
||||
}
|
||||
|
||||
int16 ScriptManager::inventoryGetItem(int8 id) {
|
||||
if (id < 49 && id >= 0)
|
||||
return getStateValue(StateKey_Inv_1_Slot + id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
void ScriptManager::inventorySetItem(int8 id, int16 item) {
|
||||
if (id < 49 && id >= 0)
|
||||
setStateValue(StateKey_Inv_1_Slot + id, item);
|
||||
}
|
||||
|
||||
void ScriptManager::inventoryAdd(int16 item) {
|
||||
int8 cnt = inventoryGetCount();
|
||||
|
||||
if (cnt < 49) {
|
||||
bool notExist = true;
|
||||
|
||||
if (cnt == 0) {
|
||||
inventorySetItem(0, 0);
|
||||
inventorySetCount(1); // we needed empty item for cycle code
|
||||
cnt = 1;
|
||||
}
|
||||
|
||||
for (int8 cur = 0; cur < cnt; cur++)
|
||||
if (inventoryGetItem(cur) == item) {
|
||||
notExist = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (notExist) {
|
||||
for (int8 i = cnt; i > 0; i--)
|
||||
inventorySetItem(i, inventoryGetItem(i - 1));
|
||||
|
||||
inventorySetItem(0, item);
|
||||
|
||||
setStateValue(StateKey_InventoryItem, item);
|
||||
|
||||
inventorySetCount(cnt + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptManager::inventoryDrop(int16 item) {
|
||||
int8 itemCount = inventoryGetCount();
|
||||
|
||||
// if items in inventory > 0
|
||||
if (itemCount != 0) {
|
||||
int8 index = 0;
|
||||
|
||||
// finding needed item
|
||||
while (index < itemCount) {
|
||||
if (inventoryGetItem(index) == item)
|
||||
break;
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
// if item in the inventory
|
||||
if (itemCount != index) {
|
||||
// shift all items left with rewrite founded item
|
||||
for (int8 v = index; v < itemCount - 1 ; v++)
|
||||
inventorySetItem(v, inventoryGetItem(v + 1));
|
||||
|
||||
// del last item
|
||||
inventorySetItem(itemCount - 1, 0);
|
||||
inventorySetCount(inventoryGetCount() - 1);
|
||||
|
||||
setStateValue(StateKey_InventoryItem, inventoryGetItem(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
void ScriptManager::inventoryCycle() {
|
||||
int8 itemCount = inventoryGetCount();
|
||||
int8 curItem = inventoryGetItem(0);
|
||||
if (itemCount > 1) {
|
||||
for (int8 i = 0; i < itemCount - 1; i++)
|
||||
inventorySetItem(i, inventoryGetItem(i + 1));
|
||||
|
||||
inventorySetItem(itemCount - 1, curItem);
|
||||
|
||||
setStateValue(StateKey_InventoryItem, inventoryGetItem(0));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
592
engines/zvision/scripting/menu.cpp
Normal file
592
engines/zvision/scripting/menu.cpp
Normal file
@@ -0,0 +1,592 @@
|
||||
/* 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 "zvision/detection.h"
|
||||
#include "zvision/graphics/render_manager.h"
|
||||
#include "zvision/scripting/menu.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
enum {
|
||||
kFocusNone = -1,
|
||||
kFocusItems = 0,
|
||||
kFocusMagic = 1,
|
||||
kFocusMain = 2
|
||||
};
|
||||
|
||||
MenuManager::MenuManager(ZVision *engine, const Common::Rect menuArea, const MenuParams params) :
|
||||
_engine(engine),
|
||||
_params(params),
|
||||
_menuBarFlag(0xFFFF),
|
||||
_menuArea(menuArea),
|
||||
_menuOrigin(menuArea.origin()),
|
||||
_menuTriggerArea(_menuOrigin, _menuArea.width(), _params.triggerHeight),
|
||||
_mainScroller(params.activePos, params.idlePos, params.period) {
|
||||
|
||||
_enableFlags.set_size(6);
|
||||
for (int8 i = 0; i < 4; i++) {
|
||||
// Generate button hotspot areas
|
||||
_menuHotspots[i] = Common::Rect(_params.wxButs[i][1], _menuArea.top, _params.wxButs[i][1] + _params.wxButs[i][0], _menuArea.bottom);
|
||||
// Initialise button animation frames
|
||||
_mainFrames[i] = _params.idleFrame;
|
||||
}
|
||||
for (int i = 0; i < 4; i++)
|
||||
_buttonAnim[i] = new LinearScroller(_params.activeFrame, _params.idleFrame, _params.buttonPeriod);
|
||||
setFocus(kFocusNone); // Ensure focus list is initialised
|
||||
_mainArea = Common::Rect(_params.wMain, _hMainMenu);
|
||||
_mainArea.moveTo(_menuOrigin + _mainScroller._pos);
|
||||
}
|
||||
|
||||
MenuManager::~MenuManager() {
|
||||
for (int i = 0; i < 4; i++)
|
||||
delete _buttonAnim[i];
|
||||
_mainBack.free();
|
||||
}
|
||||
|
||||
void MenuManager::setEnable(uint16 flags) {
|
||||
static const uint16 flagMasks[6] = {0x8, 0x4, 0x2, 0x1, 0x100, 0x200}; // Enum order: save,restore,prefs,quit,items,magic
|
||||
_menuBarFlag = flags;
|
||||
for (uint i = 0; i <= 5; i++) {
|
||||
if (_menuBarFlag & flagMasks[i])
|
||||
_enableFlags.set(i);
|
||||
else
|
||||
_enableFlags.unset(i);
|
||||
}
|
||||
}
|
||||
|
||||
void MenuManager::onMouseUp(const Common::Point &pos) {
|
||||
if (_menuFocus.front() == kFocusMain) {
|
||||
_mouseOnItem = mouseOverMain(pos);
|
||||
if (_mouseOnItem == _mainClicked)
|
||||
// Activate clicked action from main menu
|
||||
switch (_mouseOnItem) {
|
||||
case kMainMenuSave:
|
||||
_engine->getScriptManager()->changeLocation('g', 'j', 's', 'e', 0);
|
||||
setFocus(kFocusNone);
|
||||
_mainScroller.reset();
|
||||
_redraw = true;
|
||||
break;
|
||||
case kMainMenuLoad:
|
||||
_engine->getScriptManager()->changeLocation('g', 'j', 'r', 'e', 0);
|
||||
setFocus(kFocusNone);
|
||||
_mainScroller.reset();
|
||||
_redraw = true;
|
||||
break;
|
||||
case kMainMenuPrefs:
|
||||
_engine->getScriptManager()->changeLocation('g', 'j', 'p', 'e', 0);
|
||||
setFocus(kFocusNone);
|
||||
_mainScroller.reset();
|
||||
_redraw = true;
|
||||
break;
|
||||
case kMainMenuExit:
|
||||
_engine->quit(true);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
_mainClicked = -1;
|
||||
}
|
||||
|
||||
void MenuManager::onMouseDown(const Common::Point &pos) {
|
||||
if (_menuFocus.front() == kFocusMain) {
|
||||
_mouseOnItem = mouseOverMain(pos);
|
||||
// Show clicked graphic
|
||||
if ((_mouseOnItem >= 0) && (_mouseOnItem < 4))
|
||||
if (_enableFlags.get(_mouseOnItem)) {
|
||||
_mainClicked = _mouseOnItem;
|
||||
_redraw = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MenuManager::onMouseMove(const Common::Point &pos) {
|
||||
bool nowInMenu = inMenu(pos);
|
||||
if (nowInMenu != _prevInMenu)
|
||||
_redraw = true;
|
||||
_prevInMenu = nowInMenu;
|
||||
|
||||
int lastItem = _mouseOnItem;
|
||||
|
||||
switch (_menuFocus.front()) {
|
||||
case kFocusMain:
|
||||
// Inform game scripting engine that mouse is in main menu
|
||||
if (_engine->getScriptManager()->getStateValue(StateKey_MenuState) != 2)
|
||||
_engine->getScriptManager()->setStateValue(StateKey_MenuState, 2);
|
||||
_mouseOnItem = mouseOverMain(pos);
|
||||
break;
|
||||
case kFocusNone:
|
||||
// Inform game scripting engine that mouse is not in any menu
|
||||
if (_engine->getScriptManager()->getStateValue(StateKey_MenuState) != 0)
|
||||
_engine->getScriptManager()->setStateValue(StateKey_MenuState, 0);
|
||||
_mouseOnItem = -1;
|
||||
break;
|
||||
}
|
||||
_mainScroller.setActive(_menuFocus.front() == kFocusMain);
|
||||
// Update button animation status
|
||||
for (int i = 0; i < 4; i++)
|
||||
if (_menuFocus[0] == kFocusMain && _mouseOnItem == i)
|
||||
_buttonAnim[i]->setActive(true);
|
||||
else
|
||||
_buttonAnim[i]->setActive(false);
|
||||
if (lastItem != _mouseOnItem)
|
||||
_redraw = true;
|
||||
}
|
||||
|
||||
int MenuManager::mouseOverMain(const Common::Point &pos) {
|
||||
// Common::Rect mainHotspot(28,_hSideMenu);
|
||||
// mainHotspot.moveTo(mainOrigin + _mainScroller._pos);
|
||||
for (int8 i = 0; i < 4; i++) {
|
||||
if (_enableFlags.get(i) && _menuHotspots[i].contains(pos))
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void MenuManager::process(uint32 deltatime) {
|
||||
if (_mainScroller.update(deltatime)) {
|
||||
_mainArea.moveTo(_menuOrigin + _mainScroller._pos);
|
||||
for (int i = 0; i < 4; i++)
|
||||
_menuHotspots[i].moveTo(_menuOrigin + Common::Point(_params.wxButs[i][1], _mainScroller._pos.y));
|
||||
_redraw = true;
|
||||
}
|
||||
// Update button highlight animation frame
|
||||
for (int i = 0; i < 4; i++)
|
||||
if (_buttonAnim[i]->update(deltatime)) {
|
||||
_mainFrames[i] = _buttonAnim[i]->_pos;
|
||||
_redraw = true;
|
||||
}
|
||||
if (_redraw) {
|
||||
_engine->getRenderManager()->clearMenuSurface();
|
||||
redrawAll();
|
||||
_redraw = false;
|
||||
}
|
||||
}
|
||||
|
||||
void MenuNemesis::redrawAll() {
|
||||
redrawMain();
|
||||
}
|
||||
|
||||
void MenuManager::redrawMain() {
|
||||
// Draw menu background
|
||||
_engine->getRenderManager()->blitSurfaceToMenu(_mainBack, _mainScroller._pos.x, _mainScroller._pos.y, 0);
|
||||
// Draw buttons
|
||||
if (_menuFocus.front() == kFocusMain)
|
||||
for (int8 i = 0; i < 4; i++) {
|
||||
if (_enableFlags.get(i) && (_mainFrames[i] >= 0)) {
|
||||
if (_mainClicked == i)
|
||||
_engine->getRenderManager()->blitSurfaceToMenu(_mainButtons[i][_params.clickedFrame], _params.wxButs[i][1], _mainScroller._pos.y, 0);
|
||||
else
|
||||
_engine->getRenderManager()->blitSurfaceToMenu(_mainButtons[i][_mainFrames[i]], _params.wxButs[i][1], _mainScroller._pos.y, 0);
|
||||
}
|
||||
}
|
||||
_clean = false;
|
||||
}
|
||||
|
||||
void MenuManager::setFocus(int8 currentFocus) {
|
||||
_menuFocus.set(currentFocus);
|
||||
assert(_menuFocus.size() <= 4);
|
||||
}
|
||||
|
||||
MenuZGI::MenuZGI(ZVision *engine, const Common::Rect menuArea) :
|
||||
MenuManager(engine, menuArea, zgiParams),
|
||||
_itemsScroller(Common::Point(0, 0), Common::Point(_wSideMenuTab - _wSideMenu, 0), _sideMenuPeriod),
|
||||
_magicScroller(Common::Point(-_wSideMenu, 0), Common::Point(-_wSideMenuTab, 0), _sideMenuPeriod),
|
||||
_itemsOrigin(menuArea.left, menuArea.top),
|
||||
_magicOrigin(menuArea.right, menuArea.top) {
|
||||
|
||||
_magicArea = Common::Rect(_magicOrigin + _magicScroller._pos, _wSideMenu, _hSideMenu);
|
||||
_itemsArea = Common::Rect(_itemsOrigin + _itemsScroller._pos, _wSideMenu, _hSideMenu);
|
||||
|
||||
// Buffer main menu background
|
||||
_engine->getRenderManager()->readImageToSurface("gmzau031.tga", _mainBack, false);
|
||||
|
||||
char buf[24];
|
||||
for (int i = 0; i < 4; i++) {
|
||||
// Buffer menu buttons
|
||||
Common::sprintf_s(buf, "gmzmu%2.2x1.tga", i);
|
||||
_engine->getRenderManager()->readImageToSurface(buf, _mainButtons[i][0], false);
|
||||
Common::sprintf_s(buf, "gmznu%2.2x1.tga", i);
|
||||
_engine->getRenderManager()->readImageToSurface(buf, _mainButtons[i][1], false);
|
||||
}
|
||||
for (int i = 1; i < 4; i++) {
|
||||
// Buffer full menu backgrounds
|
||||
Common::sprintf_s(buf, "gmzau%2.2x1.tga", i);
|
||||
_engine->getRenderManager()->readImageToSurface(buf, _menuBack[i - 1], false);
|
||||
}
|
||||
for (int i = 0; i < 50; i++) {
|
||||
_items[i][0] = NULL;
|
||||
_items[i][1] = NULL;
|
||||
_itemId[i] = 0;
|
||||
}
|
||||
for (int i = 0; i < 12; i++) {
|
||||
_magic[i][0] = NULL;
|
||||
_magic[i][1] = NULL;
|
||||
_magicId[i] = 0;
|
||||
}
|
||||
// Initialise focus sequence
|
||||
setFocus(kFocusMain);
|
||||
setFocus(kFocusMagic);
|
||||
setFocus(kFocusItems);
|
||||
setFocus(kFocusNone);
|
||||
}
|
||||
|
||||
MenuZGI::~MenuZGI() {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
_menuBack[i].free();
|
||||
}
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int j = 0; j < 2; j++)
|
||||
_mainButtons[i][j].free();
|
||||
}
|
||||
for (int i = 0; i < 50; i++) {
|
||||
if (_items[i][0]) {
|
||||
_items[i][0]->free();
|
||||
delete _items[i][0];
|
||||
}
|
||||
if (_items[i][1]) {
|
||||
_items[i][1]->free();
|
||||
delete _items[i][1];
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < 12; i++) {
|
||||
if (_magic[i][0]) {
|
||||
_magic[i][0]->free();
|
||||
delete _magic[i][0];
|
||||
}
|
||||
if (_magic[i][1]) {
|
||||
_magic[i][1]->free();
|
||||
delete _magic[i][1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool MenuZGI::inMenu(const Common::Point &pos) const {
|
||||
return _menuTriggerArea.contains(pos) || (_menuFocus.front() != kFocusNone);
|
||||
}
|
||||
|
||||
void MenuZGI::onMouseUp(const Common::Point &pos) {
|
||||
if (inMenu(pos))
|
||||
// _redraw = true;
|
||||
switch (_menuFocus.front()) {
|
||||
case kFocusItems:
|
||||
if (_enableFlags.get(kItemsMenu)) {
|
||||
int itemCount = _engine->getScriptManager()->getStateValue(StateKey_Inv_TotalSlots);
|
||||
if (itemCount == 0)
|
||||
itemCount = 20;
|
||||
int i = mouseOverItem(pos, itemCount);
|
||||
if (i != -1) {
|
||||
int32 mouseItem = _engine->getScriptManager()->getStateValue(StateKey_InventoryItem);
|
||||
if (mouseItem >= 0 && mouseItem < 0xE0) {
|
||||
_engine->getScriptManager()->inventoryDrop(mouseItem);
|
||||
_engine->getScriptManager()->inventoryAdd(_engine->getScriptManager()->getStateValue(StateKey_Inv_StartSlot + i));
|
||||
_engine->getScriptManager()->setStateValue(StateKey_Inv_StartSlot + i, mouseItem);
|
||||
_redraw = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case kFocusMagic:
|
||||
if (_enableFlags.get(kMagicMenu)) {
|
||||
int i = mouseOverMagic(pos);
|
||||
if (i != -1) {
|
||||
|
||||
uint itemnum = _engine->getScriptManager()->getStateValue(StateKey_Spell_1 + i);
|
||||
if (itemnum != 0) {
|
||||
if (_engine->getScriptManager()->getStateValue(StateKey_Reversed_Spellbooc) == 1)
|
||||
itemnum = 0xEE + i;
|
||||
else
|
||||
itemnum = 0xE0 + i;
|
||||
}
|
||||
if (itemnum)
|
||||
if (_engine->getScriptManager()->getStateValue(StateKey_InventoryItem) == 0 || _engine->getScriptManager()->getStateValue(StateKey_InventoryItem) >= 0xE0)
|
||||
_engine->getScriptManager()->setStateValue(StateKey_Active_Spell, itemnum);
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
case kFocusMain:
|
||||
MenuManager::onMouseUp(pos);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MenuZGI::onMouseMove(const Common::Point &pos) {
|
||||
if (!inMenu(pos)) {
|
||||
_mainScroller.reset();
|
||||
_magicScroller.reset();
|
||||
_itemsScroller.reset();
|
||||
}
|
||||
|
||||
// Set focus to topmost layer of menus that mouse is currently over
|
||||
for (uint8 i = 0; i < _menuFocus.size(); i++) {
|
||||
switch (_menuFocus[i]) {
|
||||
case kFocusItems:
|
||||
if (_itemsArea.contains(pos)) {
|
||||
setFocus(kFocusItems);
|
||||
i = _menuFocus.size() + 1;
|
||||
}
|
||||
break;
|
||||
case kFocusMagic:
|
||||
if (_magicArea.contains(pos)) {
|
||||
setFocus(kFocusMagic);
|
||||
i = _menuFocus.size() + 1;
|
||||
}
|
||||
break;
|
||||
case kFocusMain:
|
||||
if (_mainArea.contains(pos)) {
|
||||
setFocus(kFocusMain);
|
||||
i = _menuFocus.size() + 1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
setFocus(kFocusNone);
|
||||
break;
|
||||
}
|
||||
}
|
||||
_itemsScroller.setActive(_menuFocus.front() == kFocusItems);
|
||||
_magicScroller.setActive(_menuFocus.front() == kFocusMagic);
|
||||
if (_menuFocus.front() != kFocusNone) {
|
||||
switch (_menuFocus.front()) {
|
||||
case kFocusItems:
|
||||
if (_enableFlags.get(kItemsMenu)) {
|
||||
int itemCount = _engine->getScriptManager()->getStateValue(StateKey_Inv_TotalSlots);
|
||||
if (itemCount == 0)
|
||||
itemCount = 20;
|
||||
else if (itemCount > 50)
|
||||
itemCount = 50;
|
||||
int lastItem = _mouseOnItem;
|
||||
_mouseOnItem = mouseOverItem(pos, itemCount);
|
||||
if (lastItem != _mouseOnItem)
|
||||
if (_engine->getScriptManager()->getStateValue(StateKey_Inv_StartSlot + _mouseOnItem) || _engine->getScriptManager()->getStateValue(StateKey_Inv_StartSlot + lastItem))
|
||||
_redraw = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case kFocusMagic:
|
||||
if (_enableFlags.get(kMagicMenu)) {
|
||||
int lastItem = _mouseOnItem;
|
||||
_mouseOnItem = mouseOverMagic(pos);
|
||||
|
||||
if (lastItem != _mouseOnItem)
|
||||
if (_engine->getScriptManager()->getStateValue(StateKey_Spell_1 + _mouseOnItem) || _engine->getScriptManager()->getStateValue(StateKey_Spell_1 + lastItem))
|
||||
_redraw = true;
|
||||
|
||||
}
|
||||
break;
|
||||
case kFocusMain:
|
||||
break;
|
||||
}
|
||||
}
|
||||
MenuManager::onMouseMove(pos);
|
||||
}
|
||||
|
||||
int MenuZGI::mouseOverItem(const Common::Point &pos, int itemCount) {
|
||||
int itemWidth = (_wSideMenu - 28) / itemCount;
|
||||
Common::Rect itemHotspot = Common::Rect(28, _hSideMenu);
|
||||
itemHotspot.moveTo(_itemsOrigin + _itemsScroller._pos);
|
||||
for (int i = 0; i < itemCount; i++) {
|
||||
if (itemHotspot.contains(pos))
|
||||
return i;
|
||||
itemHotspot.translate(itemWidth, 0);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int MenuZGI::mouseOverMagic(const Common::Point &pos) {
|
||||
Common::Rect magicHotspot(28, _hSideMenu);
|
||||
magicHotspot.moveTo(_magicOrigin + _magicScroller._pos);
|
||||
magicHotspot.translate(28, 0); // Offset from end of menu
|
||||
for (int i = 0; i < 12; i++) {
|
||||
if (magicHotspot.contains(pos))
|
||||
return i;
|
||||
magicHotspot.translate(_magicWidth, 0);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void MenuZGI::process(uint32 deltatime) {
|
||||
if (_itemsScroller.update(deltatime)) {
|
||||
_itemsArea.moveTo(_itemsOrigin + _itemsScroller._pos);
|
||||
_redraw = true;
|
||||
}
|
||||
if (_magicScroller.update(deltatime)) {
|
||||
_magicArea.moveTo(_magicOrigin + _magicScroller._pos);
|
||||
_redraw = true;
|
||||
}
|
||||
MenuManager::process(deltatime);
|
||||
}
|
||||
|
||||
void MenuZGI::redrawAll() {
|
||||
if (MenuManager::inMenu())
|
||||
for (int8 i = _menuFocus.size() - 1; i >= 0; i--)
|
||||
switch (_menuFocus[i]) {
|
||||
case kFocusItems:
|
||||
if (_enableFlags.get(kItemsMenu)) {
|
||||
redrawItems();
|
||||
}
|
||||
break;
|
||||
case kFocusMagic:
|
||||
if (_enableFlags.get(kMagicMenu)) {
|
||||
redrawMagic();
|
||||
}
|
||||
break;
|
||||
case kFocusMain:
|
||||
redrawMain();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MenuZGI::redrawMagic() {
|
||||
const int16 yOrigin = _menuArea.width();
|
||||
_engine->getRenderManager()->blitSurfaceToMenu(_menuBack[kFocusMagic], yOrigin + _magicScroller._pos.x, 0, 0);
|
||||
for (int i = 0; i < 12; i++) {
|
||||
bool inrect = false;
|
||||
if (_mouseOnItem == i)
|
||||
inrect = true;
|
||||
uint curItemId = _engine->getScriptManager()->getStateValue(StateKey_Spell_1 + i);
|
||||
if (curItemId) {
|
||||
if (_engine->getScriptManager()->getStateValue(StateKey_Reversed_Spellbooc) == 1)
|
||||
curItemId = 0xEE + i;
|
||||
else
|
||||
curItemId = 0xE0 + i;
|
||||
}
|
||||
if (curItemId != 0) {
|
||||
if (_itemId[i] != curItemId) {
|
||||
char buf[16];
|
||||
Common::sprintf_s(buf, "gmzwu%2.2x1.tga", curItemId);
|
||||
_magic[i][0] = _engine->getRenderManager()->loadImage(buf, false);
|
||||
Common::sprintf_s(buf, "gmzxu%2.2x1.tga", curItemId);
|
||||
_magic[i][1] = _engine->getRenderManager()->loadImage(buf, false);
|
||||
_magicId[i] = curItemId;
|
||||
}
|
||||
if (inrect)
|
||||
_engine->getRenderManager()->blitSurfaceToMenu(*_magic[i][1], yOrigin + _magicScroller._pos.x + 28 + _magicWidth * i, 0, 0);
|
||||
else
|
||||
_engine->getRenderManager()->blitSurfaceToMenu(*_magic[i][0], yOrigin + _magicScroller._pos.x + 28 + _magicWidth * i, 0, 0);
|
||||
} else {
|
||||
if (_magic[i][0]) {
|
||||
_magic[i][0]->free();
|
||||
delete _magic[i][0];
|
||||
_magic[i][0] = NULL;
|
||||
}
|
||||
if (_magic[i][1]) {
|
||||
_magic[i][1]->free();
|
||||
delete _magic[i][1];
|
||||
_magic[i][1] = NULL;
|
||||
}
|
||||
_magicId[i] = 0;
|
||||
}
|
||||
}
|
||||
_clean = false;
|
||||
}
|
||||
|
||||
void MenuZGI::redrawItems() {
|
||||
_engine->getRenderManager()->blitSurfaceToMenu(_menuBack[kFocusItems], _itemsScroller._pos.x, 0, 0);
|
||||
int itemCount = _engine->getScriptManager()->getStateValue(StateKey_Inv_TotalSlots);
|
||||
if (itemCount == 0)
|
||||
itemCount = 20;
|
||||
else if (itemCount > 50)
|
||||
itemCount = 50;
|
||||
int itemWidth = (_wSideMenu - 28) / itemCount;
|
||||
|
||||
for (int i = 0; i < itemCount; i++) {
|
||||
bool inrect = false;
|
||||
if (_mouseOnItem == i)
|
||||
inrect = true;
|
||||
uint curItemId = _engine->getScriptManager()->getStateValue(StateKey_Inv_StartSlot + i);
|
||||
|
||||
if (curItemId != 0) {
|
||||
if (_itemId[i] != curItemId) {
|
||||
char buf[16];
|
||||
Common::sprintf_s(buf, "gmzwu%2.2x1.tga", curItemId);
|
||||
_items[i][0] = _engine->getRenderManager()->loadImage(buf, false);
|
||||
Common::sprintf_s(buf, "gmzxu%2.2x1.tga", curItemId);
|
||||
_items[i][1] = _engine->getRenderManager()->loadImage(buf, false);
|
||||
_itemId[i] = curItemId;
|
||||
}
|
||||
if (inrect)
|
||||
_engine->getRenderManager()->blitSurfaceToMenu(*_items[i][1], _itemsScroller._pos.x + itemWidth * i, 0, 0);
|
||||
else
|
||||
_engine->getRenderManager()->blitSurfaceToMenu(*_items[i][0], _itemsScroller._pos.x + itemWidth * i, 0, 0);
|
||||
} else {
|
||||
if (_items[i][0]) {
|
||||
_items[i][0]->free();
|
||||
delete _items[i][0];
|
||||
_items[i][0] = NULL;
|
||||
}
|
||||
if (_items[i][1]) {
|
||||
_items[i][1]->free();
|
||||
delete _items[i][1];
|
||||
_items[i][1] = NULL;
|
||||
}
|
||||
_itemId[i] = 0;
|
||||
}
|
||||
}
|
||||
_clean = false;
|
||||
}
|
||||
|
||||
MenuNemesis::MenuNemesis(ZVision *engine, const Common::Rect menuArea) :
|
||||
MenuManager(engine, menuArea, nemesisParams) {
|
||||
|
||||
// Buffer menu background image
|
||||
_engine->getRenderManager()->readImageToSurface("bar.tga", _mainBack, false);
|
||||
|
||||
char buf[24];
|
||||
for (int i = 0; i < 4; i++)
|
||||
// Buffer menu buttons
|
||||
for (int j = 0; j < 6; j++) {
|
||||
Common::sprintf_s(buf, "butfrm%d%d.tga", i + 1, j);
|
||||
_engine->getRenderManager()->readImageToSurface(buf, _mainButtons[i][j], false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
MenuNemesis::~MenuNemesis() {
|
||||
for (int i = 0; i < 4; i++)
|
||||
for (int j = 0; j < 6; j++)
|
||||
_mainButtons[i][j].free();
|
||||
|
||||
}
|
||||
|
||||
bool MenuNemesis::inMenu(const Common::Point &pos) const {
|
||||
return _menuTriggerArea.contains(pos) || (_menuFocus.front() != kFocusNone);
|
||||
}
|
||||
|
||||
void MenuNemesis::onMouseMove(const Common::Point &pos) {
|
||||
// Trigger main menu scrolldown to get mouse over main trigger area
|
||||
// Set focus to topmost layer of menus that mouse is currently over
|
||||
if (_mainArea.contains(pos) || _menuTriggerArea.contains(pos))
|
||||
setFocus(kFocusMain);
|
||||
else
|
||||
setFocus(kFocusNone);
|
||||
MenuManager::onMouseMove(pos);
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
192
engines/zvision/scripting/menu.h
Normal file
192
engines/zvision/scripting/menu.h
Normal file
@@ -0,0 +1,192 @@
|
||||
/* 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 ZVISION_MENU_H
|
||||
#define ZVISION_MENU_H
|
||||
|
||||
#include "common/array.h"
|
||||
#include "common/bitarray.h"
|
||||
#include "common/rect.h"
|
||||
#include "graphics/surface.h"
|
||||
#include "zvision/zvision.h"
|
||||
#include "zvision/common/focus_list.h"
|
||||
#include "zvision/common/scroller.h"
|
||||
#include "zvision/scripting/script_manager.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
enum {
|
||||
kMainMenuSave = 0,
|
||||
kMainMenuLoad = 1,
|
||||
kMainMenuPrefs = 2,
|
||||
kMainMenuExit = 3,
|
||||
kItemsMenu = 4,
|
||||
kMagicMenu = 5
|
||||
};
|
||||
|
||||
struct MenuParams {
|
||||
int16 wxButs[4][2]; // Widths & X positions of main menu buttons; {Save, Restore, Prefs, Quit}
|
||||
int16 wMain; // Width of main menu background
|
||||
int8 idleFrame; // Frame to display of unselected main menu button
|
||||
int8 activeFrame; // Frame to display of selected main menu button when mouse is down
|
||||
int8 clickedFrame; // Frame to display of selected main menu button when mouse is down
|
||||
Common::Point activePos; // Fully scrolled main menu position, relative to origin of menu area
|
||||
Common::Point idlePos; // Fully retracted main menu position, relative to origin of menu area
|
||||
int16 period; // Duration of main menu scrolldown
|
||||
int16 triggerHeight; // Height of menu trigger area when inactive
|
||||
int16 buttonPeriod; // Duration of main menu button animation
|
||||
};
|
||||
|
||||
// NB - menu area is same width as working window.
|
||||
|
||||
static const MenuParams nemesisParams {
|
||||
{ {120, -1}, {144, 120}, {128, 264}, {120, 392} },
|
||||
512,
|
||||
-1,
|
||||
4,
|
||||
5,
|
||||
Common::Point(0, 0),
|
||||
Common::Point(0, -32),
|
||||
500,
|
||||
2,
|
||||
500
|
||||
};
|
||||
|
||||
static const MenuParams zgiParams {
|
||||
{ {135, 50}, {135, 185}, {135, 320}, {135, 455} },
|
||||
580,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
Common::Point(30, 0),
|
||||
Common::Point(30, -20),
|
||||
250,
|
||||
32,
|
||||
0
|
||||
};
|
||||
|
||||
class MenuManager {
|
||||
public:
|
||||
MenuManager(ZVision *engine, const Common::Rect menuArea, const MenuParams params);
|
||||
virtual ~MenuManager();
|
||||
virtual void onMouseMove(const Common::Point &pos);
|
||||
virtual void onMouseDown(const Common::Point &pos);
|
||||
virtual void onMouseUp(const Common::Point &pos);
|
||||
virtual void process(uint32 deltaTimeInMillis);
|
||||
bool inMenu() const {
|
||||
return _prevInMenu;
|
||||
}
|
||||
virtual bool inMenu(const Common::Point &pos) const {
|
||||
return false;
|
||||
} // For widescreen mod; used to suspend panning, tilting & scripting triggers when the mouse is within the working window but also in the menu.
|
||||
|
||||
void mainMouseDown(const Common::Point &pos); // Show clicked graphic under selected button
|
||||
bool mainMouseMove(const Common::Point &pos); // return true if selected button has changed
|
||||
|
||||
void setEnable(uint16 flags);
|
||||
uint16 getEnable() const {
|
||||
return _menuBarFlag;
|
||||
}
|
||||
bool getEnable(uint8 flag) const {
|
||||
return _enableFlags.get(flag);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void redrawAll() {}
|
||||
void redrawMain();
|
||||
int mouseOverMain(const Common::Point &pos);
|
||||
void setFocus(int8 currentFocus);
|
||||
|
||||
bool _prevInMenu = false;
|
||||
bool _redraw = true;
|
||||
int _mouseOnItem = -1;
|
||||
static const uint8 _hMainMenu = 32;
|
||||
int8 _mainClicked = -1;
|
||||
|
||||
ZVision *_engine;
|
||||
const MenuParams _params;
|
||||
uint16 _menuBarFlag;
|
||||
const Common::Rect _menuArea;
|
||||
const Common::Point _menuOrigin;
|
||||
const Common::Rect _menuTriggerArea;
|
||||
Graphics::Surface _mainBack;
|
||||
Graphics::Surface _mainButtons[4][6];
|
||||
Common::BitArray _enableFlags;
|
||||
Common::Rect _mainArea;
|
||||
Common::Rect _menuHotspots[4];
|
||||
int8 _mainFrames[4]; // Frame to display of each main menu button; first row is currently displayed, 2nd row is backbuffer for idle animations
|
||||
Scroller _mainScroller;
|
||||
FocusList<int8> _menuFocus; // Order in which menus have most recently had focus; determines current mouse focus & order in which to redraw them.
|
||||
bool _clean = false; // Whether or not to blank
|
||||
LinearScroller *_buttonAnim[4];
|
||||
};
|
||||
|
||||
class MenuZGI: public MenuManager {
|
||||
public:
|
||||
MenuZGI(ZVision *engine, Common::Rect menuArea);
|
||||
~MenuZGI() override;
|
||||
void onMouseMove(const Common::Point &pos) override; // NB Pos is in screen coordinates
|
||||
void onMouseUp(const Common::Point &pos) override;
|
||||
void process(uint32 deltaTimeInMillis) override;
|
||||
bool inMenu(const Common::Point &pos) const override;
|
||||
private:
|
||||
void redrawAll() override;
|
||||
Graphics::Surface _menuBack[3];
|
||||
Graphics::Surface *_items[50][2];
|
||||
uint _itemId[50];
|
||||
|
||||
Graphics::Surface *_magic[12][2];
|
||||
uint _magicId[12];
|
||||
// void redrawMain(bool hasFocus) override;
|
||||
void redrawItems();
|
||||
void redrawMagic();
|
||||
int mouseOverItem(const Common::Point &pos, int itemCount);
|
||||
int mouseOverMagic(const Common::Point &pos);
|
||||
|
||||
static const uint16 _hSideMenu = 32;
|
||||
static const uint16 _wSideMenu = 600;
|
||||
static const uint16 _wSideMenuTab = 20;
|
||||
static const int16 _magicWidth = 47;
|
||||
|
||||
const int16 _sideMenuPeriod = 300;
|
||||
|
||||
Scroller _itemsScroller, _magicScroller;
|
||||
|
||||
const Common::Point _magicOrigin;
|
||||
const Common::Point _itemsOrigin;
|
||||
|
||||
Common::Rect _magicArea;
|
||||
Common::Rect _itemsArea;
|
||||
};
|
||||
|
||||
class MenuNemesis: public MenuManager {
|
||||
public:
|
||||
MenuNemesis(ZVision *engine, Common::Rect menuArea);
|
||||
~MenuNemesis() override;
|
||||
void onMouseMove(const Common::Point &pos) override;
|
||||
bool inMenu(const Common::Point &pos) const override;
|
||||
private:
|
||||
void redrawAll() override;
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
77
engines/zvision/scripting/puzzle.h
Normal file
77
engines/zvision/scripting/puzzle.h
Normal file
@@ -0,0 +1,77 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ZVISION_PUZZLE_H
|
||||
#define ZVISION_PUZZLE_H
|
||||
|
||||
#include "common/list.h"
|
||||
#include "common/ptr.h"
|
||||
#include "zvision/scripting/actions.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
struct Puzzle {
|
||||
Puzzle() : key(0), addedBySetState(false) {}
|
||||
|
||||
~Puzzle() {
|
||||
for (auto & action : resultActions)
|
||||
delete action;
|
||||
}
|
||||
|
||||
/** How criteria should be decided */
|
||||
enum CriteriaOperator {
|
||||
EQUAL_TO,
|
||||
NOT_EQUAL_TO,
|
||||
GREATER_THAN,
|
||||
LESS_THAN
|
||||
};
|
||||
|
||||
/** Criteria for a Puzzle result to be fired */
|
||||
struct CriteriaEntry {
|
||||
/** The key of a global state */
|
||||
uint32 key;
|
||||
/**
|
||||
* What we're comparing the value of the global state against
|
||||
* This can either be a pure value or it can be the key of another global state
|
||||
*/
|
||||
uint32 argument;
|
||||
/** How to do the comparison */
|
||||
CriteriaOperator criteriaOperator;
|
||||
/** Whether 'argument' is the key of a global state (true) or a pure value (false) */
|
||||
bool argumentIsAKey;
|
||||
};
|
||||
|
||||
enum StateFlags {
|
||||
ONCE_PER_INST = 0x01,
|
||||
DISABLED = 0x02,
|
||||
DO_ME_NOW = 0x04
|
||||
};
|
||||
|
||||
uint32 key;
|
||||
Common::List<Common::List <CriteriaEntry> > criteriaList;
|
||||
// This has to be list of pointers because ResultAction is abstract
|
||||
Common::List<ResultAction *> resultActions;
|
||||
bool addedBySetState;
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
566
engines/zvision/scripting/scr_file_handling.cpp
Normal file
566
engines/zvision/scripting/scr_file_handling.cpp
Normal file
@@ -0,0 +1,566 @@
|
||||
/* 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/scummsys.h"
|
||||
#include "common/str.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/tokenizer.h"
|
||||
#include "zvision/detection.h"
|
||||
#include "zvision/zvision.h"
|
||||
#include "zvision/scripting/actions.h"
|
||||
#include "zvision/scripting/puzzle.h"
|
||||
#include "zvision/scripting/script_manager.h"
|
||||
#include "zvision/scripting/controls/fist_control.h"
|
||||
#include "zvision/scripting/controls/hotmov_control.h"
|
||||
#include "zvision/scripting/controls/input_control.h"
|
||||
#include "zvision/scripting/controls/lever_control.h"
|
||||
#include "zvision/scripting/controls/paint_control.h"
|
||||
#include "zvision/scripting/controls/push_toggle_control.h"
|
||||
#include "zvision/scripting/controls/safe_control.h"
|
||||
#include "zvision/scripting/controls/save_control.h"
|
||||
#include "zvision/scripting/controls/slot_control.h"
|
||||
#include "zvision/scripting/controls/titler_control.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
void ScriptManager::parseScrFile(const Common::Path &fileName, ScriptScope &scope) {
|
||||
auto parse = [&](Common::File & file) {
|
||||
while (!file.eos()) {
|
||||
Common::String line = file.readLine();
|
||||
if (file.err())
|
||||
error("Error parsing scr file: %s", fileName.toString().c_str());
|
||||
trimCommentsAndWhiteSpace(&line);
|
||||
if (line.empty())
|
||||
continue;
|
||||
if (line.matchString("puzzle:*", true)) {
|
||||
Puzzle *puzzle = new Puzzle();
|
||||
if (sscanf(line.c_str(), "puzzle:%u", &(puzzle->key)) != 1) {
|
||||
debugC(kDebugScript, "Malformed puzzle string: %s", line.c_str());
|
||||
continue;
|
||||
}
|
||||
if (getStateFlag(puzzle->key) & Puzzle::ONCE_PER_INST)
|
||||
setStateValue(puzzle->key, 0);
|
||||
parsePuzzle(puzzle, file);
|
||||
scope.puzzles.push_back(puzzle);
|
||||
} else if (line.matchString("control:*", true)) {
|
||||
Control *ctrl = parseControl(line, file);
|
||||
if (ctrl)
|
||||
scope.controls.push_back(ctrl);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Common::File mainFile;
|
||||
Common::File auxFile;
|
||||
Common::String auxFileName = fileName.toString();
|
||||
replace(auxFileName, Common::String(".scr"), Common::String(".aux"));
|
||||
debugC(1, kDebugFile, "Auxiliary filename %s", auxFileName.c_str());
|
||||
Common::Path auxFilePath(auxFileName);
|
||||
debugC(1, kDebugFile, "Auxiliary path %s", auxFilePath.toString().c_str());
|
||||
|
||||
if (!mainFile.open(fileName))
|
||||
error("Script file not found: %s", fileName.toString().c_str());
|
||||
else {
|
||||
debugC(1, kDebugScript, "Parsing primary script file %s", fileName.toString().c_str());
|
||||
parse(mainFile);
|
||||
// TODO - add config option to disable/enable auxiliary scripting
|
||||
if (auxFile.exists(auxFilePath)) {
|
||||
debugC(1, kDebugFile, "Auxiliary script file found");
|
||||
if (auxFile.open(auxFilePath)) {
|
||||
debugC(1, kDebugScript, "Parsing auxiliary script file %s", auxFilePath.toString().c_str());
|
||||
parse(auxFile);
|
||||
} else
|
||||
debugC(1, kDebugFile, "Unable to open auxiliary script file %s", auxFilePath.toString().c_str());
|
||||
}
|
||||
}
|
||||
scope.procCount = 0;
|
||||
}
|
||||
|
||||
void ScriptManager::parsePuzzle(Puzzle *puzzle, Common::SeekableReadStream &stream) {
|
||||
Common::String line = stream.readLine();
|
||||
trimCommentsAndWhiteSpace(&line);
|
||||
|
||||
while (!stream.eos() && !line.contains('}')) {
|
||||
if (line.matchString("criteria {", true)) {
|
||||
parseCriteria(stream, puzzle->criteriaList, puzzle->key);
|
||||
} else if (line.matchString("results {", true)) {
|
||||
parseResults(stream, puzzle->resultActions, puzzle->key);
|
||||
// WORKAROUNDS:
|
||||
switch (_engine->getGameId()) {
|
||||
case GID_NEMESIS:
|
||||
// WORKAROUND for a script bug in Zork Nemesis, room ve5e (tuning
|
||||
// fork box closeup). If the player leaves the screen while the
|
||||
// box is open, puzzle 19398 shows the animation where the box
|
||||
// closes, but the box state (state variable 19397) is not updated.
|
||||
// We insert the missing assignment for the box state here.
|
||||
// Fixes bug #6803.
|
||||
if (puzzle->key == 19398)
|
||||
puzzle->resultActions.push_back(new ActionAssign(_engine, 11, "19397, 0"));
|
||||
break;
|
||||
case GID_GRANDINQUISITOR:
|
||||
switch (puzzle->key) {
|
||||
case 10836:
|
||||
// WORKAROUND for bug #10604. If the player is looking at the
|
||||
// cigar box when Antharia Jack returns to examine the lamp,
|
||||
// pp1f_video_flag remains 1. Later, when the player returns
|
||||
// to pick up the lantern, the game will try to play the
|
||||
// cutscene again, but since that script has already been
|
||||
// run the player gets stuck in a dark room instead. We have
|
||||
// to add the assignment action to the front, or it won't be
|
||||
// reached because changing the location terminates the script.
|
||||
//
|
||||
// Fixing it this way only keeps the bug from happening. It
|
||||
// will not repair old savegames.
|
||||
//
|
||||
// Note that the bug only affects the DVD version. The CD
|
||||
// version doesn't have a separate room for the cutscene.
|
||||
if (_engine->getFeatures() & ADGF_DVD)
|
||||
puzzle->resultActions.push_front(new ActionAssign(_engine, 11, "10803, 0"));
|
||||
break;
|
||||
// WORKAROUND for a script bug in Zork: Grand Inquisitor, room dc10.
|
||||
// Background heartbeat sound effect never terminates upon location change.
|
||||
case 2341:
|
||||
case 2344:
|
||||
case 17545:
|
||||
puzzle->resultActions.push_front(new ActionKill(_engine, 11, "02310"));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if (line.matchString("flags {", true)) {
|
||||
setStateFlag(puzzle->key, parseFlags(stream));
|
||||
}
|
||||
|
||||
line = stream.readLine();
|
||||
trimCommentsAndWhiteSpace(&line);
|
||||
}
|
||||
|
||||
puzzle->addedBySetState = false;
|
||||
}
|
||||
|
||||
bool ScriptManager::parseCriteria(Common::SeekableReadStream &stream, Common::List<Common::List<Puzzle::CriteriaEntry> > &criteriaList, uint32 key) const {
|
||||
// Loop until we find the closing brace
|
||||
Common::String line = stream.readLine();
|
||||
trimCommentsAndWhiteSpace(&line);
|
||||
|
||||
// Skip any commented out criteria. If all the criteria are commented out,
|
||||
// we might end up with an invalid criteria list (bug #6776).
|
||||
while (line.empty()) {
|
||||
line = stream.readLine();
|
||||
trimCommentsAndWhiteSpace(&line);
|
||||
}
|
||||
|
||||
// Criteria can be empty
|
||||
if (line.contains('}')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create a new List to hold the CriteriaEntries
|
||||
criteriaList.push_back(Common::List<Puzzle::CriteriaEntry>());
|
||||
|
||||
// WORKAROUNDS
|
||||
switch (_engine->getGameId()) {
|
||||
case GID_NEMESIS:
|
||||
// WORKAROUND for a script bug in Zork: Nemesis, room td9e (fist puzzle)
|
||||
// Since we patch the script that triggers when manipulating the left fist
|
||||
// (below), we add an additional check for the left fist sound, so that it
|
||||
// doesn't get killed immediately when the left fist animation starts.
|
||||
// Together with the workaround below, it fixes bug #6783.
|
||||
if (key == 3594) {
|
||||
Puzzle::CriteriaEntry entry;
|
||||
entry.key = 567;
|
||||
entry.criteriaOperator = Puzzle::NOT_EQUAL_TO;
|
||||
entry.argumentIsAKey = false;
|
||||
entry.argument = 1;
|
||||
|
||||
criteriaList.back().push_back(entry);
|
||||
}
|
||||
break;
|
||||
case GID_GRANDINQUISITOR:
|
||||
// WORKAROUND for a script bug in Zork: Grand Inquisitor, room me2j
|
||||
// (Closing the Time Tunnels). When the time tunnel is open the game
|
||||
// shows a close-up of only the tunnel, instead of showing the entire
|
||||
// booth. However, the scripts that draw the lever in its correct
|
||||
// state do not test this flag, causing it to be drawn when it should
|
||||
// not be. This fixes bug #6770.
|
||||
if (key == 9536) {
|
||||
Puzzle::CriteriaEntry entry;
|
||||
entry.key = 9404; // me2j_time_tunnel_open
|
||||
entry.criteriaOperator = Puzzle::EQUAL_TO;
|
||||
entry.argumentIsAKey = false;
|
||||
entry.argument = 0;
|
||||
|
||||
criteriaList.back().push_back(entry);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
while (!stream.eos() && !line.contains('}')) {
|
||||
Puzzle::CriteriaEntry entry;
|
||||
|
||||
// Split the string into tokens using ' ' as a delimiter
|
||||
Common::StringTokenizer tokenizer(line);
|
||||
Common::String token;
|
||||
|
||||
// Parse the id out of the first token
|
||||
token = tokenizer.nextToken();
|
||||
if (sscanf(token.c_str(), "[%u]", &(entry.key)) != 1) {
|
||||
debugC(kDebugScript, "Malformed criteria ID string %s", token.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// WORKAROUND for a script bug in Zork: Nemesis, room td9e (fist puzzle)
|
||||
// Check for the state of animation 567 (left fist) when manipulating
|
||||
// the fingers of the left fist (puzzle slots 3582, 3583).
|
||||
// Together with the workaround above, it fixes bug #6783.
|
||||
if (_engine->getGameId() == GID_NEMESIS && (key == 3582 || key == 3583) && entry.key == 568)
|
||||
entry.key = 567;
|
||||
|
||||
// Parse the operator out of the second token
|
||||
token = tokenizer.nextToken();
|
||||
if (token.c_str()[0] == '=')
|
||||
entry.criteriaOperator = Puzzle::EQUAL_TO;
|
||||
else if (token.c_str()[0] == '!')
|
||||
entry.criteriaOperator = Puzzle::NOT_EQUAL_TO;
|
||||
else if (token.c_str()[0] == '>')
|
||||
entry.criteriaOperator = Puzzle::GREATER_THAN;
|
||||
else if (token.c_str()[0] == '<')
|
||||
entry.criteriaOperator = Puzzle::LESS_THAN;
|
||||
|
||||
// There are supposed to be three tokens, but there is no
|
||||
// guarantee that there will be a space between the second and
|
||||
// the third one (bug #6774)
|
||||
if (token.size() == 1) {
|
||||
token = tokenizer.nextToken();
|
||||
} else {
|
||||
token.deleteChar(0);
|
||||
}
|
||||
|
||||
// First determine if the last token is an id or a value
|
||||
// Then parse it into 'argument'
|
||||
if (token.contains('[')) {
|
||||
if (sscanf(token.c_str(), "[%u]", &(entry.argument)) != 1) {
|
||||
debugC(kDebugScript, "Malformed token string %s", token.c_str());
|
||||
return false;
|
||||
}
|
||||
entry.argumentIsAKey = true;
|
||||
} else {
|
||||
if (sscanf(token.c_str(), "%u", &(entry.argument)) != 1) {
|
||||
debugC(kDebugScript, "Malformed token string %s", token.c_str());
|
||||
return false;
|
||||
}
|
||||
entry.argumentIsAKey = false;
|
||||
}
|
||||
|
||||
// WORKAROUND for a script bug in Zork: Grand Inquisitor. If the
|
||||
// fire timer is killed (e.g. by the inventory screen) with less
|
||||
// than 10 units left, it will get stuck and never time out. We
|
||||
// work around that by changing the condition from "greater than
|
||||
// 10" to "greater than 0 but not 2 (the magic time-out value)".
|
||||
//
|
||||
// I have a sneaking suspicion that there may be other timer
|
||||
// glitches like this, but this one makes the game unplayable
|
||||
// and is easy to trigger.
|
||||
if (_engine->getGameId() == GID_GRANDINQUISITOR && key == 17162) {
|
||||
Puzzle::CriteriaEntry entry0;
|
||||
entry0.key = 17161; // pe_fire
|
||||
entry0.criteriaOperator = Puzzle::GREATER_THAN;
|
||||
entry0.argumentIsAKey = false;
|
||||
entry0.argument = 0;
|
||||
|
||||
criteriaList.back().push_back(entry0);
|
||||
|
||||
entry.criteriaOperator = Puzzle::NOT_EQUAL_TO;
|
||||
entry.argument = 2;
|
||||
}
|
||||
|
||||
criteriaList.back().push_back(entry);
|
||||
|
||||
line = stream.readLine();
|
||||
trimCommentsAndWhiteSpace(&line);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ScriptManager::parseResults(Common::SeekableReadStream &stream, Common::List<ResultAction *> &actionList, uint32 key) const {
|
||||
// Loop until we find the closing brace
|
||||
Common::String line = stream.readLine();
|
||||
trimCommentsAndWhiteSpace(&line);
|
||||
line.toLowercase();
|
||||
|
||||
// TODO: Re-order the if-then statements in order of highest occurrence
|
||||
// While within results block
|
||||
while (!stream.eos() && !line.contains('}')) {
|
||||
// Skip empty lines
|
||||
if (line.empty()) {
|
||||
line = stream.readLine();
|
||||
trimCommentsAndWhiteSpace(&line);
|
||||
line.toLowercase();
|
||||
continue;
|
||||
}
|
||||
debugC(4, kDebugScript, "Result line: %s", line.c_str());
|
||||
const char *chrs = line.c_str();
|
||||
uint pos;
|
||||
if (line.matchString("action:*", true))
|
||||
pos = 6;
|
||||
else if (line.matchString("event:*", true))
|
||||
pos = 5;
|
||||
else if (line.matchString("background:*", true))
|
||||
pos = 10;
|
||||
else
|
||||
continue;
|
||||
if (pos < line.size()) { // Stuff left
|
||||
uint startpos = pos + 1; // first character after colon
|
||||
// Scan for next colon or opening bracket
|
||||
for (pos = startpos; pos < line.size(); pos++)
|
||||
if (chrs[pos] == ':' || chrs[pos] == '(')
|
||||
break;
|
||||
|
||||
debugC(4, kDebugScript, "startpos %d, pos %d, line.size %d", startpos, pos, line.size());
|
||||
int32 slot = 11; // Non-setting default slot
|
||||
Common::String args = "";
|
||||
Common::String act(chrs + startpos, chrs + pos);
|
||||
// Extract arguments, if any
|
||||
if (pos < line.size()) {
|
||||
|
||||
startpos = pos + 1;
|
||||
|
||||
if (chrs[pos] == ':') {
|
||||
for (pos = startpos; pos < line.size(); pos++)
|
||||
if (chrs[pos] == '(')
|
||||
break;
|
||||
// Extract slotkey, if specified
|
||||
Common::String strSlot(chrs + startpos, chrs + pos);
|
||||
slot = atoi(strSlot.c_str());
|
||||
|
||||
startpos = pos + 1;
|
||||
}
|
||||
|
||||
if (pos < line.size()) {
|
||||
for (pos = startpos; pos < line.size(); pos++)
|
||||
if (chrs[pos] == ')')
|
||||
break;
|
||||
|
||||
args = Common::String(chrs + startpos, chrs + pos);
|
||||
}
|
||||
}
|
||||
debugC(4, kDebugScript, "Action string: '%s', slot %d, arguments string '%s'", act.c_str(), slot, args.c_str());
|
||||
|
||||
// Parse for the action type
|
||||
if (act.matchString("add", true)) {
|
||||
actionList.push_back(new ActionAdd(_engine, slot, args));
|
||||
} else if (act.matchString("animplay", true)) {
|
||||
actionList.push_back(new ActionPlayAnimation(_engine, slot, args));
|
||||
} else if (act.matchString("animpreload", true)) {
|
||||
actionList.push_back(new ActionPreloadAnimation(_engine, slot, args));
|
||||
} else if (act.matchString("animunload", true)) {
|
||||
// Only used by ZGI (locations cd6e, cd6k, dg2f, dg4e, dv1j)
|
||||
actionList.push_back(new ActionUnloadAnimation(_engine, slot, args));
|
||||
} else if (act.matchString("attenuate", true)) {
|
||||
actionList.push_back(new ActionAttenuate(_engine, slot, args));
|
||||
} else if (act.matchString("assign", true)) {
|
||||
if (_engine->getGameId() == GID_GRANDINQUISITOR && key == 17761) {
|
||||
// WORKAROUND for a script bug in Zork: Grand Inquisitor, room tp1e.
|
||||
// Background looping sound effect continuously restarts on every game cycle.
|
||||
// This is caused by resetting itself to zero as a result of itself.
|
||||
// Simple fix is to simply not generate this result assignment action at all.
|
||||
} else
|
||||
actionList.push_back(new ActionAssign(_engine, slot, args));
|
||||
} else if (act.matchString("change_location", true)) {
|
||||
actionList.push_back(new ActionChangeLocation(_engine, slot, args));
|
||||
} else if (act.matchString("crossfade", true)) {
|
||||
actionList.push_back(new ActionCrossfade(_engine, slot, args));
|
||||
} else if (act.matchString("cursor", true)) {
|
||||
actionList.push_back(new ActionCursor(_engine, slot, args));
|
||||
} else if (act.matchString("debug", true)) {
|
||||
// Not used. Purposely left empty
|
||||
} else if (act.matchString("delay_render", true)) {
|
||||
actionList.push_back(new ActionDelayRender(_engine, slot, args));
|
||||
} else if (act.matchString("disable_control", true)) {
|
||||
actionList.push_back(new ActionDisableControl(_engine, slot, args));
|
||||
} else if (act.matchString("disable_venus", true)) {
|
||||
// Not used. Purposely left empty
|
||||
} else if (act.matchString("display_message", true)) {
|
||||
actionList.push_back(new ActionDisplayMessage(_engine, slot, args));
|
||||
} else if (act.matchString("dissolve", true)) {
|
||||
actionList.push_back(new ActionDissolve(_engine));
|
||||
} else if (act.matchString("distort", true)) {
|
||||
// Only used by Zork: Nemesis for the "treatment" puzzle in the Sanitarium (aj30)
|
||||
actionList.push_back(new ActionDistort(_engine, slot, args));
|
||||
} else if (act.matchString("enable_control", true)) {
|
||||
actionList.push_back(new ActionEnableControl(_engine, slot, args));
|
||||
} else if (act.matchString("flush_mouse_events", true)) {
|
||||
actionList.push_back(new ActionFlushMouseEvents(_engine, slot));
|
||||
} else if (act.matchString("inventory", true)) {
|
||||
actionList.push_back(new ActionInventory(_engine, slot, args));
|
||||
} else if (act.matchString("kill", true)) {
|
||||
// Only used by ZGI
|
||||
actionList.push_back(new ActionKill(_engine, slot, args));
|
||||
} else if (act.matchString("menu_bar_enable", true)) {
|
||||
actionList.push_back(new ActionMenuBarEnable(_engine, slot, args));
|
||||
} else if (act.matchString("music", true)) {
|
||||
actionList.push_back(new ActionMusic(_engine, slot, args, false));
|
||||
} else if (act.matchString("pan_track", true)) {
|
||||
actionList.push_back(new ActionPanTrack(_engine, slot, args));
|
||||
} else if (act.matchString("playpreload", true)) {
|
||||
actionList.push_back(new ActionPlayPreloadAnimation(_engine, slot, args));
|
||||
} else if (act.matchString("preferences", true)) {
|
||||
actionList.push_back(new ActionPreferences(_engine, slot, args));
|
||||
} else if (act.matchString("quit", true)) {
|
||||
actionList.push_back(new ActionQuit(_engine, slot));
|
||||
} else if (act.matchString("random", true)) {
|
||||
actionList.push_back(new ActionRandom(_engine, slot, args));
|
||||
} else if (act.matchString("region", true)) {
|
||||
// Only used by Zork: Nemesis
|
||||
actionList.push_back(new ActionRegion(_engine, slot, args));
|
||||
} else if (act.matchString("restore_game", true)) {
|
||||
// Only used by ZGI to load the restart game slot, r.svr.
|
||||
// Used by the credits screen.
|
||||
actionList.push_back(new ActionRestoreGame(_engine, slot, args));
|
||||
} else if (act.matchString("rotate_to", true)) {
|
||||
actionList.push_back(new ActionRotateTo(_engine, slot, args));
|
||||
} else if (act.matchString("save_game", true)) {
|
||||
// Not used. Purposely left empty
|
||||
} else if (act.matchString("set_partial_screen", true)) {
|
||||
actionList.push_back(new ActionSetPartialScreen(_engine, slot, args));
|
||||
} else if (act.matchString("set_screen", true)) {
|
||||
actionList.push_back(new ActionSetScreen(_engine, slot, args));
|
||||
} else if (act.matchString("set_venus", true)) {
|
||||
// Not used. Purposely left empty
|
||||
} else if (act.matchString("stop", true)) {
|
||||
actionList.push_back(new ActionStop(_engine, slot, args));
|
||||
} else if (act.matchString("streamvideo", true)) {
|
||||
actionList.push_back(new ActionStreamVideo(_engine, slot, args));
|
||||
} else if (act.matchString("syncsound", true)) {
|
||||
actionList.push_back(new ActionSyncSound(_engine, slot, args));
|
||||
} else if (act.matchString("timer", true)) {
|
||||
actionList.push_back(new ActionTimer(_engine, slot, args));
|
||||
} else if (act.matchString("ttytext", true)) {
|
||||
actionList.push_back(new ActionTtyText(_engine, slot, args));
|
||||
} else if (act.matchString("universe_music", true)) {
|
||||
actionList.push_back(new ActionMusic(_engine, slot, args, true));
|
||||
} else if (act.matchString("copy_file", true)) {
|
||||
// Not used. Purposely left empty
|
||||
} else {
|
||||
warning("Unhandled result action type: %s", line.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
line = stream.readLine();
|
||||
trimCommentsAndWhiteSpace(&line);
|
||||
line.toLowercase();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
uint ScriptManager::parseFlags(Common::SeekableReadStream &stream) const {
|
||||
uint flags = 0;
|
||||
|
||||
// Loop until we find the closing brace
|
||||
Common::String line = stream.readLine();
|
||||
trimCommentsAndWhiteSpace(&line);
|
||||
|
||||
while (!stream.eos() && !line.contains('}')) {
|
||||
if (line.matchString("ONCE_PER_INST", true)) {
|
||||
flags |= Puzzle::ONCE_PER_INST;
|
||||
} else if (line.matchString("DO_ME_NOW", true)) {
|
||||
flags |= Puzzle::DO_ME_NOW;
|
||||
} else if (line.matchString("DISABLED", true)) {
|
||||
flags |= Puzzle::DISABLED;
|
||||
}
|
||||
|
||||
line = stream.readLine();
|
||||
trimCommentsAndWhiteSpace(&line);
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
Control *ScriptManager::parseControl(Common::String &line, Common::SeekableReadStream &stream) {
|
||||
uint32 key;
|
||||
char controlTypeBuffer[20];
|
||||
|
||||
if (sscanf(line.c_str(), "control:%u %s {", &key, controlTypeBuffer) != 2) {
|
||||
debugC(kDebugScript, "Malformed control string: %s", line.c_str());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Common::String controlType(controlTypeBuffer);
|
||||
if (controlType.equalsIgnoreCase("push_toggle")) {
|
||||
// WORKAROUND for a script bug in ZGI: There is an invalid hotspot
|
||||
// at scene em1h (bottom of tower), which points to a missing
|
||||
// script em1n. This is a hotspot at the right of the screen.
|
||||
// In the original, this hotspot doesn't lead anywhere anyway,
|
||||
// so instead of moving to a missing scene, we just remove the
|
||||
// hotspot altogether. The alternative would be to just process
|
||||
// and ignore invalid scenes, but I don't think it's worth the
|
||||
// effort. Fixes bug #6780.
|
||||
if (_engine->getGameId() == GID_GRANDINQUISITOR && key == 5653)
|
||||
return NULL;
|
||||
return new PushToggleControl(_engine, key, stream);
|
||||
} else if (controlType.equalsIgnoreCase("flat")) {
|
||||
Control::parseFlatControl(_engine);
|
||||
return NULL;
|
||||
} else if (controlType.equalsIgnoreCase("pana")) {
|
||||
Control::parsePanoramaControl(_engine, stream);
|
||||
return NULL;
|
||||
} else if (controlType.equalsIgnoreCase("tilt")) {
|
||||
// Only used in Zork Nemesis, handles tilt controls (ZGI doesn't have a tilt view)
|
||||
Control::parseTiltControl(_engine, stream);
|
||||
return NULL;
|
||||
} else if (controlType.equalsIgnoreCase("slot")) {
|
||||
return new SlotControl(_engine, key, stream);
|
||||
} else if (controlType.equalsIgnoreCase("input")) {
|
||||
return new InputControl(_engine, key, stream);
|
||||
} else if (controlType.equalsIgnoreCase("save")) {
|
||||
return new SaveControl(_engine, key, stream);
|
||||
} else if (controlType.equalsIgnoreCase("lever")) {
|
||||
// Only used in Zork Nemesis, handles draggable levers (te2e, tm7e, tp2e, tt2e, tz2e)
|
||||
return new LeverControl(_engine, key, stream);
|
||||
} else if (controlType.equalsIgnoreCase("safe")) {
|
||||
// Only used in Zork Nemesis, handles the safe in the Asylum (ac4g)
|
||||
return new SafeControl(_engine, key, stream);
|
||||
} else if (controlType.equalsIgnoreCase("hotmovie")) {
|
||||
// Only used in Zork Nemesis, handles movies where the player needs to click on something (mj7g, vw3g)
|
||||
return new HotMovControl(_engine, key, stream);
|
||||
} else if (controlType.equalsIgnoreCase("fist")) {
|
||||
// Only used in Zork Nemesis, handles the door lock puzzle with the skeletal fingers (td9e)
|
||||
return new FistControl(_engine, key, stream);
|
||||
} else if (controlType.equalsIgnoreCase("paint")) {
|
||||
// Only used in Zork Nemesis, handles the painting puzzle screen in Lucien's room in Irondune (ch4g)
|
||||
return new PaintControl(_engine, key, stream);
|
||||
} else if (controlType.equalsIgnoreCase("titler")) {
|
||||
// Only used in Zork Nemesis, handles the death screen with the Restore/Exit buttons (cjde)
|
||||
return new TitlerControl(_engine, key, stream);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
912
engines/zvision/scripting/script_manager.cpp
Normal file
912
engines/zvision/scripting/script_manager.cpp
Normal file
@@ -0,0 +1,912 @@
|
||||
/* 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/algorithm.h"
|
||||
#include "common/config-manager.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/hashmap.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/stream.h"
|
||||
#include "zvision/zvision.h"
|
||||
#include "zvision/file/save_manager.h"
|
||||
#include "zvision/graphics/render_manager.h"
|
||||
#include "zvision/graphics/cursors/cursor_manager.h"
|
||||
#include "zvision/scripting/actions.h"
|
||||
#include "zvision/scripting/menu.h"
|
||||
#include "zvision/scripting/script_manager.h"
|
||||
#include "zvision/scripting/effects/timer_effect.h"
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
ScriptManager::ScriptManager(ZVision *engine)
|
||||
: _engine(engine),
|
||||
_currentlyFocusedControl(0),
|
||||
_activeControls(NULL) {
|
||||
}
|
||||
|
||||
ScriptManager::~ScriptManager() {
|
||||
cleanScriptScope(_universe);
|
||||
cleanScriptScope(_world);
|
||||
cleanScriptScope(_room);
|
||||
cleanScriptScope(_nodeview);
|
||||
_controlEvents.clear();
|
||||
}
|
||||
|
||||
void ScriptManager::initialize(bool restarted) {
|
||||
if (restarted) {
|
||||
_globalState.clear();
|
||||
_globalStateFlags.clear();
|
||||
}
|
||||
cleanScriptScope(_universe);
|
||||
cleanScriptScope(_world);
|
||||
cleanScriptScope(_room);
|
||||
cleanScriptScope(_nodeview);
|
||||
_currentLocation.node = 0;
|
||||
_currentLocation.world = 0;
|
||||
_currentLocation.room = 0;
|
||||
_currentLocation.view = 0;
|
||||
if (restarted) {
|
||||
for (auto &fx : _activeSideFx)
|
||||
delete fx;
|
||||
_activeSideFx.clear();
|
||||
_referenceTable.clear();
|
||||
switch (_engine->getGameId()) {
|
||||
case GID_GRANDINQUISITOR:
|
||||
// Bypass logo video
|
||||
setStateValue(16966, 1);
|
||||
// Ensure post-logo screen redraw is not inhibited in CD version
|
||||
setStateValue(5813, 1);
|
||||
// Bypass additional logo videos in DVD version
|
||||
setStateValue(19810, 1);
|
||||
setStateValue(19848, 1);
|
||||
break;
|
||||
case GID_NEMESIS:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
parseScrFile("universe.scr", _universe);
|
||||
changeLocation('g', 'a', 'r', 'y', 0);
|
||||
|
||||
_controlEvents.clear();
|
||||
if (restarted)
|
||||
_engine->loadSettings();
|
||||
}
|
||||
|
||||
bool ScriptManager::changingLocation() const {
|
||||
return _currentLocation != _nextLocation;
|
||||
}
|
||||
|
||||
void ScriptManager::process(uint deltaTimeMillis) {
|
||||
// If the location is changing, the script that did that may have
|
||||
// triggered other scripts, so we give them all a few extra cycles to
|
||||
// run. This fixes some missing scoring in ZGI, and quite
|
||||
// possibly other minor glitches as well.
|
||||
//
|
||||
// Another idea would be to change if there are pending scripts
|
||||
// in the exec queues, but that could cause this to hang
|
||||
// indefinitely.
|
||||
for (uint8 pass = 0; pass <= (changingLocation() ? _changeLocationExtraCycles : 0); pass++) {
|
||||
updateNodes(pass == 0 ? deltaTimeMillis : 0);
|
||||
debugC(5, kDebugLoop, "Script nodes updated");
|
||||
if (!execScope(_nodeview))
|
||||
break;
|
||||
if (!execScope(_room))
|
||||
break;
|
||||
if (!execScope(_world))
|
||||
break;
|
||||
if (!execScope(_universe))
|
||||
break;
|
||||
}
|
||||
updateControls(deltaTimeMillis);
|
||||
if (changingLocation())
|
||||
ChangeLocationReal(false);
|
||||
}
|
||||
|
||||
bool ScriptManager::execScope(ScriptScope &scope) {
|
||||
// Swap queues
|
||||
PuzzleList *tmp = scope.execQueue;
|
||||
scope.execQueue = scope.scopeQueue;
|
||||
scope.scopeQueue = tmp;
|
||||
scope.scopeQueue->clear();
|
||||
|
||||
for (auto &puzzle : scope.puzzles)
|
||||
puzzle->addedBySetState = false;
|
||||
|
||||
switch (getStateValue(StateKey_ExecScopeStyle)) {
|
||||
case 0: // ZGI
|
||||
if (scope.procCount < 2)
|
||||
for (auto &puzzle : scope.puzzles) {
|
||||
if (!checkPuzzleCriteria(puzzle, scope.procCount))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
for (auto &puzzle : (*scope.execQueue)) {
|
||||
if (!checkPuzzleCriteria(puzzle, scope.procCount))
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 1: // Nemesis
|
||||
default:
|
||||
for (auto &puzzle : scope.puzzles) {
|
||||
if (!checkPuzzleCriteria(puzzle, scope.procCount))
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (scope.procCount < 2)
|
||||
scope.procCount++;
|
||||
return true;
|
||||
}
|
||||
|
||||
void ScriptManager::referenceTableAddPuzzle(uint32 key, PuzzleRef ref) {
|
||||
if (_referenceTable.contains(key)) {
|
||||
Common::Array<PuzzleRef> *arr = &_referenceTable[key];
|
||||
for (uint32 i = 0; i < arr->size(); i++) {
|
||||
if ((*arr)[i].puz == ref.puz)
|
||||
return;
|
||||
}
|
||||
}
|
||||
_referenceTable[key].push_back(ref);
|
||||
}
|
||||
|
||||
void ScriptManager::addPuzzlesToReferenceTable(ScriptScope &scope) {
|
||||
// Iterate through each local Puzzle
|
||||
for (auto &puzzle : scope.puzzles) {
|
||||
Puzzle *puzzlePtr = puzzle;
|
||||
|
||||
PuzzleRef ref;
|
||||
ref.scope = &scope;
|
||||
ref.puz = puzzlePtr;
|
||||
|
||||
referenceTableAddPuzzle(puzzlePtr->key, ref);
|
||||
|
||||
// Iterate through each CriteriaEntry and add a reference from the criteria key to the Puzzle
|
||||
for (auto &criteria : puzzle->criteriaList) {
|
||||
for (auto &entry : criteria)
|
||||
referenceTableAddPuzzle(entry.key, ref);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptManager::updateNodes(uint deltaTimeMillis) {
|
||||
// If process() returns true, it means the node can be deleted
|
||||
for (auto fx = _activeSideFx.begin(); fx != _activeSideFx.end();) {
|
||||
if ((*fx)->process(deltaTimeMillis)) {
|
||||
delete(*fx);
|
||||
// Remove the node
|
||||
fx = _activeSideFx.erase(fx);
|
||||
}
|
||||
else
|
||||
++fx;
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptManager::updateControls(uint deltaTimeMillis) {
|
||||
if (!_activeControls)
|
||||
return;
|
||||
|
||||
// Process only one event
|
||||
if (!_controlEvents.empty()) {
|
||||
Common::Event _event = _controlEvents.front();
|
||||
Common::Point imageCoord;
|
||||
switch (_event.type) {
|
||||
case Common::EVENT_LBUTTONDOWN:
|
||||
imageCoord = _engine->getRenderManager()->screenSpaceToImageSpace(_event.mouse);
|
||||
onMouseDown(_event.mouse, imageCoord);
|
||||
break;
|
||||
case Common::EVENT_LBUTTONUP:
|
||||
imageCoord = _engine->getRenderManager()->screenSpaceToImageSpace(_event.mouse);
|
||||
onMouseUp(_event.mouse, imageCoord);
|
||||
break;
|
||||
case Common::EVENT_KEYDOWN:
|
||||
onKeyDown(_event.kbd);
|
||||
break;
|
||||
case Common::EVENT_KEYUP:
|
||||
onKeyUp(_event.kbd);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
_controlEvents.pop_front();
|
||||
}
|
||||
|
||||
for (auto &control : (*_activeControls)) {
|
||||
if (control->process(deltaTimeMillis))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool ScriptManager::checkPuzzleCriteria(Puzzle *puzzle, uint counter) {
|
||||
// Check if the puzzle is already finished
|
||||
// Also check that the puzzle isn't disabled
|
||||
if (getStateValue(puzzle->key) == 1 || (getStateFlag(puzzle->key) & Puzzle::DISABLED))
|
||||
return true;
|
||||
|
||||
// Check each Criteria
|
||||
if (counter == 0 && (getStateFlag(puzzle->key) & Puzzle::DO_ME_NOW) == 0)
|
||||
return true;
|
||||
|
||||
// WORKAROUNDS:
|
||||
switch (_engine->getGameId()) {
|
||||
case GID_NEMESIS:
|
||||
switch (puzzle->key) {
|
||||
case 16418:
|
||||
// WORKAROUND for script bug in Zork Nemesis, room mc30 (Monastery Entry)
|
||||
// Rumble sound effect should cease upon changing location to me10 (Hall of Masks),
|
||||
// but this puzzle erroneously restarted it immediately after.
|
||||
if(changingLocation())
|
||||
return true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
bool criteriaMet = false;
|
||||
for (auto &criteria : puzzle->criteriaList) {
|
||||
criteriaMet = false;
|
||||
|
||||
for (auto &entry : criteria) {
|
||||
// Get the value to compare against
|
||||
int argumentValue;
|
||||
if (entry.argumentIsAKey)
|
||||
argumentValue = getStateValue(entry.argument);
|
||||
else
|
||||
argumentValue = entry.argument;
|
||||
|
||||
// Do the comparison
|
||||
switch (entry.criteriaOperator) {
|
||||
case Puzzle::EQUAL_TO:
|
||||
criteriaMet = getStateValue(entry.key) == argumentValue;
|
||||
break;
|
||||
case Puzzle::NOT_EQUAL_TO:
|
||||
criteriaMet = getStateValue(entry.key) != argumentValue;
|
||||
break;
|
||||
case Puzzle::GREATER_THAN:
|
||||
criteriaMet = getStateValue(entry.key) > argumentValue;
|
||||
break;
|
||||
case Puzzle::LESS_THAN:
|
||||
criteriaMet = getStateValue(entry.key) < argumentValue;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// If one check returns false, don't keep checking
|
||||
if (!criteriaMet)
|
||||
break;
|
||||
}
|
||||
|
||||
// If any of the Criteria are *fully* met, then execute the results
|
||||
if (criteriaMet)
|
||||
break;
|
||||
}
|
||||
|
||||
// criteriaList can be empty. Aka, the puzzle should be executed immediately
|
||||
if (puzzle->criteriaList.empty() || criteriaMet) {
|
||||
debugC(1, kDebugPuzzle, "Puzzle %u criteria passed. Executing its ResultActions", puzzle->key);
|
||||
|
||||
// Set the puzzle as completed
|
||||
setStateValue(puzzle->key, 1);
|
||||
|
||||
for (auto &result : puzzle->resultActions) {
|
||||
if (!result->execute())
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ScriptManager::cleanStateTable() {
|
||||
for (auto entry = _globalState.begin(); entry != _globalState.end(); ++entry) {
|
||||
// If the value is equal to zero, we can purge it since getStateValue()
|
||||
// will return zero if _globalState doesn't contain a key
|
||||
if (entry->_value == 0) {
|
||||
// Remove the node
|
||||
_globalState.erase(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptManager::cleanScriptScope(ScriptScope &scope) {
|
||||
scope.privQueueOne.clear();
|
||||
scope.privQueueTwo.clear();
|
||||
scope.scopeQueue = &scope.privQueueOne;
|
||||
scope.execQueue = &scope.privQueueTwo;
|
||||
for (auto &puzzle : scope.puzzles)
|
||||
delete(puzzle);
|
||||
|
||||
scope.puzzles.clear();
|
||||
|
||||
for (auto &control : scope.controls)
|
||||
delete(control);
|
||||
|
||||
scope.controls.clear();
|
||||
|
||||
scope.procCount = 0;
|
||||
}
|
||||
|
||||
int ScriptManager::getStateValue(uint32 key) {
|
||||
if (_globalState.contains(key))
|
||||
return _globalState[key];
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ScriptManager::queuePuzzles(uint32 key) {
|
||||
if (_referenceTable.contains(key)) {
|
||||
Common::Array<PuzzleRef> *arr = &_referenceTable[key];
|
||||
for (int32 i = arr->size() - 1; i >= 0; i--) {
|
||||
if (!(*arr)[i].puz->addedBySetState) {
|
||||
(*arr)[i].scope->scopeQueue->push_back((*arr)[i].puz);
|
||||
(*arr)[i].puz->addedBySetState = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptManager::setStateValue(uint32 key, int value) {
|
||||
if (value == 0)
|
||||
_globalState.erase(key);
|
||||
else
|
||||
_globalState[key] = value;
|
||||
|
||||
queuePuzzles(key);
|
||||
}
|
||||
|
||||
void ScriptManager::setStateValueSilent(uint32 key, int value) {
|
||||
if (value == 0)
|
||||
_globalState.erase(key);
|
||||
else
|
||||
_globalState[key] = value;
|
||||
}
|
||||
|
||||
uint ScriptManager::getStateFlag(uint32 key) {
|
||||
if (_globalStateFlags.contains(key))
|
||||
return _globalStateFlags[key];
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ScriptManager::setStateFlag(uint32 key, uint value) {
|
||||
queuePuzzles(key);
|
||||
|
||||
_globalStateFlags[key] |= value;
|
||||
}
|
||||
|
||||
void ScriptManager::setStateFlagSilent(uint32 key, uint value) {
|
||||
if (value == 0)
|
||||
_globalStateFlags.erase(key);
|
||||
else
|
||||
_globalStateFlags[key] = value;
|
||||
}
|
||||
|
||||
void ScriptManager::unsetStateFlag(uint32 key, uint value) {
|
||||
queuePuzzles(key);
|
||||
|
||||
if (_globalStateFlags.contains(key)) {
|
||||
_globalStateFlags[key] &= ~value;
|
||||
if (_globalStateFlags[key] == 0)
|
||||
_globalStateFlags.erase(key);
|
||||
}
|
||||
}
|
||||
|
||||
Control *ScriptManager::getControl(uint32 key) {
|
||||
for (auto &control : (*_activeControls)) {
|
||||
if (control->getKey() == key)
|
||||
return control;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ScriptManager::focusControl(uint32 key) {
|
||||
if (!_activeControls)
|
||||
return;
|
||||
|
||||
if (_currentlyFocusedControl == key)
|
||||
return;
|
||||
|
||||
for (auto &control : (*_activeControls)) {
|
||||
uint32 controlKey = control->getKey();
|
||||
if (controlKey == key)
|
||||
control->focus();
|
||||
else if (controlKey == _currentlyFocusedControl)
|
||||
control->unfocus();
|
||||
}
|
||||
_currentlyFocusedControl = key;
|
||||
}
|
||||
|
||||
void ScriptManager::setFocusControlKey(uint32 key) {
|
||||
_currentlyFocusedControl = key;
|
||||
}
|
||||
|
||||
void ScriptManager::addSideFX(ScriptingEffect *fx) {
|
||||
_activeSideFx.push_back(fx);
|
||||
}
|
||||
|
||||
ScriptingEffect *ScriptManager::getSideFX(uint32 key) {
|
||||
for (auto &fx : _activeSideFx) {
|
||||
if (fx->getKey() == key)
|
||||
return fx;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ScriptManager::deleteSideFx(uint32 key) {
|
||||
for (auto fx = _activeSideFx.begin(); fx != _activeSideFx.end(); ++fx) {
|
||||
if ((*fx)->getKey() == key) {
|
||||
delete(*fx);
|
||||
_activeSideFx.erase(fx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptManager::stopSideFx(uint32 key) {
|
||||
for (auto fx = _activeSideFx.begin(); fx != _activeSideFx.end(); ++fx) {
|
||||
if ((*fx)->getKey() == key) {
|
||||
bool ret = (*fx)->stop();
|
||||
if (ret) {
|
||||
delete(*fx);
|
||||
_activeSideFx.erase(fx);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptManager::killSideFx(uint32 key) {
|
||||
for (auto fx = _activeSideFx.begin(); fx != _activeSideFx.end(); ++fx) {
|
||||
if ((*fx)->getKey() == key) {
|
||||
(*fx)->kill();
|
||||
delete(*fx);
|
||||
_activeSideFx.erase(fx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptManager::killSideFxType(ScriptingEffect::ScriptingEffectType type) {
|
||||
for (auto fx = _activeSideFx.begin(); fx != _activeSideFx.end();) {
|
||||
if ((*fx)->getType() & type) {
|
||||
(*fx)->kill();
|
||||
delete(*fx);
|
||||
fx = _activeSideFx.erase(fx);
|
||||
} else {
|
||||
++fx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptManager::onMouseDown(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
|
||||
debugC(1, kDebugMouse, "Mouse screen coordinates: %d, %d, background/script coordinates: %d, %d", screenSpacePos.x, screenSpacePos.y, backgroundImageSpacePos.x, backgroundImageSpacePos.y);
|
||||
if (!_activeControls)
|
||||
return;
|
||||
|
||||
for (auto control = _activeControls->reverse_begin(); control != _activeControls->end(); control--) {
|
||||
if ((*control)->onMouseDown(screenSpacePos, backgroundImageSpacePos))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptManager::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
|
||||
if (!_activeControls)
|
||||
return;
|
||||
|
||||
for (auto control = _activeControls->reverse_begin(); control != _activeControls->end(); control--) {
|
||||
if ((*control)->onMouseUp(screenSpacePos, backgroundImageSpacePos))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool ScriptManager::onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
|
||||
if (!_activeControls)
|
||||
return false;
|
||||
|
||||
for (auto control = _activeControls->reverse_begin(); control != _activeControls->end(); control--) {
|
||||
if ((*control)->onMouseMove(screenSpacePos, backgroundImageSpacePos))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ScriptManager::onKeyDown(Common::KeyState keyState) {
|
||||
if (!_activeControls)
|
||||
return;
|
||||
|
||||
for (auto &control : (*_activeControls)) {
|
||||
if (control->onKeyDown(keyState))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptManager::onKeyUp(Common::KeyState keyState) {
|
||||
if (!_activeControls)
|
||||
return;
|
||||
|
||||
for (auto &control : (*_activeControls)) {
|
||||
if (control->onKeyUp(keyState))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptManager::changeLocation(const Location &_newLocation) {
|
||||
changeLocation(_newLocation.world, _newLocation.room, _newLocation.node, _newLocation.view, _newLocation.offset);
|
||||
}
|
||||
|
||||
void ScriptManager::changeLocation(char world, char room, char node, char view, uint32 offset) {
|
||||
debugC(1, kDebugScript, "\tPreparing to change location");
|
||||
_nextLocation.world = world;
|
||||
_nextLocation.room = room;
|
||||
_nextLocation.node = node;
|
||||
_nextLocation.view = view;
|
||||
_nextLocation.offset = offset;
|
||||
// If next location is 0000, return to the previous location.
|
||||
if (_nextLocation == "0000") {
|
||||
if (getStateValue(StateKey_World) != 'g' || getStateValue(StateKey_Room) != 'j') {
|
||||
_nextLocation.world = getStateValue(StateKey_LastWorld);
|
||||
_nextLocation.room = getStateValue(StateKey_LastRoom);
|
||||
_nextLocation.node = getStateValue(StateKey_LastNode);
|
||||
_nextLocation.view = getStateValue(StateKey_LastView);
|
||||
_nextLocation.offset = getStateValue(StateKey_LastViewPos);
|
||||
} else {
|
||||
_nextLocation.world = getStateValue(StateKey_Menu_LastWorld);
|
||||
_nextLocation.room = getStateValue(StateKey_Menu_LastRoom);
|
||||
_nextLocation.node = getStateValue(StateKey_Menu_LastNode);
|
||||
_nextLocation.view = getStateValue(StateKey_Menu_LastView);
|
||||
_nextLocation.offset = getStateValue(StateKey_Menu_LastViewPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptManager::ChangeLocationReal(bool isLoading) {
|
||||
assert(_nextLocation.world != 0);
|
||||
debugC(1, kDebugScript, "\tChanging location to: World %c, Room %c, Node %c, View %c, Offset %u", _nextLocation.world, _nextLocation.room, _nextLocation.node, _nextLocation.view, _nextLocation.offset);
|
||||
|
||||
const bool enteringMenu = (_nextLocation.world == 'g' && _nextLocation.room == 'j');
|
||||
const bool leavingMenu = (_currentLocation.world == 'g' && _currentLocation.room == 'j');
|
||||
const bool isSaveScreen = (enteringMenu && _nextLocation.node == 's' && _nextLocation.view == 'e');
|
||||
const bool isRestoreScreen = (enteringMenu && _nextLocation.node == 'r' && _nextLocation.view == 'e');
|
||||
|
||||
if (enteringMenu && !ConfMan.getBool("originalsaveload")) {
|
||||
if (isSaveScreen || isRestoreScreen) {
|
||||
// Hook up the ScummVM save/restore dialog
|
||||
bool gameSavedOrLoaded = _engine->getSaveManager()->scummVMSaveLoadDialog(isSaveScreen);
|
||||
if (!gameSavedOrLoaded || isSaveScreen) {
|
||||
// Reload the current room
|
||||
_nextLocation.world = _currentLocation.world;
|
||||
_nextLocation.room = _currentLocation.room;
|
||||
_nextLocation.node = _currentLocation.node;
|
||||
_nextLocation.view = _currentLocation.view;
|
||||
_nextLocation.offset = _currentLocation.offset;
|
||||
|
||||
return;
|
||||
} else {
|
||||
_currentLocation.world = 'g';
|
||||
_currentLocation.room = '0';
|
||||
_currentLocation.node = '0';
|
||||
_currentLocation.view = '0';
|
||||
_currentLocation.offset = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_engine->setRenderDelay(2); // Necessary to ensure proper redraw in certain locations, in particular the infinite corridor in Zork Grand Inquisitor (room th20)
|
||||
|
||||
if (!leavingMenu) {
|
||||
if (!isLoading && !enteringMenu) {
|
||||
setStateValue(StateKey_LastWorld, getStateValue(StateKey_World));
|
||||
setStateValue(StateKey_LastRoom, getStateValue(StateKey_Room));
|
||||
setStateValue(StateKey_LastNode, getStateValue(StateKey_Node));
|
||||
setStateValue(StateKey_LastView, getStateValue(StateKey_View));
|
||||
setStateValue(StateKey_LastViewPos, getStateValue(StateKey_ViewPos));
|
||||
} else {
|
||||
setStateValue(StateKey_Menu_LastWorld, getStateValue(StateKey_World));
|
||||
setStateValue(StateKey_Menu_LastRoom, getStateValue(StateKey_Room));
|
||||
setStateValue(StateKey_Menu_LastNode, getStateValue(StateKey_Node));
|
||||
setStateValue(StateKey_Menu_LastView, getStateValue(StateKey_View));
|
||||
setStateValue(StateKey_Menu_LastViewPos, getStateValue(StateKey_ViewPos));
|
||||
}
|
||||
}
|
||||
|
||||
if (enteringMenu) {
|
||||
if (isSaveScreen && !leavingMenu)
|
||||
_engine->getSaveManager()->prepareSaveBuffer();
|
||||
}
|
||||
else if (leavingMenu)
|
||||
_engine->getSaveManager()->flushSaveBuffer();
|
||||
|
||||
setStateValue(StateKey_World, _nextLocation.world);
|
||||
setStateValue(StateKey_Room, _nextLocation.room);
|
||||
setStateValue(StateKey_Node, _nextLocation.node);
|
||||
setStateValue(StateKey_View, _nextLocation.view);
|
||||
setStateValue(StateKey_ViewPos, _nextLocation.offset);
|
||||
|
||||
_referenceTable.clear();
|
||||
addPuzzlesToReferenceTable(_universe);
|
||||
|
||||
_engine->getMenuManager()->setEnable(0xFFFF);
|
||||
|
||||
TransitionLevel level = NONE;
|
||||
Common::Path filePath;
|
||||
if (_nextLocation.world != _currentLocation.world)
|
||||
level = WORLD;
|
||||
else if (_nextLocation.room != _currentLocation.room)
|
||||
level = ROOM;
|
||||
else if (_nextLocation.node != _currentLocation.node)
|
||||
level = NODE;
|
||||
else if (_nextLocation.view != _currentLocation.view)
|
||||
level = VIEW;
|
||||
|
||||
switch (level) {
|
||||
case WORLD:
|
||||
cleanScriptScope(_world);
|
||||
filePath = Common::Path(Common::String::format("%c.scr", _nextLocation.world));
|
||||
parseScrFile(filePath, _world);
|
||||
// fall through
|
||||
case ROOM:
|
||||
cleanScriptScope(_room);
|
||||
filePath = Common::Path(Common::String::format("%c%c.scr", _nextLocation.world, _nextLocation.room));
|
||||
parseScrFile(filePath, _room);
|
||||
// fall through
|
||||
case NODE:
|
||||
case VIEW:
|
||||
cleanScriptScope(_nodeview);
|
||||
filePath = Common::Path(Common::String::format("%c%c%c%c.scr", _nextLocation.world, _nextLocation.room, _nextLocation.node, _nextLocation.view));
|
||||
parseScrFile(filePath, _nodeview);
|
||||
addPuzzlesToReferenceTable(_world);
|
||||
addPuzzlesToReferenceTable(_room);
|
||||
addPuzzlesToReferenceTable(_nodeview);
|
||||
break;
|
||||
case NONE:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
_activeControls = &_nodeview.controls;
|
||||
|
||||
// Revert to the idle cursor
|
||||
_engine->getCursorManager()->changeCursor(CursorIndex_Idle);
|
||||
|
||||
// Change the background position
|
||||
_engine->getRenderManager()->setBackgroundPosition(_nextLocation.offset);
|
||||
|
||||
if (_currentLocation == "0000")
|
||||
level = WORLD;
|
||||
if (level != NONE)
|
||||
_currentLocation = _nextLocation;
|
||||
switch (level) {
|
||||
case WORLD:
|
||||
execScope(_world);
|
||||
// fall through
|
||||
case ROOM:
|
||||
execScope(_room);
|
||||
// fall through
|
||||
case NODE:
|
||||
case VIEW:
|
||||
execScope(_nodeview);
|
||||
break;
|
||||
case NONE:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
_engine->getRenderManager()->checkBorders();
|
||||
|
||||
_engine->onMouseMove(); // Trigger a pseudo mouse movement to change cursor if we enter the new location with it already over a hotspot
|
||||
|
||||
debugC(1, kDebugScript, "\tLocation change complete");
|
||||
}
|
||||
|
||||
void ScriptManager::serialize(Common::WriteStream *stream) {
|
||||
stream->writeUint32BE(MKTAG('Z', 'N', 'S', 'G'));
|
||||
stream->writeUint32LE(4);
|
||||
stream->writeUint32LE(0);
|
||||
stream->writeUint32BE(MKTAG('L', 'O', 'C', ' '));
|
||||
stream->writeUint32LE(8);
|
||||
stream->writeByte(getStateValue(StateKey_World));
|
||||
stream->writeByte(getStateValue(StateKey_Room));
|
||||
stream->writeByte(getStateValue(StateKey_Node));
|
||||
stream->writeByte(getStateValue(StateKey_View));
|
||||
stream->writeUint32LE(getStateValue(StateKey_ViewPos));
|
||||
|
||||
for (auto &fx : _activeSideFx)
|
||||
fx->serialize(stream);
|
||||
|
||||
stream->writeUint32BE(MKTAG('F', 'L', 'A', 'G'));
|
||||
|
||||
int32 slots = _engine->getGameId() == GID_NEMESIS ? 31000 : 21000;
|
||||
// Original games use key values up to 29500 and 19737, respectively
|
||||
// Values 30001~31000 and 20001~21000 are now set aside for auxiliary scripting to add extra directional audio effects.
|
||||
|
||||
stream->writeUint32LE(slots * 2);
|
||||
|
||||
for (int32 i = 0; i < slots; i++)
|
||||
stream->writeUint16LE(getStateFlag(i));
|
||||
|
||||
stream->writeUint32BE(MKTAG('P', 'U', 'Z', 'Z'));
|
||||
stream->writeUint32LE(slots * 2);
|
||||
|
||||
for (int32 i = 0; i < slots; i++)
|
||||
stream->writeSint16LE(getStateValue(i));
|
||||
}
|
||||
|
||||
void ScriptManager::deserialize(Common::SeekableReadStream *stream) {
|
||||
// Clear out the current table values
|
||||
_globalState.clear();
|
||||
_globalStateFlags.clear();
|
||||
|
||||
cleanScriptScope(_nodeview);
|
||||
cleanScriptScope(_room);
|
||||
cleanScriptScope(_world);
|
||||
|
||||
_currentLocation.node = 0;
|
||||
_currentLocation.world = 0;
|
||||
_currentLocation.room = 0;
|
||||
_currentLocation.view = 0;
|
||||
|
||||
for (auto &fx : _activeSideFx)
|
||||
delete fx;
|
||||
|
||||
_activeSideFx.clear();
|
||||
_referenceTable.clear();
|
||||
|
||||
if (stream->readUint32BE() != MKTAG('Z', 'N', 'S', 'G') || stream->readUint32LE() != 4) {
|
||||
changeLocation('g', 'a', 'r', 'y', 0);
|
||||
return;
|
||||
}
|
||||
|
||||
stream->seek(4, SEEK_CUR);
|
||||
|
||||
if (stream->readUint32BE() != MKTAG('L', 'O', 'C', ' ') || stream->readUint32LE() != 8) {
|
||||
changeLocation('g', 'a', 'r', 'y', 0);
|
||||
return;
|
||||
}
|
||||
|
||||
Location nextLocation;
|
||||
|
||||
nextLocation.world = stream->readByte();
|
||||
nextLocation.room = stream->readByte();
|
||||
nextLocation.node = stream->readByte();
|
||||
nextLocation.view = stream->readByte();
|
||||
nextLocation.offset = stream->readUint32LE() & 0x0000FFFF;
|
||||
|
||||
while (stream->pos() < stream->size()) {
|
||||
uint32 tag = stream->readUint32BE();
|
||||
uint32 tagSize = stream->readUint32LE();
|
||||
switch (tag) {
|
||||
case MKTAG('T', 'I', 'M', 'R'): {
|
||||
uint32 key = stream->readUint32LE();
|
||||
uint32 time = stream->readUint32LE();
|
||||
if (_engine->getGameId() == GID_GRANDINQUISITOR)
|
||||
time /= 100;
|
||||
else if (_engine->getGameId() == GID_NEMESIS)
|
||||
time /= 1000;
|
||||
addSideFX(new TimerNode(_engine, key, time));
|
||||
}
|
||||
break;
|
||||
case MKTAG('F', 'L', 'A', 'G'):
|
||||
for (uint32 i = 0; i < tagSize / 2; i++)
|
||||
setStateFlagSilent(i, stream->readUint16LE());
|
||||
break;
|
||||
case MKTAG('P', 'U', 'Z', 'Z'):
|
||||
for (uint32 i = 0; i < tagSize / 2; i++)
|
||||
setStateValueSilent(i, stream->readUint16LE());
|
||||
break;
|
||||
default:
|
||||
stream->seek(tagSize, SEEK_CUR);
|
||||
}
|
||||
}
|
||||
|
||||
_nextLocation = nextLocation;
|
||||
|
||||
ChangeLocationReal(true);
|
||||
|
||||
_engine->setRenderDelay(10);
|
||||
setStateValue(StateKey_RestoreFlag, 1);
|
||||
|
||||
_engine->loadSettings();
|
||||
}
|
||||
|
||||
Location ScriptManager::getCurrentLocation() const {
|
||||
Location location = _currentLocation;
|
||||
location.offset = _engine->getRenderManager()->getCurrentBackgroundOffset();
|
||||
|
||||
return location;
|
||||
}
|
||||
|
||||
Location ScriptManager::getLastLocation() {
|
||||
Location location;
|
||||
location.world = getStateValue(StateKey_LastWorld);
|
||||
location.room = getStateValue(StateKey_LastRoom);
|
||||
location.node = getStateValue(StateKey_LastNode);
|
||||
location.view = getStateValue(StateKey_LastView);
|
||||
location.offset = getStateValue(StateKey_LastViewPos);
|
||||
|
||||
return location;
|
||||
}
|
||||
|
||||
Location ScriptManager::getLastMenuLocation() {
|
||||
Location location;
|
||||
location.world = getStateValue(StateKey_Menu_LastWorld);
|
||||
location.room = getStateValue(StateKey_Menu_LastRoom);
|
||||
location.node = getStateValue(StateKey_Menu_LastNode);
|
||||
location.view = getStateValue(StateKey_Menu_LastView);
|
||||
location.offset = getStateValue(StateKey_Menu_LastViewPos);
|
||||
|
||||
return location;
|
||||
}
|
||||
|
||||
void ScriptManager::addEvent(Common::Event event) {
|
||||
_controlEvents.push_back(event);
|
||||
}
|
||||
|
||||
void ScriptManager::flushEvent(Common::EventType type) {
|
||||
auto event = _controlEvents.begin();
|
||||
while (event != _controlEvents.end()) {
|
||||
if ((*event).type == type)
|
||||
event = _controlEvents.erase(event);
|
||||
else
|
||||
event++;
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptManager::trimCommentsAndWhiteSpace(Common::String *string) const {
|
||||
for (int i = string->size() - 1; i >= 0; i--) {
|
||||
if ((*string)[i] == '#')
|
||||
string->erase(i);
|
||||
}
|
||||
string->trim();
|
||||
}
|
||||
|
||||
ValueSlot::ValueSlot(ScriptManager *scriptManager, const char *slotValue):
|
||||
_scriptManager(scriptManager) {
|
||||
_value = 0;
|
||||
_slot = false;
|
||||
const char *isSlot = strstr(slotValue, "[");
|
||||
if (isSlot) {
|
||||
_slot = true;
|
||||
_value = atoi(isSlot + 1);
|
||||
} else {
|
||||
_slot = false;
|
||||
_value = atoi(slotValue);
|
||||
}
|
||||
}
|
||||
|
||||
int16 ValueSlot::getValue() {
|
||||
if (_slot) {
|
||||
if (_value >= 0)
|
||||
return _scriptManager->getStateValue(_value);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
return _value;
|
||||
}
|
||||
|
||||
} // End of namespace ZVision
|
||||
412
engines/zvision/scripting/script_manager.h
Normal file
412
engines/zvision/scripting/script_manager.h
Normal file
@@ -0,0 +1,412 @@
|
||||
/* 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 ZVISION_SCRIPT_MANAGER_H
|
||||
#define ZVISION_SCRIPT_MANAGER_H
|
||||
|
||||
#include "common/events.h"
|
||||
#include "common/hashmap.h"
|
||||
#include "common/queue.h"
|
||||
#include "zvision/scripting/control.h"
|
||||
#include "zvision/scripting/puzzle.h"
|
||||
#include "zvision/scripting/scripting_effect.h"
|
||||
|
||||
namespace Common {
|
||||
class String;
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
class ZVision;
|
||||
|
||||
enum StateKey {
|
||||
StateKey_World = 3,
|
||||
StateKey_Room = 4,
|
||||
StateKey_Node = 5,
|
||||
StateKey_View = 6,
|
||||
StateKey_ViewPos = 7,
|
||||
StateKey_KeyPress = 8,
|
||||
StateKey_InventoryItem = 9,
|
||||
StateKey_LMouse = 10,
|
||||
StateKey_NotSet = 11, // This key doesn't set
|
||||
StateKey_Rounds = 12,
|
||||
StateKey_Venus = 13,
|
||||
StateKey_RMouse = 18,
|
||||
StateKey_MenuState = 19,
|
||||
StateKey_RestoreFlag = 20,
|
||||
StateKey_Quitting = 39,
|
||||
StateKey_LastWorld = 40,
|
||||
StateKey_LastRoom = 41,
|
||||
StateKey_LastNode = 42,
|
||||
StateKey_LastView = 43,
|
||||
StateKey_LastViewPos = 44,
|
||||
StateKey_Menu_LastWorld = 45,
|
||||
StateKey_Menu_LastRoom = 46,
|
||||
StateKey_Menu_LastNode = 47,
|
||||
StateKey_Menu_LastView = 48,
|
||||
StateKey_Menu_LastViewPos = 49,
|
||||
StateKey_KbdRotateSpeed = 50,
|
||||
StateKey_Subtitles = 51,
|
||||
StateKey_StreamSkipKey = 52,
|
||||
StateKey_RotateSpeed = 53,
|
||||
StateKey_Volume = 56,
|
||||
StateKey_Qsound = 57,
|
||||
StateKey_VenusEnable = 58,
|
||||
StateKey_HighQuality = 59,
|
||||
StateKey_VideoLineSkip = 65,
|
||||
StateKey_Platform = 66, // 0 = Windows, !0 = DOS
|
||||
StateKey_InstallLevel = 67,
|
||||
StateKey_CountryCode = 68,
|
||||
StateKey_CPU = 69, // !1 = 486, 1 = i586/Pentium
|
||||
StateKey_MovieCursor = 70,
|
||||
StateKey_NoTurnAnim = 71,
|
||||
StateKey_WIN958 = 72, // 0 = high system RAM, !0 = low system RAM (<8MB)
|
||||
StateKey_ShowErrorDlg = 73,
|
||||
StateKey_DebugCheats = 74,
|
||||
StateKey_JapanFonts = 75,
|
||||
StateKey_ExecScopeStyle = 76, // 0 = ZGI, 1 = Nemesis
|
||||
StateKey_Brightness = 77,
|
||||
StateKey_MPEGMovies = 78,
|
||||
StateKey_EF9_R = 91,
|
||||
StateKey_EF9_G = 92,
|
||||
StateKey_EF9_B = 93,
|
||||
StateKey_EF9_Speed = 94,
|
||||
StateKey_Inv_Cnt_Slot = 100,
|
||||
StateKey_Inv_1_Slot = 101,
|
||||
StateKey_Inv_49_Slot = 149,
|
||||
// ZGI only
|
||||
StateKey_Inv_TotalSlots = 150,
|
||||
StateKey_Inv_StartSlot = 151,
|
||||
StateKey_Spell_1 = 191,
|
||||
StateKey_Active_Spell = 205,
|
||||
StateKey_Reversed_Spellbooc = 206
|
||||
};
|
||||
|
||||
struct Location {
|
||||
Location() : world('g'), room('a'), node('r'), view('y'), offset(0) {}
|
||||
|
||||
char world;
|
||||
char room;
|
||||
char node;
|
||||
char view;
|
||||
uint32 offset;
|
||||
};
|
||||
|
||||
inline bool operator==(const Location& lhs, const Location& rhs) {
|
||||
return (
|
||||
lhs.world == rhs.world &&
|
||||
lhs.room == rhs.room &&
|
||||
lhs.node == rhs.node &&
|
||||
lhs.view == rhs.view
|
||||
);
|
||||
}
|
||||
|
||||
inline bool operator==(const Location& lhs, const char* rhs) {
|
||||
Common::String lhsStr = Common::String::format("%c%c%c%c", lhs.world, lhs.room, lhs.node, lhs.view);
|
||||
return lhsStr == rhs;
|
||||
}
|
||||
|
||||
inline bool operator!=(const Location& lhs, const Location& rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
inline bool operator!=(const Location& lhs, const char* rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
typedef Common::List<Puzzle *> PuzzleList;
|
||||
typedef Common::Queue<Puzzle *> PuzzleQueue;
|
||||
typedef Common::List<Control *> ControlList;
|
||||
typedef Common::HashMap<uint32, int32> StateMap;
|
||||
typedef Common::List<ScriptingEffect *> SideFXList;
|
||||
typedef Common::List<Common::Event> EventList;
|
||||
|
||||
class ScriptManager {
|
||||
public:
|
||||
ScriptManager(ZVision *engine);
|
||||
~ScriptManager();
|
||||
|
||||
private:
|
||||
ZVision *_engine;
|
||||
|
||||
struct ScriptScope {
|
||||
uint32 procCount;
|
||||
|
||||
PuzzleList *scopeQueue; // For adding puzzles to queue
|
||||
PuzzleList *execQueue; // Switch to it when execute
|
||||
PuzzleList privQueueOne;
|
||||
PuzzleList privQueueTwo;
|
||||
|
||||
PuzzleList puzzles;
|
||||
ControlList controls;
|
||||
};
|
||||
|
||||
struct PuzzleRef {
|
||||
Puzzle *puz;
|
||||
ScriptScope *scope;
|
||||
};
|
||||
|
||||
typedef Common::HashMap<uint32, Common::Array<PuzzleRef> > PuzzleMap;
|
||||
|
||||
/**
|
||||
* Holds the global state variable. Do NOT directly modify this. Use the accessors and
|
||||
* mutators getStateValue() and setStateValue(). This ensures that Puzzles that reference a
|
||||
* particular state key are checked after the key is modified.
|
||||
*/
|
||||
StateMap _globalState;
|
||||
/** Holds execute flags */
|
||||
StateMap _globalStateFlags;
|
||||
/** References _globalState keys to Puzzles */
|
||||
PuzzleMap _referenceTable;
|
||||
/** Holds the currently active controls */
|
||||
ControlList *_activeControls;
|
||||
|
||||
EventList _controlEvents;
|
||||
|
||||
ScriptScope _universe;
|
||||
ScriptScope _world;
|
||||
ScriptScope _room;
|
||||
ScriptScope _nodeview;
|
||||
|
||||
/** Holds the currently active timers, musics, other */
|
||||
SideFXList _activeSideFx;
|
||||
|
||||
Location _currentLocation;
|
||||
Location _nextLocation;
|
||||
const uint8 _changeLocationExtraCycles = 16;
|
||||
|
||||
uint32 _currentlyFocusedControl;
|
||||
|
||||
public:
|
||||
enum TransitionLevel {
|
||||
NONE,
|
||||
VIEW,
|
||||
NODE,
|
||||
ROOM,
|
||||
WORLD
|
||||
};
|
||||
|
||||
void initialize(bool restarted = false);
|
||||
void process(uint deltaTimeMillis);
|
||||
void queuePuzzles(uint32 key);
|
||||
|
||||
int getStateValue(uint32 key);
|
||||
void setStateValue(uint32 key, int value);
|
||||
|
||||
uint getStateFlag(uint32 key);
|
||||
void setStateFlag(uint32 key, uint value);
|
||||
void unsetStateFlag(uint32 key, uint value);
|
||||
|
||||
void addControl(Control *control);
|
||||
Control *getControl(uint32 key);
|
||||
|
||||
void enableControl(uint32 key);
|
||||
void disableControl(uint32 key);
|
||||
|
||||
void focusControl(uint32 key);
|
||||
// Only change focus control without call focus/unfocus.
|
||||
void setFocusControlKey(uint32 key);
|
||||
|
||||
void addSideFX(ScriptingEffect *fx);
|
||||
ScriptingEffect *getSideFX(uint32 key);
|
||||
void deleteSideFx(uint32 key);
|
||||
void stopSideFx(uint32 key);
|
||||
void killSideFx(uint32 key);
|
||||
void killSideFxType(ScriptingEffect::ScriptingEffectType type);
|
||||
|
||||
void addEvent(Common::Event);
|
||||
void flushEvent(Common::EventType type);
|
||||
|
||||
/**
|
||||
* Called when LeftMouse is pushed.
|
||||
*
|
||||
* @param screenSpacePos The position of the mouse in screen space
|
||||
* @param backgroundImageSpacePos The position of the mouse in background image space
|
||||
*/
|
||||
void onMouseDown(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos);
|
||||
/**
|
||||
* Called when LeftMouse is lifted.
|
||||
*
|
||||
* @param screenSpacePos The position of the mouse in screen space
|
||||
* @param backgroundImageSpacePos The position of the mouse in background image space
|
||||
*/
|
||||
void onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos);
|
||||
/**
|
||||
* Called on every MouseMove.
|
||||
*
|
||||
* @param screenSpacePos The position of the mouse in screen space
|
||||
* @param backgroundImageSpacePos The position of the mouse in background image space
|
||||
* @return Was the cursor changed?
|
||||
*/
|
||||
bool onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos);
|
||||
/**
|
||||
* Called when a key is pressed.
|
||||
*
|
||||
* @param keycode The key that was pressed
|
||||
*/
|
||||
void onKeyDown(Common::KeyState keyState);
|
||||
/**
|
||||
* Called when a key is released.
|
||||
*
|
||||
* @param keycode The key that was pressed
|
||||
*/
|
||||
void onKeyUp(Common::KeyState keyState);
|
||||
|
||||
/** Mark next location */
|
||||
void changeLocation(char world, char room, char node, char view, uint32 offset);
|
||||
void changeLocation(const Location &_newLocation);
|
||||
|
||||
bool changingLocation() const;
|
||||
|
||||
void serialize(Common::WriteStream *stream);
|
||||
void deserialize(Common::SeekableReadStream *stream);
|
||||
|
||||
Location getCurrentLocation() const;
|
||||
Location getLastLocation();
|
||||
Location getLastMenuLocation();
|
||||
|
||||
/**
|
||||
* Removes any line comments using '#' as a sequence start.
|
||||
* Then removes any trailing and leading 'whitespace' using String::trim()
|
||||
* Note: String::trim uses isspace() to determine what is whitespace and what is not.
|
||||
*
|
||||
* @param string The string to modify. It is modified in place
|
||||
*/
|
||||
void trimCommentsAndWhiteSpace(Common::String *string) const;
|
||||
|
||||
private:
|
||||
void referenceTableAddPuzzle(uint32 key, PuzzleRef ref);
|
||||
void addPuzzlesToReferenceTable(ScriptScope &scope);
|
||||
void updateNodes(uint deltaTimeMillis);
|
||||
void updateControls(uint deltaTimeMillis);
|
||||
/**
|
||||
* Check a puzzle's criteria; execute its actions and set its state to 1 if these critera are met.
|
||||
* Will not check or execute if:
|
||||
* Puzzle is disabled
|
||||
* Puzzle has already triggered and has a state value of 1
|
||||
* procCount has reached zero AND do_me_now is not set
|
||||
*
|
||||
* @param puzzle puzzle to check
|
||||
* @param counter procCount from this puzzle's scope container
|
||||
* Returns true if OK to keep calling this function this frame; false if we should break and start next frame (only used by RestoreGame action)
|
||||
*/
|
||||
bool checkPuzzleCriteria(Puzzle *puzzle, uint counter);
|
||||
void cleanStateTable(); // Set all global state values to zero
|
||||
void cleanScriptScope(ScriptScope &scope); // Resets everything in this scope, all lists empty, procCount to zero.
|
||||
bool execScope(ScriptScope &scope);
|
||||
|
||||
/** Perform change location */
|
||||
void ChangeLocationReal(bool isLoading);
|
||||
|
||||
int8 inventoryGetCount();
|
||||
void inventorySetCount(int8 cnt);
|
||||
int16 inventoryGetItem(int8 id);
|
||||
void inventorySetItem(int8 id, int16 item);
|
||||
|
||||
void setStateFlagSilent(uint32 key, uint value);
|
||||
void setStateValueSilent(uint32 key, int value);
|
||||
|
||||
public:
|
||||
void inventoryAdd(int16 item);
|
||||
void inventoryDrop(int16 item);
|
||||
void inventoryCycle();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Parses a script file into triggers and events
|
||||
*
|
||||
* @param fileName Name of the .scr file
|
||||
* @param isGlobal Are the puzzles included in the file global (true). AKA, the won't be purged during location changes
|
||||
*/
|
||||
void parseScrFile(const Common::Path &fileName, ScriptScope &scope);
|
||||
|
||||
/**
|
||||
* Parses the stream into a Puzzle object
|
||||
* Helper method for parseScrFile.
|
||||
*
|
||||
* @param puzzle The object to store what is parsed
|
||||
* @param stream Scr file stream
|
||||
*/
|
||||
void parsePuzzle(Puzzle *puzzle, Common::SeekableReadStream &stream);
|
||||
|
||||
/**
|
||||
* Parses the stream into a Criteria object
|
||||
* Helper method for parsePuzzle.
|
||||
*
|
||||
* @param criteria Pointer to the Criteria object to fill
|
||||
* @param stream Scr file stream
|
||||
* @param key Puzzle key (for workarounds)
|
||||
* @return Whether any criteria were read
|
||||
*/
|
||||
bool parseCriteria(Common::SeekableReadStream &stream, Common::List<Common::List<Puzzle::CriteriaEntry> > &criteriaList, uint32 key) const;
|
||||
|
||||
/**
|
||||
* Parses the stream into a ResultAction objects
|
||||
* Helper method for parsePuzzle.
|
||||
*
|
||||
* @param stream Scr file stream
|
||||
* @param actionList The list where the results will be added
|
||||
* @param key Puzzle key (for workarounds)
|
||||
* @return Created Results object
|
||||
*/
|
||||
void parseResults(Common::SeekableReadStream &stream, Common::List<ResultAction *> &actionList, uint32 key) const;
|
||||
|
||||
/**
|
||||
* Helper method for parsePuzzle. Parses the stream into a bitwise or of the StateFlags enum
|
||||
*
|
||||
* @param stream Scr file stream
|
||||
* @return Bitwise OR of all the flags set within the puzzle
|
||||
*/
|
||||
uint parseFlags(Common::SeekableReadStream &stream) const;
|
||||
|
||||
/**
|
||||
* Helper method for parseScrFile. Parses the stream into a Control object
|
||||
*
|
||||
* @param line The line initially read
|
||||
* @param stream Scr file stream
|
||||
*/
|
||||
Control *parseControl(Common::String &line, Common::SeekableReadStream &stream);
|
||||
};
|
||||
|
||||
/**
|
||||
* Instances of this polymorphic class function either as a store of a single value, or as a "slot" that returns a StateValue
|
||||
*
|
||||
* @param line The line initially read
|
||||
* @param slotValue A text string containing a number, which may be enclosed within square braces.
|
||||
* If square braces are not present, getValue() will return slotValue.
|
||||
* If square braces are present, getValue() will return the StateValue to which slotValue is the key.
|
||||
*
|
||||
* Once instantiated, the value and nature of slotValue may not be changed.
|
||||
*/
|
||||
class ValueSlot {
|
||||
public:
|
||||
ValueSlot(ScriptManager *scriptManager, const char *slotValue);
|
||||
int16 getValue();
|
||||
private:
|
||||
int16 _value;
|
||||
bool _slot;
|
||||
ScriptManager *_scriptManager;
|
||||
};
|
||||
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif
|
||||
123
engines/zvision/scripting/scripting_effect.h
Normal file
123
engines/zvision/scripting/scripting_effect.h
Normal file
@@ -0,0 +1,123 @@
|
||||
/* 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 SCRIPTING_EFFECT_H_INCLUDED
|
||||
#define SCRIPTING_EFFECT_H_INCLUDED
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
struct Point;
|
||||
class WriteStream;
|
||||
}
|
||||
|
||||
namespace ZVision {
|
||||
|
||||
class ZVision;
|
||||
|
||||
/**
|
||||
* The base class that represents effects created from Actions.
|
||||
* This class is virtual.
|
||||
*
|
||||
* Detailed Description:
|
||||
* A scene has Controls. By interacting with the controls, the user
|
||||
* causes Actions to execute. Certain Actions create 'effects', for
|
||||
* example, a sound or an animation. This is the base class for
|
||||
* those effects.
|
||||
*/
|
||||
class ScriptingEffect {
|
||||
public:
|
||||
|
||||
enum ScriptingEffectType {
|
||||
SCRIPTING_EFFECT_ANIM = 1,
|
||||
SCRIPTING_EFFECT_AUDIO = 2,
|
||||
SCRIPTING_EFFECT_DISTORT = 4,
|
||||
SCRIPTING_EFFECT_PANTRACK = 8,
|
||||
SCRIPTING_EFFECT_REGION = 16,
|
||||
SCRIPTING_EFFECT_TIMER = 32,
|
||||
SCRIPTING_EFFECT_TTYTXT = 64,
|
||||
SCRIPTING_EFFECT_UNKNOWN = 128,
|
||||
SCRIPTING_EFFECT_ALL = 255
|
||||
};
|
||||
|
||||
ScriptingEffect() : _engine(0), _key(0), _type(SCRIPTING_EFFECT_UNKNOWN) {}
|
||||
ScriptingEffect(ZVision *engine, uint32 key, ScriptingEffectType type) : _engine(engine), _key(key), _type(type) {}
|
||||
virtual ~ScriptingEffect() {}
|
||||
|
||||
uint32 getKey() {
|
||||
return _key;
|
||||
}
|
||||
ScriptingEffectType getType() {
|
||||
return _type;
|
||||
}
|
||||
|
||||
virtual bool process(uint32 deltaTimeInMillis) {
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Serialize a SideFX for save game use. This should only be used if a SideFX needs
|
||||
* to save values that would be different from initialization. AKA a TimerNode needs to
|
||||
* store the amount of time left on the timer. Any Controls overriding this *MUST* write
|
||||
* their key as the first data outputted. The default implementation is NOP.
|
||||
*
|
||||
* NOTE: If this method is overridden, you MUST also override deserialize()
|
||||
* and needsSerialization()
|
||||
*
|
||||
* @param stream Stream to write any needed data to
|
||||
*/
|
||||
virtual void serialize(Common::WriteStream *stream) {}
|
||||
/**
|
||||
* De-serialize data from a save game stream. This should only be implemented if the
|
||||
* SideFX also implements serialize(). The calling method assumes the size of the
|
||||
* data read from the stream exactly equals that written in serialize(). The default
|
||||
* implementation is NOP.
|
||||
*
|
||||
* NOTE: If this method is overridden, you MUST also override serialize()
|
||||
* and needsSerialization()
|
||||
*
|
||||
* @param stream Save game file stream
|
||||
*/
|
||||
virtual void deserialize(Common::SeekableReadStream *stream) {}
|
||||
/**
|
||||
* If a SideFX overrides serialize() and deserialize(), this should return true
|
||||
*
|
||||
* @return Does the SideFX need save game serialization?
|
||||
*/
|
||||
virtual inline bool needsSerialization() {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool stop() {
|
||||
return true;
|
||||
}
|
||||
virtual void kill() {}
|
||||
|
||||
protected:
|
||||
ZVision *_engine;
|
||||
uint32 _key;
|
||||
ScriptingEffectType _type;
|
||||
|
||||
// Static member functions
|
||||
public:
|
||||
|
||||
};
|
||||
} // End of namespace ZVision
|
||||
|
||||
#endif // SCRIPTING_EFFECT_H_INCLUDED
|
||||
Reference in New Issue
Block a user