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_llist.h"
25 #include "ultima/nuvie/misc/u6_misc.h"
26 #include "ultima/nuvie/misc/map_entity.h"
27 #include "ultima/nuvie/core/game.h"
28 #include "ultima/nuvie/views/view.h"
29 #include "ultima/nuvie/views/view_manager.h"
30 #include "ultima/nuvie/actors/actor_manager.h"
31 #include "ultima/nuvie/actors/actor.h"
32 #include "ultima/nuvie/actors/u6_actor.h"
33 #include "ultima/nuvie/core/party.h"
34 #include "ultima/nuvie/core/player.h"
35 #include "ultima/nuvie/gui/widgets/msg_scroll.h"
36 #include "ultima/nuvie/core/map.h"
37 #include "ultima/nuvie/core/game_clock.h"
38 #include "ultima/nuvie/core/book.h"
39 #include "ultima/nuvie/core/events.h"
40 #include "ultima/nuvie/gui/widgets/map_window.h"
41 #include "ultima/nuvie/core/timed_event.h"
42 #include "ultima/nuvie/core/egg_manager.h"
43 #include "ultima/nuvie/core/anim_manager.h"
44 #include "ultima/nuvie/sound/sound_manager.h"
45 #include "ultima/nuvie/core/effect.h"
46 #include "ultima/nuvie/core/weather.h"
47 #include "ultima/nuvie/script/script.h"
48 #include "ultima/nuvie/keybinding/keys.h"
49 #include "ultima/nuvie/gui/widgets/background.h"
50 #include "ultima/nuvie/gui/widgets/command_bar.h"
51 #include "ultima/nuvie/usecode/u6_usecode.h"
52 #include "ultima/nuvie/usecode/u6_object_types.h"
53 #include "ultima/nuvie/actors/u6_work_types.h"
54
55 namespace Ultima {
56 namespace Nuvie {
57
58 #define MESG_ANIM_HIT_WORLD ANIM_CB_HIT_WORLD
59 #define MESG_ANIM_HIT ANIM_CB_HIT
60 #define MESG_TEXT_READY MSGSCROLL_CB_TEXT_READY
61 #define MESG_DATA_READY CB_DATA_READY
62 #define MESG_EFFECT_COMPLETE EFFECT_CB_COMPLETE
63 #define MESG_TIMED CB_TIMED
64 #define MESG_INPUT_CANCELED CB_INPUT_CANCELED
65
66 // numbered by entrance quality, "" = no name
67 static const char *u6_dungeons[21] = {
68 "",
69 "Deceit",
70 "Despise",
71 "Destard",
72 "Wrong",
73 "Covetous",
74 "Shame",
75 "Hythloth",
76 "GSA",
77 "Control",
78 "Passion",
79 "Diligence",
80 "Tomb of Kings",
81 "Ant Mound",
82 "Swamp Cave",
83 "Spider Cave",
84 "Cyclops Cave",
85 "Heftimus Cave",
86 "Heroes' Hole",
87 "Pirate Cave",
88 "Buccaneer's Cave"
89 };
90
91 // Red moongate teleport locations.
92 static const struct {
93 uint16 x;
94 uint16 y;
95 uint8 z;
96 } red_moongate_tbl[] = {
97 {0x0, 0x0, 0x0},
98 {0x383, 0x1f3, 0x0},
99 {0x3a7, 0x106, 0x0},
100 {0x1b3, 0x18b, 0x0},
101 {0x1f7, 0x166, 0x0},
102 {0x93, 0x373, 0x0},
103 {0x397, 0x3a6, 0x0},
104 {0x44, 0x2d, 0x5},
105 {0x133, 0x160, 0x0},
106 {0xbc, 0x2d, 0x5},
107 {0x9f, 0x3ae, 0x0},
108 {0x2e3, 0x2bb, 0x0},
109 {0x0, 0x0, 0x0},
110 {0x0, 0x0, 0x0},
111 {0x0, 0x0, 0x0},
112 {0xe3, 0x83, 0x0},
113 {0x17, 0x16, 0x1},
114 {0x80, 0x56, 0x5},
115 {0x6c, 0xdd, 0x5},
116 {0x39b, 0x36c, 0x0},
117 {0x127, 0x26, 0x0},
118 {0x4b, 0x1fb, 0x0},
119 {0x147, 0x336, 0x0},
120 {0x183, 0x313, 0x0},
121 {0x33f, 0xa6, 0x0},
122 {0x29b, 0x43, 0x0}
123 };
124
125 #define USE_U6_POTION_BLUE 0x0
126 #define USE_U6_POTION_RED 0x1
127 #define USE_U6_POTION_YELLOW 0x2
128 #define USE_U6_POTION_GREEN 0x3
129 #define USE_U6_POTION_ORANGE 0x4
130 #define USE_U6_POTION_PURPLE 0x5
131 #define USE_U6_POTION_BLACK 0x6
132 #define USE_U6_POTION_WHITE 0x7
133
134 // numbered by potion object frame number
135 static const char *u6_potions[8] = {
136 "an awaken", // blue
137 "a cure", // red
138 "a heal", // yellow
139 "a poison", // green
140 "a sleep", // orange
141 "a protection", // purple
142 "an invisibility", // black
143 "an xray vision" // white
144 };
145
146 // convenient macro to grab a target object - when first called the player will
147 // be prompted with P, and upon target selection the object will be set to O
148 #define USECODE_SELECT_OBJ(O, P) \
149 { \
150 static bool selected_obj = false; \
151 if(!selected_obj) \
152 { \
153 game->get_event()->get_direction(P); \
154 game->get_event()->request_input(this, obj); \
155 selected_obj = true; \
156 return false; \
157 } \
158 else { O = items.obj_ref; selected_obj = false; } \
159 }
160 #define USECODE_SELECT_ACTOR(O, P) \
161 { \
162 static bool selected_obj = false; \
163 if(!selected_obj) \
164 { \
165 game->get_event()->get_target(P); \
166 game->get_event()->request_input(this, obj); \
167 selected_obj = true; \
168 return false; \
169 } \
170 else { A = items.actor2_ref; selected_obj = false; } \
171 }
172 #define USECODE_SELECT_TARGET(T, P) \
173 { \
174 static bool selected_obj = false; \
175 if(!selected_obj) \
176 { \
177 game->get_event()->get_target(P); \
178 game->get_event()->request_input(this, obj); \
179 selected_obj = true; \
180 return false; \
181 } \
182 else { T = items.mapcoord_ref; selected_obj = false; } \
183 }
184 #define USECODE_SELECT_DIRECTION(T, P) \
185 { \
186 static bool selected_obj = false; \
187 if(!selected_obj) \
188 { \
189 game->get_event()->get_target(P); \
190 game->get_event()->request_input(this, obj); \
191 selected_obj = true; \
192 return false; \
193 } \
194 else { T = items.mapcoord_ref; selected_obj = false; } \
195 }
196
197
U6UseCode(Game * g,Configuration * cfg)198 U6UseCode::U6UseCode(Game *g, Configuration *cfg) : UseCode(g, cfg) {
199 }
200
~U6UseCode()201 U6UseCode::~U6UseCode() {
202 }
203
204
205 /* Is the object a food (or drink) item? */
is_food(Obj * obj)206 bool U6UseCode::is_food(Obj *obj) {
207 const U6ObjectType *type = get_object_type(obj->obj_n, obj->frame_n);
208 return (type && (type->flags & OBJTYPE_FOOD));
209 }
210
211
is_container(Obj * obj)212 bool U6UseCode::is_container(Obj *obj) {
213 const U6ObjectType *type = get_object_type(obj->obj_n, obj->frame_n);
214 return (type && (type->flags & OBJTYPE_CONTAINER));
215 }
216
is_container(uint16 obj_n,uint8 frame_n)217 bool U6UseCode::is_container(uint16 obj_n, uint8 frame_n) {
218 const U6ObjectType *type = get_object_type(obj_n, frame_n);
219 return (type && (type->flags & OBJTYPE_CONTAINER));
220 }
221
is_readable(Obj * obj)222 bool U6UseCode::is_readable(Obj *obj) {
223 const U6ObjectType *type = get_object_type(obj->obj_n, obj->frame_n);
224 return ((type && (type->flags & OBJTYPE_BOOK)) || obj->obj_n == OBJ_U6_CLOCK
225 || obj->obj_n == OBJ_U6_SUNDIAL);
226 }
227
228
229 /* Is there `ev' usecode for an object? */
has_usecode(Obj * obj,UseCodeEvent ev)230 bool U6UseCode::has_usecode(Obj *obj, UseCodeEvent ev) {
231 const U6ObjectType *type = get_object_type(obj->obj_n, obj->frame_n, ev);
232 if (!type && !UseCode::has_usecode(obj, ev))
233 return (false);
234 return (true);
235 }
236
has_usecode(Actor * actor,UseCodeEvent ev)237 bool U6UseCode::has_usecode(Actor *actor, UseCodeEvent ev) {
238 const U6ObjectType *type = get_object_type(actor->get_obj_n(), actor->get_frame_n(), ev);
239 if (!type || type->flags == OBJTYPE_CONTAINER)
240 return (false);
241 return (true);
242 }
243
244 /* USE object. Actor is the actor using the object. */
use_obj(Obj * obj,Actor * actor)245 bool U6UseCode::use_obj(Obj *obj, Actor *actor) {
246 if (UseCode::has_usecode(obj, USE_EVENT_USE)) { //use script based usecode.
247 return UseCode::use_obj(obj, actor);
248 }
249
250 const U6ObjectType *type = get_object_type(obj->obj_n, obj->frame_n, USE_EVENT_USE);
251 set_itemref(actor, items.actor2_ref);
252 return (uc_event(type, USE_EVENT_USE, obj));
253 }
254
255 /* LOOK at object. Actor is the actor looking at the object. */
look_obj(Obj * obj,Actor * actor)256 bool U6UseCode::look_obj(Obj *obj, Actor *actor) {
257 const U6ObjectType *type = get_object_type(obj->obj_n, obj->frame_n, USE_EVENT_LOOK);
258 set_itemref(actor);
259 return (uc_event(type, USE_EVENT_LOOK, obj));
260 }
261
262 /* PASS object. Actor is the actor trying to pass the object. It takes
263 target coordinates in case the object has multiple tiles. */
pass_obj(Obj * obj,Actor * actor,uint16 x,uint16 y)264 bool U6UseCode::pass_obj(Obj *obj, Actor *actor, uint16 x, uint16 y) {
265 const U6ObjectType *type = get_object_type(obj->obj_n, obj->frame_n, USE_EVENT_PASS);
266 static MapCoord loc;
267 loc.x = x;
268 loc.y = y;
269 loc.z = obj->z;
270 set_itemref(actor);
271 set_itemref(&loc);
272 return (uc_event(type, USE_EVENT_PASS, obj));
273 }
274
275 /* SEARCH nearby object. Actor is the actor searching. */
search_obj(Obj * obj,Actor * actor)276 bool U6UseCode::search_obj(Obj *obj, Actor *actor) {
277 const U6ObjectType *type = get_object_type(obj->obj_n, obj->frame_n, USE_EVENT_SEARCH);
278 set_itemref(actor);
279 return (uc_event(type, USE_EVENT_SEARCH, obj));
280 }
281
282
283 /* Callback from timer or other class. User_data is the object which will
284 * receive the message (if applicable). */
callback(uint16 msg,CallBack * caller,void * msg_data)285 uint16 U6UseCode::callback(uint16 msg, CallBack *caller, void *msg_data) {
286 Obj *obj = (Obj *)callback_user_data;
287 if (!obj) {
288 DEBUG(0, LEVEL_ERROR, "UseCode: internal message %d sent to NULL object\n", msg);
289 return (0);
290 }
291 return (message_obj(obj, (CallbackMessage)msg, msg_data));
292 }
293
294
295 /* Call MESSAGE function for an object. Msg_data is assigned to the appropriate
296 * itemref. The USE function is called in response to a DATA_READY message.
297 * Returns false if there is no usecode for that object.
298 */
message_obj(Obj * obj,CallbackMessage msg,void * msg_data)299 bool U6UseCode::message_obj(Obj *obj, CallbackMessage msg, void *msg_data) {
300 const U6ObjectType *type = get_object_type(obj->obj_n, obj->frame_n, USE_EVENT_MESSAGE);
301 items.msg_ref = &msg;
302 switch (msg) { // set itemref from msgdata
303 case MESG_TIMED:
304 items.uint_ref = (uint32 *)msg_data;
305 break;
306 case MESG_ANIM_HIT_WORLD:
307 items.mapcoord_ref = (MapCoord *)msg_data;
308 break;
309 case MESG_ANIM_HIT:
310 items.ent_ref = (MapEntity *)msg_data;
311 break;
312 case MESG_TEXT_READY:
313 items.string_ref = (string *)msg_data;
314 break;
315 case MESG_DATA_READY:
316 items.data_ref = (char *)msg_data; // pointer to EventInput structure
317 items.obj_ref = ((EventInput *)items.data_ref)->obj;
318 items.actor2_ref = ((EventInput *)items.data_ref)->actor;
319 items.mapcoord_ref = ((EventInput *)items.data_ref)->loc;
320 items.string_ref = ((EventInput *)items.data_ref)->str;
321 return uc_event(get_object_type(obj->obj_n, obj->frame_n),
322 USE_EVENT_USE, obj);
323 case MESG_INPUT_CANCELED:
324 return uc_event(get_object_type(obj->obj_n, obj->frame_n),
325 USE_EVENT_INPUT_CANCEL, obj);
326 default :
327 break;
328 }
329
330 return uc_event(type, USE_EVENT_MESSAGE, obj);
331 }
332
333
334 /* MOVE nearby object in a relative direction. */
move_obj(Obj * obj,sint16 rel_x,sint16 rel_y)335 bool U6UseCode::move_obj(Obj *obj, sint16 rel_x, sint16 rel_y) {
336 const U6ObjectType *type = get_object_type(obj->obj_n, obj->frame_n, USE_EVENT_MOVE);
337 static MapCoord dir;
338 dir.sx = rel_x;
339 dir.sy = rel_y;
340 set_itemref(&dir);
341 return (uc_event(type, USE_EVENT_MOVE, obj));
342 }
343
344
345 /* Call post-LOAD or UNLOAD usecode for an object.
346 * Returns false if there is no usecode for that object. */
load_obj(Obj * obj)347 bool U6UseCode::load_obj(Obj *obj) {
348 const U6ObjectType *type = get_object_type(obj->obj_n, obj->frame_n, USE_EVENT_LOAD);
349 return (uc_event(type, USE_EVENT_LOAD, obj));
350 }
351
352
353 /* Call READY or UNREADY usecode for an object. */
ready_obj(Obj * obj,Actor * actor)354 bool U6UseCode::ready_obj(Obj *obj, Actor *actor) {
355 const U6ObjectType *type = get_object_type(obj->obj_n, obj->frame_n, USE_EVENT_READY);
356 set_itemref(actor);
357 return (uc_event(type, USE_EVENT_READY, obj));
358 }
359
360
361 /* Call GET usecode for an object. */
get_obj(Obj * obj,Actor * actor)362 bool U6UseCode::get_obj(Obj *obj, Actor *actor) {
363 const U6ObjectType *type = get_object_type(obj->obj_n, obj->frame_n, USE_EVENT_GET);
364 set_itemref(actor);
365 return (uc_event(type, USE_EVENT_GET, obj));
366 }
367
368
369 /* Call DROP usecode for an object. */
drop_obj(Obj * obj,Actor * actor,uint16 x,uint16 y,uint16 qty)370 bool U6UseCode::drop_obj(Obj *obj, Actor *actor, uint16 x, uint16 y, uint16 qty) {
371 const U6ObjectType *type = get_object_type(obj->obj_n, obj->frame_n, USE_EVENT_DROP);
372 static MapCoord loc; // use statics for pointers
373 static uint32 obj_qty;
374 loc.x = x;
375 loc.y = y;
376 set_itemref(actor);
377 set_itemref(&loc);
378 items.uint_ref = &obj_qty;
379 return (uc_event(type, USE_EVENT_DROP, obj));
380 }
381
382
383 /* Return pointer to object-type in list for object N:F, or NULL if none. */
get_object_type(uint16 n,uint8 f,UseCodeEvent ev)384 inline const U6ObjectType *U6UseCode::get_object_type(uint16 n, uint8 f, UseCodeEvent ev) {
385 const U6ObjectType *type = U6ObjectTypes;
386 while (type->obj_n != OBJ_U6_NOTHING) {
387 if (type->obj_n == n && (type->frame_n == 0xFF || type->frame_n == f)
388 && ((type->trigger & ev) || ev == 0))
389 return (type);
390 ++type;
391 }
392 return (NULL);
393 }
394
395
396 /* Call usecode function of the U6ObjectType, with event `ev', for `obj'.
397 * The meaning of the return value is different for each event.
398 * Returns false if the type is invalid or doesn't respond to the event.
399 */
uc_event(const U6ObjectType * type,UseCodeEvent ev,Obj * obj)400 bool U6UseCode::uc_event(const U6ObjectType *type, UseCodeEvent ev, Obj *obj) {
401 if (!type || type->obj_n == OBJ_U6_NOTHING)
402 return (false);
403 if (type->trigger & ev) {
404 dbg_print_event(ev, obj);
405 bool ucret = (this->*type->usefunc)(obj, ev);
406 clear_items(); // clear references for next call
407 return (ucret); // return from usecode function
408 }
409 return (false); // doesn't respond to event
410 }
411
412
lock_door(Obj * obj)413 void U6UseCode::lock_door(Obj *obj) {
414 if (is_unlocked_door(obj))
415 obj->frame_n += 4;
416 }
417
unlock_door(Obj * obj)418 void U6UseCode::unlock_door(Obj *obj) {
419 if (is_locked_door(obj))
420 obj->frame_n -= 4;
421 }
422
unlock(Obj * obj)423 void U6UseCode::unlock(Obj *obj) {
424 if (is_locked_door(obj)) {
425 unlock_door(obj);
426 } else if (is_locked_chest(obj)) {
427 unlock_chest(obj);
428 }
429 }
430
lock(Obj * obj)431 void U6UseCode::lock(Obj *obj) {
432 if (is_magically_locked(obj) || is_locked(obj))
433 return;
434
435 if (is_closed_door(obj)) {
436 lock_door(obj);
437 } else if (is_closed_chest(obj)) {
438 lock_chest(obj);
439 }
440 }
441
442 // USE: unlock locked doors, open/close other doors
use_door(Obj * obj,UseCodeEvent ev)443 bool U6UseCode::use_door(Obj *obj, UseCodeEvent ev) {
444 Obj *key_obj;
445 bool print = (items.actor_ref == player->get_actor());
446
447 if (is_magically_locked_door(obj)) {
448 if (print) scroll->display_string("\nmagically locked\n");
449 return true;
450 }
451
452 if (is_locked_door(obj)) { // locked door
453 key_obj = player->get_actor()->inventory_get_object(OBJ_U6_KEY, obj->quality);
454 if (obj->quality != 0 && key_obj != NULL) { // we have the key for this door so lets unlock it.
455 unlock_door(obj);
456 if (print) scroll->display_string("\nunlocked\n");
457 } else if (print) scroll->display_string("\nlocked\n");
458
459 return true;
460 }
461
462 if (obj->frame_n <= 3) { //the door is open
463 if (!map->is_passable(obj->x, obj->y, obj->z) || map->actor_at_location(obj->x, obj->y, obj->z)) { //don't close door if blocked
464 if (print) scroll->display_string("\nNot now!\n");
465 } else { //close the door
466 obj->frame_n += 4;
467 if (print) scroll->display_string("\nclosed!\n");
468 }
469 } else {
470 process_effects(obj, items.actor_ref); //process traps.
471 obj->frame_n -= 4;
472 if (print) scroll->display_string("\nopened!\n");
473 }
474
475 return true;
476 }
477
478 // USE: climb up or down a ladder (entire party)
use_ladder(Obj * obj,UseCodeEvent ev)479 bool U6UseCode::use_ladder(Obj *obj, UseCodeEvent ev) {
480 uint16 x = obj->x, y = obj->y;
481 uint8 z;
482 if (!player->in_party_mode()) {
483 scroll->display_string("\nNot in solo mode.\n");
484 return true;
485 }
486
487 if (UseCode::out_of_use_range(obj, true))
488 return true;
489
490 if (obj->frame_n == 0) { // DOWN
491 if (obj->z == 0) { //handle the transition from the surface to the first dungeon level
492 x = (obj->x & 0x07) | (obj->x >> 2 & 0xF8);
493 y = (obj->y & 0x07) | (obj->y >> 2 & 0xF8);
494 }
495 z = obj->z + 1;
496 } else { //UP
497 if (obj->z == 1) { //we use obj->quality to tell us which surface chunk to come up in.
498 x = obj->x / 8 * 8 * 4 + ((obj->quality & 0x03) * 8) + (obj->x - obj->x / 8 * 8);
499 y = obj->y / 8 * 8 * 4 + ((obj->quality >> 2 & 0x03) * 8) + (obj->y - obj->y / 8 * 8);
500 }
501 z = obj->z - 1;
502 }
503
504 party->dismount_from_horses();
505
506 MapCoord ladder(obj->x, obj->y, obj->z), destination(x, y, z);
507 party->walk(&ladder, &destination, 100);
508 if (z != 0 && z != 5)
509 game->get_weather()->set_wind_dir(NUVIE_DIR_NONE);
510 return true;
511 }
512
513
514 // USE: Open the passhtrough! Close the passthrough!
use_passthrough(Obj * obj,UseCodeEvent ev)515 bool U6UseCode::use_passthrough(Obj *obj, UseCodeEvent ev) {
516 uint16 new_x, new_y;
517 uint8 new_frame_n;
518 char action_string[6]; // either 'Open' or 'Close'
519 bool print = (items.actor_ref == player->get_actor());
520
521 new_x = obj->x;
522 new_y = obj->y;
523 new_frame_n = obj->frame_n;
524
525 if (obj->frame_n < 2) { // the pass through is currently closed.
526 if (obj->obj_n == OBJ_U6_V_PASSTHROUGH)
527 new_y--;
528 else // OBJ_U6_H_PASSTHROUGH
529 new_x--;
530
531 new_frame_n = 2; // open the pass through
532 strcpy(action_string, "Open");
533 } else { // the pass through is currently open.
534 if (obj->obj_n == OBJ_U6_V_PASSTHROUGH)
535 new_y++;
536 else // OBJ_U6_H_PASSTHROUGH
537 new_x++;
538
539 new_frame_n = 0; // close the pass through
540 strcpy(action_string, "Close");
541 }
542
543 if (!map->actor_at_location(new_x, new_y, obj->z)) {
544 obj_manager->move(obj, new_x, new_y, obj->z);
545 obj->frame_n = new_frame_n;
546 if (print) {
547 scroll->display_string("\n");
548 scroll->display_string(action_string);
549 scroll->display_string(" the passthrough.\n");
550 }
551 } else if (print)
552 scroll->display_string("\nNot now!\n");
553
554 return true;
555 }
556
557 // for use with levers and switches, target_obj_n is either OBJ_U6_PORTCULLIS or OBJ_U6_ELECTRIC_FIELD
use_switch(Obj * obj,UseCodeEvent ev)558 bool U6UseCode::use_switch(Obj *obj, UseCodeEvent ev) {
559 Obj *doorway_obj;
560 Obj *portc_obj;
561 U6LList *obj_list;
562 U6Link *link;
563 uint16 target_obj_n = 0;
564 const char *message = NULL;
565 const char *fail_message = NULL;
566 bool success = false;
567 bool print = (items.actor_ref == player->get_actor());
568
569 if (obj->obj_n == OBJ_U6_LEVER) {
570 target_obj_n = OBJ_U6_PORTCULLIS;
571 message = "\nSwitch the lever, you hear a noise.\n";
572 fail_message = "\nSwitch the lever, strange, nothing happened.\n";
573 } else if (obj->obj_n == OBJ_U6_SWITCH) {
574 if (obj->quality == 113 && obj->x == 139 && obj->y == 0 && obj->z == 1) { // hack for Covetous
575 doorway_obj = obj_manager->get_obj_of_type_from_location(OBJ_U6_DOORWAY, 0, 0, 160, 3, 1);
576 if (doorway_obj)
577 doorway_obj->quality = 113;
578 }
579 target_obj_n = OBJ_U6_ELECTRIC_FIELD;
580 message = "\nOperate the switch, you hear a noise.\n";
581 fail_message = "\nOperate the switch, strange, nothing happened.\n";
582 }
583
584 doorway_obj = obj_manager->find_obj(obj->z, OBJ_U6_DOORWAY, obj->quality);
585 for (; doorway_obj != NULL; doorway_obj = obj_manager->find_next_obj(obj->z, doorway_obj)) {
586 obj_list = obj_manager->get_obj_list(doorway_obj->x, doorway_obj->y, doorway_obj->z);
587
588 for (portc_obj = NULL, link = obj_list->start(); link != NULL; link = link->next) { // find existing portcullis.
589 if (((Obj *)link->data)->obj_n == target_obj_n) {
590 portc_obj = (Obj *)link->data;
591 break;
592 }
593 }
594 success = true;
595
596 if (portc_obj == NULL) { //no barrier object, so lets create one.
597 portc_obj = obj_manager->copy_obj(doorway_obj);
598 portc_obj->obj_n = target_obj_n;
599 portc_obj->quality = 0;
600 if (target_obj_n == OBJ_U6_PORTCULLIS) {
601 if (portc_obj->frame_n == 9) //FIX Hack for cream buildings might need one for virt wall.
602 portc_obj->frame_n = 1;
603 } else
604 portc_obj->frame_n = 0;
605
606 obj_manager->add_obj(portc_obj, true);
607 } else { //delete barrier object.
608 obj_list->remove(portc_obj);
609 delete_obj(portc_obj);
610 }
611 }
612
613 toggle_frame(obj);
614 if (print)
615 scroll->display_string(success ? message : fail_message);
616 return true;
617 }
618
619
620 /* USE: light or douse various fire objects (toggling their frame number)
621 */
use_firedevice(Obj * obj,UseCodeEvent ev)622 bool U6UseCode::use_firedevice(Obj *obj, UseCodeEvent ev) {
623 if (obj->obj_n == OBJ_U6_BRAZIER && obj->frame_n == 2)
624 return (true); // holy flames can't be doused
625 if (obj->obj_n == OBJ_U6_FIREPLACE) {
626 if (obj->frame_n == 1 || obj->frame_n == 3) {
627 use_firedevice_message(obj, false);
628 obj->frame_n--;
629 } else {
630 use_firedevice_message(obj, true);
631 obj->frame_n++;
632 }
633 } else {
634 toggle_frame(obj);
635 use_firedevice_message(obj, (bool)obj->frame_n);
636 }
637 return (true);
638 }
639
640
641 /* SEARCH: discover and open door
642 * USE: open or close door
643 */
use_secret_door(Obj * obj,UseCodeEvent ev)644 bool U6UseCode::use_secret_door(Obj *obj, UseCodeEvent ev) {
645 if (ev == USE_EVENT_USE) {
646 if (obj->frame_n == 1 || obj->frame_n == 3)
647 obj->frame_n--;
648 else
649 obj->frame_n++;
650 return (true);
651 } else if (ev == USE_EVENT_SEARCH) {
652 scroll->display_string("a secret door");
653 if (obj->frame_n == 0 || obj->frame_n == 2)
654 obj->frame_n++;
655 return (true);
656 }
657 return (true);
658 }
659
660
661 /* Use: Open/close container. If container is open, Search.
662 * Search: Dump container contents.
663 */
use_container(Obj * obj,UseCodeEvent ev)664 bool U6UseCode::use_container(Obj *obj, UseCodeEvent ev) {
665 if (ev == USE_EVENT_USE) {
666 if (is_locked_chest(obj) || is_magically_locked_chest(obj)) {
667 if (is_locked_chest(obj) && obj->quality != 0) {
668 Obj *key_obj = player->get_actor()->inventory_get_object(OBJ_U6_KEY, obj->quality);
669 if (key_obj != NULL) { // we have the key for this chest so lets unlock it.
670 unlock_chest(obj);
671 scroll->display_string("\nunlocked\n");
672 return true;
673 }
674 }
675
676 scroll->display_string("\nNo effect\n");
677 return true;
678 }
679 if ((obj->obj_n == OBJ_U6_CHEST && !obj->is_in_inventory()) || obj->obj_n == OBJ_U6_CRATE || obj->obj_n == OBJ_U6_BARREL)
680 toggle_frame(obj); //open / close object
681 if (obj->frame_n == 0 || (obj->obj_n != OBJ_U6_CHEST && obj->obj_n != OBJ_U6_CRATE && obj->obj_n != OBJ_U6_BARREL)
682 || (obj->obj_n == OBJ_U6_CHEST && obj->frame_n < 2 && obj->is_in_inventory())) {
683 process_effects(obj, items.actor_ref); //run any effects that might be stored in this container. Eg Poison explosion.
684
685 if (Game::get_game()->doubleclick_opens_containers() && obj->obj_n != OBJ_U6_DEER
686 && obj->obj_n != OBJ_U6_SHIP && obj->obj_n != OBJ_U6_STONE_LION) { // just search for these
687 game->get_view_manager()->open_container_view(obj);
688 } else if (obj->is_in_inventory()) {
689 scroll->display_string("\nNot usable\n");
690 return true;
691 } else {
692 scroll->display_string("\nSearching here, you find ");
693 bool found_objects = search_obj(obj, items.actor_ref);
694 scroll->display_string(found_objects ? ".\n" : "nothing.\n");
695 }
696 }
697 return (true);
698 } else if (ev == USE_EVENT_SEARCH) { // search message already printed
699 return (UseCode::search_container(obj));
700 // if(obj->container && obj->container->end())
701 // {
702 // new TimedContainerSearch(obj);
703 // return(true);
704 // }
705 } else if (ev == USE_EVENT_GET) {
706 if (is_chest(obj) && obj->frame_n == 0) //open chest
707 obj->frame_n = 1; //close the chest
708 return true;
709 }
710 return (false);
711 }
712
713 /* Use rune to free shrine */
use_rune(Obj * obj,UseCodeEvent ev)714 bool U6UseCode::use_rune(Obj *obj, UseCodeEvent ev) {
715 char mantras[][8] = {"AHM", "MU", "RA", "BEH", "CAH", "SUMM", "OM", "LUM"};
716 Obj *force_field = NULL;
717 uint16 rune_obj_offset = obj->obj_n - OBJ_U6_RUNE_HONESTY;
718 MapCoord player_location = player->get_actor()->get_location();
719
720
721 scroll->cancel_input_request();
722
723 if (ev == USE_EVENT_USE) {
724 scroll->display_string("Mantra: ");
725
726 scroll->set_input_mode(true, NULL, true);
727 scroll->request_input(this, obj);
728
729 return (false);
730 } else if (ev == USE_EVENT_MESSAGE && items.string_ref) {
731 scroll->display_string("\n");
732
733 char *mantra = new char[items.string_ref->size() + 1];
734 strcpy(mantra, items.string_ref->c_str());
735
736 if (scumm_stricmp(mantra, mantras[rune_obj_offset]) == 0) {
737 // find the matching force field for this shrine. match rune offset against force field quality
738 force_field = obj_manager->find_obj(player_location.z, OBJ_U6_FORCE_FIELD, rune_obj_offset);
739
740 // make sure the player is right next to the force field.
741 if (force_field && abs(player_location.x - force_field->x) < 2 && abs(player_location.y - force_field->y) < 2) {
742 game->get_sound_manager()->playSfx(NUVIE_SFX_CASTING_MAGIC_P1, SFX_PLAY_ASYNC);
743 game->get_sound_manager()->playSfx(NUVIE_SFX_CASTING_MAGIC_P2, SFX_PLAY_SYNC);
744 AsyncEffect *e = new AsyncEffect(new XorEffect(1000));
745 e->run();
746
747 remove_gargoyle_egg(force_field->x, force_field->y, force_field->z);
748
749 obj_manager->remove_obj_from_map(force_field);
750 delete force_field;
751
752
753 scroll->display_string("\nDone!\n");
754 } else
755 scroll->display_string("\nNo effect!\n");
756 } else
757 scroll->display_string("\nWrong mantra!\n");
758
759 scroll->display_string("\n");
760 scroll->display_prompt();
761 delete[] mantra;
762 }
763
764 return true;
765 }
766
remove_gargoyle_egg(uint16 x,uint16 y,uint8 z)767 void U6UseCode::remove_gargoyle_egg(uint16 x, uint16 y, uint8 z) {
768 Std::list<Egg *> *egg_list;
769 Std::list<Egg *>::iterator egg_itr;
770
771 egg_list = game->get_egg_manager()->get_egg_list();
772
773 for (egg_itr = egg_list->begin(); egg_itr != egg_list->end();) {
774 Egg *egg = *egg_itr;
775 egg_itr++;
776
777 Obj *egg_obj = egg->obj;
778
779 if (abs(x - egg_obj->x) < 20 && abs(y - egg_obj->y) < 20 && z == egg_obj->z) {
780 if (egg_obj->find_in_container(OBJ_U6_GARGOYLE, 0, false, 0, false) || egg_obj->find_in_container(OBJ_U6_WINGED_GARGOYLE, 0, false, 0, false)) {
781 DEBUG(0, LEVEL_DEBUGGING, "Removed egg at (%x,%x,%x)", egg_obj->x, egg_obj->y, egg_obj->z);
782 game->get_egg_manager()->remove_egg(egg_obj, false);
783 obj_manager->unlink_from_engine(egg_obj);
784 delete_obj(egg_obj);
785
786 }
787 }
788 }
789 }
790
use_vortex_cube(Obj * obj,UseCodeEvent ev)791 bool U6UseCode::use_vortex_cube(Obj *obj, UseCodeEvent ev) {
792 if (ev == USE_EVENT_SEARCH)
793 return UseCode::search_container(obj);
794
795 Obj *britannian_lens, *gargoyle_lens;
796 Obj *container_obj;
797 Obj *codex;
798 U6Link *link;
799 uint8 moonstone_check = 0;
800 MapCoord player_location = player->get_actor()->get_location();
801 codex = obj_manager->find_obj(player_location.z, OBJ_U6_CODEX, 128); // 128 = codex's book id
802
803 if (Game::get_game()->doubleclick_opens_containers() && (obj->is_in_inventory() || !codex
804 || abs(player_location.x - codex->x) > 11 || abs(player_location.y - codex->y) > 11)) { //FIXME this should probably be mapwindow size)
805 game->get_view_manager()->open_container_view(obj);
806 return true;
807 }
808 if (obj->container != NULL || player_location.z == 0) { // make sure we've got all 8 moonstones in our vortex cube.
809 britannian_lens = obj_manager->find_obj(player_location.z, OBJ_U6_BRITANNIAN_LENS, 0, OBJ_NOMATCH_QUALITY);
810 gargoyle_lens = obj_manager->find_obj(player_location.z, OBJ_U6_GARGOYLE_LENS, 0, OBJ_NOMATCH_QUALITY);
811
812 // make sure the player is close to the codex
813 if (codex && abs(player_location.x - codex->x) < 11 && abs(player_location.y - codex->y) < 11) { //FIXME this should probably be mapwindow size
814 // check that the lenses are in the correct place.
815 if (britannian_lens && gargoyle_lens &&
816 britannian_lens->x == 0x399 && britannian_lens->y == 0x353 && britannian_lens->z == 0 &&
817 gargoyle_lens->x == 0x39d && gargoyle_lens->y == 0x353 && gargoyle_lens->z == 0) {
818 for (link = obj->container->start(); link != NULL; link = link->next) {
819 container_obj = (Obj *)link->data;
820 if (container_obj->obj_n == OBJ_U6_MOONSTONE) {
821 moonstone_check |= 1 << container_obj->frame_n;
822 }
823 }
824
825 if (moonstone_check == 0xff) { // have we got all 8 moonstones?
826 obj_manager->remove_obj_from_map(codex);
827 delete codex;
828
829 scroll->display_string("\nThe Codex has vanished!\n");
830
831 //FIXME put weird swirl effect in here.
832
833 game->get_map_window()->Hide();
834 game->get_scroll()->Hide();
835 game->get_background()->Hide();
836 game->get_command_bar()->Hide();
837 game->get_event()->close_gumps();
838 if (game->get_view_manager()->get_current_view())
839 game->get_view_manager()->get_current_view()->Hide();
840
841 game->get_script()->play_cutscene("/ending.lua");
842 game->quit();
843
844 return (true);
845 }
846 }
847 }
848 }
849
850 DEBUG(0, LEVEL_DEBUGGING, "moonstone_check = %d\n", moonstone_check);
851 scroll->display_string("\nNo Effect!\n");
852
853 return true;
854 }
855
856
857 /* Use bell or pull-chain, ring and animate nearby bell.
858 */
use_bell(Obj * obj,UseCodeEvent ev)859 bool U6UseCode::use_bell(Obj *obj, UseCodeEvent ev) {
860 Obj *bell = NULL;
861 if (ev != USE_EVENT_USE)
862 return (false);
863 if (obj->obj_n == OBJ_U6_BELL)
864 bell = obj;
865 else
866 bell = bell_find(obj);
867 if (bell) {
868 obj_manager->animate_forwards(bell, 2);
869 }
870 Game::get_game()->get_sound_manager()->playSfx(NUVIE_SFX_BELL);
871
872 return (true);
873 }
874
875
876 /* Find bell near its pull-chain.
877 */
bell_find(Obj * chain_obj)878 Obj *U6UseCode::bell_find(Obj *chain_obj) {
879 Obj *bell = NULL;
880 for (uint16 x = chain_obj->x - 8; x <= chain_obj->x + 8; x++)
881 for (uint16 y = chain_obj->y - 8; y <= chain_obj->y + 8 && !bell; y++)
882 bell = obj_manager->get_obj_of_type_from_location(OBJ_U6_BELL, x, y, chain_obj->z);
883 return (bell);
884 }
885
886
887 //cranks control drawbridges.
use_crank(Obj * obj,UseCodeEvent ev)888 bool U6UseCode::use_crank(Obj *obj, UseCodeEvent ev) {
889 uint16 x, y;
890 uint8 level;
891 uint16 b_width;
892 bool bridge_open;
893 Obj *start_obj;
894
895 start_obj = drawbridge_find(obj);
896
897 if (start_obj->frame_n == 3) // bridge open
898 bridge_open = true;
899 else
900 bridge_open = false;
901
902 x = start_obj->x;
903 y = start_obj->y;
904 level = start_obj->z;
905 drawbridge_remove(x, y, level, &b_width);
906
907 // find and animate chain
908 if (!(start_obj = obj_manager->get_obj_of_type_from_location(OBJ_U6_CHAIN, obj->x + 1, obj->y, obj->z)))
909 start_obj = obj_manager->get_obj_of_type_from_location(OBJ_U6_CHAIN, obj->x - 1, obj->y, obj->z);
910 if (start_obj)
911 obj_manager->animate_forwards(start_obj, 3);
912
913 if (bridge_open) {
914 obj_manager->animate_backwards(obj, 3); // animate crank
915 drawbridge_close(x, y, level, b_width);
916 } else {
917 obj_manager->animate_forwards(obj, 3);
918 drawbridge_open(x, y, level, b_width);
919 }
920
921 return true;
922 }
923
drawbridge_find(Obj * crank_obj)924 Obj *U6UseCode::drawbridge_find(Obj *crank_obj) {
925 uint16 i, j;
926 Obj *start_obj, *tmp_obj;
927
928 for (i = 0; i < 6; i++) { // search on right side of crank.
929 start_obj = obj_manager->get_obj_of_type_from_location(OBJ_U6_DRAWBRIDGE, crank_obj->x + 1, crank_obj->y + i, crank_obj->z);
930 if (start_obj != NULL) // this means we are using the left crank.
931 return start_obj;
932 }
933
934 for (i = 0; i < 6; i++) { // search on left side of crank.
935 tmp_obj = obj_manager->get_obj_of_type_from_location(OBJ_U6_DRAWBRIDGE, crank_obj->x - 1, crank_obj->y + i, crank_obj->z);
936
937 if (tmp_obj != NULL) { // this means we are using the right crank.
938 //find the start of the drawbridge on the left.
939 // we do this by searching to the left of the crank till we hit the crank on the otherside.
940 // we then move right 1 tile and down 'i' tiles to the start object. :)
941 for (j = 1; j < crank_obj->x; j++) {
942 tmp_obj = obj_manager->get_obj_of_type_from_location(OBJ_U6_CRANK, crank_obj->x - j, crank_obj->y, crank_obj->z);
943 if (tmp_obj && tmp_obj->obj_n == OBJ_U6_CRANK) {
944 start_obj = obj_manager->get_obj_of_type_from_location(OBJ_U6_DRAWBRIDGE, tmp_obj->x + 1, tmp_obj->y + i, tmp_obj->z);
945 return start_obj;
946 }
947 }
948 }
949 }
950
951 return NULL;
952 }
953
drawbridge_open(uint16 x,uint16 y,uint8 level,uint16 b_width)954 void U6UseCode::drawbridge_open(uint16 x, uint16 y, uint8 level, uint16 b_width) {
955 uint16 i, j;
956 Obj *obj;
957
958 y++;
959
960 for (i = 0;; i++) {
961 obj = new_obj(OBJ_U6_DRAWBRIDGE, 3, x, y + i, level); //left side chain
962 obj_manager->add_obj(obj, true);
963
964 obj = new_obj(OBJ_U6_DRAWBRIDGE, 5, x + b_width - 1, y + i, level); //right side chain
965 obj_manager->add_obj(obj, true);
966
967 for (j = 0; j < b_width - 2; j++) {
968 obj = new_obj(OBJ_U6_DRAWBRIDGE, 4, x + 1 + j, y + i, level);
969 obj_manager->add_obj(obj, true);
970 }
971
972 if (map->is_passable(x, y + i + 1, level)) //we extend the drawbridge until we hit a passable tile.
973 break;
974 }
975
976 i++;
977
978 for (j = 0; j < b_width - 2; j++) { //middle bottom tiles
979 obj = new_obj(OBJ_U6_DRAWBRIDGE, 1, x + 1 + j, y + i, level);
980 obj_manager->add_obj(obj, true);
981 }
982
983 obj = new_obj(OBJ_U6_DRAWBRIDGE, 0, x, y + i, level); //bottom left
984 obj_manager->add_obj(obj, true);
985
986 obj = new_obj(OBJ_U6_DRAWBRIDGE, 2, x + b_width - 1, y + i, level); // bottom right
987 obj_manager->add_obj(obj, true);
988
989 scroll->display_string("\nOpen the drawbridge.\n");
990
991 return;
992 }
993
drawbridge_close(uint16 x,uint16 y,uint8 level,uint16 b_width)994 void U6UseCode::drawbridge_close(uint16 x, uint16 y, uint8 level, uint16 b_width) {
995 uint16 i;
996 Obj *obj;
997
998 y--;
999
1000 obj = new_obj(OBJ_U6_DRAWBRIDGE, 6, x - 1, y, level); //left side
1001 obj_manager->add_obj(obj, true);
1002
1003 obj = new_obj(OBJ_U6_DRAWBRIDGE, 8, x + b_width - 1, y, level); //right side
1004 obj_manager->add_obj(obj, true);
1005
1006 for (i = 0; i < b_width - 1; i++) {
1007 obj = new_obj(OBJ_U6_DRAWBRIDGE, 7, x + i, y, level); //middle
1008 obj_manager->add_obj(obj, true);
1009 }
1010
1011 scroll->display_string("\nClose the drawbridge.\n");
1012 }
1013
1014 /* GET: clear buried moonstone location
1015 * (we never know if it is buried or just lying there)
1016 * USE: drop at user's current location, and update buried location
1017 */
use_moonstone(Obj * obj,UseCodeEvent ev)1018 bool U6UseCode::use_moonstone(Obj *obj, UseCodeEvent ev) {
1019 if (ev == USE_EVENT_GET) {
1020 Weather *weather = game->get_weather();
1021 /* FIXME: need to check weights here already?
1022 * Check original's behavior when moonstone cannot be gotten due
1023 * to weight limitations. Moving it normally doesn't unbury it,
1024 * so probably failing to get it shouldn't either.
1025 */
1026 weather->set_moonstone(obj->frame_n, MapCoord(0, 0, 0)) ;
1027 //scroll->display_string("\nMoonstone dug up. (FIXME)\n");
1028 weather->update_moongates();
1029 return true;
1030 } else if (ev == USE_EVENT_USE) {
1031 Weather *weather = game->get_weather();
1032 MapCoord loc = Game::get_game()->get_player()->get_actor()->get_location();
1033 Tile *map_tile = map->get_tile(loc.x, loc.y, loc.z);
1034
1035 if ((map_tile->tile_num < 1 || map_tile->tile_num > 7) && (map_tile->tile_num < 0x10 || map_tile->tile_num > 0x6f)) {
1036 scroll->display_string("Cannot be buried here!\n");
1037 return true;
1038 }
1039
1040 weather->set_moonstone(obj->frame_n, loc) ;
1041 scroll->display_string("buried.\n");
1042
1043 obj_manager->moveto_map(obj, loc);
1044 obj->status |= OBJ_STATUS_OK_TO_TAKE;
1045 weather->update_moongates();
1046 return true;
1047 }
1048 return false;
1049 }
1050
1051 /* USE: select location on ground to bury orb; when location is passed, open
1052 * a red moongate to a new location
1053 */
use_orb(Obj * obj,UseCodeEvent ev)1054 bool U6UseCode::use_orb(Obj *obj, UseCodeEvent ev) {
1055 Obj *gate;
1056 uint16 x, y, ox, oy;
1057 uint8 px, py, z, oz;
1058 uint8 position;
1059 Actor *lord_british;
1060 MapCoord *mapcoord_ref = items.mapcoord_ref;
1061
1062 // can't use obj->is_on_map() since new container gumps will
1063 // have corpse items in an npc's inventoty
1064 if (!party->has_obj(87, 0, false)) { // make sure orb of moons is in party inventory
1065 scroll->display_string("\nNot usable\n");
1066 return true;
1067 }
1068
1069 player->get_actor()->get_location(&x, &y, &z);
1070 lord_british = actor_manager->get_actor(U6_LORD_BRITISH_ACTOR_NUM);
1071
1072 // The player must ask Lord British about the orb before it can be used.
1073 // This sets the flag 0x20 in LB's flags field which allows the orb to be used.
1074
1075 if ((lord_british->get_talk_flags() & U6_LORD_BRITISH_ORB_CHECK_FLAG) == 0) {
1076 scroll->display_string("\nYou can't figure out how to use it.\n");
1077 return true;
1078 }
1079
1080 // if(!orb_activated)
1081 // {
1082 // scroll->display_string("\nYou must recharge it first!\n");
1083 // return true;
1084 // }
1085
1086 if (ev == USE_EVENT_INPUT_CANCEL ||
1087 (items.obj_ref && !items.obj_ref->is_on_map())) { // selected item in inventory
1088 scroll->display_string("Failed\n");
1089 return true;
1090 }
1091
1092 if (!mapcoord_ref) {
1093 game->get_event()->get_target(MapCoord(x, y, z), "Where: ");
1094 game->get_event()->request_input(this, obj);
1095 return false; // no prompt
1096 }
1097
1098 ox = mapcoord_ref->x;
1099 oy = mapcoord_ref->y;
1100 oz = mapcoord_ref->z;
1101
1102 px = 3 + ox - x;
1103 py = 2 + oy - y;
1104
1105 if (px > 5 || py > 4 || // Moongate out of range.
1106 items.actor2_ref || // Actor at location.
1107 !map->is_passable(ox, oy, oz)) { // Location not passable.
1108 scroll->display_string("Failed.\n");
1109 return true;
1110 }
1111
1112 position = px + py * 5;
1113
1114 if (position >= 12 && position <= 14) // The three middle positions go noware.
1115 position = 0;
1116
1117 gate = new_obj(OBJ_U6_RED_GATE, 1, ox, oy, z);
1118 gate->quality = position;
1119 gate->set_temporary();
1120
1121 new VanishEffect(VANISH_WAIT);
1122 obj_manager->add_obj(gate, true);
1123 game->get_map_window()->updateBlacking(); // next update not until Effect completes
1124 scroll->display_string("a red moon gate appears.\n");
1125
1126 return true;
1127 }
1128
1129
drawbridge_remove(uint16 x,uint16 y,uint8 level,uint16 * bridge_width)1130 void U6UseCode::drawbridge_remove(uint16 x, uint16 y, uint8 level, uint16 *bridge_width) {
1131 uint16 w, h;
1132
1133 //remove end of closed drawbridge.
1134 // if present.
1135 if (x > 0)
1136 obj_manager->remove_obj_type_from_location(OBJ_U6_DRAWBRIDGE, x - 1, y, level);
1137
1138 *bridge_width = 0;
1139
1140 //remove the rest of the bridge.
1141 for (h = 0, w = 1; w != 0; h++) {
1142 for (w = 0;; w++) {
1143 if (obj_manager->remove_obj_type_from_location(OBJ_U6_DRAWBRIDGE, x + w, y + h, level) == false) {
1144 if (w != 0)
1145 *bridge_width = w;
1146 break;
1147 }
1148 }
1149 }
1150
1151 return;
1152 }
1153
1154 // USE: fishing pole. Attempt to catch a fish from an adjacent water square.
use_fishing_pole(Obj * obj,UseCodeEvent ev)1155 bool U6UseCode::use_fishing_pole(Obj *obj, UseCodeEvent ev) {
1156 ViewManager *view_manager = game->get_view_manager();
1157 Actor *player_actor;
1158 Obj *fish;
1159 uint16 x, y;
1160 uint8 z;
1161
1162 player_actor = player->get_actor();
1163
1164 player_actor->get_location(&x, &y, &z);
1165
1166 if (use_find_water(&x, &y, &z) == false) {
1167 scroll->display_string("\nYou need to stand next to water.\n");
1168 return true;
1169 }
1170
1171 if (NUVIE_RAND() % 100 <= 20) {
1172 fish = new Obj();
1173
1174 fish->obj_n = OBJ_U6_FISH;
1175 if (!player_actor->can_carry_object(fish)) {
1176 scroll->display_string("\nGot it, but can't carry it.\n");
1177 if (use_boat_find_land(&x, &y, &z) == false) { //we couldn't find an empty spot for the fish.
1178 //so back into the water with thee.
1179 delete fish;
1180 return true;
1181 }
1182
1183 fish->x = x;
1184 fish->y = y;
1185 fish->z = z;
1186 fish->set_ok_to_take(true);
1187 obj_manager->add_obj(fish);
1188 return true;
1189 }
1190
1191 player_actor->inventory_add_object(fish);
1192
1193 if (!game->is_new_style())
1194 view_manager->set_inventory_mode();
1195
1196 view_manager->update(); //FIX this should be moved higher up in UseCode
1197
1198 scroll->display_string("\nGot it!\n");
1199 } else
1200 scroll->display_string("\nDidn't get a fish.\n");
1201
1202 return true;
1203 }
1204
use_find_water(uint16 * x,uint16 * y,uint8 * z)1205 inline bool U6UseCode::use_find_water(uint16 *x, uint16 *y, uint8 *z) {
1206 if (map->is_water(*x, *y - 1, *z)) { //UP
1207 *y = *y - 1;
1208 return true;
1209 }
1210 if (map->is_water(*x + 1, *y, *z)) { //RIGHT
1211 *x = *x + 1;
1212 return true;
1213 }
1214 if (map->is_water(*x, *y + 1, *z)) { //DOWN
1215 *y = *y + 1;
1216 return true;
1217 }
1218 if (map->is_water(*x - 1, *y, *z)) { //LEFT
1219 *x = *x - 1;
1220 return true;
1221 }
1222
1223 return false;
1224 }
1225
1226
1227 /* Use shovel in one of 8 directions. If used in a dungeon level get a chance of
1228 * finding gold or a fountain (to make a wish).
1229 */
use_shovel(Obj * obj,UseCodeEvent ev)1230 bool U6UseCode::use_shovel(Obj *obj, UseCodeEvent ev) {
1231 Obj *dug_up_obj = NULL;
1232 Obj *ladder_obj;
1233 MapCoord from, dig_at, ladder;
1234
1235 if (ev == USE_EVENT_INPUT_CANCEL) {
1236 scroll->display_string("nowhere.\n");
1237 return true;
1238 }
1239
1240 if (!items.mapcoord_ref) { // get direction (FIXME: should return relative dir)
1241 if (!obj->is_readied()) {
1242 scroll->display_string("\nNot readied.\n");
1243 return (true);
1244 }
1245 if (items.actor_ref == NULL) { // happens when you use on a widget
1246 scroll->display_string("nowhere.\n");
1247 return true;
1248 }
1249 Actor *parent = obj->get_actor_holding_obj();
1250 from = parent->get_location();
1251
1252 // game->get_event()->useselect_mode(obj, "Direction: ");
1253 game->get_event()->get_direction(from, "Direction: ");
1254 if (game->get_map_window()->get_interface() == INTERFACE_NORMAL)
1255 game->get_event()->do_not_show_target_cursor = true;
1256 game->get_event()->request_input(this, obj);
1257 return (false);
1258 }
1259
1260 Actor *parent = obj->get_actor_holding_obj();
1261 from = parent->get_location();
1262
1263 dig_at = *items.mapcoord_ref;
1264
1265 if (game->get_map_window()->get_interface() < INTERFACE_FULLSCREEN) {
1266 dig_at.sx = (dig_at.sx == 0) ? 0 : (dig_at.sx < 0) ? -1 : 1;
1267 dig_at.sy = (dig_at.sy == 0) ? 0 : (dig_at.sy < 0) ? -1 : 1;
1268 }
1269
1270 scroll->display_string(get_direction_name(dig_at.x, dig_at.y));
1271 if (dig_at.sx == 0 && dig_at.sy == 0) {
1272 scroll->display_string(".\n");
1273 return (true); // display prompt
1274 }
1275 scroll->display_string(".\n\n");
1276
1277 dig_at.x += from.x;
1278 dig_at.y += from.y;
1279 dig_at.z = from.z;
1280 if (!dig_at.is_visible()) {
1281 scroll->display_string("Not on screen.\n");
1282 return true; // display prompt
1283 } else if (!from.is_visible() && from.distance(dig_at) > 5) {
1284 scroll->display_string("Out of range.\n");
1285 return true; // display prompt
1286 } else if (game->get_map_window()->get_interface() != INTERFACE_IGNORE_BLOCK) {
1287 LineTestResult lt;
1288 if (map->lineTest(from.x, from.y, dig_at.x, dig_at.y, dig_at.z, LT_HitUnpassable, lt)) {
1289 MapCoord hit_loc = MapCoord(lt.hit_x, lt.hit_y, lt.hit_level);
1290 if (hit_loc != dig_at) {
1291 scroll->display_string("Blocked\n");
1292 return true; // display prompt
1293 }
1294 }
1295 }
1296 Obj *hole = obj_manager->get_obj_of_type_from_location(OBJ_U6_HOLE, dig_at.x, dig_at.y, dig_at.z);
1297 if (hole || dig_at.z == 5 // we can't go anywhere from the gargoyle world.
1298 || game->get_map_window()->tile_is_black(dig_at.x, dig_at.y)
1299 || (dig_at.z == 0 && (dig_at.x != 0x2c3 || dig_at.y != 0x343))) {
1300 scroll->display_string("No effect\n");
1301 return (true); // ??
1302 }
1303
1304 // try to conenct with a ladder on a lower level.
1305 ladder = dig_at;
1306 // This is to inacurate. It will detect an extra ladder 8 tiles east
1307 // plus another one 8 tiles north and 8 tiles east
1308 if (dig_at.z == 0) { //handle the transition from the surface to the first dungeon level
1309 ladder.x = (dig_at.x & 0x07) | (dig_at.x >> 2 & 0xF8);
1310 ladder.y = (dig_at.y & 0x07) | (dig_at.y >> 2 & 0xF8);
1311 }
1312
1313 ladder.z = dig_at.z + 1;
1314
1315 // if(dig_at.z != 5) already checked
1316 {
1317 ladder_obj = obj_manager->get_obj_of_type_from_location(OBJ_U6_LADDER, ladder.x, ladder.y, ladder.z);
1318 if (ladder_obj && ladder_obj->frame_n == 1) { // ladder up.
1319 scroll->display_string("You dig a hole.\n");
1320 dug_up_obj = new_obj(OBJ_U6_HOLE, 0, dig_at.x, dig_at.y, dig_at.z); //found a connecting ladder, dig a hole
1321 }
1322 }
1323 Tile *tile = map->get_tile(dig_at.x, dig_at.y, dig_at.z, true);
1324 // uncomment first check if the coord conversion gets added back
1325 if (/*(!dug_up_obj && dig_at.z == 0) ||*/ !tile // original might have checked for earth desc and no wall mask
1326 || !((tile->tile_num <= 111 && tile->tile_num >= 108) || tile->tile_num == 540)) {
1327 scroll->display_string("No Effect.\n");
1328 return (true);
1329 }
1330
1331 if (!dug_up_obj) {
1332 // 10% chance of anything
1333 if (NUVIE_RAND() % 10) {
1334 scroll->display_string("Failed\n");
1335 return (true);
1336 }
1337
1338 // Door #1 or Door #2?
1339 Obj *fountain = obj_manager->get_obj_of_type_from_location(OBJ_U6_FOUNTAIN, dig_at.x, dig_at.y, dig_at.z);
1340 if ((NUVIE_RAND() % 2)) { // original lets you stack fountains
1341 scroll->display_string("You find a water fountain.\n");
1342 if (!fountain) // don't actually add another one
1343 dug_up_obj = new_obj(OBJ_U6_FOUNTAIN, 0, dig_at.x, dig_at.y, dig_at.z);
1344 } else {
1345 scroll->display_string("You find a gold nugget.\n");
1346 dug_up_obj = new_obj(OBJ_U6_GOLD_NUGGET, 0, dig_at.x, dig_at.y, dig_at.z);
1347 dug_up_obj->status |= OBJ_STATUS_OK_TO_TAKE;
1348 }
1349 }
1350 if (dug_up_obj) {
1351 dug_up_obj->set_temporary();
1352 obj_manager->add_obj(dug_up_obj, true);
1353 }
1354 return (true);
1355 }
1356
1357
1358 /* USE: Magic fountain. Make a wish!
1359 */
use_fountain(Obj * obj,UseCodeEvent ev)1360 bool U6UseCode::use_fountain(Obj *obj, UseCodeEvent ev) {
1361 static bool get_wish = false;
1362 static Actor *wish_actor = NULL; // person receiving gift
1363
1364 scroll->cancel_input_request();
1365 if (ev == USE_EVENT_USE) {
1366 scroll->display_string("Make a wish? ");
1367 // get Y/N single char, no ENTER (FIXME: no printing)
1368 scroll->set_input_mode(true, "yn", false);
1369 scroll->request_input(this, obj);
1370 wish_actor = items.actor_ref;
1371 assert(wish_actor);
1372 return (false);
1373 } else if (ev == USE_EVENT_MESSAGE && items.string_ref) {
1374 scroll->display_string("\n");
1375 if (!get_wish) { // answered with Y/N
1376 // Y:
1377 if (*items.string_ref == "y" || *items.string_ref == "Y") {
1378 scroll->display_string("Wish for: ");
1379 // get string
1380 scroll->set_input_mode(true);
1381 scroll->request_input(this, obj);
1382 get_wish = true;
1383 } else { // N: won't wish
1384 scroll->display_string("\n");
1385 scroll->display_prompt();
1386 }
1387 } else { // answered with wish
1388 get_wish = false;
1389 bool wished_for_food = false;
1390 char *wish = (char *)malloc(items.string_ref->size() + 1);
1391 strcpy(wish, items.string_ref->c_str());
1392 if (scumm_stricmp(wish, "Food") == 0 || scumm_stricmp(wish, "Mutton") == 0
1393 || scumm_stricmp(wish, "Wine") == 0 || scumm_stricmp(wish, "Fruit") == 0
1394 || scumm_stricmp(wish, "Mead") == 0)
1395 wished_for_food = true;
1396 free(wish);
1397 if (!wished_for_food) {
1398 scroll->display_string("\nFailed\n\n");
1399 scroll->display_prompt();
1400 return (true);
1401 }
1402 // 25% chance of anything
1403 if ((NUVIE_RAND() % 4) != 0) {
1404 scroll->display_string("\nNo effect\n\n");
1405 scroll->display_prompt();
1406 return (true);
1407 }
1408 scroll->display_string("\nYou got food");
1409 // must be able to carry it
1410 if (!wish_actor->can_carry_object(OBJ_U6_MEAT_PORTION, 1)) {
1411 scroll->display_string(", but you can't carry it.\n\n");
1412 scroll->display_prompt();
1413 return (true);
1414 }
1415 scroll->display_string(".\n\n");
1416 scroll->display_prompt();
1417 assert(wish_actor);
1418 wish_actor->inventory_new_object(OBJ_U6_MEAT_PORTION, 1);
1419 }
1420 } else
1421 get_wish = false;
1422 return (false);
1423 }
1424
1425
1426 /* USE: Make a rubber ducky sound. */
use_rubber_ducky(Obj * obj,UseCodeEvent ev)1427 bool U6UseCode::use_rubber_ducky(Obj *obj, UseCodeEvent ev) {
1428 if (items.actor_ref == player->get_actor())
1429 scroll->display_string("\nSqueak!\n");
1430 Game::get_game()->get_sound_manager()->playSfx(NUVIE_SFX_RUBBER_DUCK); //FIXME towns says "Quack! Quack!" and plays sfx twice.
1431 return (true);
1432 }
1433
parseLatLongString(U6UseCodeLatLonEnum mode,Std::string * input)1434 sint16 U6UseCode::parseLatLongString(U6UseCodeLatLonEnum mode, Std::string *input) {
1435 uint16 len = input->length();
1436 sint16 val = 0;
1437 for (uint16 i = 0; i < len; i++) {
1438 char c = (*input)[i];
1439 if (c < '0' || c > '9') {
1440 c = toupper(c);
1441 if (mode == LAT) {
1442 if (c == 'N' || c == 'S') {
1443 if (c == 'N')
1444 val = -val;
1445 } else {
1446 val = 100;
1447 }
1448 } else {
1449 if (c == 'E' || c == 'W') {
1450 if (c == 'W')
1451 val = -val;
1452 } else {
1453 val = 100;
1454 }
1455 }
1456 break;
1457 }
1458
1459 val = val * 10 + (*input)[i] - 48;
1460 }
1461
1462 return val;
1463 }
1464
1465 /* USE: Crystal ball
1466 */
use_crystal_ball(Obj * obj,UseCodeEvent ev)1467 bool U6UseCode::use_crystal_ball(Obj *obj, UseCodeEvent ev) {
1468 static enum { GET_LAT, GET_LON} mode = GET_LAT;
1469 static MapCoord loc;
1470 static Actor *actor = NULL;
1471
1472 scroll->cancel_input_request();
1473 if (ev == USE_EVENT_USE) {
1474 actor = items.actor_ref;
1475 if ((int)NUVIE_RAND() % 30 < (45 - actor->get_intelligence()) / 2) { //use crystal ball saving roll.
1476 game->get_script()->call_actor_hit(actor, (NUVIE_RAND() % 10) + 1, SCRIPT_DISPLAY_HIT_MSG);
1477 scroll->display_string("\n");
1478 scroll->display_prompt();
1479 return false;
1480 }
1481
1482 mode = GET_LAT;
1483 scroll->display_string("Enter degrees followed by N, S, E or W.\n\nAt latitude=");
1484 scroll->set_input_mode(true);
1485 scroll->request_input(this, obj);
1486
1487 return (false);
1488 } else if (ev == USE_EVENT_MESSAGE && items.string_ref) {
1489 if (mode == GET_LAT) {
1490 sint16 lat = parseLatLongString(LAT, items.string_ref);
1491 if (lat > 80 || lat < -44) {
1492 scroll->display_string("\n\n");
1493 scroll->display_prompt();
1494 return false;
1495 }
1496
1497 loc.y = lat * 8 + 360;
1498 scroll->display_string("\n");
1499 scroll->display_string(" longitude=");
1500 scroll->set_input_mode(true);
1501 scroll->request_input(this, obj);
1502 mode = GET_LON;
1503 } else if (mode == GET_LON) {
1504 scroll->display_string("\n");
1505 sint16 lon = parseLatLongString(LON, items.string_ref);
1506 if (lon > 88 || lon < -37) {
1507 scroll->display_string("\n\n");
1508 scroll->display_prompt();
1509 return false;
1510 }
1511
1512 loc.x = lon * 8 + 304;
1513
1514 actor->get_location(NULL, NULL, &loc.z);
1515 if (loc.z != 0) {
1516 loc.x = loc.x / 4;
1517 loc.y = loc.y / 4;
1518 }
1519
1520 AsyncEffect *e = new AsyncEffect(new WizardEyeEffect(loc, 0x28));
1521 e->run(EFFECT_PROCESS_GUI_INPUT);
1522 scroll->display_string("\nDone\n\n");
1523 scroll->display_prompt();
1524 }
1525
1526 }
1527
1528 return (false);
1529 }
1530
1531 /* USE: Enter instrument playing mode, with sound for used object. */
play_instrument(Obj * obj,UseCodeEvent ev)1532 bool U6UseCode::play_instrument(Obj *obj, UseCodeEvent ev) {
1533 // FIXME: need intrument sounds AND a config option to simply change music
1534 // track when an instrument is played. Maybe NORTH_KEY and SOUTH_KEY can cycle through sounds/music and DO_ACTION_KEY can play it.
1535 /// FIXME: also some floating music note icons like in U7
1536 game->get_event()->close_gumps(); // gumps will steal input
1537 const char *musicmsg = (obj->obj_n == OBJ_U6_PANPIPES) ? "panpipes"
1538 : (obj->obj_n == OBJ_U6_HARPSICHORD) ? "harpsichord"
1539 : (obj->obj_n == OBJ_U6_HARP) ? "harp"
1540 : (obj->obj_n == OBJ_U6_LUTE) ? "lute"
1541 : (obj->obj_n == OBJ_U6_XYLOPHONE) ? "xylophone"
1542 : "musical instrument";
1543 if (items.data_ref) {
1544 Common::KeyCode key = ((EventInput *)items.data_ref)->key;
1545 ActionKeyType key_type = ((EventInput *)items.data_ref)->action_key_type;
1546 if (key == Common::KEYCODE_0) DEBUG(0, LEVEL_WARNING, "FIXME: %s: modulate 0\n", musicmsg);
1547 if (key == Common::KEYCODE_1) DEBUG(0, LEVEL_WARNING, "FIXME: %s: modulate 1\n", musicmsg);
1548 if (key == Common::KEYCODE_2) DEBUG(0, LEVEL_WARNING, "FIXME: %s: modulate 2\n", musicmsg);
1549 if (key == Common::KEYCODE_3) DEBUG(0, LEVEL_WARNING, "FIXME: %s: modulate 3\n", musicmsg);
1550 if (key == Common::KEYCODE_4) DEBUG(0, LEVEL_WARNING, "FIXME: %s: modulate 4\n", musicmsg);
1551 if (key == Common::KEYCODE_5) DEBUG(0, LEVEL_WARNING, "FIXME: %s: modulate 5\n", musicmsg);
1552 if (key == Common::KEYCODE_6) DEBUG(0, LEVEL_WARNING, "FIXME: %s: modulate 6\n", musicmsg);
1553 if (key == Common::KEYCODE_7) DEBUG(0, LEVEL_WARNING, "FIXME: %s: modulate 7\n", musicmsg);
1554 if (key == Common::KEYCODE_8) DEBUG(0, LEVEL_WARNING, "FIXME: %s: modulate 8\n", musicmsg);
1555 if (key == Common::KEYCODE_9) DEBUG(0, LEVEL_WARNING, "FIXME: %s: modulate 9\n", musicmsg);
1556 return (key_type != DO_ACTION_KEY && key_type != CANCEL_ACTION_KEY);
1557 } else
1558 game->get_event()->key_redirect(this, obj);
1559 return false;
1560 }
1561
1562
1563 // use_firedevice()
use_firedevice_message(Obj * obj,bool lit)1564 bool U6UseCode::use_firedevice_message(Obj *obj, bool lit) {
1565 if (items.actor_ref != player->get_actor())
1566 return true;
1567 scroll->display_string("\n");
1568 scroll->display_string(obj_manager->get_obj_name(obj));
1569 if (lit)
1570 scroll->display_string(" is lit.\n");
1571 else
1572 scroll->display_string(" is doused.\n");
1573
1574 return true;
1575 }
1576
1577 /* USE: Eat/drink food object. Hic!
1578 */
use_food(Obj * obj,UseCodeEvent ev)1579 bool U6UseCode::use_food(Obj *obj, UseCodeEvent ev) {
1580 if (ev == USE_EVENT_USE) {
1581 if (items.actor_ref == player->get_actor()) {
1582 if (obj->obj_n == OBJ_U6_WINE || obj->obj_n == OBJ_U6_MEAD
1583 || obj->obj_n == OBJ_U6_ALE) {
1584 scroll->display_string("\nYou drink it.\n");
1585
1586 player->add_alcohol(); // add to drunkeness
1587 } else
1588 scroll->display_string("\nYou eat the food.\n");
1589 }
1590 destroy_obj(obj, 1);
1591 }
1592 return (true);
1593 }
1594
1595
1596 /* USE: Use potion. If actor2 is passed, give them the potion, else select
1597 * actor2. */
use_potion(Obj * obj,UseCodeEvent ev)1598 bool U6UseCode::use_potion(Obj *obj, UseCodeEvent ev) {
1599 ActorManager *am = actor_manager;
1600
1601 if (ev == USE_EVENT_USE) {
1602 if (!items.actor2_ref && !items.obj_ref && !items.mapcoord_ref) {
1603 game->get_event()->get_target(items.actor_ref->get_location(), "On whom: ");
1604 game->get_event()->request_input(this, obj);
1605 } else if (!items.actor2_ref) { // no selection
1606 scroll->display_string("nobody\n");
1607 return (true);
1608 } else { // use potion
1609 sint8 party_num = party->get_member_num(items.actor2_ref);
1610 scroll->display_string(party_num >= 0 ? party->get_actor_name(party_num)
1611 : am->look_actor(items.actor2_ref));
1612 scroll->display_string("\n");
1613
1614 if (party_num < 0) // can't force potions on non-party members
1615 scroll->display_string("No effect\n");
1616 else {
1617 switch (obj->frame_n) {
1618 case USE_U6_POTION_RED:
1619 ((U6Actor *)items.actor2_ref)->set_poisoned(false);
1620 destroy_obj(obj);
1621 break;
1622 case USE_U6_POTION_YELLOW:
1623 ((U6Actor *)items.actor2_ref)->heal();
1624 destroy_obj(obj);
1625 break;
1626 case USE_U6_POTION_GREEN:
1627 ((U6Actor *)items.actor2_ref)->set_poisoned(true);
1628 destroy_obj(obj);
1629 break;
1630 case USE_U6_POTION_BLUE:
1631 ((U6Actor *)items.actor2_ref)->set_asleep(false);
1632 destroy_obj(obj);
1633 break;
1634 case USE_U6_POTION_PURPLE:
1635 ((U6Actor *)items.actor2_ref)->set_protected(true);
1636 destroy_obj(obj);
1637 break;
1638 case USE_U6_POTION_WHITE:
1639 new U6WhitePotionEffect(2500, 6000, obj);
1640 break; // wait for message to delete potion
1641 case USE_U6_POTION_BLACK:
1642 //new SpellTargetEffect(items.actor2_ref, obj);
1643 // or effect_mgr->wait_for_effect(new SpellTargetEffect(items.actor2_ref), this, obj);
1644 items.actor2_ref->set_invisible(true);
1645 destroy_obj(obj);
1646 break;
1647 case USE_U6_POTION_ORANGE:
1648 //items.actor2_ref->set_worktype(WORKTYPE_U6_SLEEP);
1649 items.actor2_ref->set_asleep(true);
1650 //party->set_active(party_num, !(items.actor2_ref->is_sleeping() || items.actor2_ref->is_paralyzed()));
1651 player->set_actor(party->get_leader_actor());
1652 player->set_mapwindow_centered(true);
1653 destroy_obj(obj);
1654 break;
1655
1656 default:
1657 if (obj->frame_n <= 7) {
1658 scroll->display_string("Drink %s potion!\n", u6_potions[obj->frame_n]);
1659 //scroll->display_string(u6_potions[obj->frame_n]);
1660 //scroll->display_string(" potion!\n");
1661 } else
1662 scroll->display_string("\nNo effect\n");
1663 destroy_obj(obj);
1664 }
1665 }
1666 return true;
1667 }
1668 } else if (ev == USE_EVENT_INPUT_CANCEL) {
1669 scroll->display_string("No effect\n");
1670 return true;
1671 } else if (ev == USE_EVENT_MESSAGE) { // assume message is from potion effect
1672 if (*items.msg_ref == MESG_EFFECT_COMPLETE && obj->frame_n == USE_U6_POTION_WHITE) { // white
1673 destroy_obj(obj);
1674 }
1675 }
1676 return false;
1677 }
1678
lock_pick_dex_check()1679 bool U6UseCode::lock_pick_dex_check() {
1680 int dex = player->get_actor()->get_dexterity();
1681 if (player->get_actor()->is_cursed()) {
1682 if (dex <= 3)
1683 dex = 1;
1684 else
1685 dex -= 3;
1686 }
1687
1688 if ((int)NUVIE_RAND() % 30 < (45 - dex) / 2)
1689 return true;
1690
1691 return false;
1692 }
1693
1694 /* Use a key on obj_ref (a door). */
use_key(Obj * obj,UseCodeEvent ev)1695 bool U6UseCode::use_key(Obj *obj, UseCodeEvent ev) {
1696 Obj *door_obj = NULL;
1697 if (ev == USE_EVENT_USE) {
1698 USECODE_SELECT_OBJ(door_obj, "On "); // door_obj <- items.obj_ref or from user
1699 if (!door_obj) {
1700 scroll->display_string("nothing\n");
1701 return true;
1702 } else {
1703 if (UseCode::out_of_use_range(door_obj, false))
1704 return true;
1705
1706 scroll->display_string(obj_manager->get_obj_name(door_obj));
1707 scroll->display_string("\n");
1708
1709 if (!is_door(door_obj) && !is_chest(door_obj)) {
1710 scroll->display_string("No effect\n");
1711 return true;
1712 }
1713
1714 if (obj->obj_n == OBJ_U6_LOCK_PICK && lock_pick_dex_check() == true) {
1715 Game::get_game()->get_sound_manager()->playSfx(NUVIE_SFX_FAILURE);
1716 scroll->display_string("\nKey broke.\n");
1717 if (obj->qty > 1) {
1718 obj->qty -= 1;
1719 } else {
1720 UseCode::search_container(obj, false); //need to remove rolling pin if there is one
1721 obj_manager->unlink_from_engine(obj);
1722 delete_obj(obj);
1723 }
1724
1725 return true;
1726 }
1727
1728 //FIXME need to handle locked chests.
1729 if (((obj->obj_n == OBJ_U6_KEY && door_obj->quality != 0 && door_obj->quality == obj->quality)
1730 || (obj->obj_n == OBJ_U6_LOCK_PICK && door_obj->quality == 0))
1731 && (is_closed_door(door_obj) || is_closed_chest(door_obj))
1732 && !is_magically_locked(door_obj)) {
1733 if (is_locked(door_obj)) {
1734 unlock(door_obj);
1735 scroll->display_string("\nunlocked!\n");
1736 } else {
1737 lock(door_obj);
1738 scroll->display_string("\nlocked!\n");
1739 }
1740 } else if (is_door(door_obj) && door_obj->frame_n <= 3
1741 && ((obj->obj_n == OBJ_U6_KEY && door_obj->quality != 0 && door_obj->quality == obj->quality)
1742 || (obj->obj_n == OBJ_U6_LOCK_PICK && door_obj->quality == 0)))
1743 scroll->display_string("\nCan't (Un)lock an opened door\n");
1744 else
1745 scroll->display_string("\nNo effect\n");
1746 return true;
1747 }
1748 } else if (ev == USE_EVENT_INPUT_CANCEL) {
1749 scroll->display_string("nothing\n");
1750 return true;
1751 } else if (ev == USE_EVENT_GET && obj->obj_n == OBJ_U6_LOCK_PICK) { //need to remove rolling pin if there is one
1752 UseCode::search_container(obj, false);
1753 return true;
1754 } else if (ev == USE_EVENT_SEARCH && obj->obj_n == OBJ_U6_LOCK_PICK) //need to remove rolling pin if there is one
1755 return (UseCode::search_container(obj));
1756
1757 return false;
1758 }
1759
1760
1761 /* USE: Enter and exit sea-going vessels. (entire party)
1762 */
use_boat(Obj * obj,UseCodeEvent ev)1763 bool U6UseCode::use_boat(Obj *obj, UseCodeEvent ev) {
1764 Actor *ship_actor;
1765 uint16 lx, ly;
1766 uint8 lz;
1767
1768 if (ev == USE_EVENT_SEARCH)
1769 return (UseCode::search_container(obj));
1770 else if (ev == USE_EVENT_USE && obj->has_container())
1771 return use_container(obj, USE_EVENT_USE);
1772 else if (ev == USE_EVENT_LOOK || ev == USE_EVENT_GET) {
1773 if (obj->quality != 0 && party->has_obj(OBJ_U6_SHIP_DEED, obj->quality)) {
1774 if (obj->obj_n == OBJ_U6_SKIFF)
1775 obj->set_ok_to_take(true);
1776 obj->quality = 0;
1777 }
1778 if (ev == USE_EVENT_GET)
1779 return true;
1780 }
1781
1782 if (ev != USE_EVENT_USE)
1783 return (false);
1784 ship_actor = actor_manager->get_actor(0); //get the vehicle actor.
1785
1786 // get out of boat
1787 if (party->is_in_vehicle()) {
1788 ship_actor->get_location(&lx, &ly, &lz); //retrieve actor position for land check.
1789
1790 if (use_boat_find_land(&lx, &ly, &lz)) { //we must be next to land to disembark
1791 Obj *objP = ship_actor->make_obj();
1792 objP->qty = ship_actor->get_hp(); // Hull Strength
1793
1794 party->exit_vehicle(lx, ly, lz);
1795
1796 obj_manager->add_obj(objP);
1797 } else {
1798 scroll->display_string("\nOnly next to land.\n");
1799 return true;
1800 }
1801
1802 return true;
1803 }
1804
1805 if (obj->is_on_map() == false) {
1806 scroll->display_string("\nNot usable\n");
1807 return (true);
1808 }
1809 if ((obj->obj_n == OBJ_U6_SKIFF || obj->obj_n == OBJ_U6_RAFT)
1810 && !map->is_water(obj->x, obj->y, obj->z, true)) {
1811 scroll->display_string("\nYou must place it in water first.\n");
1812 return true;
1813 }
1814 if (!player->in_party_mode()) {
1815 scroll->display_string("\nNot in solo mode.\n");
1816 return (true);
1817 }
1818
1819 if (obj->obj_n == OBJ_U6_SHIP) { //If we are using a ship we need to use its center object.
1820 obj = use_boat_find_center(obj); //return the center object
1821 if (obj == NULL) {
1822 scroll->display_string("\nShip not usable\n");
1823 return true;
1824 }
1825 }
1826
1827 if (obj->quality != 0) { //deed check
1828 if (party->has_obj(OBJ_U6_SHIP_DEED, obj->quality) == false) {
1829 scroll->display_string("\nA deed is required.\n");
1830 return true;
1831 }
1832 if (obj->obj_n == OBJ_U6_SKIFF)
1833 obj->set_ok_to_take(true);
1834 obj->quality = 0;
1835 }
1836
1837 if (UseCode::out_of_use_range(obj, true))
1838 return true;
1839
1840 // walk to vehicle if necessary
1841 if (!party->is_at(obj->x, obj->y, obj->z)) {
1842 party->enter_vehicle(obj);
1843 return (true);
1844 }
1845
1846 // use it (replace ship with vehicle actor)
1847 ship_actor->init_from_obj(obj, ACTOR_CHANGE_BASE_OBJ_N);
1848 if (obj->obj_n == OBJ_U6_SHIP)
1849 ship_actor->set_hp(obj->qty); // Hull Strength
1850 ship_actor->show(); // Swift!
1851 obj_manager->remove_obj_from_map(obj);
1852 delete_obj(obj);
1853
1854 party->hide(); // set in-vehicle
1855 player->set_actor(ship_actor);
1856 party->set_in_vehicle(true);
1857 return (true);
1858 }
1859
use_boat_find_center(Obj * obj)1860 inline Obj *U6UseCode::use_boat_find_center(Obj *obj) {
1861 Obj *new_obj;
1862 uint16 x, y;
1863 uint8 ship_direction = (obj->frame_n % 8) / 2; //find the direction based on the frame_n
1864
1865 if (obj->frame_n >= 8 && obj->frame_n < 16) //center obj
1866 return obj;
1867
1868 x = obj->x;
1869 y = obj->y;
1870
1871 if (obj->frame_n < 8) { //front obj
1872 switch (ship_direction) {
1873 case NUVIE_DIR_N :
1874 y++;
1875 break;
1876 case NUVIE_DIR_E :
1877 x--;
1878 break;
1879 case NUVIE_DIR_S :
1880 y--;
1881 break;
1882 case NUVIE_DIR_W :
1883 x++;
1884 break;
1885 }
1886 } else {
1887 if (obj->frame_n >= 16 && obj->frame_n < 24) { //back obj
1888 switch (ship_direction) {
1889 case NUVIE_DIR_N :
1890 y--;
1891 break;
1892 case NUVIE_DIR_E :
1893 x++;
1894 break;
1895 case NUVIE_DIR_S :
1896 y++;
1897 break;
1898 case NUVIE_DIR_W :
1899 x--;
1900 break;
1901 }
1902 }
1903 }
1904
1905 new_obj = obj_manager->get_objBasedAt(x, y, obj->z, true);
1906
1907 if (new_obj != NULL && new_obj->obj_n == OBJ_U6_SHIP)
1908 return new_obj;
1909
1910 return NULL;
1911 }
1912
use_boat_find_land(uint16 * x,uint16 * y,uint8 * z)1913 inline bool U6UseCode::use_boat_find_land(uint16 *x, uint16 *y, uint8 *z) {
1914 if (map->is_passable(*x, *y - 1, *z)) { //UP
1915 *y = *y - 1;
1916 return true;
1917 }
1918 if (map->is_passable(*x + 1, *y, *z)) { //RIGHT
1919 *x = *x + 1;
1920 return true;
1921 }
1922 if (map->is_passable(*x, *y + 1, *z)) { //DOWN
1923 *y = *y + 1;
1924 return true;
1925 }
1926 if (map->is_passable(*x - 1, *y, *z)) { //LEFT
1927 *x = *x - 1;
1928 return true;
1929 }
1930
1931 return false;
1932 }
1933
1934 /* construct a balloon using the balloon plans */
use_balloon_plans(Obj * obj,UseCodeEvent ev)1935 bool U6UseCode::use_balloon_plans(Obj *obj, UseCodeEvent ev) {
1936 if (ev == USE_EVENT_LOOK)
1937 return look_sign(obj, ev);
1938
1939 MapCoord player_location = player->get_actor()->get_location();
1940 bool missing_obj = false;
1941 Obj *balloon;
1942
1943 if (ev != USE_EVENT_USE)
1944 return (false);
1945
1946 scroll->display_string("\n");
1947
1948 //make sure the party is carrying the required parts.
1949
1950 if (!party->has_obj(OBJ_U6_MAMMOTH_SILK_BAG, 0)) {
1951 scroll->display_string("Missing a mammoth silk bag.\n");
1952 missing_obj = true;
1953 }
1954
1955 if (!party->has_obj(OBJ_U6_BALLOON_BASKET, 0)) {
1956 scroll->display_string("Missing a balloon basket.\n");
1957 missing_obj = true;
1958 }
1959
1960 if (!party->has_obj(OBJ_U6_CAULDRON, 0)) {
1961 scroll->display_string("Missing a cauldron.\n");
1962 missing_obj = true;
1963 }
1964
1965 if (!party->has_obj(OBJ_U6_ROPE, 0)) {
1966 scroll->display_string("Missing a rope.\n");
1967 missing_obj = true;
1968 }
1969
1970 // Make the balloon if we have all the parts.
1971 if (!missing_obj) {
1972 party->remove_obj(OBJ_U6_MAMMOTH_SILK_BAG, 0);
1973 party->remove_obj(OBJ_U6_BALLOON_BASKET, 0);
1974 party->remove_obj(OBJ_U6_CAULDRON, 0);
1975 party->remove_obj(OBJ_U6_ROPE, 0);
1976
1977 balloon = new_obj(OBJ_U6_BALLOON, 0, player_location.x, player_location.y, player_location.z);
1978
1979 if (balloon && obj_manager->add_obj(balloon)) {
1980 balloon->set_ok_to_take(true);
1981 scroll->display_string("Done!\n");
1982 }
1983 }
1984
1985 return true;
1986 }
1987
1988
1989 /* USE: balloon. (entire party)
1990 */
use_balloon(Obj * obj,UseCodeEvent ev)1991 bool U6UseCode::use_balloon(Obj *obj, UseCodeEvent ev) {
1992 Actor *balloon_actor;
1993 Actor *balloonist;
1994 MapCoord spot(0, 0, 0);
1995 uint16 lx, ly;
1996 uint8 lz;
1997
1998 if (ev != USE_EVENT_USE)
1999 return (false);
2000
2001 if (Game::get_game()->get_player()->in_party_mode()) {
2002 balloonist = Game::get_game()->get_party()->get_leader_actor();
2003 } else {
2004 balloonist = Game::get_game()->get_player()->get_actor();
2005 }
2006 spot = balloonist->get_location();
2007 if ((spot.z > 0) && (spot.z < 5)) {
2008 scroll->display_string("\nNot usable\n");
2009 return (true);
2010 }
2011
2012 if (obj->obj_n == OBJ_U6_BALLOON) {
2013 if (!obj->is_on_map()) {
2014 // if in party mode, find a spot around the avatar that is_passable,
2015 // else a spot around the person using it.
2016 // drop the balloon there, and inflate it.
2017
2018 uint16 x, y;
2019 x = spot.x;
2020 y = spot.y;
2021
2022 bool dropped = false;
2023 for (sint8 iy = -1; iy < 2; iy++) { // FIXME scan order
2024 for (sint8 ix = -1; ix < 2; ix++) {
2025 DEBUG(0, LEVEL_DEBUGGING, "can drop at %d %d?\n", ix, iy);
2026 if (Game::get_game()->get_map_window()->can_drop_or_move_obj(x + ix, y + iy, balloonist, obj) == MSG_SUCCESS) {
2027 DEBUG(0, LEVEL_DEBUGGING, "yes, can drop at %d %d.\n", x + ix, y + iy);
2028 obj_manager->unlink_from_engine(obj);
2029 obj->x = x + ix;
2030 obj->y = y + iy;
2031 obj->z = spot.z;
2032
2033 dropped = true;
2034 iy = 2;
2035 ix = 2;
2036 }
2037 }
2038 }
2039 if (!dropped) {
2040 // drop on 'spot' instead.
2041 obj_manager->unlink_from_engine(obj);
2042 obj->x = spot.x;
2043 obj->y = spot.y;
2044 obj->z = spot.z;
2045 dropped = true;
2046 }
2047 obj->status |= OBJ_STATUS_OK_TO_TAKE;
2048 obj_manager->add_obj(obj, OBJ_ADD_TOP);
2049 }
2050
2051 obj->obj_n = OBJ_U6_INFLATED_BALLOON;
2052 obj->frame_n = 3;
2053 scroll->display_string("\nDone!\n");
2054 return true;
2055 }
2056
2057 balloon_actor = actor_manager->get_actor(0); //get the vehicle actor.
2058
2059 // get out of balloon
2060 if (party->is_in_vehicle()) { // FIXME: use balloon when in skiff...
2061 balloon_actor->get_location(&lx, &ly, &lz); //retrieve actor position for land check.
2062
2063 if (use_boat_find_land(&lx, &ly, &lz)) { //we must be next to land to disembark
2064 Obj *objP;
2065
2066 party->show();
2067 balloon_actor->hide();
2068 balloon_actor->set_worktype(0);
2069
2070 player->set_actor(party->get_actor(0));
2071 player->move(lx, ly, lz, false);
2072 balloon_actor->obj_n = OBJ_U6_NO_VEHICLE;
2073 balloon_actor->frame_n = 0;
2074 balloon_actor->init();
2075 balloon_actor->move(0, 0, 0, ACTOR_FORCE_MOVE);
2076
2077 objP = new_obj(OBJ_U6_BALLOON, 0, lx, ly, lz);
2078 objP->status |= OBJ_STATUS_OK_TO_TAKE;
2079 obj_manager->add_obj(objP, OBJ_ADD_TOP);
2080 } else {
2081 scroll->display_string("\nOnly next to land.\n");
2082 return true;
2083 }
2084
2085 party->set_in_vehicle(false);
2086
2087 return true;
2088 }
2089
2090 if (!player->in_party_mode()) {
2091 scroll->display_string("\nNot in solo mode.\n");
2092 return (true);
2093 }
2094
2095 if (UseCode::out_of_use_range(obj, true))
2096 return true;
2097 // walk to vehicle if necessary
2098 if (!party->is_at(obj->x, obj->y, obj->z)) {
2099 party->enter_vehicle(obj);
2100 return (true); // display prompt
2101 }
2102
2103 // use it (replace ship with vehicle actor)
2104 balloon_actor->init_from_obj(obj, ACTOR_CHANGE_BASE_OBJ_N);
2105 balloon_actor->show(); // Swift!
2106 obj_manager->remove_obj_from_map(obj);
2107 delete_obj(obj);
2108
2109 party->hide(); // set in-vehicle
2110 player->set_actor(balloon_actor);
2111 party->set_in_vehicle(true);
2112 return (true);
2113 }
2114
2115
2116 /* using a cow fills an empty bucket in the player's inventory with milk */
use_cow(Obj * obj,UseCodeEvent ev)2117 bool U6UseCode::use_cow(Obj *obj, UseCodeEvent ev) {
2118 if (ev != USE_EVENT_USE)
2119 return (false);
2120
2121 // return fill_bucket(OBJ_U6_BUCKET_OF_MILK);
2122 fill_bucket(OBJ_U6_BUCKET_OF_MILK);
2123 return true;
2124 }
2125
2126 /* using a well fills an empty bucket in the player's inventory with water */
use_well(Obj * obj,UseCodeEvent ev)2127 bool U6UseCode::use_well(Obj *obj, UseCodeEvent ev) {
2128 if (ev != USE_EVENT_USE)
2129 return (false);
2130
2131 // return fill_bucket(OBJ_U6_BUCKET_OF_WATER);
2132 fill_bucket(OBJ_U6_BUCKET_OF_WATER);
2133 return true;
2134 }
2135
2136 // fill an empty bucket in the player actor's inventory with some liquid
fill_bucket(uint16 filled_bucket_obj_n)2137 bool U6UseCode::fill_bucket(uint16 filled_bucket_obj_n) {
2138 Actor *player_actor;
2139 Obj *bucket;
2140
2141 player_actor = player->get_actor();
2142
2143 if (!player_actor->inventory_has_object(OBJ_U6_BUCKET)) {
2144 if (player_actor->inventory_has_object(OBJ_U6_BUCKET_OF_MILK) ||
2145 player_actor->inventory_has_object(OBJ_U6_BUCKET_OF_WATER)) {
2146 scroll->display_string("\nYou need an empty bucket.\n");
2147 return true;
2148 } else {
2149 scroll->display_string("\nYou need a bucket.\n");
2150 return true;
2151 }
2152 }
2153
2154 // Fill the first empty bucket in player's inventory.
2155
2156 bucket = player_actor->inventory_get_object(OBJ_U6_BUCKET);
2157 player_actor->inventory_remove_obj(bucket);
2158
2159 bucket->obj_n = filled_bucket_obj_n;
2160
2161 player_actor->inventory_add_object(bucket);
2162
2163 scroll->display_string("\nDone\n");
2164
2165 return true;
2166 }
2167
2168
2169 // USE: replace a bucket of milk in the player's inventory with butter
use_churn(Obj * obj,UseCodeEvent ev)2170 bool U6UseCode::use_churn(Obj *obj, UseCodeEvent ev) {
2171 ViewManager *view_manager = game->get_view_manager();
2172 Actor *player_actor;
2173 Obj *bucket;
2174 Obj *butter;
2175
2176 player_actor = player->get_actor();
2177
2178 if (!player_actor->inventory_has_object(OBJ_U6_BUCKET_OF_MILK)) {
2179 scroll->display_string("\nYou need some milk.\n");
2180 return true;
2181 }
2182
2183 bucket = player_actor->inventory_get_object(OBJ_U6_BUCKET_OF_MILK);
2184 player_actor->inventory_remove_obj(bucket);
2185
2186 bucket->obj_n = OBJ_U6_BUCKET;
2187
2188 butter = new Obj();
2189
2190 butter->obj_n = OBJ_U6_BUTTER;
2191 player_actor->inventory_add_object(butter);
2192 player_actor->inventory_add_object(bucket);
2193 if (!game->is_new_style())
2194 view_manager->set_inventory_mode();
2195
2196 view_manager->update(); //FIX this should be moved higher up in UseCode
2197
2198 scroll->display_string("\nDone\n");
2199 return true;
2200 }
2201
2202
2203 // USE: fill an empty honey jar in the player's inventory
use_beehive(Obj * obj,UseCodeEvent ev)2204 bool U6UseCode::use_beehive(Obj *obj, UseCodeEvent ev) {
2205 ViewManager *view_manager = game->get_view_manager();
2206 Actor *player_actor;
2207 Obj *honey_jar;
2208
2209 player_actor = player->get_actor();
2210
2211 if (!player_actor->inventory_has_object(OBJ_U6_HONEY_JAR)) {
2212 if (player_actor->inventory_has_object(OBJ_U6_JAR_OF_HONEY)) {
2213 scroll->display_string("\nYou need an empty honey jar.\n");
2214 } else {
2215 scroll->display_string("\nYou need a honey jar.\n");
2216 }
2217
2218 return true;
2219 }
2220
2221 honey_jar = player_actor->inventory_get_object(OBJ_U6_HONEY_JAR);
2222 player_actor->inventory_remove_obj(honey_jar);
2223
2224 honey_jar->obj_n = OBJ_U6_JAR_OF_HONEY; //fill the empty jar with honey.
2225
2226 player_actor->inventory_add_object(honey_jar); // add the filled jar back to the player's inventory
2227 if (!game->is_new_style())
2228 view_manager->set_inventory_mode();
2229
2230 view_manager->update(); //FIX this should be moved higher up in UseCode
2231
2232 scroll->display_string("\nDone\n");
2233 return true;
2234 }
2235
2236
2237 /* USE: Mount or dismount from a horse. Don't allow using another horse if
2238 * already riding one.
2239 */
use_horse(Obj * obj,UseCodeEvent ev)2240 bool U6UseCode::use_horse(Obj *obj, UseCodeEvent ev) {
2241 Actor *actor, *player_actor;
2242 Obj *actor_obj;
2243
2244 if (ev != USE_EVENT_USE)
2245 return (false);
2246
2247 actor = actor_manager->get_actor(obj->quality); // horse or horse with rider
2248 if (!actor)
2249 return false;
2250
2251 player_actor = items.actor_ref;
2252 if (player_actor->get_actor_num() == U6_SHERRY_ACTOR_NUM) {
2253 scroll->display_string("Sherry says: \"Eeek!!! I'm afraid of horses!\"\n");
2254 return true;
2255 } else if (player_actor->get_actor_num() == U6_BEHLEM_ACTOR_NUM) {
2256 scroll->display_string("BehLem says: \"Horses are for food!\"\n");
2257 return true;
2258 } else if (obj->obj_n == OBJ_U6_HORSE && player_actor->obj_n == OBJ_U6_HORSE_WITH_RIDER) {
2259 scroll->display_string("You're already on a horse!\n");
2260 return true;
2261 } else if (party->is_in_vehicle()) {
2262 Game::get_game()->get_event()->display_not_aboard_vehicle(false);
2263 return true;
2264 }
2265
2266 actor_obj = actor->make_obj();
2267
2268 //dismount from horse. revert to original actor type.
2269 //Add a temporary horse actor onto the map.
2270 if (obj->obj_n == OBJ_U6_HORSE_WITH_RIDER) {
2271 actor->clear();
2272 if (actor == player_actor)
2273 actor->set_worktype(0x02); // PLAYER
2274
2275 actor_obj->obj_n = actor->base_obj_n; //revert to normal actor type
2276 actor_obj->frame_n = actor->old_frame_n;
2277
2278 actor->init_from_obj(actor_obj);
2279
2280 // create a temporary horse on the map.
2281 actor_manager->create_temp_actor(OBJ_U6_HORSE, NO_OBJ_STATUS, obj->x, obj->y, obj->z, ACTOR_ALIGNMENT_DEFAULT, WORKTYPE_U6_ANIMAL_WANDER);
2282 } else if (!actor_manager->is_temp_actor(actor)) { // Try to mount horse. Don't use permenant Actors eg Smith, push-me pull-you
2283 scroll->display_string("\nHorse not boardable!\n");
2284 } else { // mount up.
2285 if (UseCode::out_of_use_range(obj, true))
2286 return true;
2287 actor_manager->clear_actor(actor); //clear the temp horse actor from the map.
2288
2289 actor_obj->obj_n = OBJ_U6_HORSE_WITH_RIDER;
2290
2291 player_actor->move(actor_obj->x, actor_obj->y, actor_obj->z); //this will center the map window
2292 player_actor->init_from_obj(actor_obj);
2293
2294 delete_obj(actor_obj);
2295 }
2296
2297 return true;
2298 }
2299
use_fan(Obj * obj,UseCodeEvent ev)2300 bool U6UseCode::use_fan(Obj *obj, UseCodeEvent ev) {
2301 uint8 wind_tbl[] = {4, 5, 6, 7, 1, 2, 3, 0};
2302 uint8 wind_dir;
2303 Weather *weather = game->get_weather();
2304 scroll->display_string("\nYou feel a breeze.\n");
2305
2306 wind_dir = weather->get_wind_dir();
2307
2308 if (wind_dir == NUVIE_DIR_NONE)
2309 wind_dir = NUVIE_DIR_NW;
2310
2311 //cycle through the wind directions.
2312 weather->set_wind_dir(wind_tbl[wind_dir]);
2313
2314 return true;
2315 }
2316
2317 /* USE: Sextant. Display Latitude/Longitude coords centered on LB's castle.
2318 */
use_sextant(Obj * obj,UseCodeEvent ev)2319 bool U6UseCode::use_sextant(Obj *obj, UseCodeEvent ev) {
2320 MapCoord location;
2321 char buf[18]; // "\nxxoS, xxoE\n"
2322 char lat, lon;
2323 uint16 x, y;
2324
2325 if (ev != USE_EVENT_USE)
2326 return false;
2327
2328 location = player->get_actor()->get_location();
2329
2330 //only use sextant on surface level.
2331 if (location.z == 0) {
2332 x = location.x / 8;
2333 if (x > 38) {
2334 lon = 'E';
2335 x -= 38;
2336 } else {
2337 x = 38 - x;
2338 lon = 'W';
2339 }
2340
2341 y = location.y / 8;
2342 if (y > 45) {
2343 lat = 'S';
2344 y -= 45;
2345 } else {
2346 y = 45 - y;
2347 lat = 'N';
2348 }
2349
2350 sprintf(buf, "\n%d{%c, %d{%c\n", y, lat, x, lon);
2351 scroll->display_string(buf);
2352 } else
2353 scroll->display_string("\nNot usable\n");
2354
2355 return (true);
2356 }
2357
use_staff(Obj * obj,UseCodeEvent ev)2358 bool U6UseCode::use_staff(Obj *obj, UseCodeEvent ev) {
2359 if (ev != USE_EVENT_USE)
2360 return false;
2361
2362 if (obj->is_readied() == false) {
2363 scroll->display_string("\nNot readied.\n");
2364 return true;
2365 }
2366
2367 Obj *charge = obj->find_in_container(OBJ_U6_CHARGE, 0, OBJ_NOMATCH_QUALITY);
2368
2369 if (charge) {
2370 uint8 spell_num = charge->quality;
2371 obj_manager->unlink_from_engine(charge);
2372 delete_obj(charge);
2373
2374 Game::get_game()->get_event()->cast_spell_directly(spell_num);
2375 }
2376
2377 return true;
2378 }
2379
2380 /* Pass: Allow normal move if player's Quest Flag is set.
2381 */
pass_quest_barrier(Obj * obj,UseCodeEvent ev)2382 bool U6UseCode::pass_quest_barrier(Obj *obj, UseCodeEvent ev) {
2383 if (ev == USE_EVENT_PASS)
2384 if (player->get_quest_flag() == 0) {
2385 // block everyone, only print message when player attempts to pass
2386 if (items.actor_ref == player->get_actor())
2387 scroll->message("\n\"Thou art not upon a Sacred Quest!\n"
2388 "Passage denied!\"\n\n");
2389 return (false);
2390 }
2391 return (true);
2392 }
2393
2394
2395 /* LOOK: Get (possibly translate) book data for readable object. Disallow search
2396 * (return true) if book data was displayed.
2397 */
look_sign(Obj * obj,UseCodeEvent ev)2398 bool U6UseCode::look_sign(Obj *obj, UseCodeEvent ev) {
2399 char *data;
2400 Book *book = game->get_book(); // ??
2401
2402 if (ev == USE_EVENT_LOOK) {
2403 MapCoord obj_loc = MapCoord(obj->x, obj->y, obj->z);
2404 MapCoord player_loc = player->get_actor()->get_location();
2405 InterfaceType interface = game->get_map_window()->get_interface();
2406 bool too_far = (player_loc.distance(obj_loc) > 1 && interface == INTERFACE_NORMAL);
2407 bool blocked = (interface != INTERFACE_IGNORE_BLOCK
2408 && !game->get_map_window()->can_get_obj(player->get_actor(), obj));
2409 if ((obj->quality == 0 && obj->obj_n != OBJ_U6_BOOK) || (!obj->is_in_inventory()
2410 && (obj->obj_n == OBJ_U6_BOOK || obj->obj_n == OBJ_U6_SCROLL) && (too_far || blocked))) {
2411 scroll->display_string("\n");
2412 return true; // display prompt
2413 }
2414 // read
2415 if (items.actor_ref == player->get_actor()) {
2416 scroll->display_string(":\n\n");
2417 uint8 book_num = obj->quality - 1;
2418 if (obj->quality == 0)
2419 book_num = 126;
2420 if ((data = book->get_book_data(book_num))) {
2421 /*
2422 // FIX Any alternate-font text is in < >, Runic is capitalized,
2423 // Gargish is lower-case. Translations follow untranslated text and
2424 // are wrapped in & &.
2425 if(data[0] == '<' && data[strlen(data)-1] == '>') //Britannian text is wrapped in '<' '>' chars
2426 {
2427 scroll->display_string(&data[1],strlen(data)-2, 1); // 1 for britannian font.
2428 scroll->display_string("\n",1);
2429 }
2430 else
2431 {
2432 */
2433 bool using_gump = game->is_using_text_gumps();
2434 if (using_gump) {
2435 switch (obj->obj_n) {
2436 case OBJ_U6_BOOK:
2437 case OBJ_U6_PICTURE:
2438 case OBJ_U6_SCROLL:
2439 case OBJ_U6_GRAVE:
2440 case OBJ_U6_CROSS: // wooden cross used as grave marker (text like grave)
2441 case OBJ_U6_BALLOON_PLANS:
2442 case OBJ_U6_BOOK_OF_CIRCLES:
2443 case OBJ_U6_CODEX:
2444 game->get_view_manager()->open_scroll_gump(data, strlen(data));
2445 break;
2446 case OBJ_U6_SIGN:
2447 if (strlen(data) > 20) { // FIXME sign text needs to fit on multiple lines
2448 using_gump = false;
2449 break;
2450 }
2451 game->get_view_manager()->open_sign_gump(data, strlen(data));
2452 break;
2453 case OBJ_U6_SIGN_ARROW:
2454
2455 default:
2456 using_gump = false;
2457 }
2458 }
2459 if (!using_gump) {
2460 scroll->set_autobreak(true);
2461 scroll->display_string(data, strlen(data)); //normal font
2462 scroll->display_string("\n\t"); // '\t' = auto break off.
2463 }
2464 //scroll->set_autobreak(false);
2465 // }
2466 free(data);
2467 }
2468
2469 }
2470 return true;
2471 }
2472 return false;
2473 }
2474
2475
2476 /* LOOK: Display the current time. Disallow search.
2477 */
look_clock(Obj * obj,UseCodeEvent ev)2478 bool U6UseCode::look_clock(Obj *obj, UseCodeEvent ev) {
2479 GameClock *clock = game->get_clock();
2480 if (obj->obj_n == OBJ_U6_SUNDIAL
2481 && (clock->get_hour() < 5 || clock->get_hour() > 19))
2482 return (true); // don't get time from sundial at night
2483 if (ev == USE_EVENT_LOOK && items.actor_ref == player->get_actor()) {
2484 scroll->display_string("\nThe time is ");
2485 scroll->display_string(clock->get_time_string());
2486 scroll->display_string("\n");
2487 }
2488 return (true);
2489 }
2490
2491
2492 /* test (need to determine use of true/false return)
2493 */
look_mirror(Obj * obj,UseCodeEvent ev)2494 bool U6UseCode::look_mirror(Obj *obj, UseCodeEvent ev) {
2495 // ViewManager *view_manager = game->get_view_manager();
2496 if (ev == USE_EVENT_LOOK && items.actor_ref == player->get_actor()) {
2497 uint16 x, y;
2498 uint8 z;
2499 items.actor_ref->get_location(&x, &y, &z);
2500 if (x == obj->x && y > obj->y && y <= (obj->y + 2)) {
2501 scroll->display_string("\nYou can see yourself!");
2502 game->get_event()->display_portrait(items.actor_ref);
2503 }
2504 scroll->display_string("\n");
2505 return (true);
2506 }
2507 return (false);
2508 }
2509
2510
2511 /* PASS: if not in party mode, say that you cannot enter and do normal move
2512 * else walk all party members to cave, give dungeon name, and move to dungeon
2513 */
enter_dungeon(Obj * obj,UseCodeEvent ev)2514 bool U6UseCode::enter_dungeon(Obj *obj, UseCodeEvent ev) {
2515 if (!party->contains_actor(items.actor_ref))
2516 return false;
2517
2518 const char *prefix = "", *dungeon_name = "";
2519 uint16 x = obj->x, y = obj->y;
2520 uint8 z = obj->z;
2521
2522 if (party->is_in_vehicle()) //don't enter if in a balloon.
2523 return true;
2524
2525 if (!player->in_party_mode()) {
2526 scroll->display_string("\n\nNot in solo mode.\n");
2527 return (true);
2528 }
2529
2530 if (ev == USE_EVENT_USE && UseCode::out_of_use_range(obj, true))
2531 return true;
2532
2533 if (obj->quality < 21)
2534 dungeon_name = u6_dungeons[obj->quality];
2535 if (obj->quality >= 1 && obj->quality <= 7)
2536 prefix = "dungeon ";
2537 else if (obj->quality >= 9 && obj->quality <= 11)
2538 prefix = "shrine of ";
2539 else
2540 prefix = "";
2541
2542 party->dismount_from_horses();
2543
2544 // don't activate if autowalking from linking exit
2545 if ((ev == USE_EVENT_PASS || ev == USE_EVENT_USE) && items.actor_ref == player->get_actor() && !party->get_autowalk()) {
2546 ActorManager *actorMan = Game::get_game()->get_actor_manager();
2547 if (obj->quality != 0 && party->contains_actor(3) && actorMan->get_actor(3)->is_alive()) {
2548 // scroll->printf("%s says, \"This is the %s%s.\"\n\n",blah->name, prefix, dungeon_name);
2549 scroll->display_string("Shamino says, \"This is the ");
2550 scroll->display_string(prefix);
2551 scroll->display_string(dungeon_name);
2552 scroll->display_string(".\"\n\n");
2553 scroll->display_prompt();
2554 }
2555 MapCoord entrance(x, y, z);
2556 // going down
2557 if (z == 0) { // from surface, do superchunk translation
2558 x = (x & 0x07) | (x >> 2 & 0xF8);
2559 y = (y & 0x07) | (y >> 2 & 0xF8);
2560 }
2561 if (z < 5)
2562 z += 1;
2563 else
2564 z -= 1;
2565
2566 MapCoord exitPos(x, y, z);
2567 // if(obj->obj_n == OBJ_U6_HOLE) // fall down hole faster
2568 // party->walk(&entrance, &exitPos, 100);
2569 // else
2570 // party->walk(&entrance, &exitPos);
2571 party->walk(&entrance, &exitPos, 100);
2572 game->get_weather()->set_wind_dir(NUVIE_DIR_NONE);
2573 return (true);
2574 } else if ((ev == USE_EVENT_PASS || ev == USE_EVENT_USE) && party->get_autowalk()) // party can use now
2575 return (true);
2576 return (false);
2577 }
2578
enter_moongate(Obj * obj,UseCodeEvent ev)2579 bool U6UseCode::enter_moongate(Obj *obj, UseCodeEvent ev) {
2580 /* shared between blue and red gates */
2581 /* PASS: if not in party mode, say that you cannot enter and do normal move
2582 * else walk all party members to moongate and teleport.
2583 */
2584 uint16 x = obj->x, y = obj->y;
2585 uint8 z = obj->z;
2586 MapCoord exitPos(0, 0, 0);
2587
2588 if (party->is_in_vehicle())
2589 return true;
2590 if (items.mapcoord_ref->x != x)
2591 return true; // don't step onto the left tile of a moongate
2592
2593 if (!player->in_party_mode()) {
2594 scroll->display_string("\nYou must be in party mode to enter.\n\n");
2595 scroll->display_prompt();
2596 return (true);
2597 }
2598
2599 // don't activate if autowalking from linking exitPos
2600 if (ev == USE_EVENT_PASS && items.actor_ref == player->get_actor() && !party->get_autowalk()) {
2601 if (obj->obj_n == OBJ_U6_RED_GATE) {
2602 if (obj->quality > 25) {
2603 DEBUG(0, LEVEL_ERROR, "invalid moongate destination %d\n", obj->quality);
2604 return false;
2605 }
2606 if (!party->has_obj(87, 0, false)) { // make sure orb of moons is in party inventory
2607 scroll->display_string("\nYou forgot the Orb of the Moons!\n");
2608 return true;
2609 }
2610 if ((obj->quality > 0 && obj->quality < 12) ||
2611 (obj->quality > 14 && obj->quality < 26)) { //positions 0, 12, 13 and 14 go nowhere.
2612 x = red_moongate_tbl[obj->quality].x; // set our moongate destination from the lookup table.
2613 y = red_moongate_tbl[obj->quality].y;
2614 z = red_moongate_tbl[obj->quality].z;
2615 }
2616 exitPos = MapCoord(x, y, z);
2617 } else if (obj->obj_n == OBJ_U6_MOONGATE) {
2618 // FIXME: Duplication from PartyView, this ought to be separated
2619 /* we don't care if the moons are in the sky,
2620 * (to make permanent moongates work)
2621 * if the moongate is there, it goes somewhere
2622 */
2623 Weather *weather = game->get_weather();
2624 GameClock *clock = Game::get_game()->get_clock();
2625 uint8 day = clock->get_day();
2626 uint8 hour = clock->get_hour();
2627 uint8 phaseTrammel = uint8(nearbyint((day - 1) / TRAMMEL_PHASE)) % 8;
2628 sint8 phaseb = (day - 1) % uint8(nearbyint(FELUCCA_PHASE * 8)) - 1;
2629 uint8 phaseFelucca = (phaseb >= 0) ? phaseb : 0;
2630 uint8 posTrammel = ((hour + 1) + 3 * phaseTrammel) % 24;
2631 uint8 posFelucca = ((hour - 1) + 3 * phaseFelucca) % 24;
2632 uint8 absTrammel = abs(posTrammel - 12);
2633 uint8 absFelucca = abs(posFelucca - 12);
2634 if (absTrammel < absFelucca) {
2635 // Trammel wins.
2636 exitPos = weather->get_moonstone(8 - phaseTrammel);
2637 } else {
2638 // Feluccality!
2639 exitPos = weather->get_moonstone(8 - phaseFelucca);
2640 }
2641 if (exitPos.x == 0 && exitPos.y == 0 && exitPos.z == 0) {
2642 exitPos = MapCoord(x, y, z); // stay put.
2643 }
2644 }
2645 party->walk(obj, &exitPos);
2646 return (true);
2647 } else if (ev == USE_EVENT_PASS && party->get_autowalk()) // party can use now
2648 if (party->contains_actor(items.actor_ref))
2649 return (true);
2650 return (true);
2651 }
2652
2653
2654 /* USE: Light powder keg if unlit
2655 * MESSAGE: Timed: Explode; Effect complete: delete powder keg
2656 */
use_powder_keg(Obj * obj,UseCodeEvent ev)2657 bool U6UseCode::use_powder_keg(Obj *obj, UseCodeEvent ev) {
2658 if (ev == USE_EVENT_USE) {
2659 game->get_script()->call_use_keg(obj);
2660 }
2661 return true;
2662 }
2663
2664
2665 /* Use: Fire! (block input, start cannonball anim, release input on hit)
2666 * Message: Effect complete. Return to prompt.
2667 * Move: Change direction if necessary
2668 */
use_cannon(Obj * obj,UseCodeEvent ev)2669 bool U6UseCode::use_cannon(Obj *obj, UseCodeEvent ev) {
2670 MapCoord *mapcoord_ref = items.mapcoord_ref;
2671
2672 if (ev == USE_EVENT_USE) {
2673 scroll->display_string("\nFire!\n");
2674 // FIXME: new UseCodeEffect(obj, cannonballtile, dir) // sets WAIT mode
2675 new CannonballEffect(obj); // sets WAIT mode
2676 // Note: waits for effect to complete and sends MESG_EFFECT_COMPLETE
2677 return (false);
2678 } else if (ev == USE_EVENT_MESSAGE) {
2679 if (*items.msg_ref == MESG_EFFECT_COMPLETE) {
2680 scroll->display_string("\n");
2681 scroll->display_prompt();
2682 }
2683 return (true);
2684 } else if (ev == USE_EVENT_MOVE) {
2685 // allow normal move
2686 if ((obj->frame_n == 0 && mapcoord_ref->sy < 0)
2687 || (obj->frame_n == 1 && mapcoord_ref->sx > 0)
2688 || (obj->frame_n == 2 && mapcoord_ref->sy > 0)
2689 || (obj->frame_n == 3 && mapcoord_ref->sx < 0))
2690 return (true);
2691 else { // aim cannon in new direction
2692 if (mapcoord_ref->sy < 0)
2693 obj->frame_n = 0;
2694 else if (mapcoord_ref->sy > 0)
2695 obj->frame_n = 2;
2696 else if (mapcoord_ref->sx < 0)
2697 obj->frame_n = 3;
2698 else if (mapcoord_ref->sx > 0)
2699 obj->frame_n = 1;
2700 return (false);
2701 }
2702 }
2703 return (false);
2704 }
2705
2706
2707 /* USE: Hatch egg.
2708 */
use_egg(Obj * obj,UseCodeEvent ev)2709 bool U6UseCode::use_egg(Obj *obj, UseCodeEvent ev) {
2710 EggManager *egg_manager = obj_manager->get_egg_manager();
2711 bool success = egg_manager->spawn_egg(obj, NUVIE_RAND() % 100);
2712 if (items.actor_ref)
2713 scroll->display_string(success ? "\nSpawned!\n" : "\nNo effect.\n");
2714 return (true);
2715 }
2716
2717 /* USE: Open spellbook for casting, if equipped.
2718 * LOOK: Open for spell inspection.
2719 */
use_spellbook(Obj * obj,UseCodeEvent ev)2720 bool U6UseCode::use_spellbook(Obj *obj, UseCodeEvent ev) {
2721 if (ev == USE_EVENT_USE) {
2722 Game::get_game()->get_event()->endAction(); // FIXME: this should call Magic directly
2723 Game::get_game()->get_event()->newAction(CAST_MODE);
2724 if (obj->is_readied()) {
2725 /* TODO open spellbook for casting */
2726
2727 }
2728 } else if (ev == USE_EVENT_LOOK) {
2729 scroll->display_string("\n");
2730 /* TODO open spellbook for reading */
2731
2732 }
2733 return (true);
2734 }
2735
2736 /* Use: Light torch if readied or on the ground.
2737 * Ready: Get a torch from a stack and equip it.
2738 * Unready: Unlight torch.
2739 * Get: Equip torch if lit
2740 * Drop: Unlight torch
2741 */
torch(Obj * obj,UseCodeEvent ev)2742 bool U6UseCode::torch(Obj *obj, UseCodeEvent ev) {
2743 if (ev == USE_EVENT_USE) {
2744 if (obj->frame_n == 1) {
2745 extinguish_torch(obj);
2746 return (true);
2747 }
2748 // light
2749 if (obj->is_on_map()) {
2750 Obj *torch = obj_manager->get_obj_from_stack(obj, 1);
2751 if (torch != obj)
2752 obj_manager->add_obj(torch, true); // keep new one on map
2753 light_torch(torch);
2754 return true;
2755 } else { // so is readied or in inventory
2756 Obj *torch = obj;
2757 Actor *actor;
2758 if (obj->is_in_inventory() == false) // container on map
2759 actor = actor_manager->get_player();
2760 else
2761 actor = actor_manager->get_actor_holding_obj(obj);
2762 bool can_light_it = true; // only set FALSE on some error
2763 bool in_container = obj->is_in_container();
2764
2765 if (!obj->is_readied()) {
2766 torch = obj_manager->get_obj_from_stack(obj, 1);
2767 if (torch != obj) // keep new one in inventory
2768 actor->inventory_add_object_nostack(torch);
2769
2770 // ready it
2771 // actor = actor_manager->get_actor_holding_obj(torch);
2772 can_light_it = actor->add_readied_object(torch);
2773 }
2774
2775 if (can_light_it) { // assume torch is readied
2776 assert(torch->is_readied());
2777 light_torch(torch);
2778 } else {
2779 assert(torch->qty == 1);
2780 if (in_container) // need old location
2781 obj_manager->moveto_container(torch, obj->get_container_obj());
2782 else if (torch->is_in_inventory()) { // assume it's not stacked
2783 actor->inventory_remove_obj(torch);
2784 actor->inventory_add_object(torch); // restack here
2785 }
2786 scroll->display_string("\nNo free hand to hold the torch.\n");
2787 }
2788 }
2789 } else if (ev == USE_EVENT_READY) {
2790 if (obj->is_readied()) { // remove
2791 if (obj->frame_n == 1) {
2792 extinguish_torch(obj);
2793 return (false); // destroyed
2794 }
2795 } else { // equip (get one from the stack)
2796 if (obj->qty > 1 && obj->frame_n == 0) { // don't change the quantity of lit torches
2797 Obj *torch = obj_manager->get_obj_from_stack(obj, obj->qty - 1);
2798 assert(torch != obj); // got a new object from the obj stack
2799 if (obj->is_in_container())
2800 obj_manager->moveto_container(torch, obj->get_container_obj(), false);
2801 else if (obj->is_in_inventory()) { // keep extras in inventory
2802 actor_manager->get_actor_holding_obj(torch)->inventory_add_object_nostack(torch);
2803 }
2804 }
2805 }
2806 return (true); // equip or remove to inventory
2807 } else if (ev == USE_EVENT_GET) {
2808 if (obj->frame_n == 0) // unlit: may get normally
2809 return (true);
2810 toggle_frame(obj); // unlight
2811 obj->qty = 1;
2812 obj_manager->remove_obj_from_map(obj); // add to inventory and USE
2813 items.actor_ref->inventory_add_object(obj); // will unstack in USE
2814 scroll->display_string("\n");
2815 torch(obj, USE_EVENT_USE);
2816 return (false); // ready or not, handled by usecode
2817 } else if (ev == USE_EVENT_DROP) {
2818 if (obj->frame_n == 0) // unlit: normal drop
2819 return (true);
2820 extinguish_torch(obj);
2821 return (false); // destroyed
2822 }
2823
2824 return (true);
2825 }
2826
2827 /* Torches disappear when extinguished. */
extinguish_torch(Obj * obj)2828 void U6UseCode::extinguish_torch(Obj *obj) {
2829 assert(obj->frame_n == 1);
2830
2831 // handled by Actor::inventory_remove_obj()
2832 // if(obj->is_in_inventory_old())
2833 // actor_manager->get_actor_holding_obj(obj)->subtract_light(TORCH_LIGHT_LEVEL);
2834 if (obj->is_readied()) {
2835 Actor *owner = actor_manager->get_actor_holding_obj(obj);
2836 if ((owner->is_in_party() || owner == player->get_actor()) && owner->is_alive()) {
2837 if (owner->get_hp() == 0) { // Avatar during Kal Lor item removal
2838 owner->remove_readied_object(obj, false);
2839 party->subtract_light_source();
2840 game->get_map_window()->updateBlacking();
2841 return;
2842 }
2843 } else { // don't extinguish on death or leaving the party
2844 game->get_map_window()->updateBlacking(); // might need this on death
2845 return;
2846 }
2847 }
2848
2849 scroll->display_string("\nA torch burned out.\n");
2850 destroy_obj(obj, 0, false);
2851 game->get_map_window()->updateBlacking();
2852 }
2853
light_torch(Obj * obj)2854 void U6UseCode::light_torch(Obj *obj) {
2855 assert(obj->qty == 1);
2856 assert(obj->frame_n == 0);
2857 assert(obj->is_readied() || obj->is_on_map());
2858 toggle_frame(obj); // light
2859 obj->status |= OBJ_STATUS_LIT;
2860 Actor *owner = NULL;
2861 if (obj->is_readied()) {
2862 owner = actor_manager->get_actor_holding_obj(obj);
2863 owner->add_light(TORCH_LIGHT_LEVEL);
2864 }
2865
2866 obj->qty = 0xc8; //torch duration. updated in lua advance_time()
2867 if (!owner || owner->is_in_party() || owner == player->get_actor())
2868 scroll->display_string("\nTorch is lit.\n");
2869 game->get_map_window()->updateBlacking();
2870 }
2871
process_effects(Obj * container_obj,Actor * actor)2872 bool U6UseCode::process_effects(Obj *container_obj, Actor *actor) {
2873 Obj *temp_obj;
2874 U6Link *obj_link, *temp_link;
2875
2876 /* Test whether this object has items inside it. */
2877 if (container_obj->container != NULL) {
2878 for (obj_link = container_obj->container->end(); obj_link != NULL;) {
2879 temp_obj = (Obj *)obj_link->data;
2880
2881 if (temp_obj->obj_n == OBJ_U6_EFFECT) {
2882 temp_link = obj_link->prev;
2883 game->get_script()->call_actor_use_effect(temp_obj, actor); //Note this call unlinks the effect object.
2884 obj_link = temp_link;
2885 } else
2886 obj_link = obj_link->prev;
2887 }
2888 }
2889
2890 return true;
2891 }
2892
2893 /* Use: Display Peer effect, showing a map of the area around the player.
2894 Message: Delete 1 gem. */
use_peer_gem(Obj * obj,UseCodeEvent ev)2895 bool U6UseCode::use_peer_gem(Obj *obj, UseCodeEvent ev) {
2896 if (ev == USE_EVENT_MESSAGE && *items.msg_ref == MESG_EFFECT_COMPLETE) {
2897 destroy_obj(obj, 1);
2898 scroll->display_string("\n");
2899 scroll->display_prompt();
2900 return true;
2901 }
2902
2903 if (ev != USE_EVENT_USE)
2904 return true;
2905
2906 uint16 x, y;
2907 uint8 z;
2908 player->get_location(&x, &y, &z);
2909 game->get_event()->close_gumps();
2910 new PeerEffect(x - (x % 8) - 18, y - (y % 8) - 18, z, obj); // wrap to chunk boundary,
2911 // and center in 11x11 MapWindow
2912 return false; // no prompt
2913 }
2914
2915 /* Ready: Apply ring's status effect to actor.
2916 Unready: Cancel status effect. */
magic_ring(Obj * obj,UseCodeEvent ev)2917 bool U6UseCode::magic_ring(Obj *obj, UseCodeEvent ev) {
2918 Actor *actor = obj->get_actor_holding_obj();
2919 if (!actor)
2920 actor = player->get_actor();
2921 if (actor->inventory_get_readied_object(ACTOR_HAND) != NULL
2922 && actor->inventory_get_readied_object(ACTOR_HAND) != obj
2923 && actor->inventory_get_readied_object(ACTOR_HAND_2) != NULL
2924 && actor->inventory_get_readied_object(ACTOR_HAND_2) != obj)
2925 return true;
2926 uint8 num_readied = actor->count_readied_objects(obj->obj_n, 0);
2927 // if(obj->obj_n == OBJ_U6_REGENERATION_RING)
2928 // actor_manager->get_actor_holding_obj(obj)->??? no visual effect
2929 // if(obj->obj_n == OBJ_U6_PROTECTION_RING)
2930 // actor_manager->get_actor_holding_obj(obj)->??? no visual effect
2931 if (obj->obj_n == OBJ_U6_INVISIBILITY_RING)
2932 actor->set_invisible((obj->is_readied() && num_readied == 1) ? false : true);
2933 return true; // do normal ready/unready
2934 }
2935
storm_cloak(Obj * obj,UseCodeEvent ev)2936 bool U6UseCode::storm_cloak(Obj *obj, UseCodeEvent ev) {
2937 Actor *actor = obj->get_actor_holding_obj();
2938 if (!actor)
2939 actor = player->get_actor();
2940 if (actor->inventory_get_readied_object(ACTOR_BODY) != NULL
2941 && actor->inventory_get_readied_object(ACTOR_BODY) != obj)
2942 return true;
2943
2944 AsyncEffect *e = new AsyncEffect(new TileBlackFadeEffect(actor, 9, 20)); //FIXME hardcoded values.
2945 e->run();
2946
2947 if (obj->is_readied() == false) {
2948 Game::get_game()->get_clock()->set_timer(GAMECLOCK_TIMER_U6_STORM, 0x14);
2949 } else {
2950 Game::get_game()->get_clock()->set_timer(GAMECLOCK_TIMER_U6_STORM, 0);
2951 }
2952
2953 return true;
2954 }
2955
2956 /* Unready/Drop/Move: Don't allow removal. */
amulet_of_submission(Obj * obj,UseCodeEvent ev)2957 bool U6UseCode::amulet_of_submission(Obj *obj, UseCodeEvent ev) {
2958 if (obj->is_readied()) {
2959 scroll->display_string("\nMagical energy prevents you from removing the amulet.\n");
2960 return false;
2961 }
2962 return true;
2963 }
2964
2965 /* Use: Learn Gargish! */
gargish_vocabulary(Obj * obj,UseCodeEvent ev)2966 bool U6UseCode::gargish_vocabulary(Obj *obj, UseCodeEvent ev) {
2967 if (ev == USE_EVENT_USE) {
2968 scroll->display_string("\n");
2969 scroll->display_string("You study the scroll!\n");
2970 player->set_gargish_flag(true);
2971 }
2972 return true;
2973 }
2974
2975 /* LOOK: Print the name of a holy brazier, and not the normal description. */
holy_flame(Obj * obj,UseCodeEvent ev)2976 bool U6UseCode::holy_flame(Obj *obj, UseCodeEvent ev) {
2977 if (obj->quality == 0 || obj->quality > 3)
2978 return true; // use normal description
2979 scroll->display_string("\nThe flame of ");
2980 if (obj->quality == 1)
2981 scroll->display_string("truth");
2982 if (obj->quality == 2)
2983 scroll->display_string("love");
2984 if (obj->quality == 3)
2985 scroll->display_string("courage");
2986 scroll->display_string(".\n");
2987 return false;
2988 }
2989
cannot_unready(Obj * obj)2990 bool U6UseCode::cannot_unready(Obj *obj) {
2991 if (!obj->is_readied())
2992 return false;
2993 if (obj->obj_n == OBJ_U6_AMULET_OF_SUBMISSION
2994 || (obj->obj_n == OBJ_U6_TORCH && obj->frame_n == 1))
2995 return true;
2996
2997 return false;
2998 }
use_harpsichord(Obj * obj,UseCodeEvent ev)2999 bool U6UseCode::use_harpsichord(Obj *obj, UseCodeEvent ev) {
3000 if (ev == USE_EVENT_SEARCH) {
3001 return search_container(obj);
3002 }
3003 return play_instrument(obj, ev);
3004 }
3005
3006 } // End of namespace Nuvie
3007 } // End of namespace Ultima
3008