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/commands/changecommand.h"
24 #include "mutationofjb/script.h"
25 #include "mutationofjb/gamedata.h"
26 
27 /** @file
28  * "CHANGE" <entity> " " <register> " " <sceneId> " " <entityId> " " <value>
29  *
30  * Changes entity register value for specified scene.
31  * <entity>   1B  Entity to change register for.
32  *                Possible values:
33  *                  'D' - door
34  *                  'O' - object
35  *                  'S' - static
36  *                  ''  - scene
37  * <register> 2B  Register name.
38  * <sceneId>  2B  Scene ID.
39  * <entityid> 2B  Entity ID.
40  * <value>    *B  Value (variable length).
41  */
42 
43 namespace MutationOfJB {
44 
parseValueString(const Common::String & valueString,bool changeEntity,uint8 & sceneId,uint8 & entityId,ChangeCommand::ChangeRegister & reg,ChangeCommand::ChangeOperation & op,ChangeCommandValue & ccv)45 bool ChangeCommandParser::parseValueString(const Common::String &valueString, bool changeEntity, uint8 &sceneId, uint8 &entityId, ChangeCommand::ChangeRegister &reg, ChangeCommand::ChangeOperation &op, ChangeCommandValue &ccv) {
46 	if (changeEntity) {
47 		if (valueString.size() < 8) {
48 			return false;
49 		}
50 	} else {
51 		if (valueString.size() < 7) {
52 			return false;
53 		}
54 	}
55 
56 	sceneId = atoi(valueString.c_str() + 3);
57 	if (changeEntity) {
58 		entityId = atoi(valueString.c_str() + 6);
59 	}
60 	const char *val = "";
61 	if (changeEntity) {
62 		if (valueString.size() >= 9) {
63 			val = valueString.c_str() + 9;
64 		}
65 	} else {
66 		if (valueString.size() >= 6) {
67 			val = valueString.c_str() + 6;
68 		}
69 	}
70 
71 	if (valueString.hasPrefix("NM")) {
72 		reg = ChangeCommand::NM;
73 		op = ChangeCommand::SetValue;
74 		strncpy(ccv._strVal, val, MAX_ENTITY_NAME_LENGTH);
75 	} else if (valueString.hasPrefix("LT")) {
76 		reg = ChangeCommand::LT;
77 		ccv._byteVal = parseInteger(val, op);
78 	} else if (valueString.hasPrefix("SX")) {
79 		reg = ChangeCommand::SX;
80 		ccv._wordVal = parseInteger(val, op);
81 	} else if (valueString.hasPrefix("SY")) {
82 		reg = ChangeCommand::SY;
83 		ccv._wordVal = parseInteger(val, op);
84 	} else if (valueString.hasPrefix("XX")) {
85 		reg = ChangeCommand::XX;
86 		ccv._wordVal = parseInteger(val, op);
87 	} else if (valueString.hasPrefix("YY")) {
88 		reg = ChangeCommand::YY;
89 		ccv._byteVal = parseInteger(val, op);
90 	} else if (valueString.hasPrefix("XL")) {
91 		reg = ChangeCommand::XL;
92 		ccv._wordVal = parseInteger(val, op);
93 	} else if (valueString.hasPrefix("YL")) {
94 		reg = ChangeCommand::YL;
95 		ccv._byteVal = parseInteger(val, op);
96 	} else if (valueString.hasPrefix("WX")) {
97 		reg = ChangeCommand::WX;
98 		ccv._wordVal = parseInteger(val, op);
99 	} else if (valueString.hasPrefix("WY")) {
100 		reg = ChangeCommand::WY;
101 		ccv._byteVal = parseInteger(val, op);
102 	} else if (valueString.hasPrefix("AC")) {
103 		reg = ChangeCommand::AC;
104 		ccv._byteVal = parseInteger(val, op);
105 	} else if (valueString.hasPrefix("FA")) {
106 		reg = ChangeCommand::FA;
107 		ccv._byteVal = parseInteger(val, op);
108 	} else if (valueString.hasPrefix("FR")) {
109 		reg = ChangeCommand::FR;
110 		ccv._byteVal = parseInteger(val, op);
111 	} else if (valueString.hasPrefix("NA")) {
112 		reg = ChangeCommand::NA;
113 		ccv._byteVal = parseInteger(val, op);
114 	} else if (valueString.hasPrefix("FS")) {
115 		reg = ChangeCommand::FS;
116 		ccv._byteVal = parseInteger(val, op);
117 	} else if (valueString.hasPrefix("CA")) {
118 		reg = ChangeCommand::CA;
119 		ccv._byteVal = parseInteger(val, op);
120 	} else if (valueString.hasPrefix("DS")) {
121 		reg = ChangeCommand::DS;
122 		ccv._byteVal = parseInteger(val, op);
123 	} else if (valueString.hasPrefix("DL")) {
124 		reg = ChangeCommand::DL;
125 		ccv._byteVal = parseInteger(val, op);
126 	} else if (valueString.hasPrefix("ND")) {
127 		reg = ChangeCommand::ND;
128 		ccv._byteVal = parseInteger(val, op);
129 	} else if (valueString.hasPrefix("NO")) {
130 		reg = ChangeCommand::NO;
131 		ccv._byteVal = parseInteger(val, op);
132 	} else if (valueString.hasPrefix("NS")) {
133 		reg = ChangeCommand::NS;
134 		ccv._byteVal = parseInteger(val, op);
135 	} else if (valueString.hasPrefix("PF")) {
136 		reg = ChangeCommand::PF;
137 		ccv._byteVal = parseInteger(val, op);
138 	} else if (valueString.hasPrefix("PL")) {
139 		reg = ChangeCommand::PL;
140 		ccv._byteVal = parseInteger(val, op);
141 	} else if (valueString.hasPrefix("PD")) {
142 		reg = ChangeCommand::PD;
143 		ccv._byteVal = parseInteger(val, op);
144 	}
145 
146 	return true;
147 }
148 
149 
parse(const Common::String & line,ScriptParseContext &,Command * & command)150 bool ChangeDoorCommandParser::parse(const Common::String &line, ScriptParseContext &, Command *&command) {
151 	if (!line.hasPrefix("CHANGED ")) {
152 		return false;
153 	}
154 	uint8 sceneId = 0;
155 	uint8 objectId = 0;
156 	ChangeCommand::ChangeRegister reg;
157 	ChangeCommand::ChangeOperation op;
158 	ChangeCommandValue val;
159 	if (!parseValueString(line.c_str() + 8, true, sceneId, objectId, reg, op, val)) {
160 		return false;
161 	}
162 
163 	command = new ChangeDoorCommand(sceneId, objectId, reg, op, val);
164 	return true;
165 }
166 
parse(const Common::String & line,ScriptParseContext &,Command * & command)167 bool ChangeObjectCommandParser::parse(const Common::String &line, ScriptParseContext &, Command *&command) {
168 	if (!line.hasPrefix("CHANGEO ")) {
169 		return false;
170 	}
171 	uint8 sceneId = 0;
172 	uint8 objectId = 0;
173 	ChangeCommand::ChangeRegister reg;
174 	ChangeCommand::ChangeOperation op;
175 	ChangeCommandValue val;
176 	if (!parseValueString(line.c_str() + 8, true, sceneId, objectId, reg, op, val)) {
177 		return false;
178 	}
179 
180 	command = new ChangeObjectCommand(sceneId, objectId, reg, op, val);
181 	return true;
182 }
183 
parse(const Common::String & line,ScriptParseContext &,Command * & command)184 bool ChangeStaticCommandParser::parse(const Common::String &line, ScriptParseContext &, Command *&command) {
185 	if (!line.hasPrefix("CHANGES ")) {
186 		return false;
187 	}
188 	uint8 sceneId = 0;
189 	uint8 objectId = 0;
190 	ChangeCommand::ChangeRegister reg;
191 	ChangeCommand::ChangeOperation op;
192 	ChangeCommandValue val;
193 	if (!parseValueString(line.c_str() + 8, true, sceneId, objectId, reg, op, val)) {
194 		return false;
195 	}
196 
197 	command = new ChangeStaticCommand(sceneId, objectId, reg, op, val);
198 	return true;
199 }
200 
parse(const Common::String & line,ScriptParseContext &,Command * & command)201 bool ChangeSceneCommandParser::parse(const Common::String &line, ScriptParseContext &, Command *&command) {
202 	if (!line.hasPrefix("CHANGE ")) {
203 		return false;
204 	}
205 	uint8 sceneId = 0;
206 	uint8 objectId = 0;
207 	ChangeCommand::ChangeRegister reg;
208 	ChangeCommand::ChangeOperation op;
209 	ChangeCommandValue val;
210 	if (!parseValueString(line.c_str() + 7, false, sceneId, objectId, reg, op, val)) {
211 		return false;
212 	}
213 
214 	command = new ChangeSceneCommand(sceneId, objectId, reg, op, val);
215 	return true;
216 }
217 
parseInteger(const char * val,ChangeCommand::ChangeOperation & op)218 int ChangeCommandParser::parseInteger(const char *val, ChangeCommand::ChangeOperation &op) {
219 	op = ChangeCommand::SetValue;
220 
221 	if (!val || !(*val)) {
222 		return 0;
223 	}
224 
225 	if (val[0] == '\\') {
226 		op = ChangeCommand::SetValue;
227 		val++;
228 	} else if (val[0] == '+') {
229 		op = ChangeCommand::AddValue;
230 		val++;
231 	} else if (val[0] == '-') {
232 		op = ChangeCommand::SubtractValue;
233 		val++;
234 	}
235 
236 	return atoi(val);
237 }
238 
239 
getRegisterAsString() const240 const char *ChangeCommand::getRegisterAsString() const {
241 	switch (_register) {
242 	case NM:
243 		return "NM";
244 	case LT:
245 		return "LT";
246 	case SX:
247 		return "SX";
248 	case SY:
249 		return "SY";
250 	case XX:
251 		return "XX";
252 	case YY:
253 		return "YY";
254 	case XL:
255 		return "XL";
256 	case YL:
257 		return "YL";
258 	case WX:
259 		return "WX";
260 	case WY:
261 		return "WY";
262 	case SP:
263 		return "SP";
264 	case AC:
265 		return "AC";
266 	case FA:
267 		return "FA";
268 	case FR:
269 		return "FR";
270 	case NA:
271 		return "NA";
272 	case FS:
273 		return "FS";
274 	case CA:
275 		return "CA";
276 	case DS:
277 		return "DS";
278 	case DL:
279 		return "DL";
280 	case ND:
281 		return "ND";
282 	case NO:
283 		return "NO";
284 	case NS:
285 		return "NS";
286 	case PF:
287 		return "PF";
288 	case PL:
289 		return "PL";
290 	case PD:
291 		return "PD";
292 	default:
293 		return "(unknown)";
294 	}
295 }
296 
getValueAsString() const297 Common::String ChangeCommand::getValueAsString() const {
298 	switch (_register) {
299 	case NM:
300 		return Common::String::format("\"%s\"", _value._strVal);
301 	case LT:
302 	case YY:
303 	case YL:
304 	case WY:
305 	case SP:
306 	case AC:
307 	case FA:
308 	case FR:
309 	case NA:
310 	case FS:
311 	case CA:
312 	case DS:
313 	case DL:
314 	case ND:
315 	case NO:
316 	case NS:
317 	case PF:
318 	case PL:
319 	case PD:
320 		return Common::String::format("%d", static_cast<int>(_value._byteVal));
321 	case SX:
322 	case SY:
323 	case XX:
324 	case XL:
325 	case WX:
326 		return Common::String::format("%d", static_cast<int>(_value._wordVal));
327 	default:
328 		return "(unknown)";
329 	}
330 }
331 
getOperationAsString() const332 const char *ChangeCommand::getOperationAsString() const {
333 	switch (_operation) {
334 	case SetValue:
335 		return "=";
336 	case AddValue:
337 		return "+=";
338 	case SubtractValue:
339 		return "-=";
340 	default:
341 		return "(unknown)";
342 	}
343 }
344 
execute(ScriptExecutionContext & scriptExecCtx)345 Command::ExecuteResult ChangeDoorCommand::execute(ScriptExecutionContext &scriptExecCtx) {
346 	Scene *const scene = scriptExecCtx.getGameData().getScene(_sceneId);
347 	if (!scene) {
348 		return Finished;
349 	}
350 
351 	Door *const door = scene->getDoor(_entityId);
352 	if (!door) {
353 		return Finished;
354 	}
355 
356 	switch (_register) {
357 	case NM:
358 		strncpy(door->_name, _value._strVal, MAX_ENTITY_NAME_LENGTH);
359 		break;
360 	case LT:
361 		door->_destSceneId = _value._byteVal;
362 		break;
363 	case SX:
364 		door->_destX = _value._wordVal;
365 		break;
366 	case SY:
367 		door->_destY = _value._wordVal;
368 		break;
369 	case XX:
370 		door->_x = _value._wordVal;
371 		break;
372 	case YY:
373 		door->_y = _value._byteVal;
374 		break;
375 	case XL:
376 		door->_width = _value._wordVal;
377 		break;
378 	case YL:
379 		door->_height = _value._byteVal;
380 		break;
381 	case WX:
382 		door->_walkToX = _value._wordVal;
383 		break;
384 	case WY:
385 		door->_walkToY = _value._byteVal;
386 		break;
387 	case SP:
388 		door->_SP = _value._byteVal;
389 		break;
390 	default:
391 		warning("Object does not support changing this register.");
392 		break;
393 	}
394 
395 	return Finished;
396 }
397 
debugString() const398 Common::String ChangeDoorCommand::debugString() const {
399 	return Common::String::format("SCENE%d.DOOR%d.%s %s %s", _sceneId, _entityId, getRegisterAsString(), getOperationAsString(), getValueAsString().c_str());
400 }
401 
execute(ScriptExecutionContext & scriptExecCtx)402 Command::ExecuteResult ChangeObjectCommand::execute(ScriptExecutionContext &scriptExecCtx) {
403 	Scene *const scene = scriptExecCtx.getGameData().getScene(_sceneId);
404 	if (!scene) {
405 		return Finished;
406 	}
407 
408 	Object *const object = scene->getObject(_entityId, true);
409 	if (!object) {
410 		return Finished;
411 	}
412 
413 	switch (_register) {
414 	case AC:
415 		object->_active = _value._byteVal;
416 		break;
417 	case FA:
418 		object->_firstFrame = _value._byteVal;
419 		break;
420 	case FR:
421 		object->_randomFrame = _value._byteVal;
422 		break;
423 	case NA:
424 		object->_numFrames = _value._byteVal;
425 		break;
426 	case FS:
427 		object->_roomFrameLSB = _value._byteVal;
428 		break;
429 	case CA:
430 		object->_currentFrame = _value._byteVal;
431 		break;
432 	case XX:
433 		object->_x = _value._wordVal;
434 		break;
435 	case YY:
436 		object->_y = _value._byteVal;
437 		break;
438 	case XL:
439 		object->_width = _value._wordVal;
440 		break;
441 	case YL:
442 		object->_height = _value._byteVal;
443 		break;
444 	case WX:
445 		object->_WX = _value._wordVal;
446 		break;
447 	case WY:
448 		object->_roomFrameMSB = _value._byteVal;
449 		break;
450 	case SP:
451 		object->_SP = _value._byteVal;
452 		break;
453 	default:
454 		warning("Object does not support changing this register.");
455 		break;
456 	}
457 
458 	return Finished;
459 }
460 
debugString() const461 Common::String ChangeObjectCommand::debugString() const {
462 	return Common::String::format("SCENE%d.OBJECT%d.%s %s %s", _sceneId, _entityId, getRegisterAsString(), getOperationAsString(), getValueAsString().c_str());
463 }
464 
execute(ScriptExecutionContext & scriptExecCtx)465 Command::ExecuteResult ChangeStaticCommand::execute(ScriptExecutionContext &scriptExecCtx) {
466 	Scene *const scene = scriptExecCtx.getGameData().getScene(_sceneId);
467 	if (!scene) {
468 		return Finished;
469 	}
470 
471 	Static *const stat = scene->getStatic(_entityId);
472 	if (!stat) {
473 		return Finished;
474 	}
475 
476 	switch (_register) {
477 	case AC:
478 		stat->_active = _value._byteVal;
479 		break;
480 	case NM:
481 		strncpy(stat->_name, _value._strVal, MAX_ENTITY_NAME_LENGTH);
482 		break;
483 	case XX:
484 		stat->_x = _value._wordVal;
485 		break;
486 	case YY:
487 		stat->_y = _value._byteVal;
488 		break;
489 	case XL:
490 		stat->_width = _value._wordVal;
491 		break;
492 	case YL:
493 		stat->_height = _value._byteVal;
494 		break;
495 	case WX:
496 		stat->_walkToX = _value._wordVal;
497 		break;
498 	case WY:
499 		stat->_walkToY = _value._byteVal;
500 		break;
501 	case SP:
502 		stat->_walkToFrame = _value._byteVal;
503 		break;
504 	default:
505 		warning("Object does not support changing this register.");
506 		break;
507 	}
508 
509 	return Finished;
510 }
511 
debugString() const512 Common::String ChangeStaticCommand::debugString() const {
513 	return Common::String::format("SCENE%d.STATIC%d.%s %s %s", _sceneId, _entityId, getRegisterAsString(), getOperationAsString(), getValueAsString().c_str());
514 }
515 
execute(ScriptExecutionContext & scriptExecCtx)516 Command::ExecuteResult ChangeSceneCommand::execute(ScriptExecutionContext &scriptExecCtx) {
517 	Scene *const scene = scriptExecCtx.getGameData().getScene(_sceneId);
518 	if (!scene) {
519 		return Finished;
520 	}
521 
522 	switch (_register) {
523 	case DS:
524 		scene->_startup = _value._byteVal;
525 		break;
526 	case DL:
527 		scene->_delay = _value._byteVal;
528 		break;
529 	case ND:
530 		scene->_noDoors = _value._byteVal;
531 		break;
532 	case NO:
533 		scene->_noObjects = _value._byteVal;
534 		break;
535 	case NS:
536 		scene->_noStatics = _value._byteVal;
537 		break;
538 	case PF:
539 		scene->_palRotFirst = _value._byteVal;
540 		break;
541 	case PL:
542 		scene->_palRotLast = _value._byteVal;
543 		break;
544 	case PD:
545 		scene->_palRotDelay = _value._byteVal;
546 		break;
547 	default:
548 		warning("Scene does not support changing this register.");
549 		break;
550 	}
551 
552 	return Finished;
553 }
554 
debugString() const555 Common::String ChangeSceneCommand::debugString() const {
556 	return Common::String::format("SCENE%d.%s %s %s", _sceneId, getRegisterAsString(), getOperationAsString(), getValueAsString().c_str());
557 }
558 
559 }
560