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/gui/widgets/msg_scroll.h"
26 #include "ultima/nuvie/actors/actor_manager.h"
27 #include "ultima/nuvie/actors/actor.h"
28 #include "ultima/nuvie/usecode/usecode.h"
29 #include "ultima/nuvie/gui/widgets/map_window.h"
30 #include "ultima/nuvie/script/script.h"
31 #include "ultima/nuvie/core/events.h"
32 
33 namespace Ultima {
34 namespace Nuvie {
35 
UseCode(Game * g,Configuration * cfg)36 UseCode::UseCode(Game *g, Configuration *cfg) {
37 	game = g;
38 	config = cfg;
39 	obj_manager = NULL;
40 	map = NULL;
41 	player = NULL;
42 	scroll = NULL;
43 	actor_manager = NULL;
44 	obj_manager = NULL;
45 	party = NULL;
46 	script = NULL;
47 
48 	script_thread = NULL;
49 
50 	clear_items();
51 }
52 
~UseCode()53 UseCode::~UseCode() {
54 	if (script_thread) {
55 		delete script_thread;
56 	}
57 }
58 
init(ObjManager * om,Map * m,Player * p,MsgScroll * ms)59 bool UseCode::init(ObjManager *om, Map *m, Player *p, MsgScroll *ms) {
60 	obj_manager = om;
61 	map = m;
62 	player = p;
63 	scroll = ms;
64 
65 	actor_manager = game->get_actor_manager();
66 	party = player->get_party();
67 	script = game->get_script();
68 
69 	return true;
70 }
71 
72 
73 
74 /* Clear items.
75  */
clear_items()76 void UseCode::clear_items() {
77 	memset(&items, 0, sizeof(items));
78 	/*    items.uint_ref = NULL;
79 	    items.sint_ref = NULL;
80 	    items.obj_ref = NULL;
81 	    items.actor_ref = items.actor2_ref = NULL;
82 	    items.mapcoord_ref = NULL;
83 	    items.msg_ref = NULL;
84 	    items.string_ref = NULL;
85 	    items.ent_ref = NULL;
86 	    items.data_ref = NULL; */
87 }
88 
get_running_script()89 ScriptThread *UseCode::get_running_script() {
90 	if (script_thread && script_thread->is_running())
91 		return script_thread;
92 
93 	return NULL;
94 }
95 
is_script_running()96 bool UseCode::is_script_running() {
97 	if (script_thread && script_thread->is_running())
98 		return true;
99 
100 	return false;
101 }
102 
is_container(Obj * obj)103 bool UseCode::is_container(Obj *obj) {
104 	return script->call_is_container_obj(obj->obj_n);
105 }
106 
has_usecode(Obj * obj,UseCodeEvent ev)107 bool UseCode::has_usecode(Obj *obj, UseCodeEvent ev) {
108 	return script->call_has_usecode(obj, ev);
109 }
110 
use_obj(Obj * obj,Actor * actor)111 bool UseCode::use_obj(Obj *obj, Actor *actor) {
112 	if (script_thread) {
113 		delete script_thread;
114 		script_thread = NULL;
115 	}
116 
117 	script_thread = script->call_use_obj(obj, actor);
118 
119 	if (script_thread) {
120 		script_thread->start();
121 		if (script_thread->finished()) {
122 			delete script_thread;
123 			script_thread = NULL;
124 		}
125 	}
126 
127 	return true;//script->call_use_obj(obj, actor);
128 }
129 
130 // use obj at location with src_obj as object_ref
use_obj(uint16 x,uint16 y,uint8 z,Obj * src_obj)131 bool UseCode::use_obj(uint16 x, uint16 y, uint8 z, Obj *src_obj) {
132 	Obj *obj;
133 
134 	obj = obj_manager->get_obj(x, y, z, true);
135 
136 	if (obj == NULL)
137 		return false;
138 
139 	return use_obj(obj, src_obj);
140 }
141 
ready_obj(Obj * obj,Actor * actor)142 bool UseCode::ready_obj(Obj *obj, Actor *actor) {
143 	return script->call_ready_obj(obj, actor);
144 }
145 
move_obj(Obj * obj,sint16 rel_x,sint16 rel_y)146 bool UseCode::move_obj(Obj *obj, sint16 rel_x, sint16 rel_y) {
147 	return script->call_move_obj(obj, rel_x, rel_y);
148 }
149 
toggle_frame(Obj * obj)150 void UseCode::toggle_frame(Obj *obj) {
151 	if (obj->frame_n > 0)
152 		obj->frame_n--;
153 	else
154 		obj->frame_n = 1;
155 }
156 
157 
158 /* Print container contents and dump them on top of the container.
159  */
160 //FIXME! some of this logic should go elsewhere.
search_container(Obj * obj,bool show_string)161 bool UseCode::search_container(Obj *obj, bool show_string) {
162 	Obj *temp_obj;
163 	U6Link *obj_link;
164 
165 	/* Test whether this object has items inside it. */
166 	if ((obj->container != NULL) &&
167 	        ((obj_link = obj->container->end()) != NULL)) {
168 		/* Add objects to obj_list. */
169 		for (; obj_link != NULL;) {
170 			temp_obj = (Obj *)obj_link->data;
171 			obj_link = obj_link->prev;
172 			/*
173 			obj_list->add(temp_obj);
174 			temp_obj->status |= OBJ_STATUS_OK_TO_TAKE;
175 			temp_obj->set_on_map(obj_list); //ERIC temp_obj->status &= ~OBJ_STATUS_IN_CONTAINER;
176 			temp_obj->x = obj->x;
177 			temp_obj->y = obj->y;
178 			temp_obj->z = obj->z;
179 			*/
180 			obj_manager->moveto_map(temp_obj, obj->is_in_container() ? MapCoord(obj->get_container_obj(true)) : MapCoord(obj));
181 			if (show_string) {
182 				scroll->display_string(obj_manager->look_obj(temp_obj, true));
183 				if (obj_link) // more objects left
184 					scroll->display_string(obj_link->prev ? ", " : ", and ");
185 			}
186 		}
187 		/* Remove objects from the container. */
188 		//obj->container->removeAll();
189 		return true;
190 	}
191 	return false;
192 }
193 
194 
195 /* Remove last object in container and return a pointer to it.
196  */
get_obj_from_container(Obj * obj)197 Obj *UseCode::get_obj_from_container(Obj *obj) {
198 	Obj *temp_obj;
199 	if (obj->container && obj->container->end()) {
200 		temp_obj = (Obj *)obj->container->end()->data;
201 		obj->container->remove(temp_obj); // a pop_back() may be more efficient
202 		return (temp_obj);
203 	}
204 	return (NULL);
205 }
206 
207 
208 /* Print name of event being sent and the object receiving it.
209  */
dbg_print_event(UseCodeEvent event,Obj * obj)210 void UseCode::dbg_print_event(UseCodeEvent event, Obj *obj) {
211 	string do_string = "";
212 	switch (event) {
213 	case USE_EVENT_USE:
214 		do_string = "Use";
215 		break;
216 	case USE_EVENT_LOOK:
217 		do_string = "Look at";
218 		break;
219 	case USE_EVENT_PASS:
220 		do_string = "Pass";
221 		break;
222 	case USE_EVENT_SEARCH:
223 		do_string = "Search";
224 		break;
225 	case USE_EVENT_MOVE:
226 		do_string = "Move";
227 		break;
228 	case USE_EVENT_LOAD:
229 		do_string = "Load";
230 		break;
231 	case USE_EVENT_MESSAGE:
232 		do_string = "Message";
233 		break;
234 	case USE_EVENT_READY:
235 		do_string = "(Un)Equip";
236 		break;
237 	case USE_EVENT_GET:
238 		do_string = "Get";
239 		break;
240 	case USE_EVENT_DROP:
241 		do_string = "Drop";
242 		break;
243 	}
244 	if (do_string != "")
245 		DEBUG(0, LEVEL_DEBUGGING, "UseCode: %s object %d:%d (%03x,%03x,%x)\n", do_string.c_str(),
246 		      obj->obj_n, obj->frame_n, obj->x, obj->y, obj->z);
247 	else
248 		DEBUG(0, LEVEL_DEBUGGING, "UseCode: Events 0x%04x sent to object %d:%d (%03x,%03x,%x)\n",
249 		      event, obj->obj_n, obj->frame_n, obj->x, obj->y, obj->z);
250 }
251 
252 
253 /* Subtract `count' from object quantity. Destroy the object completely if all
254  * stacked objects were removed, or the object is not stackable, or `count' is
255  * 0. This means it will be removed from the world or an actor's inventory, and
256  * deleted.
257  * Returns the original object if it still exists, because the count was smaller
258  * than the object stack, or it could not be completely destroyed for whatever
259  * reason. Returns NULL if the object was destroyed.
260  */
destroy_obj(Obj * obj,uint32 count,bool run_usecode)261 Obj *UseCode::destroy_obj(Obj *obj, uint32 count, bool run_usecode) {
262 	//ActorManager *actor_manager = Game::get_game()->get_actor_manager();
263 	//bool removed = false;
264 
265 	// subtract
266 	if (count > 0 && obj_manager->is_stackable(obj) && obj->qty > count)
267 		obj->qty -= count;
268 	else { // destroy
269 		obj_manager->unlink_from_engine(obj, run_usecode);
270 		delete_obj(obj);
271 		obj = NULL;
272 	}
273 
274 	return (obj);
275 }
276 
277 /*
278  * don't autowalk long distances to objects when foes are nearby or select obj outside of range
279  */
out_of_use_range(Obj * obj,bool check_enemies)280 bool UseCode::out_of_use_range(Obj *obj, bool check_enemies) {
281 	if (!obj) // this should be checked before you get here
282 		return true;
283 	if (obj->is_in_inventory())
284 		return false;
285 
286 	MapCoord player_loc = player->get_actor()->get_location();
287 	MapCoord obj_loc = MapCoord(obj->x, obj->y, obj->z);
288 
289 	if (!check_enemies) {
290 		if (player_loc.distance(obj_loc) > 1
291 		        && game->get_map_window()->get_interface() == INTERFACE_NORMAL) {
292 			scroll->display_string("\nOut of range.\n");
293 			return true;
294 		} else if (!game->get_map_window()->can_get_obj(player->get_actor(), obj)) {
295 			scroll->display_string("\nBlocked.\n");
296 			return true;
297 		} else
298 			return false;
299 	} else if (player_loc.distance(obj_loc) > 1) { // only setup for objects that already checked range and blocking limit
300 		ActorList *enemies = 0;
301 
302 		if ((enemies = player->get_actor()->find_enemies())) {
303 			scroll->display_string("\nOut of range.\n");
304 			delete enemies;
305 			return true;
306 		}
307 		delete enemies;
308 	}
309 	return false;
310 }
311 
useCodeTypeToString(UseCodeType type)312 const char *useCodeTypeToString(UseCodeType type) {
313 	switch (type) {
314 	case USE :
315 		return "use";
316 	case MOVE :
317 		return "move";
318 	case GET :
319 		return "get";
320 	default :
321 		return "other";
322 	}
323 }
324 
325 } // End of namespace Nuvie
326 } // End of namespace Ultima
327