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 ®, 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