1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "mutationofjb/script.h"
24
25 #include "common/hashmap.h"
26 #include "common/hash-str.h"
27 #include "common/stream.h"
28 #include "common/debug.h"
29 #include "mutationofjb/commands/command.h"
30 #include "mutationofjb/commands/ifcommand.h"
31 #include "mutationofjb/commands/ifitemcommand.h"
32 #include "mutationofjb/commands/ifpiggycommand.h"
33 #include "mutationofjb/commands/endblockcommand.h"
34 #include "mutationofjb/commands/changecommand.h"
35 #include "mutationofjb/commands/saycommand.h"
36 #include "mutationofjb/commands/additemcommand.h"
37 #include "mutationofjb/commands/removeitemcommand.h"
38 #include "mutationofjb/commands/removeallitemscommand.h"
39 #include "mutationofjb/commands/labelcommand.h"
40 #include "mutationofjb/commands/gotocommand.h"
41 #include "mutationofjb/commands/camefromcommand.h"
42 #include "mutationofjb/commands/callmacrocommand.h"
43 #include "mutationofjb/commands/newroomcommand.h"
44 #include "mutationofjb/commands/renamecommand.h"
45 #include "mutationofjb/commands/definestructcommand.h"
46 #include "mutationofjb/commands/talkcommand.h"
47 #include "mutationofjb/commands/randomcommand.h"
48 #include "mutationofjb/commands/setcolorcommand.h"
49 #include "mutationofjb/commands/specialshowcommand.h"
50 #include "mutationofjb/commands/switchpartcommand.h"
51 #include "mutationofjb/commands/loadplayercommand.h"
52 #include "mutationofjb/commands/bitmapvisibilitycommand.h"
53 #include "mutationofjb/commands/playanimationcommand.h"
54 #include "mutationofjb/commands/setobjectframecommand.h"
55 #include "mutationofjb/game.h"
56
57 namespace MutationOfJB {
58
getParsers()59 static CommandParser **getParsers() {
60 static CommandParser *parsers[] = {
61 new IfPiggyCommandParser,
62 new IfItemCommandParser,
63 new IfCommandParser,
64 new CameFromCommandParser,
65 new CallMacroCommandParser,
66 new EndBlockCommandParser,
67 new ChangeDoorCommandParser,
68 new ChangeObjectCommandParser,
69 new ChangeStaticCommandParser,
70 new ChangeSceneCommandParser,
71 new DefineStructCommandParser,
72 new SayCommandParser,
73 new TalkCommandParser,
74 new AddItemCommandParser,
75 new RemoveItemCommandParser,
76 new RemoveAllItemsCommandParser,
77 new RenameCommandParser,
78 new NewRoomCommandParser,
79 new GotoCommandParser,
80 new LabelCommandParser,
81 new RandomCommandParser,
82 new RandomBlockStartParser,
83 new SetColorCommandParser,
84 new SpecialShowCommandParser,
85 new SwitchPartCommandParser,
86 new LoadPlayerCommandParser,
87 new BitmapVisibilityCommandParser,
88 new PlayAnimationCommandParser,
89 new SetObjectFrameCommandParser,
90 nullptr
91 };
92
93 return parsers;
94 }
95
96
ScriptParseContext(Common::SeekableReadStream & stream)97 ScriptParseContext::ScriptParseContext(Common::SeekableReadStream &stream) :
98 _stream(stream),
99 _currentCommand(nullptr),
100 _lastCommand(nullptr),
101 _pendingRandomCommand(nullptr) {}
102
readLine(Common::String & line)103 bool ScriptParseContext::readLine(Common::String &line) {
104 do {
105 Common::String str = _stream.readLine();
106 if (str.empty())
107 continue;
108
109 if (str[0] != '.') {
110 line = str;
111 if (line[0] == '*') {
112 line.deleteChar(0);
113 }
114 return true;
115 }
116 } while (!_stream.eos());
117
118 return false;
119 }
120
addConditionalCommand(ConditionalCommand * command,char tag,bool firstHash)121 void ScriptParseContext::addConditionalCommand(ConditionalCommand *command, char tag, bool firstHash) {
122 ConditionalCommandInfo cmi = {command, tag, firstHash};
123 _pendingCondCommands.push_back(cmi);
124 }
125
126
pushReturnCommand(Command * cmd)127 void ScriptExecutionContext::pushReturnCommand(Command *cmd) {
128 _callStack.push(cmd);
129 }
130
popReturnCommand()131 Command *ScriptExecutionContext::popReturnCommand() {
132 if (_callStack.empty()) {
133 return nullptr;
134 }
135
136 return _callStack.pop();
137 }
138
getGame()139 Game &ScriptExecutionContext::getGame() {
140 return _game;
141 }
142
getGameData()143 GameData &ScriptExecutionContext::getGameData() {
144 return _game.getGameData();
145 }
146
clear()147 void ScriptExecutionContext::clear() {
148 _callStack.clear();
149 }
150
runActiveCommand()151 Command::ExecuteResult ScriptExecutionContext::runActiveCommand() {
152 while (_activeCommand) {
153 const Command::ExecuteResult result = _activeCommand->execute(*this);
154 if (result == Command::Finished) {
155 _activeCommand = _activeCommand->next();
156 } else {
157 return result;
158 }
159 }
160
161 return Command::Finished;
162 }
163
startCommand(Command * cmd)164 Command::ExecuteResult ScriptExecutionContext::startCommand(Command *cmd) {
165 if (_activeCommand) {
166 warning("Trying to start command while another one is running.");
167 return Command::Finished;
168 }
169 getGameData()._color = WHITE; // The original game resets the color to WHITE beforing running script sections.
170 clear();
171 _activeCommand = cmd;
172 return runActiveCommand();
173 }
174
startStartupSection()175 Command::ExecuteResult ScriptExecutionContext::startStartupSection() {
176 Script *localScript = _localScriptOverride ? _localScriptOverride : _game.getLocalScript();
177
178 if (localScript) {
179 Command *const startupCmd = localScript->getStartup(_game.getGameData().getCurrentScene()->_startup);
180 if (startupCmd) {
181 return startCommand(startupCmd);
182 }
183 }
184
185 return Command::Finished;
186 }
187
getMacro(const Common::String & name) const188 Command *ScriptExecutionContext::getMacro(const Common::String &name) const {
189 Command *cmd = nullptr;
190
191 Script *const localScript = _localScriptOverride ? _localScriptOverride : _game.getLocalScript();
192 Script *const globalScript = _game.getGlobalScript();
193
194 if (localScript) {
195 cmd = localScript->getMacro(name);
196 }
197
198 if (!cmd && globalScript) {
199 cmd = globalScript->getMacro(name);
200 }
201
202 return cmd;
203 }
204
getExtra(const Common::String & name) const205 Command *ScriptExecutionContext::getExtra(const Common::String &name) const {
206 Command *cmd = nullptr;
207
208 Script *const localScript = _localScriptOverride ? _localScriptOverride : _game.getLocalScript();
209 Script *const globalScript = _game.getGlobalScript();
210
211 if (localScript) {
212 cmd = localScript->getExtra(name);
213 }
214
215 if (!cmd && globalScript) {
216 cmd = globalScript->getExtra(name);
217 }
218
219 return cmd;
220 }
221
isCommandRunning() const222 bool ScriptExecutionContext::isCommandRunning() const {
223 return _activeCommand;
224 }
225
loadFromStream(Common::SeekableReadStream & stream)226 bool Script::loadFromStream(Common::SeekableReadStream &stream) {
227 destroy();
228
229 CommandParser **parsers = getParsers();
230
231 ScriptParseContext parseCtx(stream);
232
233 Common::String line;
234
235 Command *lastCmd = nullptr;
236 CommandParser *lastParser = nullptr;
237 while (parseCtx.readLine(line)) {
238 Command *currentCmd = nullptr;
239 CommandParser *currentParser = nullptr;
240
241 for (CommandParser **parser = parsers; *parser; ++parser) {
242 if ((*parser)->parse(line, parseCtx, currentCmd)) {
243 currentParser = *parser;
244 break;
245 }
246 }
247 if (!currentParser) {
248 continue;
249 }
250
251 if (lastParser) {
252 lastParser->transition(parseCtx, lastCmd, currentCmd, currentParser);
253 }
254
255 if (currentCmd) {
256 _allCommands.push_back(currentCmd);
257 }
258
259 lastCmd = currentCmd;
260 lastParser = currentParser;
261 }
262
263 for (CommandParser **parser = parsers; *parser; ++parser) {
264 (*parser)->finish(parseCtx);
265 }
266
267 for (ActionInfos::iterator it = parseCtx._actionInfos.begin(); it != parseCtx._actionInfos.end(); ++it) {
268 _actionInfos[it->_action].push_back(*it);
269 }
270
271 _macros = parseCtx._macros;
272 _startups = parseCtx._startups;
273 _extras = parseCtx._extras;
274
275 return true;
276 }
277
destroy()278 void Script::destroy() {
279 for (Commands::iterator it = _allCommands.begin(); it != _allCommands.end(); ++it) {
280 delete *it;
281 }
282 _allCommands.clear();
283 }
284
~Script()285 Script::~Script() {
286 destroy();
287 }
288
getActionInfos(ActionInfo::Action action)289 const ActionInfos &Script::getActionInfos(ActionInfo::Action action) {
290 return _actionInfos[action];
291 }
292
getAllCommands() const293 const Commands &Script::getAllCommands() const {
294 return _allCommands;
295 }
296
getMacros() const297 const Macros &Script::getMacros() const {
298 return _macros;
299 }
300
getMacro(const Common::String & name) const301 Command *Script::getMacro(const Common::String &name) const {
302 Macros::const_iterator it = _macros.find(name);
303 if (it == _macros.end()) {
304 return nullptr;
305 }
306
307 return it->_value;
308 }
309
getStartups() const310 const Startups &Script::getStartups() const {
311 return _startups;
312 }
313
getStartup(uint8 startupId) const314 Command *Script::getStartup(uint8 startupId) const {
315 Startups::const_iterator it = _startups.find(startupId);
316 if (it == _startups.end()) {
317 return nullptr;
318 }
319
320 return it->_value;
321 }
322
getExtra(const Common::String & name) const323 Command *Script::getExtra(const Common::String &name) const {
324 Extras::const_iterator it = _extras.find(name);
325 if (it == _extras.end()) {
326 return nullptr;
327 }
328
329 return it->_value;
330 }
331
332 }
333