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