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