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 "ultima/nuvie/core/nuvie_defs.h"
24 #include "ultima/nuvie/misc/u6_misc.h"
25 #include "ultima/nuvie/core/player.h"
26 #include "ultima/nuvie/core/party.h"
27 #include "ultima/nuvie/usecode/u6_usecode.h"
28 #include "ultima/nuvie/actors/actor_manager.h"
29 #include "ultima/nuvie/core/game_clock.h"
30 #include "ultima/nuvie/core/map.h"
31 #include "ultima/nuvie/gui/widgets/map_window.h"
32 #include "ultima/nuvie/core/effect.h"
33 #include "ultima/nuvie/script/script.h"
34 #include "ultima/nuvie/core/converse_interpret.h"
35 #include "ultima/nuvie/core/converse_speech.h"
36
37 namespace Ultima {
38 namespace Nuvie {
39
40 //#define CONVERSE_DEBUG
41
ConverseInterpret(Converse * owner)42 ConverseInterpret::ConverseInterpret(Converse *owner) {
43 converse = owner;
44 b_frame = NULL;
45 decl_v = decl_t = 0x00;
46 in_start = 0;
47
48 unwait();
49 answer_mode = ANSWER_NO;
50 stopped = false;
51 db_lvar = false;
52 db_loc = 0;
53 db_offset = 0;
54 }
55
56
~ConverseInterpret()57 ConverseInterpret::~ConverseInterpret() {
58 leave_all(); // deletes b_frame when empty
59 }
60
61
62 /* Returns a value from the current location in the ConvScript, respecting any
63 * present data-size definition.
64 */
read_value()65 struct ConverseInterpret::in_val_s ConverseInterpret::read_value() {
66 ConvScript *cs = converse->script;
67 converse_value nval = cs->read();
68 uint8 dtype = nval;
69 if (nval == 0xd3)
70 nval = cs->read();
71 else if (nval == 0xd2)
72 nval = cs->read4();
73 else if (nval == 0xd4)
74 nval = cs->read2();
75 else
76 dtype = 0;
77 struct in_val_s ival;
78 ival.v = nval;
79 ival.d = dtype;
80 return (ival);
81 }
82
83
84 /* Read a control statement from the script.
85 */
collect_input()86 void ConverseInterpret::collect_input() {
87 ConvScript *cs = converse->script;
88 converse_value cval = cs->peek();
89 // special cases
90 if (cval == U6OP_JUMP) { // 0xb0 (location)
91 add_val(cs->read()); // OPC
92 add_val(cs->read4());
93 return;
94 } else if (cval == U6OP_SIDENT) { // 0xff, get NPC identity
95 add_val(cs->read()); // OPC
96 add_val(cs->read()); // number
97 add_text(); // name
98 return;
99 } else if (cval == U6OP_KEYWORDS // 0xef, get keyword list
100 || cval == U6OP_ASKC // ...0xf8, or characters allowed as input
101 || cval == U6OP_SLOOK) { // ...0xf1, or description
102 add_val(cs->read()); // OPC
103 add_text();
104 return;
105 }
106 // standard
107 uint32 arg_num = 1; // which arg. of control statement will be evaluated
108 do {
109 if (is_print(cs->peek()) && (cs->overflow(1) || !is_valop(cs->peek(1))))
110 break;
111 if (cs->peek() == U6OP_EVAL) { // 0xa7
112 cs->skip();
113 // HACK forced by this input-collection design, don't eval if not
114 // running (but this was supposed to be checked only in exec())
115 if (!top_frame() || top_frame()->run) // "no frame" == run
116 eval(arg_num++);
117 } else {
118 struct in_val_s nval = read_value();
119 add_val(nval.v, nval.d);
120 }
121 } while (!cs->overflow() && !is_ctrl(cs->peek()));
122 }
123
124
125 /* Interpret control codes and text from the ConvScript.
126 */
step()127 void ConverseInterpret::step() {
128 ConvScript *cs = converse->script;
129
130 flush();
131
132 while (!waiting() && !cs->overflow() && !converse->is_waiting_for_scroll()) {
133 if (is_print(cs->peek())) {
134 add_text(); // collect
135 converse->set_output(get_text());
136 } else if (is_ctrl(cs->peek())) {
137 in_start = cs->pos();
138 collect_input(); // get opcode + data (1 statement/instruction)
139 } else {
140 #ifdef CONVERSE_DEBUG
141 DEBUG(1, LEVEL_DEBUGGING, "Converse: skipped 0x%02x at %04x\n", cs->peek(), cs->pos());
142 #endif
143 converse->print("[Tried to print a control char.]\n");
144 cs->skip();
145 }
146 exec(); // run it
147 }
148
149 if (cs->overflow() && !stopped) {
150 converse->print("\n[EOF]\n");
151 stop();
152 }
153 }
154
155
156 /* Collect text from script and add to end of collected text statement, or add
157 * one character to text.
158 */
add_text(unsigned char c)159 void ConverseInterpret::add_text(unsigned char c) {
160 ConvScript *cs = converse->script;
161 do {
162 text.append(1, (unsigned char)cs->read());
163 } while (!cs->overflow() && is_print(cs->peek()));
164 }
165
166
167 /* Add some code or data to end of collected control statement.
168 */
add_val(converse_value c,uint8 d)169 void ConverseInterpret::add_val(converse_value c, uint8 d) {
170 struct in_val_s ivs;
171 ivs.v = c;
172 ivs.d = d;
173 in.push_back(ivs);
174 }
175
176
177 /* Delete the top-level frame. If no frames are left, run==true for all code.
178 */
leave()179 void ConverseInterpret::leave() {
180 if (top_frame()) {
181 struct convi_frame_s *fp = b_frame->top();
182 #ifdef CONVERSE_DEBUG
183 DEBUG(1, LEVEL_DEBUGGING, "Converse: ...leave %02x...\n", fp->start_c);
184 #endif
185 delete fp;
186 fp = NULL;
187 b_frame->pop();
188 if (b_frame->empty()) {
189 delete b_frame;
190 b_frame = NULL;
191 }
192 }
193 }
194
195
196 /* Create new top-level frame for a code block starting at control code `c'.
197 */
enter(converse_value c)198 void ConverseInterpret::enter(converse_value c) {
199 struct convi_frame_s *ef = new convi_frame_s;
200 ef->start = in_start; // start location
201 ef->run = top_frame() ? top_frame()->run : true; // run if parent is
202 ef->break_c = 0x00;
203 ef->start_c = c;
204 if (!b_frame)
205 b_frame = new stack<struct convi_frame_s *>;
206 b_frame->push(ef);
207 #ifdef CONVERSE_DEBUG
208 DEBUG(1, LEVEL_DEBUGGING, "Converse: ...enter %02x...\n", ef->start_c);
209 #endif
210 }
211
212
213 /* Leave the top-frame for end-code matching `c', and enter a new frame for
214 * other codes. Toggle run mode if `c' is break-code.
215 */
do_frame(converse_value c)216 void ConverseInterpret::do_frame(converse_value c) {
217 switch (c) {
218 case U6OP_IF: // 0xa1
219 enter(U6OP_IF);
220 break;
221 case U6OP_ENDIF: // 0xa2
222 leave(); // (0xa1)
223 break;
224 case U6OP_KEYWORDS: // 0xef
225 if (!top_frame() || top_frame()->start_c != U6OP_KEYWORDS)
226 enter(U6OP_KEYWORDS); // 0xef...0xef...0xee, can't be recursive
227 break;
228 case U6OP_ENDANSWER: // 0xee
229 leave(); // (0xef)
230 break;
231 }
232 if (c != 0x00 && c == get_break())
233 set_run(!get_run());
234 }
235
236
237 /* Run (or skip) the stored control and text statements, and clear them.
238 */
exec()239 void ConverseInterpret::exec() {
240 do_frame(get_val(0));
241 if (!top_frame() || top_frame()->run) { // "no frame" == run
242 if (val_count())
243 do_ctrl();
244 if (!converse->get_output().empty())
245 do_text();
246 }
247 #ifdef CONVERSE_DEBUG
248 else if (get_val(0) != 0x00) // show stepped over ctrl code (not text)
249 DEBUG(1, LEVEL_DEBUGGING, "Converse: %04x ----: %02x\n", in_start, get_val(0));
250 #endif
251 flush();
252 converse->set_output(""); // clear output
253 }
254
255
256 /* Print script text, resolving special symbols as they are encountered.
257 */
do_text()258 void ConverseInterpret::do_text() {
259 string output = get_formatted_text(converse->get_output().c_str());
260 converse->print(output.c_str());
261 }
262
263 /* Print script text, resolving special symbols as they are encountered.
264 */
get_formatted_text(const char * c_str)265 string ConverseInterpret::get_formatted_text(const char *c_str) {
266 unsigned int i = 0;
267 char symbol[3] = { '\0', '\0', '\0' },
268 intval[16];
269 string output;
270 const uint32 len = strlen(c_str);
271 uint32 last_value = 0;
272
273 while (i < len) {
274 switch (c_str[i]) {
275
276 case '$' :
277 case '#' : // copy symbol
278 strncpy(symbol, &c_str[i], 2);
279 // evaluate
280 if (!strcmp(symbol, "$G")) // gender title
281 output.append(converse->player->get_gender_title());
282 else if (!strcmp(symbol, "$N")) // npc name
283 output.append(converse->_name.c_str());
284 else if (!strcmp(symbol, "$P")) // player name
285 output.append(converse->player->get_name());
286 else if (!strcmp(symbol, "$T")) // time of day
287 output.append(converse->clock->get_time_of_day_string());
288 else if (!strcmp(symbol, "$Y")) // Y-string
289 output.append(get_ystr());
290 else if (!strcmp(symbol, "$Z")) // previous input
291 output.append(converse->get_svar(U6TALK_VAR_INPUT));
292 else if (symbol[0] == '$' // value of a string variable
293 && Common::isDigit(symbol[1]))
294 output.append(converse->get_svar(strtol(&symbol[1], NULL, 10)));
295 else if (symbol[0] == '#' // value of a variable
296 && Common::isDigit(symbol[1])) {
297 last_value = converse->get_var(strtol(&symbol[1], NULL, 10));
298 snprintf(intval, 16, "%u", last_value);
299
300 output.append((char *)intval);
301 output.append("");
302 } else
303 output.append(symbol);
304
305 i += 2;
306 break;
307
308 case '{' :
309 i++;
310 break; //FIXME what does this FM-Towns command do?
311
312 case '~' :
313 if (i + 3 <= len) {
314 i++;
315 if (c_str[i] == 'P')
316 converse->get_speech()->play_speech(converse->script_num, (int)strtol(&c_str[i + 1], NULL, 10));
317
318 for (i++; Common::isDigit(c_str[i]) && i < len;)
319 i++;
320 }
321 break;
322
323 case '/' : // singular
324 case '\\' : { // plural
325 bool show = false;
326 if ((last_value == 1 && c_str[i] == '/') ||
327 (last_value != 1 && c_str[i] == '\\')) show = true;
328 ++i;
329 unsigned int count = 0;
330 while (i + count < len &&
331 c_str[i + count] >= 'a' && c_str[i + count] <= 'z')
332 // NOTE: the above check might not work for non-english
333 count++;
334 if (show) output.append(count, c_str[i]);
335 i += count;
336 break;
337 }
338
339 default :
340 //skip fm-towns keyword commands when in original ui mode.
341 // They take the following form. '+actor_numberKeyword+' eg. '+3leave+'
342 if (c_str[i] == '+' && !Game::get_game()->is_new_style()) {
343 if (i + 3 <= len) {
344 i++;
345
346 for (; c_str[i] != 0x2b && i < len;) //0x2b = '+'
347 i++;
348
349 i++;
350 }
351 } else {
352 output.append(1, c_str[i]);
353 i += 1;
354 }
355 break;
356 }
357 }
358
359 return output;
360 }
361
362
363 /* Execute a control statement/instruction (control code + optional arguments
364 * which must have been evaluated already.)
365 */
do_ctrl()366 void ConverseInterpret::do_ctrl() {
367 stack<converse_typed_value> st;
368 #ifdef CONVERSE_DEBUG
369 DEBUG(1, LEVEL_DEBUGGING, "Converse: %04x INSTR:", in_start);
370 for (uint32 v = 0; v < val_count(); v++)
371 DEBUG(1, LEVEL_DEBUGGING, " 0x%02x", get_val(v));
372 DEBUG(1, LEVEL_DEBUGGING, "\n");
373 #endif
374
375 converse_typed_value v = {U6OP_VAR, 0};
376 while (val_count()) {
377 v.val = pop_val();
378 st.push(v);
379 }
380 op(st);
381 }
382
383
384 /* Returns last item value from input list (and removes it from the same.)
385 */
pop_val()386 converse_value ConverseInterpret::pop_val() {
387 converse_value ret = 0;
388 if (!in.empty()) {
389 ret = get_val(val_count() - 1);
390 in.resize(val_count() - 1);
391 }
392 return (ret);
393 }
394
395
396 /* Returns last item data-size/type from value list (and removes it from the
397 * same.)
398 */
pop_val_size()399 uint8 ConverseInterpret::pop_val_size() {
400 converse_value ret = 0;
401 if (!in.empty()) {
402 ret = get_val_size(val_count() - 1);
403 in.resize(val_count() - 1);
404 }
405 return (ret);
406 }
407
408
409 /* Returns any item value from the input list (and leaves the list alone.)
410 */
get_val(uint32 vi)411 converse_value ConverseInterpret::get_val(uint32 vi) {
412 if (vi < in.size())
413 return (in[vi].v);
414 return (0);
415 }
416
417
418 /* Returns any item data-size/type from the input list (and leaves the list
419 * alone.)
420 */
get_val_size(uint32 vi)421 uint8 ConverseInterpret::get_val_size(uint32 vi) {
422 if (vi < in.size())
423 return (in[vi].d);
424 return (0);
425 }
426
set_ystr(const char * s)427 void ConverseInterpret::set_ystr(const char *s) {
428 ystring = s ? s : "";
429 converse->set_svar(U6TALK_VAR_YSTRING, ystring.c_str());
430 }
431
432 /* Set the return string `sn' to `s'.
433 */
set_rstr(uint32 sn,const char * s)434 void ConverseInterpret::set_rstr(uint32 sn, const char *s) {
435 if (sn >= rstrings.size())
436 rstrings.resize(rstrings.size() + 1);
437 rstrings[sn] = s ? s : "";
438 }
439
440
441 /* Add a return string.
442 * Returns the index into the list, which is used by an instruction.
443 */
add_rstr(const char * s)444 converse_value ConverseInterpret::add_rstr(const char *s) {
445 rstrings.push_back(s ? s : "");
446 return (rstrings.size() - 1);
447 }
448
449
450 /* Returns and removes top item from a value stack.
451 */
pop_arg(stack<converse_typed_value> & vs)452 converse_value ConverseInterpret::pop_arg(stack<converse_typed_value> &vs) {
453 converse_value ret = 0;
454 if (!vs.empty()) {
455 converse_typed_value val = vs.top();
456 ret = val.val;
457 vs.pop();
458 }
459 return (ret);
460 }
461
pop_typed_arg(stack<converse_typed_value> & vs)462 converse_typed_value ConverseInterpret::pop_typed_arg(stack<converse_typed_value> &vs) {
463 converse_typed_value ret = {0, 0};
464 if (!vs.empty()) {
465 ret = vs.top();
466 vs.pop();
467 }
468 return (ret);
469 }
470
471 #if 0
472 bool MDTalkInterpret::op(stack<converse_value> &i) {
473 bool success = true;
474 converse_value v[4], in; // args
475
476 switch (in == pop_arg(i)) {
477 default:
478 i.push(in);
479 success = ConverseInterpret::op(i);
480 }
481 return (success);
482 }
483 #endif
484
op_create_new(stack<converse_typed_value> & i)485 bool ConverseInterpret::op_create_new(stack<converse_typed_value> &i) {
486 converse_value v[4];
487 Actor *cnpc = NULL;
488
489 v[0] = pop_arg(i); // npc
490 v[1] = pop_arg(i); // obj
491 v[2] = pop_arg(i); // qual
492 v[3] = pop_arg(i); // quant
493 cnpc = converse->actors->get_actor(npc_num(v[0]));
494 if (cnpc) {
495 if (Game::get_game()->get_game_type() == NUVIE_GAME_U6 && v[1] == 76) { // Amulet of Submission
496 cnpc->remove_readied_object(ACTOR_NECK);
497 Obj *cnpc_obj = cnpc->inventory_new_object(76, 1, 0);
498 cnpc->add_readied_object(cnpc_obj);
499 } else
500 cnpc->inventory_new_object(v[1], v[3], v[2]);
501 }
502
503 return true;
504 }
505
op_create_new(stack<converse_typed_value> & i)506 bool WOUConverseInterpret::op_create_new(stack<converse_typed_value> &i) {
507 converse_value v[4];
508 Actor *cnpc = NULL;
509
510 v[0] = pop_arg(i); // npc
511 v[1] = pop_arg(i); // obj
512 v[2] = pop_arg(i); // qual
513 v[3] = pop_arg(i); // quant
514 cnpc = converse->actors->get_actor(npc_num(v[0]));
515 if (cnpc) {
516 if (cnpc->can_carry_object(v[1], v[3])) {
517 cnpc->inventory_new_object(v[1], v[3], v[2]);
518 converse->set_var(WOUTALK_VAR_ADD_TO_INVENTORY_FAILED, 0);
519 } else {
520 converse->set_var(WOUTALK_VAR_ADD_TO_INVENTORY_FAILED, 1); //This var is set to true when the object cannot be added to the actor's inventory
521 }
522 }
523
524 return true;
525 }
526
527 /* Run control code with arguments/operands (arranged on a stack from
528 * top to bottom.)
529 */
op(stack<converse_typed_value> & i)530 bool ConverseInterpret::op(stack<converse_typed_value> &i) {
531 bool success = true;
532 converse_value v[4] = { 0, 0, 0, 0 }; // args
533 converse_value inVal;
534 ConvScript *cs = converse->script;
535 Actor *cnpc = NULL;
536 Obj *cnpc_obj = NULL;
537 Player *player = converse->player;
538 // converse_db_s *cdb;
539
540 inVal = pop_arg(i);
541
542 #ifdef CONVERSE_DEBUG
543 DEBUG(1, LEVEL_DEBUGGING, "op %s(%x)\n", op_str(inVal), inVal);
544 #endif
545
546 switch (inVal) {
547 case U6OP_SLEEP: // 0x9e
548 // Note: It's usually unecessary to wait for the effect, as it
549 // pauses input and the user can't continue the conversation until
550 // the effect is complete.
551 new SleepEffect(5); // sleep until sunrise
552 break;
553 case U6OP_HORSE: // 0x9c
554 // FIXME: probably need to do more real actor/object set-up here
555 cnpc = converse->actors->get_actor(npc_num(pop_arg(i)));
556 cnpc_obj = cnpc->make_obj();
557 cnpc_obj->frame_n = 0; // FIX for actors orginal direction.
558 cnpc_obj->obj_n = OBJ_U6_HORSE_WITH_RIDER; // mount up.
559 cnpc->init_from_obj(cnpc_obj);
560 delete_obj(cnpc_obj);
561 break;
562 case U6OP_IF: // 0xa1 (test val)
563 set_break(U6OP_ELSE);
564 set_run(pop_arg(i) ? true : false);
565 break;
566 case U6OP_ENDIF: // 0xa2
567 break; // (frame only)
568 case U6OP_ELSE: // 0xa3
569 break; // (frame only_
570 case U6OP_SETF: // 0xa4 (npc, flagnum)
571 cnpc = converse->actors->get_actor(npc_num(pop_arg(i)));
572 if (cnpc)
573 cnpc->set_flag(pop_arg(i));
574 break;
575 case U6OP_CLEARF: // 0xa5 (npc, flagnum)
576 cnpc = converse->actors->get_actor(npc_num(pop_arg(i)));
577 if (cnpc)
578 cnpc->clear_flag(pop_arg(i));
579 break;
580 case U6OP_DECL: // 0xa6
581 db_lvar = false;
582 v[0] = pop_arg(i); // variable
583 v[1] = pop_arg(i); // type
584
585 if (!i.empty()) {
586 converse_typed_value top = i.top();
587 if ((top.val == 0xb2 || top.val == 0xb4)) {
588 if (top.val == 0xb2) { // eg DB1[#2] = ...
589 pop_arg(i); //0xb2
590 pop_arg(i); //0xb4 DATA op.
591 db_lvar = true;
592 db_loc = v[0];
593 db_offset = converse->get_var(v[1]);
594 } else if (top.val == 0xb4) { // eg DB1[0] = ...
595 pop_arg(i); //0xb4 DATA op.
596 db_lvar = true;
597 db_loc = v[0];
598 db_offset = v[1];
599 }
600 #ifdef CONVERSE_DEBUG
601 DEBUG(1, LEVEL_DEBUGGING, "DB lvar found. location = %x, offset = %x", db_loc, db_offset);
602 #endif
603 }
604 }
605 let(v[0], v[1]);
606 break;
607 case U6OP_ASSIGN: // 0xa8
608 case U6OP_SETNAME: // 0xd8
609 v[0] = pop_arg(i); // value
610 if (inVal == U6OP_ASSIGN) { // 0xa8
611 if (db_lvar) {
612 set_db_integer(db_loc, db_offset, v[0]);
613 } else if (decl_v <= U6TALK_VAR__LAST_) {
614 if (decl_t == 0xb3)
615 converse->set_svar(decl_v, get_rstr(v[0]));
616 else if (decl_t == 0xb2)
617 converse->set_var(decl_v, v[0]);
618 } else {
619 converse->print("[Illegal variable]\n");
620 DEBUG(0, LEVEL_ERROR, "[Illegal variable] decl_v = %x decl_t = %x\n", decl_v, decl_t);
621 }
622 } else { // // 0xd8, assign name of npc `result' to ystr
623 v[0] = npc_num(v[0]);
624 // get name from party information
625 if (player->get_party()->contains_actor(v[0])) {
626 v[0] = player->get_party()->get_member_num(v[0]);
627 set_ystr(player->get_party()->get_actor_name(v[0]));
628 } else
629 set_ystr(converse->npc_name(v[0])); // read from script
630 }
631 let(); // clear
632 break;
633 case U6OP_JUMP: // 0xb0
634 v[0] = pop_arg(i);
635 #ifdef CONVERSE_DEBUG
636 DEBUG(1, LEVEL_DEBUGGING, "Converse: JUMP 0x%04x\n", v[0]);
637 #endif
638 cs->seek(v[0]);
639 leave_all(); // always run
640 break;
641 case U6OP_DPRINT: { // 0xb5
642 v[0] = pop_arg(i); // db location
643 v[1] = pop_arg(i); // index
644 char *dstring = get_db_string(v[0], v[1]);
645 if (dstring) {
646 converse->set_output(dstring); // data may have special symbols
647 // converse->print(dstring); // data can have no special symbols -- wrong
648 // converse->print("\n");
649 free(dstring);
650 }
651 break;
652 }
653 case U6OP_BYE: // 0xb6
654 stop();
655 break;
656 case U6OP_NEW: // 0xb9 (npc, obj, qual, quant)
657 op_create_new(i);
658 break;
659 case U6OP_DELETE: // 0xba
660 //bool Actor::inventory_del_object(uint16 obj_n, uint8 qty, uint8 quality);
661 v[0] = pop_arg(i); // npc
662 v[1] = pop_arg(i); // obj
663 v[2] = pop_arg(i); // qual
664 v[3] = pop_arg(i); // quant
665 cnpc = converse->actors->get_actor(npc_num(v[0]));
666 if (cnpc)
667 cnpc->inventory_del_object(v[1], v[3], v[2]);
668 break;
669 case U6OP_GIVE: // 0xc9
670 v[0] = pop_arg(i); // obj
671 v[1] = pop_arg(i); // qual
672 v[2] = pop_arg(i); // from
673 v[3] = pop_arg(i); // to
674 cnpc = converse->actors->get_actor(npc_num(v[2]));
675 if (!cnpc)
676 break;
677 cnpc->inventory_del_object(v[0], 1, v[1]);
678 cnpc = converse->actors->get_actor(npc_num(v[3]));
679 if (cnpc)
680 cnpc->inventory_new_object(v[0], 1, v[1]);
681 break;
682 case U6OP_ADDKARMA: // 0xc4
683 player->add_karma(pop_arg(i));
684 break;
685 case U6OP_SUBKARMA: // 0xc5
686 player->subtract_karma(pop_arg(i));
687 break;
688 case MDOP_MISC_ACTION: // 0xd1
689 v[0] = pop_arg(i); // action number
690 Game::get_game()->get_script()->call_talk_script(v[0]);
691 break;
692 case U6OP_RESURRECT: { // 0xd6
693 v[0] = npc_num(pop_arg(i));
694 uint16 body = OBJ_U6_DEAD_BODY;
695
696 if (Game::get_game()->get_game_type() == NUVIE_GAME_SE)
697 body = OBJ_SE_DEAD_BODY;
698 else if (Game::get_game()->get_game_type() == NUVIE_GAME_MD)
699 body = OBJ_MD_DEAD_BODY;
700
701 if (v[0] == 0) // Party
702 cnpc = converse->player->get_party()->who_has_obj(body , 0, false);
703 else
704 cnpc = converse->actors->get_actor(v[0]);
705
706 cnpc_obj = cnpc->inventory_get_object(body, 0, false);
707 if (Game::get_game()->get_game_type() == NUVIE_GAME_U6 && !cnpc_obj) {
708 if (v[0] == 0)
709 cnpc = converse->player->get_party()->who_has_obj(OBJ_U6_MOUSE, 0, false);
710 cnpc_obj = cnpc->inventory_get_object(OBJ_U6_MOUSE, 0, false);
711 }
712 if (cnpc_obj != NULL) {
713 if (converse->actors->resurrect_actor(cnpc_obj, converse->player->get_actor()->get_location())) {
714 converse->objects->unlink_from_engine(cnpc_obj);
715 delete_obj(cnpc_obj);
716 }
717 }
718 break;
719 }
720 case U6OP_HEAL: // 0xd9
721 cnpc = converse->actors->get_actor(npc_num(pop_arg(i)));
722 if (cnpc)
723 cnpc->heal();
724 break;
725 case U6OP_CURE: // 0xdb
726 cnpc = converse->actors->get_actor(npc_num(pop_arg(i)));
727 if (cnpc)
728 cnpc->set_poisoned(false);
729 break;
730 case U6OP_WORKTYPE: // 0xcd
731 v[0] = pop_arg(i); // npc
732 v[1] = pop_arg(i); // worktype
733 cnpc = converse->actors->get_actor(npc_num(v[0]));
734 if (cnpc)
735 cnpc->set_worktype(v[1]);
736 break;
737 case U6OP_INVENTORY: // 0xbe
738 converse->print("!inventory\n");
739 break;
740 case U6OP_PORTRAIT: // 0xbf
741 converse->show_portrait(npc_num(pop_arg(i)));
742 break;
743 case U6OP_WAIT: // 0xcb, set page-break on scroller and wait
744 converse->print("*");
745 wait();
746 break;
747 case U6OP_ENDANSWER: // 0xee
748 break; // (frame only)
749 case U6OP_KEYWORDS: // 0xef (text:keywords)
750 if (answer_mode != ANSWER_DONE) { // havn't already answered
751 answer_mode = ANSWER_NO;
752 if (check_keywords(get_text(), converse->get_input()))
753 answer_mode = ANSWER_YES;
754 }
755 break; // (frame only)
756 case U6OP_SIDENT: // 0xff, arg 1 is id number, name follows
757 v[0] = pop_arg(i);
758 if (v[0] != converse->npc_num)
759 DEBUG(0, LEVEL_WARNING,
760 "Converse: npc number inVal script (%d) does not"
761 " match actor number (%d)\n", v[0], converse->npc_num);
762 converse->_name = get_text(); // collected
763 break;
764 case U6OP_SLOOK: // 0xf1, description follows
765 converse->_desc = get_formatted_text(get_text().c_str()); // collected
766 converse->print("\nYou see ");
767 converse->print(converse->_desc.c_str());
768 converse->print("\n");
769 break;
770 case U6OP_SCONVERSE: // 0xf2
771 break;
772 case U6OP_SPREFIX: // 0xf3, Unknown
773 break;
774 case U6OP_ANSWER: // 0xf6
775 set_break(U6OP_KEYWORDS); // run or skip to next 0xef
776 if (answer_mode == ANSWER_YES) { // depending on input comparison
777 set_run(true);
778 answer_mode = ANSWER_DONE; // don't do further comparisons
779 } else
780 set_run(false);
781 break;
782 case U6OP_ASK: // 0xf7
783 answer_mode = ANSWER_NO; // reset answer switch
784 converse->collect_input();
785 break;
786 case U6OP_ASKC: // 0xf8 (blocking, single character input)
787 answer_mode = ANSWER_NO; // reset answer switch
788 converse->poll_input(get_text().c_str(), false); // collected=allowed input
789 break;
790 case U6OP_INPUTSTR: // 0xf9 (string or integer)
791 answer_mode = ANSWER_NO; // reset answer switch
792 // fall through
793 case U6OP_INPUT: // 0xfb (integer)
794 v[0] = pop_arg(i); // var
795 v[1] = pop_arg(i); // type (should be 0xb2)
796 let(v[0], v[1]);
797 converse->poll_input();
798 break;
799 case U6OP_INPUTNUM: // 0xfc
800 v[0] = pop_arg(i); // var
801 v[1] = pop_arg(i); // type
802 let(v[0], v[1]);
803 converse->poll_input("0123456789");
804 break;
805 default:
806 converse->print("[Unknown command]\n");
807 DEBUG(0, LEVEL_ERROR, "Converse: UNK OP=%02x\n", inVal);
808 success = false;
809 }
810 return (success);
811 }
812
813
814 /* The other set of codes, these operate on the input values, so call them
815 * valops. Output goes back to the stack.
816 */
evop(stack<converse_typed_value> & i)817 bool ConverseInterpret::evop(stack<converse_typed_value> &i) {
818 bool success = true;
819 converse_value v[4]; // input
820 converse_typed_value inVal;
821 converse_typed_value out;
822 Actor *cnpc = NULL;
823 Obj *cnpc_obj = NULL;
824 // converse_db_s *cdb;
825 Player *player = converse->player;
826
827 out.type = U6OP_VAR;
828 out.val = 0;
829
830 inVal.val = pop_arg(i);
831
832 #ifdef CONVERSE_DEBUG
833 DEBUG(1, LEVEL_DEBUGGING, "evop %s(%x)\n", evop_str(inVal.val), inVal.val);
834 #endif
835
836 switch (inVal.val) {
837 case U6OP_GT: // 0x81
838 v[1] = pop_arg(i);
839 v[0] = pop_arg(i);
840 if (v[0] > v[1])
841 out.val = 1;
842 break;
843 case U6OP_GE: // 0x82
844 v[1] = pop_arg(i);
845 v[0] = pop_arg(i);
846 if (v[0] >= v[1])
847 out.val = 1;
848 break;
849 case U6OP_LT: // 0x83
850 v[1] = pop_arg(i);
851 v[0] = pop_arg(i);
852 if (v[0] < v[1])
853 out.val = 1;
854 break;
855 case U6OP_LE: // 0x84
856 v[1] = pop_arg(i);
857 v[0] = pop_arg(i);
858 if (v[0] <= v[1])
859 out.val = 1;
860 break;
861 case U6OP_NE: // 0x85
862 if (pop_arg(i) != pop_arg(i))
863 out.val = 1;
864 break;
865 case U6OP_EQ: // 0x86
866 out.val = evop_eq(i);
867 break;
868 case U6OP_ADD: // 0x90
869 out.val = pop_arg(i) + pop_arg(i);
870 break;
871 case U6OP_SUB: // 0x91
872 v[1] = pop_arg(i);
873 v[0] = pop_arg(i);
874 out.val = v[0] - v[1];
875 break;
876 case U6OP_MUL: // 0x92
877 out.val = pop_arg(i) * pop_arg(i);
878 break;
879 case U6OP_DIV: // 0x93
880 v[1] = pop_arg(i);
881 v[0] = pop_arg(i);
882 if (v[1] == 0) {
883 DEBUG(0, LEVEL_ERROR, "Converse: Divide error\n");
884 success = false;
885 stop();
886 } else
887 out.val = v[0] / v[1];
888 break;
889 case U6OP_LOR: // 0x94
890 v[1] = pop_arg(i);
891 v[0] = pop_arg(i);
892 if (v[0] || v[1])
893 out.val = 1;
894 break;
895 case U6OP_LAND: // 0x95
896 v[1] = pop_arg(i);
897 v[0] = pop_arg(i);
898 if (v[0] && v[1])
899 out.val = 1;
900 break;
901 case U6OP_CANCARRY: // 0x9a
902 cnpc = converse->actors->get_actor(pop_arg(i));
903 if (cnpc)
904 out.val = (converse_value)((cnpc->inventory_get_max_weight()
905 - cnpc->get_inventory_weight()) * 10);
906 break;
907 case U6OP_WEIGHT: { // 0x9b
908 v[1] = pop_arg(i); // quantity
909 v[0] = pop_arg(i); // object
910 float weight = (float)converse->objects->get_obj_weight_unscaled(v[0]) * v[1]; //FIXME should this be scaled down by 10???
911 if (converse->objects->has_reduced_weight(v[0]))
912 weight /= 10;
913 out.val = weight;
914 break;
915 }
916 case U6OP_HORSED: // 0x9d
917 cnpc = converse->actors->get_actor(npc_num(pop_arg(i)));
918 cnpc_obj = cnpc->make_obj();
919 if (cnpc_obj->obj_n == OBJ_U6_HORSE_WITH_RIDER)
920 out.val = 1;
921 delete_obj(cnpc_obj);
922 break;
923 case U6OP_HASOBJ: // 0x9f
924 v[2] = pop_arg(i); // quality
925 v[1] = pop_arg(i); // object
926 v[0] = pop_arg(i); // npc
927 cnpc = converse->actors->get_actor(npc_num(v[0]));
928 if (cnpc && cnpc->inventory_has_object(v[1], v[2], (v[2] != 0 ? true : false))) //don't search quality if quality is 0
929 out.val = 1;
930 break;
931 case U6OP_RAND: // 0xa0
932 v[1] = pop_arg(i); // max.
933 v[0] = pop_arg(i); // min.
934 if (v[0] >= v[1])
935 out.val = v[0];
936 else
937 out.val = v[0] + (NUVIE_RAND() % (v[1] - v[0] + 1));
938 break;
939 case U6OP_FLAG: // 0xab (npc, flag)
940 v[1] = pop_arg(i);
941 v[0] = pop_arg(i);
942 #ifdef CONVERSE_DEBUG
943 DEBUG(1, LEVEL_DEBUGGING, "npc=%d bit=%d\n", v[0], v[1]);
944 #endif
945 if (v[1] <= 7) {
946 cnpc = converse->actors->get_actor(npc_num(v[0]));
947 if (cnpc && (cnpc->get_talk_flags() & (1 << v[1])))
948 out.val = 1;
949 }
950 break;
951 case U6OP_VAR: // 0xb2
952 out.val = converse->get_var(pop_arg(i));
953 break;
954 case U6OP_SVAR: // 0xb3 (using new rstring)
955 out.val = add_rstr(converse->get_svar(pop_arg(i))); // rstr num
956 out.type = U6OP_SVAR;
957 break;
958 #if 0 /* old read style tries to detect if data is string or integer */
959 case U6OP_DATA: // 0xb4
960 v[1] = pop_arg(i); // index
961 v[0] = pop_arg(i); // db location
962 cdb = get_db(v[0], v[1]);
963 if (!cdb)
964 out.val = 0;
965 else if (cdb->type == 0)
966 out.val = add_rstr(cdb->s);
967 else
968 out.val = cdb->i;
969 delete cdb;
970 break;
971 #else /* new read assumes DATA may be assigned, and depend on variable type */
972 case U6OP_DATA: // 0xb4
973 v[1] = pop_arg(i); // index
974 v[0] = pop_arg(i); // db location
975
976 if (decl_t == 0xb3) {
977 char *dstring = get_db_string(v[0], v[1]);
978 if (dstring) {
979 out.val = add_rstr(dstring);
980 free(dstring);
981 } else
982 out.val = add_rstr("");
983
984 out.type = U6OP_SVAR;
985 } else // this will work for 0xB2 assignment and normal stack ops
986 out.val = get_db_integer(v[0], v[1]);
987 break;
988 #endif
989 case U6OP_INDEXOF: // 0xb7
990 v[1] = pop_arg(i); // string variable
991 v[0] = pop_arg(i); // db location
992 out.val = find_db_string(v[0], converse->get_svar(v[1]));
993 break;
994 case U6OP_OBJCOUNT: // 0xbb
995 v[1] = pop_arg(i); // object
996 v[0] = pop_arg(i); // npc
997 cnpc = converse->actors->get_actor(npc_num(v[0])); //FIXME should this be party?
998 if (cnpc)
999 out.val = cnpc->inventory_count_object(v[1]);
1000 break;
1001 case U6OP_INPARTY: // 0xc6
1002 if (player->get_party()->contains_actor(npc_num(pop_arg(i))))
1003 out.val = 1;
1004 break;
1005 case U6OP_OBJINPARTY: { // 0xc7 ?? check if party has object
1006 v[1] = pop_arg(i); // qual
1007 v[0] = pop_arg(i); // obj
1008 bool has_mouse = false; // resurrect others first
1009 if (!player->get_party()->has_obj(v[0], v[1], false) && (v[0] != OBJ_U6_DEAD_BODY
1010 || Game::get_game()->get_game_type() != NUVIE_GAME_U6
1011 || !(has_mouse = player->get_party()->has_obj(OBJ_U6_MOUSE, v[1], false))))
1012 out.val = 0x8001; // something OR'ed or u6val version of "no npc"?
1013 else {
1014 cnpc = player->get_party()->who_has_obj(has_mouse ? OBJ_U6_MOUSE : v[0], v[1], false);
1015 assert(cnpc);
1016 out.val = cnpc->get_actor_num(); // first NPC that has object (sometimes 0xFFFF?)
1017 }
1018 break;
1019 }
1020 case U6OP_JOIN: // 0xca
1021 cnpc = converse->actors->get_actor(npc_num(pop_arg(i)));
1022 if (cnpc) {
1023 if (player->get_party()->contains_actor(cnpc))
1024 out.val = 3; // 3: ALREADY IN PARTY
1025 else if (player->get_party()->get_party_size() >=
1026 player->get_party()->get_party_max())
1027 out.val = 2; // 2: PARTY TOO LARGE
1028 else if (player->get_actor()->get_actor_num() == 0) // vehicle
1029 out.val = 1; // 1: NOT ON LAND
1030 else
1031 player->get_party()->add_actor(cnpc);
1032 // 0: SUCCESS
1033 }
1034 break;
1035 case U6OP_LEAVE: // 0xcc
1036 cnpc = converse->actors->get_actor(npc_num(pop_arg(i)));
1037 if (cnpc) {
1038 if (!player->get_party()->contains_actor(cnpc))
1039 out.val = 2; // 2: NOT IN PARTY
1040 else if (player->get_actor()->get_actor_num() == 0) // vehicle
1041 out.val = 1; // 1: NOT ON LAND
1042 else
1043 player->get_party()->remove_actor(cnpc);
1044 // 0: SUCCESS
1045 }
1046 break;
1047 case U6OP_NPCNEARBY: // 0xd7
1048 cnpc = converse->actors->get_actor(npc_num(pop_arg(i)));
1049 if (cnpc)
1050 out.val = converse->npc->is_nearby(cnpc) ? 1 : 0;
1051 break;
1052 case U6OP_WOUNDED: // 0xda
1053 cnpc = converse->actors->get_actor(npc_num(pop_arg(i)));
1054 if (cnpc)
1055 if (cnpc->get_hp() < cnpc->get_maxhp())
1056 out.val = 1;
1057 break;
1058 case U6OP_POISONED: // 0xdc
1059 cnpc = converse->actors->get_actor(npc_num(pop_arg(i)));
1060 if (cnpc)
1061 out.val = cnpc->is_poisoned() ? 1 : 0;
1062 break;
1063 case U6OP_NPC: // 0xdd (val2=??)
1064 v[1] = pop_arg(i);
1065 v[0] = pop_arg(i); // 0+
1066 cnpc = player->get_party()->get_actor(v[0]);
1067 out.val = cnpc ? cnpc->get_actor_num() : 0;
1068 break;
1069 case U6OP_EXP: // 0xe0
1070 v[1] = pop_arg(i); // add value
1071 v[0] = pop_arg(i); // npc
1072 cnpc = converse->actors->get_actor(npc_num(v[0]));
1073 if (cnpc) {
1074 out.val = cnpc->get_exp() + v[1];
1075 cnpc->set_exp(out.val);
1076 }
1077 break;
1078 case U6OP_LVL: // 0xe1
1079 v[1] = pop_arg(i);
1080 v[0] = pop_arg(i);
1081 cnpc = converse->actors->get_actor(npc_num(v[0]));
1082 if (cnpc) {
1083 out.val = cnpc->get_level() + v[1];
1084 cnpc->set_level(out.val);
1085 }
1086 break;
1087 case U6OP_STR: // 0xe2
1088 v[1] = pop_arg(i);
1089 v[0] = pop_arg(i);
1090 cnpc = converse->actors->get_actor(npc_num(v[0]));
1091 if (cnpc) {
1092 out.val = cnpc->get_strength() + v[1];
1093 cnpc->set_strength(out.val);
1094 }
1095 break;
1096 case U6OP_INT: // 0xe3
1097 v[1] = pop_arg(i);
1098 v[0] = pop_arg(i);
1099 cnpc = converse->actors->get_actor(npc_num(v[0]));
1100 if (cnpc) {
1101 out.val = cnpc->get_intelligence() + v[1];
1102 cnpc->set_intelligence(out.val);
1103 }
1104 break;
1105 case U6OP_DEX: // 0xe4
1106 v[1] = pop_arg(i);
1107 v[0] = pop_arg(i);
1108 cnpc = converse->actors->get_actor(npc_num(v[0]));
1109 if (cnpc) {
1110 out.val = cnpc->get_dexterity() + v[1];
1111 cnpc->set_dexterity(out.val);
1112 }
1113 break;
1114 default:
1115 DEBUG(0, LEVEL_ERROR, "Converse: UNK EVOP=%02x\n", inVal);
1116 success = false;
1117 }
1118 i.push(out);
1119 return (success);
1120 }
1121
evop_eq(stack<converse_typed_value> & vs)1122 converse_value ConverseInterpret::evop_eq(stack<converse_typed_value> &vs) {
1123 converse_typed_value operand1 = pop_typed_arg(vs);
1124 converse_typed_value operand2 = pop_typed_arg(vs);
1125 converse_value out = 0;
1126
1127 if (operand1.type == U6OP_VAR) {
1128 if (operand1.val == operand2.val)
1129 out = 1;
1130 } else {
1131 if (scumm_stricmp(get_rstr(operand1.val), get_rstr(operand2.val)) == 0)
1132 out = 1;
1133 }
1134
1135 return out;
1136 }
1137
1138 /* Take the values collected so far as input (starting with value `vi') and run
1139 * any assign/compare value ops that are mixed with the data. The result goes
1140 * back to the input list, and replaces everything after and including `vi'.
1141 */
eval(uint32 vi)1142 void ConverseInterpret::eval(uint32 vi) {
1143 stack<converse_typed_value> op_stk, r_stk;
1144 uint32 v = vi;
1145 #ifdef CONVERSE_DEBUG
1146 DEBUG(1, LEVEL_DEBUGGING, "Converse: EVAL");
1147 for (uint32 w = 0; w < val_count(); w++)
1148 DEBUG(1, LEVEL_DEBUGGING, " %s0x%02x%s", (w == vi) ? "(" : "", get_val(w), (w == val_count() - 1) ? ")" : "");
1149 #endif
1150
1151 while (v < val_count()) {
1152 converse_typed_value a = {U6OP_VAR, 0};
1153 a.val = get_val(v);
1154 uint8 ds = get_val_size(v++);
1155 op_stk.push(a);
1156 if (is_valop(a.val) && !ds)
1157 evop(op_stk);
1158 }
1159 in.resize(vi);
1160 if (op_stk.empty()) // took all parameters, no return
1161 add_val(0x00);
1162 else {
1163 while (!op_stk.empty()) {
1164 r_stk.push(op_stk.top()); // reverse
1165 op_stk.pop();
1166 }
1167 while (!r_stk.empty())
1168 add_val(pop_arg(r_stk)); // return(s)
1169 }
1170
1171 #ifdef CONVERSE_DEBUG
1172 DEBUG(1, LEVEL_DEBUGGING, " ->");
1173 if (val_count()) {
1174 for (uint32 w = 0; w < val_count(); w++)
1175 DEBUG(1, LEVEL_DEBUGGING, " 0x%02x", get_val(w));
1176 DEBUG(1, LEVEL_DEBUGGING, "\n");
1177 }
1178 #endif
1179 }
1180
1181
1182 /* Return `n' as-is or convert to number of NPC from Converse.
1183 */
npc_num(uint32 n)1184 uint8 ConverseInterpret::npc_num(uint32 n) {
1185 return ((n != 0xeb) ? n : converse->npc_num);
1186 }
1187
1188
1189 /* Returns true if the keywords list contains the input string, or contains an
1190 * asterisk (matching any input).
1191 */
check_keywords(string keystr,string instr)1192 bool ConverseInterpret::check_keywords(string keystr, string instr) {
1193 const char *strt_s = NULL;
1194 char *tok_s = NULL, *cmp_s = NULL;
1195 if (keystr == "*")
1196 return (true);
1197 // check each comma-separated keyword
1198 strt_s = keystr.c_str();
1199 for (uint32 c = 0; c < strlen(strt_s); c++) {
1200 // check at start of string and each keyword
1201 if (c == 0 || strt_s[c] == ',') {
1202 // copy from keyword start to end of string/keyword
1203 uint32 l;
1204 tok_s = scumm_strdup(&strt_s[(c == 0) ? c : c + 1]);
1205 for (l = 0; l < strlen(tok_s) && tok_s[l] != ','; l++);
1206 tok_s[l] = '\0';
1207 cmp_s = scumm_strdup(instr.c_str());
1208 // trim input to keyword size
1209 if (l < strlen(cmp_s))
1210 cmp_s[l] = '\0';
1211 // compare
1212 if (!scumm_stricmp(tok_s, cmp_s)) {
1213 free(cmp_s);
1214 free(tok_s);
1215 return (true);
1216 }
1217 free(cmp_s);
1218 free(tok_s);
1219 }
1220 }
1221 return (false);
1222 }
1223
1224
1225 /* Assign input from Converse to the declared variable.
1226 */
assign_input()1227 void ConverseInterpret::assign_input() {
1228 // FIXME: Nuvie treats 0xF9-INPUTSTR & 0xFB-INPUT as identical, but in U6
1229 // 0xFB-INPUT could not input strings.
1230 if (decl_t == 0xb2)
1231 converse->set_var(decl_v, strtol(converse->get_input().c_str(), NULL, 10));
1232 if (decl_t == 0xb3)
1233 converse->set_svar(decl_v, converse->get_input().c_str());
1234 }
1235
1236
1237 /* Collect data from section at `loc', index `i'.
1238 * Returns pointer to data, which can be 8bit integer or a character string.
1239 * FIXME: there is no checking for overflow beyond the initial location
1240 * Deprecated function that guesses the data type, and reads incorrectly.
1241 */
1242 struct ConverseInterpret::converse_db_s *
get_db(uint32 loc,uint32 i)1243 ConverseInterpret::get_db(uint32 loc, uint32 i) {
1244 convscript_buffer db = converse->script->get_buffer(loc);
1245 struct converse_db_s *item = NULL;
1246 uint32 d = 0, dbuf_len = 0, p = 0, e = 0;
1247 if (!db)
1248 return (NULL);
1249
1250 // item = (struct converse_db_s *)malloc(sizeof(struct converse_db_s));
1251 item = new struct converse_db_s;
1252 item->type = 0;
1253 item->s = NULL;
1254 item->i = 0;
1255 while (e++ <= i) {
1256 if (is_print(db[p]) && is_print(db[p + 1])) {
1257 d = 0;
1258 dbuf_len = 0;
1259 item->type = 0;
1260 do {
1261 if ((d + 1) >= dbuf_len)
1262 dbuf_len += 16;
1263 item->s = (char *)realloc(item->s, dbuf_len);
1264 item->s[d++] = (char)(db[p]);
1265 item->s[d] = '\0';
1266 } while (is_print(db[++p]));
1267 } else {
1268 free(item->s);
1269 item->s = 0;
1270 item->type = 1;
1271 item->i = (uint8)(db[p++]);
1272 }
1273 ++p; // skip 0
1274 }
1275 return (item);
1276 }
1277
1278
1279 /* Collect data from section at `loc', index `i', as a string.
1280 * Returns pointer to NEW data, or NULL if only integer data is found.
1281 */
get_db_string(uint32 loc,uint32 i)1282 char *ConverseInterpret::get_db_string(uint32 loc, uint32 i) {
1283 convscript_buffer db = converse->script->get_buffer(loc);
1284 char *item = NULL;
1285 uint32 d = 0, dbuf_len = 0, /* string pointer & length */
1286 p = 0; /* pointer into db */
1287 if (!db)
1288 return (NULL);
1289 /* skip to index */
1290 uint32 e = 0;
1291 while (e++ < i) {
1292 if (db[p] == U6OP_ENDDATA)
1293 return (NULL);
1294 while (is_print(db[p++]));
1295 }
1296
1297 d = 0;
1298 dbuf_len = 0;
1299 do {
1300 if ((d + 1) >= dbuf_len) // resize buffer as needed
1301 dbuf_len += 16;
1302 item = (char *)nuvie_realloc(item, dbuf_len);
1303 item[d++] = (char)(db[p]); // copy
1304 item[d] = '\0';
1305 } while (is_print(db[++p]));
1306 return (item);
1307 }
1308
1309
1310 /* Collect data from section at `loc', index `i', as an integer.
1311 * Returns the two-byte integer value.
1312 */
get_db_integer(uint32 loc,uint32 i)1313 converse_value ConverseInterpret::get_db_integer(uint32 loc, uint32 i) {
1314 uint16 item = 0;
1315 uint32 p = 0; /* pointer into db */
1316
1317 /* skip to index */
1318 uint32 e = 0;
1319 while (e++ < i)
1320 p += 2;
1321
1322 /* use ConvScript functions to check overflow and read data correctly */
1323 uint32 old_pos = converse->script->pos();
1324 converse->script->seek(loc + p);
1325 if (!converse->script->overflow(+1))
1326 item = converse->script->read2();
1327 converse->script->seek(old_pos);
1328
1329 return ((converse_value)item);
1330 }
1331
set_db_integer(uint32 loc,uint32 i,converse_value val)1332 void ConverseInterpret::set_db_integer(uint32 loc, uint32 i, converse_value val) {
1333 #ifdef CONVERSE_DEBUG
1334 DEBUG(1, LEVEL_DEBUGGING, "set_db_integer(%x, %x, %d)\n", loc, i, val);
1335 #endif
1336 uint32 p = 0; /* pointer into db */
1337
1338 /* skip to index */
1339 uint32 e = 0;
1340 while (e++ < i)
1341 p += 2;
1342
1343 /* use ConvScript functions to check overflow and read data correctly */
1344 uint32 old_pos = converse->script->pos();
1345 converse->script->seek(loc + p);
1346 if (!converse->script->overflow(+1))
1347 converse->script->write2(val);
1348 converse->script->seek(old_pos);
1349
1350 return;
1351 }
1352
1353 /* Scan data section `loc' for `dstring'. Stop at ENDDATA.
1354 * Returns the index of the string or the index of the ENDDATA marker.
1355 */
find_db_string(uint32 loc,const char * dstring)1356 converse_value ConverseInterpret::find_db_string(uint32 loc, const char *dstring) {
1357 convscript_buffer db = converse->script->get_buffer(loc);
1358 char *item = NULL; /* item being checked */
1359 uint32 d = 0, dbuf_len = 0, /* string pointer & length */
1360 p = 0, /* pointer into db */
1361 i = 0; /* item index */
1362 #ifdef CONVERSE_DEBUG
1363 DEBUG(1, LEVEL_DEBUGGING, "\nConverse: find_db_string(0x%04x, \"%s\")\n", loc, dstring);
1364 #endif
1365 while ((converse_value)(db[p]) != U6OP_ENDDATA) {
1366 if (is_print(db[p])) {
1367 item = NULL;
1368 d = 0;
1369 dbuf_len = 0;
1370 do {
1371 if ((d + 1) >= dbuf_len) // resize buffer as needed
1372 dbuf_len += 16;
1373 item = (char *)nuvie_realloc(item, dbuf_len);
1374 item[d++] = (char)(db[p]); // copy
1375 item[d] = '\0';
1376 } while (is_print(db[++p]));
1377 ++p; // skip this unprintable now so it's not counted as an item
1378 if (item) {
1379 string item_str = item;
1380 string find_str = dstring;
1381 free(item);
1382 // match keywords format: clamp item to 4 characters
1383 if (item_str.size() > 4)
1384 item_str.resize(4);
1385 if (check_keywords(item_str, find_str))
1386 return (i);
1387 }
1388 } else ++p;
1389 ++i;
1390 }
1391 #ifdef CONVERSE_DEBUG
1392 DEBUG(0, LEVEL_DEBUGGING, "\nConverse: find_db_string: not found; returning %d\n", i);
1393 #endif
1394 return (i);
1395 }
1396
evop_str(converse_value op)1397 const char *ConverseInterpret::evop_str(converse_value op) {
1398 switch (op) {
1399 case U6OP_GT:
1400 return "U6OP_GT";
1401 case U6OP_GE:
1402 return "U6OP_GE";
1403 case U6OP_LT:
1404 return "U6OP_LT";
1405 case U6OP_LE:
1406 return "U6OP_LE";
1407 case U6OP_NE:
1408 return "U6OP_NE";
1409 case U6OP_EQ:
1410 return "U6OP_EQ";
1411 case U6OP_ADD:
1412 return "U6OP_ADD";
1413 case U6OP_SUB:
1414 return "U6OP_SUB";
1415 case U6OP_MUL:
1416 return "U6OP_MUL";
1417 case U6OP_DIV:
1418 return "U6OP_DIV";
1419 case U6OP_LOR:
1420 return "U6OP_LOR";
1421 case U6OP_LAND:
1422 return "U6OP_LAND";
1423 case U6OP_CANCARRY:
1424 return "U6OP_CANCARRY";
1425 case U6OP_WEIGHT:
1426 return "U6OP_WEIGHT";
1427 case U6OP_HORSED:
1428 return "U6OP_HORSED";
1429 case U6OP_HASOBJ:
1430 return "U6OP_HASOBJ";
1431 case U6OP_RAND:
1432 return "U6OP_RAND";
1433 case U6OP_FLAG:
1434 return "U6OP_FLAG";
1435 case U6OP_VAR:
1436 return "U6OP_VAR";
1437 case U6OP_SVAR:
1438 return "U6OP_SVAR";
1439 case U6OP_DATA:
1440 return "U6OP_DATA";
1441 case U6OP_INDEXOF:
1442 return "U6OP_INDEXOF";
1443 case U6OP_OBJCOUNT:
1444 return "U6OP_OBJCOUNT";
1445 case U6OP_INPARTY:
1446 return "U6OP_INPARTY";
1447 case U6OP_OBJINPARTY:
1448 return "U6OP_OBJINPARTY";
1449 case U6OP_JOIN:
1450 return "U6OP_JOIN";
1451 case U6OP_LEAVE:
1452 return "U6OP_LEAVE";
1453 case U6OP_NPCNEARBY:
1454 return "U6OP_NPCNEARBY";
1455 case U6OP_WOUNDED:
1456 return "U6OP_WOUNDED";
1457 case U6OP_POISONED:
1458 return "U6OP_POISONED";
1459 case U6OP_NPC:
1460 return "U6OP_NPC";
1461 case U6OP_EXP:
1462 return "U6OP_EXP";
1463 case U6OP_LVL:
1464 return "U6OP_LVL";
1465 case U6OP_STR:
1466 return "U6OP_STR";
1467 case U6OP_INT:
1468 return "U6OP_INT";
1469 case U6OP_DEX:
1470 return "U6OP_DEX";
1471 default:
1472 break;
1473 }
1474
1475 return "U6OP_UNKNOWN";
1476 }
1477
op_str(converse_value op)1478 const char *ConverseInterpret::op_str(converse_value op) {
1479 switch (op) {
1480 case U6OP_SLEEP:
1481 return "U6OP_SLEEP";
1482 case U6OP_HORSE:
1483 return "U6OP_HORSE";
1484 case U6OP_IF:
1485 return "U6OP_IF";
1486 case U6OP_ENDIF:
1487 return "U6OP_ENDIF";
1488 case U6OP_ELSE:
1489 return "U6OP_ELSE";
1490 case U6OP_SETF:
1491 return "U6OP_SETF";
1492 case U6OP_CLEARF:
1493 return "U6OP_CLEARF";
1494 case U6OP_DECL:
1495 return "U6OP_DECL";
1496 case U6OP_ASSIGN:
1497 return "U6OP_ASSIGN";
1498 case U6OP_SETNAME:
1499 return "U6OP_SETNAME";
1500 case U6OP_JUMP:
1501 return "U6OP_JUMP";
1502 case U6OP_DPRINT:
1503 return "U6OP_DPRINT";
1504 case U6OP_BYE:
1505 return "U6OP_BYE";
1506 case U6OP_NEW:
1507 return "U6OP_NEW";
1508 case U6OP_DELETE:
1509 return "U6OP_DELETE";
1510 case U6OP_GIVE:
1511 return "U6OP_GIVE";
1512 case U6OP_ADDKARMA:
1513 return "U6OP_ADDKARMA";
1514 case U6OP_SUBKARMA:
1515 return "U6OP_SUBKARMA";
1516 case U6OP_RESURRECT:
1517 return "U6OP_RESURRECT";
1518 case U6OP_HEAL:
1519 return "U6OP_HEAL";
1520 case U6OP_CURE:
1521 return "U6OP_CURE";
1522 case U6OP_WORKTYPE:
1523 return "U6OP_WORKTYPE";
1524 case U6OP_INVENTORY:
1525 return "U6OP_INVENTORY";
1526 case U6OP_PORTRAIT:
1527 return "U6OP_PORTRAIT";
1528 case U6OP_WAIT:
1529 return "U6OP_WAIT";
1530 case U6OP_ENDANSWER:
1531 return "U6OP_ENDANSWER";
1532 case U6OP_KEYWORDS:
1533 return "U6OP_KEYWORDS";
1534 case U6OP_SIDENT:
1535 return "U6OP_SIDENT";
1536 case U6OP_SLOOK:
1537 return "U6OP_SLOOK";
1538 case U6OP_SCONVERSE:
1539 return "U6OP_SCONVERSE";
1540 case U6OP_SPREFIX:
1541 return "U6OP_SPREFIX";
1542 case U6OP_ANSWER:
1543 return "U6OP_ANSWER";
1544 case U6OP_ASK:
1545 return "U6OP_ASK";
1546 case U6OP_ASKC:
1547 return "U6OP_ASKC";
1548 case U6OP_INPUTSTR:
1549 return "U6OP_INPUTSTR";
1550 case U6OP_INPUT:
1551 return "U6OP_INPUT";
1552 case U6OP_INPUTNUM:
1553 return "U6OP_INPUTNUM";
1554 default :
1555 break;
1556 }
1557
1558 return "U6OP_UNKNOWN";
1559 }
1560
1561 } // End of namespace Nuvie
1562 } // End of namespace Ultima
1563