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/core/obj_manager.h"
25 #include "ultima/nuvie/core/game.h"
26 #include "ultima/nuvie/core/u6_objects.h"
27 
28 namespace Ultima {
29 namespace Nuvie {
30 
Obj()31 Obj::Obj() {
32 	obj_n = 0;
33 	status = 0;
34 	nuvie_status = 0;
35 	frame_n = 0;
36 	qty = 0;
37 	quality = 0;
38 	parent = NULL;
39 	container = NULL;
40 	x = 0;
41 	y = 0;
42 	z = 0;
43 }
44 
Obj(Obj * sobj)45 Obj::Obj(Obj *sobj) {
46 	memcpy(this, sobj, sizeof(Obj));
47 
48 	parent = NULL;
49 	container = NULL;
50 }
51 
make_container()52 void Obj::make_container() {
53 	if (container == NULL)
54 		container = new U6LList();
55 
56 	return;
57 }
58 
get_container_obj(bool recursive)59 Obj *Obj::get_container_obj(bool recursive) {
60 	Obj *obj = (is_in_container() ? (Obj *)parent : NULL);
61 
62 	if (recursive) {
63 		while (obj && obj->is_in_container())
64 			obj = (Obj *)obj->parent;
65 	}
66 
67 	return obj;
68 }
69 
set_on_map(U6LList * map_list)70 void Obj::set_on_map(U6LList *map_list) {
71 	parent = map_list;
72 	nuvie_status &= NUVIE_OBJ_STATUS_LOC_MASK_SET;
73 	nuvie_status |= OBJ_LOC_MAP;
74 
75 	return;
76 }
77 
set_in_container(Obj * container_obj)78 void Obj::set_in_container(Obj *container_obj) {
79 	parent = (void *)container_obj;
80 	nuvie_status &= NUVIE_OBJ_STATUS_LOC_MASK_SET;
81 	nuvie_status |= OBJ_LOC_CONT;
82 
83 	return;
84 }
85 
set_invisible(bool flag)86 void Obj::set_invisible(bool flag) {
87 	if (flag)
88 		status |= OBJ_STATUS_INVISIBLE;
89 	else if (is_invisible())
90 		status ^= OBJ_STATUS_INVISIBLE;
91 
92 	return;
93 }
94 
set_temporary(bool flag)95 void Obj::set_temporary(bool flag) {
96 	if (flag)
97 		status |= OBJ_STATUS_TEMPORARY;
98 	else if (is_temporary())
99 		status ^= OBJ_STATUS_TEMPORARY;
100 
101 	return;
102 }
103 
set_ok_to_take(bool flag,bool recursive)104 void Obj::set_ok_to_take(bool flag, bool recursive) {
105 	if (flag)
106 		status |= OBJ_STATUS_OK_TO_TAKE;
107 	else if (is_ok_to_take())
108 		status ^= OBJ_STATUS_OK_TO_TAKE;
109 
110 	if (recursive && container) {
111 		for (U6Link *link = container->start(); link != NULL; link = link->next) {
112 			Obj *obj = (Obj *)link->data;
113 			obj->set_ok_to_take(flag, recursive);
114 		}
115 	}
116 }
117 
set_in_inventory()118 void Obj::set_in_inventory() {
119 	nuvie_status &= NUVIE_OBJ_STATUS_LOC_MASK_SET;
120 	nuvie_status |= OBJ_LOC_INV;
121 
122 	return;
123 }
124 
readied()125 void Obj::readied() { //set_readied() ??
126 	nuvie_status &= NUVIE_OBJ_STATUS_LOC_MASK_SET;
127 	nuvie_status |= OBJ_LOC_READIED;
128 
129 	return;
130 }
131 
set_noloc()132 void Obj::set_noloc() {
133 	parent = NULL;
134 	nuvie_status &= NUVIE_OBJ_STATUS_LOC_MASK_SET; //clear location bits 0 = no loc
135 
136 	return;
137 }
138 
set_in_script(bool flag)139 void Obj::set_in_script(bool flag) {
140 	if (flag)
141 		nuvie_status |= NUVIE_OBJ_STATUS_SCRIPTING;
142 	else if (is_script_obj())
143 		nuvie_status ^= NUVIE_OBJ_STATUS_SCRIPTING;
144 
145 	return;
146 }
147 
set_actor_obj(bool flag)148 void Obj::set_actor_obj(bool flag) {
149 	if (flag)
150 		nuvie_status |= NUVIE_OBJ_STATUS_ACTOR_OBJ;
151 	else if (is_actor_obj())
152 		nuvie_status ^= NUVIE_OBJ_STATUS_ACTOR_OBJ;
153 
154 	return;
155 }
156 
157 /* Returns true if an object is in an actor inventory, including containers and readied items. */
158 
is_in_inventory(bool check_parent)159 bool Obj::is_in_inventory(bool check_parent) {
160 	switch (get_engine_loc()) {
161 	case OBJ_LOC_INV :
162 	case OBJ_LOC_READIED :
163 		return true;
164 	case OBJ_LOC_CONT :
165 		if (check_parent)
166 			return ((Obj *)parent)->is_in_inventory(check_parent);
167 		break;
168 	default :
169 		break;
170 	}
171 
172 	return false;
173 }
174 
get_engine_loc()175 uint8 Obj::get_engine_loc() {
176 	return (nuvie_status & NUVIE_OBJ_STATUS_LOC_MASK_GET);
177 }
178 
get_actor_holding_obj()179 Actor *Obj::get_actor_holding_obj() {
180 	switch (get_engine_loc()) {
181 	case OBJ_LOC_INV :
182 	case OBJ_LOC_READIED :
183 		return (Actor *)this->parent;
184 
185 	case OBJ_LOC_CONT :
186 		return ((Obj *)parent)->get_actor_holding_obj();
187 
188 	default :
189 		break;
190 	}
191 
192 	return NULL;
193 }
194 
195 //Add child object into container, stacking if required
add(Obj * obj,bool stack)196 void Obj::add(Obj *obj, bool stack) {
197 	if (container == NULL)
198 		make_container();
199 
200 	if (stack && Game::get_game()->get_obj_manager()->is_stackable(obj))
201 		add_and_stack(obj);
202 	else
203 		container->addAtPos(0, obj);
204 
205 	obj->set_in_container(this);
206 
207 	return;
208 }
209 
add_and_stack(Obj * obj)210 void Obj::add_and_stack(Obj *obj) {
211 	U6Link *link;
212 	Obj *cont_obj;
213 
214 	//should we recurse through nested containers?
215 	for (link = container->start(); link != NULL;) {
216 		cont_obj = (Obj *)link->data;
217 		link = link->next;
218 		//match on obj_n, frame_n and quality.
219 		if (obj->obj_n == cont_obj->obj_n && obj->frame_n == cont_obj->frame_n && obj->quality == cont_obj->quality) {
220 			obj->qty += cont_obj->qty;
221 			container->replace(cont_obj, obj); //replace cont_obj with obj in container list. should we do this to link->data directly?
222 			delete_obj(cont_obj);
223 
224 			return;
225 		}
226 	}
227 
228 	container->addAtPos(0, obj); // add the object as we couldn't find another object to stack with.
229 
230 	return;
231 }
232 
233 //Remove child object from container.
remove(Obj * obj)234 bool Obj::remove(Obj *obj) {
235 	if (container == NULL)
236 		return false;
237 
238 	if (container->remove(obj) == false)
239 		return false;
240 	if (Game::get_game()->get_game_type() == NUVIE_GAME_SE) {
241 		if (obj_n == OBJ_SE_JAR)
242 			frame_n = 0; // empty jar frame
243 	}
244 	obj->x = 0;
245 	obj->y = 0;
246 	obj->z = 0;
247 
248 	obj->set_noloc();
249 
250 	return true;
251 }
252 
find_in_container(uint16 objN,uint8 quality_,bool match_quality,uint8 frameN,bool match_frame_n,Obj ** prev_obj)253 Obj *Obj::find_in_container(uint16 objN, uint8 quality_, bool match_quality, uint8 frameN, bool match_frame_n, Obj **prev_obj) {
254 	U6Link *link;
255 	Obj *obj;
256 
257 	if (container == NULL)
258 		return NULL;
259 
260 	for (link = container->start(); link != NULL; link = link->next) {
261 		obj = (Obj *)link->data;
262 		if (obj) {
263 			if (obj->obj_n == objN && (match_quality == false || obj->quality == quality_) && (match_frame_n == false || obj->frame_n == frameN)) {
264 				if (prev_obj != NULL && obj == *prev_obj)
265 					prev_obj = NULL;
266 				else {
267 					if (prev_obj == NULL || *prev_obj == NULL)
268 						return obj;
269 				}
270 			}
271 
272 			if (obj->container) {
273 				obj = obj->find_in_container(objN, quality_, match_quality, frameN, match_frame_n, prev_obj);
274 				if (obj)
275 					return obj;
276 			}
277 		}
278 	}
279 
280 	return NULL;
281 }
282 
get_total_qty(uint16 match_obj_n)283 uint32 Obj::get_total_qty(uint16 match_obj_n) {
284 	U6Link *link;
285 	Obj *obj;
286 	uint16 total_qty = 0;
287 
288 	if (obj_n == match_obj_n) {
289 		if (qty == 0)
290 			total_qty += 1;
291 		else
292 			total_qty += qty;
293 	}
294 
295 	if (container != NULL) {
296 		for (link = container->start(); link != NULL; link = link->next) {
297 			obj = (Obj *)link->data;
298 			if (obj) {
299 				if (obj->container)
300 					total_qty += obj->get_total_qty(match_obj_n);
301 				else if (obj->obj_n == match_obj_n) {
302 					if (obj->qty == 0)
303 						total_qty += 1;
304 					else
305 						total_qty += obj->qty;
306 				}
307 			}
308 		}
309 	}
310 
311 	return total_qty;
312 }
313 
container_count_objects()314 uint32 Obj::container_count_objects() {
315 	uint32 count = 0;
316 	U6Link *link;
317 
318 	if (container != NULL) {
319 		for (link = container->start(); link != NULL; link = link->next) {
320 			++count;
321 		}
322 	}
323 	return count;
324 }
325 
is_ok_to_take()326 bool Obj::is_ok_to_take() {
327 	return ((status & OBJ_STATUS_OK_TO_TAKE) || Game::get_game()->using_hackmove());
328 }
329 
330 } // End of namespace Nuvie
331 } // End of namespace Ultima
332