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 "parallaction/exec.h"
24 #include "parallaction/input.h"
25 #include "parallaction/parallaction.h"
26 #include "parallaction/sound.h"
27 
28 #include "common/textconsole.h"
29 
30 namespace Parallaction {
31 
32 #define INST_ON							1
33 #define INST_OFF						2
34 #define INST_X							3
35 #define INST_Y							4
36 #define INST_Z							5
37 #define INST_F							6
38 #define INST_LOOP						7
39 #define INST_ENDLOOP					8
40 #define INST_SHOW						9
41 #define INST_INC						10
42 #define INST_DEC						11
43 #define INST_SET						12
44 #define INST_PUT						13
45 #define INST_CALL						14
46 #define INST_WAIT						15
47 #define INST_START						16
48 #define INST_SOUND						17
49 #define INST_MOVE						18
50 #define INST_ENDSCRIPT					19
51 
52 #define SetOpcodeTable(x) table = &x;
53 
54 
55 
56 typedef Common::Functor1Mem<CommandContext&, void, CommandExec_ns> OpcodeV1;
57 #define COMMAND_OPCODE(op) table->push_back(new OpcodeV1(this, &CommandExec_ns::cmdOp_##op))
58 #define DECLARE_COMMAND_OPCODE(op) void CommandExec_ns::cmdOp_##op(CommandContext& ctxt)
59 
60 typedef Common::Functor1Mem<ProgramContext&, void, ProgramExec_ns> OpcodeV2;
61 #define INSTRUCTION_OPCODE(op) table->push_back(new OpcodeV2(this, &ProgramExec_ns::instOp_##op))
62 #define DECLARE_INSTRUCTION_OPCODE(op) void ProgramExec_ns::instOp_##op(ProgramContext& ctxt)
63 
64 extern const char *_instructionNamesRes_ns[];
65 
66 
DECLARE_INSTRUCTION_OPCODE(on)67 DECLARE_INSTRUCTION_OPCODE(on) {
68 	InstructionPtr inst = ctxt._inst;
69 
70 	inst->_a->_flags |= kFlagsActive;
71 	inst->_a->_flags &= ~kFlagsRemove;
72 }
73 
74 
DECLARE_INSTRUCTION_OPCODE(off)75 DECLARE_INSTRUCTION_OPCODE(off) {
76 	ctxt._inst->_a->_flags |= kFlagsRemove;
77 }
78 
79 
DECLARE_INSTRUCTION_OPCODE(loop)80 DECLARE_INSTRUCTION_OPCODE(loop) {
81 	InstructionPtr inst = ctxt._inst;
82 
83 	ctxt._program->_loopCounter = inst->_opB.getValue();
84 	ctxt._program->_loopStart = ctxt._ip;
85 }
86 
87 
DECLARE_INSTRUCTION_OPCODE(endloop)88 DECLARE_INSTRUCTION_OPCODE(endloop) {
89 	if (--ctxt._program->_loopCounter > 0) {
90 		ctxt._ip = ctxt._program->_loopStart;
91 	}
92 }
93 
DECLARE_INSTRUCTION_OPCODE(inc)94 DECLARE_INSTRUCTION_OPCODE(inc) {
95 	InstructionPtr inst = ctxt._inst;
96 	int16 _si = inst->_opB.getValue();
97 
98 	if (inst->_flags & kInstMod) {	// mod
99 		int16 _bx = (_si > 0 ? _si : -_si);
100 		if (ctxt._modCounter % _bx != 0) return;
101 
102 		_si = (_si > 0 ?  1 : -1);
103 	}
104 
105 	int16 lvalue = inst->_opA.getValue();
106 
107 	if (inst->_index == INST_INC) {
108 		lvalue += _si;
109 	} else {
110 		lvalue -= _si;
111 	}
112 
113 	inst->_opA.setValue(lvalue);
114 
115 }
116 
117 
DECLARE_INSTRUCTION_OPCODE(set)118 DECLARE_INSTRUCTION_OPCODE(set) {
119 	ctxt._inst->_opA.setValue(ctxt._inst->_opB.getValue());
120 }
121 
122 
DECLARE_INSTRUCTION_OPCODE(put)123 DECLARE_INSTRUCTION_OPCODE(put) {
124 	InstructionPtr inst = ctxt._inst;
125 	Common::Rect r;
126 	inst->_a->getFrameRect(r);
127 
128 	Graphics::Surface v18;
129 	v18.init(r.width(), r.height(), r.width(), inst->_a->getFrameData(), Graphics::PixelFormat::createFormatCLUT8());
130 
131 	int16 x = inst->_opA.getValue();
132 	int16 y = inst->_opB.getValue();
133 	bool mask = (inst->_flags & kInstMaskedPut) == kInstMaskedPut;
134 
135 	_vm->_gfx->patchBackground(v18, x, y, mask);
136 }
137 
DECLARE_INSTRUCTION_OPCODE(show)138 DECLARE_INSTRUCTION_OPCODE(show) {
139 	ctxt._suspend = true;
140 }
141 
DECLARE_INSTRUCTION_OPCODE(invalid)142 DECLARE_INSTRUCTION_OPCODE(invalid) {
143 	error("Can't execute invalid opcode %i", ctxt._inst->_index);
144 }
145 
DECLARE_INSTRUCTION_OPCODE(call)146 DECLARE_INSTRUCTION_OPCODE(call) {
147 	_vm->callFunction(ctxt._inst->_immediate, 0);
148 }
149 
150 
DECLARE_INSTRUCTION_OPCODE(wait)151 DECLARE_INSTRUCTION_OPCODE(wait) {
152 	if (g_engineFlags & kEngineWalking) {
153 		ctxt._ip--;
154 		ctxt._suspend = true;
155 	}
156 }
157 
158 
DECLARE_INSTRUCTION_OPCODE(start)159 DECLARE_INSTRUCTION_OPCODE(start) {
160 	ctxt._inst->_a->_flags |= (kFlagsActing | kFlagsActive);
161 }
162 
163 
DECLARE_INSTRUCTION_OPCODE(sound)164 DECLARE_INSTRUCTION_OPCODE(sound) {
165 	_vm->_activeZone = ctxt._inst->_z;
166 }
167 
168 
DECLARE_INSTRUCTION_OPCODE(move)169 DECLARE_INSTRUCTION_OPCODE(move) {
170 	InstructionPtr inst = ctxt._inst;
171 
172 	int16 x = inst->_opA.getValue();
173 	int16 y = inst->_opB.getValue();
174 
175 	_vm->scheduleWalk(x, y, false);
176 }
177 
DECLARE_INSTRUCTION_OPCODE(endscript)178 DECLARE_INSTRUCTION_OPCODE(endscript) {
179 	if ((ctxt._anim->_flags & kFlagsLooping) == 0) {
180 		ctxt._anim->_flags &= ~kFlagsActing;
181 		_vm->_cmdExec->run(ctxt._anim->_commands, ctxt._anim);
182 		ctxt._program->_status = kProgramDone;
183 	}
184 
185 	ctxt._ip = 0;
186 	ctxt._suspend = true;
187 }
188 
189 
190 
191 
DECLARE_COMMAND_OPCODE(invalid)192 DECLARE_COMMAND_OPCODE(invalid) {
193 	error("Can't execute invalid command '%i'", ctxt._cmd->_id);
194 }
195 
DECLARE_COMMAND_OPCODE(set)196 DECLARE_COMMAND_OPCODE(set) {
197 	if (ctxt._cmd->_flags & kFlagsGlobal) {
198 		ctxt._cmd->_flags &= ~kFlagsGlobal;
199 		g_globalFlags |= ctxt._cmd->_flags;
200 	} else {
201 		_vm->setLocationFlags(ctxt._cmd->_flags);
202 	}
203 }
204 
205 
DECLARE_COMMAND_OPCODE(clear)206 DECLARE_COMMAND_OPCODE(clear) {
207 	if (ctxt._cmd->_flags & kFlagsGlobal) {
208 		ctxt._cmd->_flags &= ~kFlagsGlobal;
209 		g_globalFlags &= ~ctxt._cmd->_flags;
210 	} else {
211 		_vm->clearLocationFlags(ctxt._cmd->_flags);
212 	}
213 }
214 
215 
DECLARE_COMMAND_OPCODE(start)216 DECLARE_COMMAND_OPCODE(start) {
217 	ctxt._cmd->_zone->_flags |= kFlagsActing;
218 }
219 
220 
DECLARE_COMMAND_OPCODE(speak)221 DECLARE_COMMAND_OPCODE(speak) {
222 	if (ACTIONTYPE(ctxt._cmd->_zone) == kZoneSpeak) {
223 		_vm->enterDialogueMode(ctxt._cmd->_zone);
224 	} else {
225 		_vm->_activeZone = ctxt._cmd->_zone;
226 	}
227 }
228 
229 
DECLARE_COMMAND_OPCODE(get)230 DECLARE_COMMAND_OPCODE(get) {
231 	ctxt._cmd->_zone->_flags &= ~kFlagsFixed;
232 	_vm->runZone(ctxt._cmd->_zone);
233 }
234 
235 
DECLARE_COMMAND_OPCODE(location)236 DECLARE_COMMAND_OPCODE(location) {
237 	_vm->scheduleLocationSwitch(ctxt._cmd->_string.c_str());
238 }
239 
240 
DECLARE_COMMAND_OPCODE(open)241 DECLARE_COMMAND_OPCODE(open) {
242 	_vm->updateDoor(ctxt._cmd->_zone, false);
243 }
244 
245 
DECLARE_COMMAND_OPCODE(close)246 DECLARE_COMMAND_OPCODE(close) {
247 	_vm->updateDoor(ctxt._cmd->_zone, true);
248 }
249 
DECLARE_COMMAND_OPCODE(on)250 DECLARE_COMMAND_OPCODE(on) {
251 	_vm->showZone(ctxt._cmd->_zone, true);
252 }
253 
254 
DECLARE_COMMAND_OPCODE(off)255 DECLARE_COMMAND_OPCODE(off) {
256 	_vm->showZone(ctxt._cmd->_zone, false);
257 }
258 
259 
DECLARE_COMMAND_OPCODE(call)260 DECLARE_COMMAND_OPCODE(call) {
261 	_vm->callFunction(ctxt._cmd->_callable, &ctxt._z);
262 }
263 
264 
DECLARE_COMMAND_OPCODE(toggle)265 DECLARE_COMMAND_OPCODE(toggle) {
266 	if (ctxt._cmd->_flags & kFlagsGlobal) {
267 		ctxt._cmd->_flags &= ~kFlagsGlobal;
268 		g_globalFlags ^= ctxt._cmd->_flags;
269 	} else {
270 		_vm->toggleLocationFlags(ctxt._cmd->_flags);
271 	}
272 }
273 
274 
DECLARE_COMMAND_OPCODE(drop)275 DECLARE_COMMAND_OPCODE(drop){
276 	_vm->dropItem( ctxt._cmd->_object );
277 }
278 
279 
DECLARE_COMMAND_OPCODE(quit)280 DECLARE_COMMAND_OPCODE(quit) {
281 	_vm->quitGame();
282 }
283 
284 
DECLARE_COMMAND_OPCODE(move)285 DECLARE_COMMAND_OPCODE(move) {
286 	_vm->scheduleWalk(ctxt._cmd->_move.x, ctxt._cmd->_move.y, false);
287 }
288 
289 
DECLARE_COMMAND_OPCODE(stop)290 DECLARE_COMMAND_OPCODE(stop) {
291 	ctxt._cmd->_zone->_flags &= ~kFlagsActing;
292 }
293 
CommandExec_ns(Parallaction_ns * vm)294 CommandExec_ns::CommandExec_ns(Parallaction_ns* vm) : CommandExec(vm), _vm(vm) {
295 	CommandOpcodeSet *table = 0;
296 
297 	SetOpcodeTable(_opcodes);
298 	COMMAND_OPCODE(invalid);
299 	COMMAND_OPCODE(set);
300 	COMMAND_OPCODE(clear);
301 	COMMAND_OPCODE(start);
302 	COMMAND_OPCODE(speak);
303 	COMMAND_OPCODE(get);
304 	COMMAND_OPCODE(location);
305 	COMMAND_OPCODE(open);
306 	COMMAND_OPCODE(close);
307 	COMMAND_OPCODE(on);
308 	COMMAND_OPCODE(off);
309 	COMMAND_OPCODE(call);
310 	COMMAND_OPCODE(toggle);
311 	COMMAND_OPCODE(drop);
312 	COMMAND_OPCODE(quit);
313 	COMMAND_OPCODE(move);
314 	COMMAND_OPCODE(stop);
315 }
316 
ProgramExec_ns(Parallaction_ns * vm)317 ProgramExec_ns::ProgramExec_ns(Parallaction_ns *vm) : _vm(vm) {
318 	_instructionNames = _instructionNamesRes_ns;
319 
320 	ProgramOpcodeSet *table = 0;
321 
322 	SetOpcodeTable(_opcodes);
323 	INSTRUCTION_OPCODE(invalid);
324 	INSTRUCTION_OPCODE(on);
325 	INSTRUCTION_OPCODE(off);
326 	INSTRUCTION_OPCODE(set);		// x
327 	INSTRUCTION_OPCODE(set);		// y
328 	INSTRUCTION_OPCODE(set);		// z
329 	INSTRUCTION_OPCODE(set);		// f
330 	INSTRUCTION_OPCODE(loop);
331 	INSTRUCTION_OPCODE(endloop);
332 	INSTRUCTION_OPCODE(show);
333 	INSTRUCTION_OPCODE(inc);
334 	INSTRUCTION_OPCODE(inc);		// dec
335 	INSTRUCTION_OPCODE(set);
336 	INSTRUCTION_OPCODE(put);
337 	INSTRUCTION_OPCODE(call);
338 	INSTRUCTION_OPCODE(wait);
339 	INSTRUCTION_OPCODE(start);
340 	INSTRUCTION_OPCODE(sound);
341 	INSTRUCTION_OPCODE(move);
342 	INSTRUCTION_OPCODE(endscript);
343 }
344 
345 }	// namespace Parallaction
346