1 /*
2 Copyright (C) 2000-2013 The Exult Team
3 
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 GNU General Public License for more details.
13 
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
17 */
18 
19 #ifdef HAVE_CONFIG_H
20 #  include <config.h>
21 #endif
22 
23 #include "game.h"
24 #include "gamewin.h"
25 #include "Gump_button.h"
26 #include "misc_buttons.h"
27 #include "Paperdoll_gump.h"
28 #include "contain.h"
29 #include "actors.h"
30 #include "objiter.h"
31 #include "cheat.h"
32 #include "miscinf.h"
33 #include "shapeid.h"
34 #include "ready.h"
35 #include "ammoinf.h"
36 #include "npcdollinf.h"
37 #include "objdollinf.h"
38 #include "weaponinf.h"
39 #include "ignore_unused_variable_warning.h"
40 #include "array_size.h"
41 
42 #include <cstdio>
43 
44 using std::size_t;
45 
46 //#define SHOW_USECODE_CONTAINER
47 //#define SHOW_NONREADIED_OBJECTS
48 
49 /*
50  *
51  *  SERPENT ISLE PAPERDOLL GUMP
52  *
53  */
54 
55 /*
56  *  Statics:
57  */
58 
59 // Paperdoll is completely different to Actor
60 Paperdoll_gump::Position Paperdoll_gump::disk   = {123, 137};
61 Paperdoll_gump::Position Paperdoll_gump::heart  = {97, 137};
62 Paperdoll_gump::Position Paperdoll_gump::combat = {51, 142};
63 Paperdoll_gump::Position Paperdoll_gump::cstat  = {72, 137};
64 Paperdoll_gump::Position Paperdoll_gump::cmode  = {75, 140};
65 Paperdoll_gump::Position Paperdoll_gump::halo   = {123, 120};
66 
67 Paperdoll_gump::Position Paperdoll_gump::coords[18] = {
68 	{76, 20},     /* head */      {115, 27},    /* back */
69 	{105, 65},    /* belt */      {38, 55},     /* lhand */
70 	{55, 62},     /* lfinger */   {80, 80},     /* legs */
71 	{84, 105},    /* feet */      {90, 50},     /* rfinger */
72 	{117, 55},    /* rhand */     {45, 35},     /* torso */
73 	{44, 26},     /* neck */      {69, 59},     /* ammo */
74 	{59, 19},     /* back2 */     {94, 20},     /* back 3 (shield) */
75 	{76, 26},     /* ears */      {76, 33},     /* cloak */
76 	{73, 53},     /* gloves */    {0, 0}        /* usecode container */
77 };
78 Paperdoll_gump::Position Paperdoll_gump::coords_blue[18] = {
79 	{76, 20},     /* head */      {64, 27},     /* back */
80 	{54, 56},     /* belt */      {30, 58},     /* lhand */
81 	{55, 62},     /* lfinger */   {80, 80},     /* legs */
82 	{84, 105},    /* feet */      {90, 50},     /* rfinger */
83 	{68, 50},     /* rhand */     {45, 37},     /* torso */
84 	{22, 26},     /* neck */      {69, 59},     /* ammo */
85 	{59, 19},     /* back2 */     {94, 20},     /* back 3 (shield) */
86 	{76, 26},     /* ears */      {76, 33},     /* cloak */
87 	{73, 53},     /* gloves */    {0, 0}        /* usecode container */
88 };
89 Paperdoll_gump::Position Paperdoll_gump::coords_hot[18] = {
90 	{76, 20},     /* head */      {94, 27},     /* back */
91 	{92,  61},    /* belt */      {38, 55},     /* lhand */
92 	{55, 62},     /* lfinger */   {80, 80},     /* legs */
93 	{84, 105},    /* feet */      {90, 50},     /* rfinger */
94 	{117, 55},    /* rhand */     {83, 43},     /* torso */
95 	{76, 41},     /* neck */      {69, 59},     /* ammo */
96 	{59, 19},     /* back2 */     {94, 20},     /* back 3 (shield) */
97 	{76, 26},     /* ears */      {76, 33},     /* cloak */
98 	{73, 53},     /* gloves */    {0, 0}        /* usecode container */
99 };
100 
101 
102 
103 //
104 // Paperdoll Coords
105 //
106 
107 Paperdoll_gump::Position Paperdoll_gump::body    = {46, 33};
108 Paperdoll_gump::Position Paperdoll_gump::headp   = {46, 22};
109 
110 Paperdoll_gump::Position Paperdoll_gump::beltf   = {58, 52};
111 Paperdoll_gump::Position Paperdoll_gump::neckf   = {46, 47};
112 Paperdoll_gump::Position Paperdoll_gump::beltm   = {57, 55};
113 Paperdoll_gump::Position Paperdoll_gump::neckm   = {46, 44};
114 
115 Paperdoll_gump::Position Paperdoll_gump::legsp   = {57, 66};
116 Paperdoll_gump::Position Paperdoll_gump::feetp   = {46, 99};
117 
118 Paperdoll_gump::Position Paperdoll_gump::hands   = {68, 44};
119 Paperdoll_gump::Position Paperdoll_gump::rhandp  = {68, 44};
120 Paperdoll_gump::Position Paperdoll_gump::lhandp  = {34, 65};
121 
122 Paperdoll_gump::Position Paperdoll_gump::ahand   = {28, 59};
123 Paperdoll_gump::Position Paperdoll_gump::ammo    = {28, 59};
124 
125 Paperdoll_gump::Position Paperdoll_gump::backf   = {68, 28};
126 Paperdoll_gump::Position Paperdoll_gump::back2f  = {34, 22};
127 Paperdoll_gump::Position Paperdoll_gump::backm   = {68, 22};
128 Paperdoll_gump::Position Paperdoll_gump::back2m  = {35, 22};
129 Paperdoll_gump::Position Paperdoll_gump::shieldf = {56, 25};
130 Paperdoll_gump::Position Paperdoll_gump::shieldm = {57, 22};
131 
132 
133 /*
134  *  Find the index of the closest 'spot' to a mouse point.
135  *
136  *  Output: Index, or -1 if unsuccessful.
137  */
138 
find_closest(int mx,int my,int only_empty)139 int Paperdoll_gump::find_closest(
140     int mx, int my,         // Mouse point in window.
141     int only_empty          // Only allow empty spots.
142 ) {
143 	mx -= x;
144 	my -= y;       // Get point rel. to us.
145 	long closest_squared = 1000000; // Best distance squared.
146 	int closest = -1;       // Best index.
147 
148 	Actor *npc = container->as_actor();
149 
150 	for (size_t i = 0; i < array_size(coords_hot); i++) {
151 		int spot = i;
152 
153 		int dx = mx - coords_hot[spot].x;
154 		int dy = my - coords_hot[spot].y;
155 		long dsquared = dx * dx + dy * dy;
156 
157 		// Map slots occupied by multi-slot items to the filled slot.
158 		if ((i == back_shield || i == back_2h) && npc->is_scabbard_used())
159 			spot = belt;
160 		else if (i == cloak && npc->is_neck_used())
161 			spot = amulet;
162 		else if (i == rhand && npc->is_two_handed())
163 			spot = lhand;
164 		else if ((i == rfinger || i == gloves) && npc->is_two_fingered())
165 			spot = lfinger;
166 
167 		// Better than prev and free if required.?
168 		if (dsquared < closest_squared && !(only_empty && container->get_readied(spot))) {
169 			closest_squared = dsquared;
170 			closest = spot;
171 		}
172 	}
173 
174 	return closest;
175 }
176 
177 /*
178  *  Create the gump display for an actor.
179  */
180 
Paperdoll_gump(Container_game_object * cont,int initx,int inity,int shnum)181 Paperdoll_gump::Paperdoll_gump(
182     Container_game_object *cont,    // Container it represents.
183     int initx, int inity,       // Coords. on screen.
184     int shnum           // Shape #.
185 ) : Gump(cont, initx, inity, 123, SF_PAPERDOL_VGA) {
186 	ignore_unused_variable_warning(shnum);
187 	set_object_area(TileRect(26, 0, 104, 140), 6, 145);
188 
189 	// Create Heart button
190 	heart_button = new Heart_button(this, heart.x, heart.y);
191 	Actor *actor = cont->as_actor();
192 	// Create Cstats button or Halo and Cmode
193 	if (Game::get_game_type() == BLACK_GATE) {
194 		if (actor->get_npc_num() == 0)
195 			halo_button = new Halo_button(this, halo.x, halo.y,
196 			                              actor);
197 		else
198 			halo_button = new Halo_button(this, disk.x, disk.y,
199 			                              actor);
200 
201 		cmode_button = new Combat_mode_button(this, cmode.x, cmode.y,
202 		                                      actor);
203 		cstats_button = nullptr;
204 	} else {
205 		cstats_button = new Cstats_button(this, cstat.x, cstat.y);
206 		halo_button = nullptr;
207 		cmode_button = nullptr;
208 	}
209 
210 
211 
212 	// If Avatar create Disk Button
213 	if (actor->get_npc_num() == 0)
214 		disk_button = new Disk_button(this, disk.x, disk.y);
215 	else
216 		disk_button = nullptr;
217 
218 
219 	// If Avatar create Combat Button
220 	if (actor->get_npc_num() == 0)
221 		combat_button = new Combat_button(this, combat.x, combat.y);
222 	else
223 		combat_button = nullptr;
224 
225 
226 	// Put all the objects in the right place
227 	for (size_t i = 0; i < array_size(coords); i++) {
228 		Game_object *obj = container->get_readied(i);
229 		if (obj)
230 			set_to_spot(obj, i);
231 	}
232 }
233 
234 /*
235  *  Delete actor display.
236  */
237 
~Paperdoll_gump()238 Paperdoll_gump::~Paperdoll_gump(
239 ) {
240 	delete heart_button;
241 	delete disk_button;
242 	delete combat_button;
243 	delete cstats_button;
244 	delete halo_button;
245 	delete cmode_button;
246 }
247 
248 /*
249  *  Is a given screen point on one of our buttons?
250  *
251  *  Output: ->button if so.
252  */
253 
on_button(int mx,int my)254 Gump_button *Paperdoll_gump::on_button(
255     int mx, int my          // Point in window.
256 ) {
257 	Gump_button *btn = Gump::on_button(mx, my);
258 	if (btn)
259 		return btn;
260 	else if (heart_button && heart_button->on_button(mx, my))
261 		return heart_button;
262 	else if (disk_button && disk_button->on_button(mx, my))
263 		return disk_button;
264 	else if (combat_button && combat_button->on_button(mx, my))
265 		return combat_button;
266 	else if (cstats_button && cstats_button->on_button(mx, my))
267 		return cstats_button;
268 	else if (halo_button && halo_button->on_button(mx, my))
269 		return halo_button;
270 	else if (cmode_button && cmode_button->on_button(mx, my))
271 		return cmode_button;
272 	return nullptr;
273 }
274 
275 /*
276  *  Add an object.
277  *
278  *  Output: false if cannot add it.
279  */
280 
add(Game_object * obj,int mx,int my,int sx,int sy,bool dont_check,bool combine)281 bool Paperdoll_gump::add(
282     Game_object *obj,
283     int mx, int my,         // Screen location of mouse.
284     int sx, int sy,         // Screen location of obj's hotspot.
285     bool dont_check,        // Skip volume check.
286     bool combine            // True to try to combine obj.  MAY
287     //   cause obj to be deleted.
288 ) {
289 	ignore_unused_variable_warning(sx, sy);
290 	do {
291 		Game_object *cont = find_object(mx, my);
292 
293 		if (cont && cont->add(obj, false, combine))
294 			break;
295 
296 		int index = find_closest(mx, my, 1);
297 
298 		if (index != -1 && container->add_readied(obj, index))
299 			break;
300 
301 		if (container->add(obj, dont_check, combine))
302 			break;
303 
304 		return false;
305 	} while (false);
306 
307 	// Put all the objects in the right place
308 	for (size_t i = 0; i < array_size(coords); i++) {
309 		obj = container->get_readied(i);
310 		if (obj) set_to_spot(obj, i);
311 	}
312 
313 	return true;
314 }
315 
316 /*
317  *  Set object's coords. to given spot.
318  */
319 
set_to_spot(Game_object * obj,int index)320 void Paperdoll_gump::set_to_spot(
321     Game_object *obj,
322     int index           // Spot index.
323 ) {
324 
325 	// Get shape.
326 	Shape_frame *shape = obj->get_shape();
327 	//if (!shape)           // Funny?  Try frame 0.
328 	//  shape = gwin->get_shape(obj->get_shapenum(), 0);
329 	if (!shape)
330 		return;
331 
332 	// Height and width
333 	int w = shape->get_width();
334 	int h = shape->get_height();
335 
336 	// Set object's position.
337 	obj->set_shape_pos(
338 	    coords[index].x + shape->get_xleft() - w / 2 - object_area.x,
339 	    coords[index].y + shape->get_yabove() - h / 2 - object_area.y);
340 }
341 
342 /*
343  *  Paint on screen.
344  */
345 
paint()346 void Paperdoll_gump::paint(
347 ) {
348 	const Game_object *obj;
349 
350 	// Paint Objects
351 	TileRect box = object_area;    // Paint objects inside.
352 	box.shift(x, y);        // Set box to screen location.
353 
354 	paint_shape(x, y);
355 
356 	// Paint red "checkmark".
357 	paint_elems();
358 
359 	// Get the information required about ourself
360 	Actor *actor = container->as_actor();
361 	const Paperdoll_npc *info = actor->get_info().get_npc_paperdoll();
362 
363 	if (!info) {
364 		const Shape_info &inf = ShapeID::get_info(actor->get_sexed_coloured_shape());
365 		info = inf.get_npc_paperdoll();
366 	}
367 	if (!info) {
368 		const Shape_info &inf = ShapeID::get_info(actor->get_shape_real());
369 		info = inf.get_npc_paperdoll_safe(actor->get_type_flag(Actor::tf_sex));
370 	}
371 
372 	// Spots that are female/male specific
373 	Position shield;
374 	Position back2;
375 	Position back;
376 	Position neck;
377 	Position beltp;
378 
379 	if (actor->get_type_flag(Actor::tf_sex) || info->is_npc_female()) {
380 		// Set the female spots
381 		shield = shieldf;
382 		back2  = back2f;
383 		back   = backf;
384 		neck   = neckf;
385 		beltp  = beltf;
386 	} else {            // Set the male spots
387 		shield = shieldm;
388 		back2  = back2m;
389 		back   = backm;
390 		neck   = neckm;
391 		beltp  = beltm;
392 	}
393 
394 	// Now paint. Order is very specific
395 
396 
397 	if (actor->is_scabbard_used()) {
398 		paint_object(box, info, belt,            shield.x, shield.y, 0, back_shield);
399 		paint_object(box, info, belt,            back2.x,  back2.y,  0, back_2h);
400 	} else {
401 		paint_object(box, info, back_shield,     shield.x, shield.y);
402 		paint_object(box, info, back_2h,         back2.x,  back2.y);
403 	}
404 	paint_object(box, info, backpack,        back.x,  back.y);
405 	if (actor->is_neck_used())
406 		paint_object(box, info, amulet,          body.x,   body.y,   0, cloak);
407 	else
408 		paint_object(box, info, cloak,           body.x,   body.y);
409 	paint_body(box, info);
410 	paint_object(box, info, legs,            legsp.x, legsp.y);
411 	paint_object(box, info, feet,            feetp.x, feetp.y);
412 	paint_object(box, info, quiver,          ammo.x,  ammo.y,    0, -1);
413 	paint_object(box, info, torso,           body.x,  body.y);
414 	paint_belt(box, info);
415 	paint_head(box, info);
416 	if (actor->is_neck_used()) {
417 		obj = container->get_readied(amulet);
418 		const Paperdoll_item *item1;
419 		const Paperdoll_item *item2;
420 		if (obj) {
421 			const Shape_info &inf = obj->get_info();
422 			item1 = inf.get_item_paperdoll(obj->get_framenum(), cloak);
423 			item2 = inf.get_item_paperdoll(obj->get_framenum(), cloak_clasp);
424 		} else
425 			item1 = item2 = nullptr;
426 		if (!item1 && !item2)
427 			paint_object(box, info, amulet,          neck.x,  neck.y);
428 	} else
429 		paint_object(box, info, amulet,          neck.x,  neck.y);
430 	if (!actor->is_scabbard_used())
431 		paint_object(box, info, belt,            beltp.x, beltp.y);
432 	paint_arms(box, info);
433 	paint_object_arms(box, info, torso,           body.x,  body.y,    1, torso);
434 	paint_object(box, info, earrings,        headp.x, headp.y);
435 	paint_object(box, info, head,            headp.x, headp.y);
436 	if (actor->is_neck_used())
437 		paint_object(box, info, amulet,          body.x,  body.y,    0, cloak_clasp);
438 	else
439 		paint_object(box, info, cloak,           body.x,  body.y,    0, cloak_clasp);
440 	paint_object_arms(box, info, rfinger,         lhandp.x, lhandp.y,   0);
441 	if (actor->is_two_fingered()) {
442 		obj = container->get_readied(lfinger);
443 		const Paperdoll_item *item1;
444 		if (obj) {
445 			const Shape_info &inf = obj->get_info();
446 			item1 = inf.get_item_paperdoll(obj->get_framenum(), gloves);
447 		} else
448 			item1 = nullptr;
449 		if (!item1)
450 			paint_object_arms(box, info, lfinger,         rhandp.x, rhandp.y);
451 		else
452 			paint_object_arms(box, info, lfinger,         hands.x, hands.y,   0, gloves);
453 	} else
454 		paint_object_arms(box, info, lfinger,         rhandp.x, rhandp.y,   0);
455 	paint_object_arms(box, info, gloves,          hands.x, hands.y,   0);
456 	paint_object(box, info, lhand,           lhandp.x, lhandp.y);
457 	paint_object(box, info, quiver,          ahand.x, ahand.y,   2, -1);
458 	paint_object(box, info, rhand,           rhandp.x, rhandp.y);
459 
460 	// if debugging show usecode container
461 #ifdef SHOW_USECODE_CONTAINER
462 	paint_object(box, info, ucont,  20,      20);
463 #endif
464 
465 #ifdef SHOW_NONREADIED_OBJECTS
466 	Game_object *itm;
467 	Object_iterator iter(actor->get_objects());
468 	while ((itm = iter.get_next()) != nullptr)
469 		if (actor->find_readied(itm) == -1)
470 			itm->paint();
471 #endif
472 
473 
474 	// Paint buttons.
475 	if (heart_button) heart_button->paint();
476 	if (disk_button) disk_button->paint();
477 	if (combat_button) combat_button->paint();
478 	if (cstats_button) cstats_button->paint();
479 	if (halo_button) halo_button->paint();
480 	if (cmode_button) cmode_button->paint();
481 
482 	// Show weight.
483 	int max_weight = actor->get_max_weight();
484 	int weight = actor->get_weight() / 10;
485 	char text[20];
486 	if (gwin->failed_copy_protection())
487 		snprintf(text, 6, "Oink!");
488 	else
489 		snprintf(text, 20, "%d/%d", weight, max_weight);
490 	int twidth = sman->get_text_width(2, text);
491 	sman->paint_text(2, text, x + 84 - (twidth / 2), y + 114);
492 }
493 
Get_ammo_frame(Game_object * obj,Container_game_object * container,int & frame)494 static inline bool Get_ammo_frame(
495     Game_object *obj,
496     Container_game_object *container,
497     int &frame
498 ) {
499 	const Game_object *check = container->get_readied(lhand);
500 	if (check) {
501 		const Weapon_info *winf = check->get_info().get_weapon_info();
502 		// frame == 2 for ammo held in hand, 0 for ammo in quiver.
503 		if (!winf)
504 			return frame != 2;
505 		const Ammo_info *ainf = obj->get_info().get_ammo_info();
506 		int family = ainf ? ainf->get_family_shape() : obj->get_shapenum();
507 		bool infamily = winf->get_ammo_consumed() == family;
508 		if (frame == 2 && !infamily)
509 			return false;
510 		else if (!frame)
511 			frame++;
512 	} else if (frame == 2)  // No weapon means no ammo in hand.
513 		return false;
514 	return true;
515 }
516 
517 /*
518  *  Paint a generic object on screen
519  */
520 
paint_object(const TileRect & box,const Paperdoll_npc * info,int spot,int sx,int sy,int frame,int itemtype)521 void Paperdoll_gump::paint_object(
522     const TileRect &box,       // box
523     const Paperdoll_npc *info,        // info
524     int spot,           // belt
525     int sx, int sy,         // back2x, back2y
526     int frame,          // 0
527     int itemtype            // back2h_spot
528 ) {
529 	Game_object *obj = container->get_readied(spot);
530 	if (!obj) return;
531 
532 	int old_it = itemtype;
533 	if (itemtype == -1) itemtype = spot;
534 
535 	const Paperdoll_item *item = obj->get_info().get_item_paperdoll(obj->get_framenum(), itemtype);
536 	if (!item || item->get_paperdoll_baseframe() == -1 ||
537 	        item->get_paperdoll_shape() == -1) {
538 		if ((old_it != -1 && !item) || (spot == quiver && frame == 2)) return;
539 		//if (!obj->get_tx() && !obj->get_ty()) return;
540 
541 		set_to_spot(obj, spot);
542 
543 		int shnum = Shapeinfo_lookup::GetBlueShapeData(spot);
544 		ShapeID s(shnum, 0, SF_GUMPS_VGA);
545 
546 		s.paint_shape(box.x + coords_blue[spot].x,
547 		              box.y + coords_blue[spot].y);
548 		int ox = box.x + obj->get_tx();
549 		int oy = box.y + obj->get_ty();
550 		obj->paint_shape(ox, oy);
551 		if (cheat.is_selected(obj))
552 			// Outline selected obj.
553 			obj->ShapeID::paint_outline(ox, oy, HIT_PIXEL);
554 
555 		return;
556 	} else if (spot == quiver && !Get_ammo_frame(obj, container, frame))
557 		return;
558 
559 	int f = item->get_paperdoll_frame(frame);
560 	if (item->is_gender_based() &&
561 	        (!container->as_actor()->get_type_flag(Actor::tf_sex) &&
562 	         !info->is_npc_female()))
563 		f++;
564 
565 	ShapeID s(item->get_paperdoll_shape(), f, SF_PAPERDOL_VGA);
566 	s.paint_shape(box.x + sx, box.y + sy, item->is_translucent());
567 	if (cheat.is_selected(obj)) // Outline selected obj.
568 		s.paint_outline(box.x + sx, box.y + sy, HIT_PIXEL);
569 }
570 
571 /*
572  *  Paint with arms frame
573  */
paint_object_arms(const TileRect & box,const Paperdoll_npc * info,int spot,int sx,int sy,int start,int itemtype)574 void Paperdoll_gump::paint_object_arms(
575     const TileRect &box,
576     const Paperdoll_npc *info,
577     int spot,
578     int sx, int sy,
579     int start,
580     int itemtype
581 ) {
582 	paint_object(box, info, spot, sx, sy, start + get_arm_type(), itemtype);
583 }
584 
585 /*
586  *  Paint the body
587  */
paint_body(const TileRect & box,const Paperdoll_npc * info)588 void Paperdoll_gump::paint_body(
589     const TileRect &box,
590     const Paperdoll_npc *info
591 ) {
592 	ShapeID s(info->get_body_shape(), info->get_body_frame(), SF_PAPERDOL_VGA);
593 	s.paint_shape(box.x + body.x, box.y + body.y, info->is_translucent());
594 }
595 
596 /*
597  *  Paint the belt
598  */
paint_belt(const TileRect & box,const Paperdoll_npc * info)599 void Paperdoll_gump::paint_belt(
600     const TileRect &box,
601     const Paperdoll_npc *info
602 ) {
603 	ShapeID s(10, 0, SF_PAPERDOL_VGA);
604 	if (!container->as_actor()->get_type_flag(Actor::tf_sex) &&
605 	        !info->is_npc_female())
606 		s.set_frame(1);
607 	s.paint_shape(box.x + beltm.x, box.y + beltm.y, info->is_translucent());
608 }
609 
610 /*
611  *  Paint the head
612  */
paint_head(const TileRect & box,const Paperdoll_npc * info)613 void Paperdoll_gump::paint_head(
614     const TileRect &box,
615     const Paperdoll_npc *info
616 ) {
617 	const Game_object *obj = container->get_readied(head);
618 
619 	const Paperdoll_item *item = nullptr;
620 	if (obj)
621 		item = obj->get_info().get_item_paperdoll(
622 		           obj->get_framenum(), head);
623 
624 	int f;
625 	if (item && item->get_spot_frame())
626 		f = info->get_head_frame_helm();
627 	else
628 		f = info->get_head_frame();
629 
630 	ShapeID s(info->get_head_shape(), f, SF_PAPERDOL_VGA);
631 	s.paint_shape(box.x + headp.x, box.y + headp.y, info->is_translucent());
632 }
633 
634 /*
635  *  Paint the arms
636  */
paint_arms(const TileRect & box,const Paperdoll_npc * info)637 void Paperdoll_gump::paint_arms(
638     const TileRect &box,
639     const Paperdoll_npc *info
640 ) {
641 	int frnum = info->get_arms_frame(get_arm_type());
642 	ShapeID s(info->get_arms_shape(), frnum, SF_PAPERDOL_VGA);
643 	s.paint_shape(box.x + body.x, box.y + body.y, info->is_translucent());
644 }
645 
646 
647 /*
648  *  Gets which arm frame to use
649  */
650 
get_arm_type()651 int Paperdoll_gump::get_arm_type() {
652 	const Game_object *obj = container->get_readied(lhand);
653 	if (!obj)
654 		return 0;   // Nothing in hand; normal arms.
655 	const Shape_info &inf = obj->get_info();
656 	if (inf.get_ready_type() != both_hands)
657 		return 0;   // Only two-handed weapons change arms.
658 
659 	const Paperdoll_item *item = inf.get_item_paperdoll(obj->get_framenum(), lhand);
660 	return item ? item->get_spot_frame() : 0;
661 }
662 
663 
664 /*
665  *  Find object a screen point is on.
666  *
667  *  Output: Object found, or null.
668  */
669 
find_object(int mx,int my)670 Game_object *Paperdoll_gump::find_object(
671     int mx, int my          // Mouse pos. on screen.
672 ) {
673 
674 
675 	// Check Objects
676 	TileRect box = object_area;    // Paint objects inside.
677 	box.shift(x, y);        // Set box to screen location.
678 	mx -= box.x;
679 	my -= box.y;
680 
681 	// Get the information required about ourself
682 	const Actor *actor = container->as_actor();
683 	const Paperdoll_npc *info = actor->get_info().get_npc_paperdoll();
684 
685 	if (!info) {
686 		const Shape_info &inf = ShapeID::get_info(actor->get_sexed_coloured_shape());
687 		info = inf.get_npc_paperdoll();
688 	}
689 	if (!info) {
690 		const Shape_info &inf = ShapeID::get_info(actor->get_shape_real());
691 		info = inf.get_npc_paperdoll_safe(actor->get_type_flag(Actor::tf_sex));
692 	}
693 
694 	Position shield;
695 	Position back2;
696 	Position back;
697 	Position neck;
698 	Position beltp;
699 
700 	if (actor->get_type_flag(Actor::tf_sex) || info->is_npc_female()) {
701 		shield = shieldf;
702 		back2  = back2f;
703 		back   = backf;
704 		neck   = neckf;
705 		beltp  = beltf;
706 	} else {
707 		shield = shieldm;
708 		back2  = back2m;
709 		back   = backm;
710 		neck   = neckm;
711 		beltp  = beltm;
712 	}
713 
714 	Game_object *obj;
715 
716 	// if debugging show usecode container
717 #ifdef SHOW_USECODE_CONTAINER
718 	if ((obj = check_object(mx, my, info, ucont,  20,      20)) != nullptr)
719 		return obj;
720 #endif
721 
722 	// Must be done in this order (reverse of rendering)
723 	if ((obj = check_object(mx, my, info, rhand,       rhandp.x,  rhandp.y)))
724 		return obj;
725 	if ((obj = check_object(mx, my, info, quiver,      ahand.x,  ahand.y, 2, -1)))
726 		return obj;
727 	if ((obj = check_object(mx, my, info, lhand,       lhandp.x,  lhandp.y)))
728 		return obj;
729 	if ((obj = check_object_arms(mx, my, info, gloves,      hands.x, hands.y, 0)))
730 		return obj;
731 	if (actor->is_two_fingered()) {
732 		obj = container->get_readied(lfinger);
733 		const Paperdoll_item *item1;
734 		if (obj) {
735 			const Shape_info &inf = obj->get_info();
736 			item1 = inf.get_item_paperdoll(obj->get_framenum(), gloves);
737 		} else
738 			item1 = nullptr;
739 		if (!item1 && (obj = check_object_arms(mx, my, info, lfinger,     rhandp.x, rhandp.y,   0)))
740 			return obj;
741 		else if ((obj = check_object_arms(mx, my, info, lfinger,     hands.x, hands.y, 0, gloves)))
742 			return obj;
743 	} else if ((obj = check_object_arms(mx, my, info, lfinger,     rhandp.x,  rhandp.y,  0)))
744 		return obj;
745 
746 	if ((obj = check_object_arms(mx, my, info, rfinger,     lhandp.x,  lhandp.y,  0)))
747 		return obj;
748 	if (actor->is_neck_used()) {
749 		if ((obj = check_object(mx, my, info, amulet,      body.x,   body.y,   0, cloak_clasp)))
750 			return obj;
751 	} else {
752 		if ((obj = check_object(mx, my, info, cloak,       body.x,   body.y,   0, cloak_clasp)))
753 			return obj;
754 	}
755 	if ((obj = check_object(mx, my, info, head,        headp.x,  headp.y)))
756 		return obj;
757 	if ((obj = check_object(mx, my, info, earrings,    headp.x,  headp.y)))
758 		return obj;
759 	if ((obj = check_object_arms(mx, my, info, torso,       body.x,   body.y,  1, torso)))
760 		return obj;
761 	if (check_arms(mx, my, info))
762 		return nullptr;
763 	if (!actor->is_scabbard_used())
764 		if ((obj = check_object(mx, my, info, belt,        beltp.x,  beltp.y)))
765 			return obj;
766 	if (actor->is_neck_used()) {
767 		obj = container->get_readied(amulet);
768 		const Paperdoll_item *item1;
769 		const Paperdoll_item *item2;
770 		if (obj) {
771 			const Shape_info &inf = obj->get_info();
772 			item1 = inf.get_item_paperdoll(obj->get_framenum(), cloak);
773 			item2 = inf.get_item_paperdoll(obj->get_framenum(), cloak_clasp);
774 		} else
775 			item1 = item2 = nullptr;
776 		if (!item1 && !item2 && (obj = check_object(mx, my, info, amulet,      neck.x,   neck.y)))
777 			return obj;
778 	} else if ((obj = check_object(mx, my, info, amulet,      neck.x,   neck.y)))
779 		return obj;
780 	if (check_head(mx, my, info))
781 		return nullptr;
782 	if (check_belt(mx, my, info))
783 		return nullptr;
784 	if ((obj = check_object(mx, my, info, torso,       body.x,   body.y)))
785 		return obj;
786 	if ((obj = check_object(mx, my, info, quiver,      ammo.x,   ammo.y,   0, -1)))
787 		return obj;
788 	if ((obj = check_object(mx, my, info, feet,        feetp.x,  feetp.y)))
789 		return obj;
790 	if ((obj = check_object(mx, my, info, legs,        legsp.x,  legsp.y)))
791 		return obj;
792 	if (check_body(mx, my, info))
793 		return nullptr;
794 	if (actor->is_neck_used()) {
795 		if ((obj = check_object(mx, my, info, amulet,      body.x,   body.y,   0, cloak)))
796 			return obj;
797 	} else {
798 		if ((obj = check_object(mx, my, info, cloak,       body.x,   body.y)))
799 			return obj;
800 	}
801 	if ((obj = check_object(mx, my, info, backpack,    back.x,   back.y)))
802 		return obj;
803 	if (actor->is_scabbard_used()) {
804 		if ((obj = check_object(mx, my, info, belt,        back2.x,  back2.y,  0, back_2h)))
805 			return obj;
806 		if ((obj = check_object(mx, my, info, belt,        shield.x, shield.y, 0, back_shield)))
807 			return obj;
808 	} else {
809 		if ((obj = check_object(mx, my, info, back_2h,     back2.x,  back2.y)))
810 			return obj;
811 		if ((obj = check_object(mx, my, info, back_shield, shield.x, shield.y)))
812 			return obj;
813 	}
814 	return nullptr;
815 }
816 
817 /*
818  *  Checks for a generic object on screen
819  */
820 
check_object(int mx,int my,const Paperdoll_npc * info,int spot,int sx,int sy,int frame,int itemtype)821 Game_object *Paperdoll_gump::check_object(
822     int mx, int my,
823     const Paperdoll_npc *info,
824     int spot,
825     int sx, int sy,
826     int frame,
827     int itemtype
828 ) {
829 	Game_object *obj = container->get_readied(spot);
830 	if (!obj) return nullptr;
831 
832 	int old_it = itemtype;
833 	if (itemtype == -1) itemtype = spot;
834 
835 	const Paperdoll_item *item = obj->get_info().get_item_paperdoll(obj->get_framenum(), itemtype);
836 	if (!item || item->get_paperdoll_baseframe() == -1 ||
837 	        item->get_paperdoll_shape() == -1) {
838 		if ((old_it != -1 && !item) || (spot == quiver && frame == 2)) return nullptr;
839 
840 		if (!obj->get_tx() && !obj->get_ty()) set_to_spot(obj, spot);
841 
842 		if (check_shape(mx - obj->get_tx(), my - obj->get_ty(),
843 		                obj->get_shapenum(), obj->get_framenum(), obj->get_shapefile())) {
844 			return obj;
845 		}
846 
847 		return nullptr;
848 	} else if (spot == quiver && !Get_ammo_frame(obj, container, frame))
849 		return nullptr;
850 
851 
852 	int f = item->get_paperdoll_frame(frame);
853 	if (item->is_gender_based() && (!info->is_npc_female()
854 	                                && !container->as_actor()->get_type_flag(Actor::tf_sex))) f++;
855 
856 	if (check_shape(mx - sx, my - sy, item->get_paperdoll_shape(), f, SF_PAPERDOL_VGA)) {
857 		Shape_frame *shape = obj->get_shape();
858 		int w = shape->get_width();
859 		int h = shape->get_height();
860 		// Set object's position.
861 		obj->set_shape_pos(mx + shape->get_xleft() - w / 2,
862 		                   my + shape->get_yabove() - h / 2);
863 
864 		return obj;
865 	}
866 
867 	return nullptr;
868 }
869 
870 /*
871  *  Checks for object with arms frame
872  */
check_object_arms(int mx,int my,const Paperdoll_npc * info,int spot,int sx,int sy,int start,int itemtype)873 Game_object *Paperdoll_gump::check_object_arms(
874     int mx, int my,
875     const Paperdoll_npc *info,
876     int spot,
877     int sx, int sy,
878     int start,
879     int itemtype
880 ) {
881 	return check_object(mx, my, info, spot, sx, sy, start + get_arm_type(), itemtype);
882 }
883 
884 /*
885  *  Checks for the body
886  */
check_body(int mx,int my,const Paperdoll_npc * info)887 bool Paperdoll_gump::check_body(
888     int mx, int my,
889     const Paperdoll_npc *info
890 ) {
891 	return check_shape(mx - body.x, my - body.y, info->get_body_shape(),
892 	                   info->get_body_frame(), SF_PAPERDOL_VGA);
893 }
894 
895 /*
896  *  Checks for the belt
897  */
check_belt(int mx,int my,const Paperdoll_npc * info)898 bool Paperdoll_gump::check_belt(
899     int mx, int my,
900     const Paperdoll_npc *info
901 ) {
902 	if (info->is_npc_female() || container->as_actor()->get_type_flag(Actor::tf_sex))
903 		return check_shape(mx - beltf.x, my - beltf.y, 10, 0, SF_PAPERDOL_VGA);
904 	else
905 		return check_shape(mx - beltm.x, my - beltm.y, 10, 1, SF_PAPERDOL_VGA);
906 
907 	return false;
908 }
909 
910 /*
911  *  Checks for the head
912  */
check_head(int mx,int my,const Paperdoll_npc * info)913 bool Paperdoll_gump::check_head(
914     int mx, int my,
915     const Paperdoll_npc *info
916 ) {
917 	const Game_object *obj = container->get_readied(head);
918 
919 	const Paperdoll_item *item = nullptr;
920 	if (obj)
921 		item = obj->get_info().get_item_paperdoll(
922 		           obj->get_framenum(), head);
923 
924 	int f;
925 	if (item && item->get_spot_frame())
926 		f = info->get_head_frame_helm();
927 	else
928 		f = info->get_head_frame();
929 
930 	return check_shape(mx - headp.x, my - headp.y,
931 	                   info->get_head_shape(), f, SF_PAPERDOL_VGA);
932 }
933 
934 /*
935  *  Checks for the arms
936  */
check_arms(int mx,int my,const Paperdoll_npc * info)937 bool Paperdoll_gump::check_arms(
938     int mx, int my,
939     const Paperdoll_npc *info
940 ) {
941 	int frnum = info->get_arms_frame(get_arm_type());
942 	return check_shape(mx - body.x, my - body.y,
943 	                   info->get_arms_shape(), frnum, SF_PAPERDOL_VGA);
944 }
945 
946 /*
947  *  Generic Shaper checking
948  */
check_shape(int px,int py,int shape,int frame,ShapeFile file)949 bool Paperdoll_gump::check_shape(
950     int px, int py,
951     int shape, int frame,
952     ShapeFile file
953 ) {
954 	ShapeID sid(shape, frame, file);
955 	Shape_frame *s = sid.get_shape();
956 
957 	// If no shape, return
958 	if (!s) return false;
959 
960 	TileRect r = gwin->get_shape_rect(s, 0, 0);
961 
962 	// If point not in rectangle, return
963 	if (!r.has_point(px, py)) return false;
964 
965 	// If point not in shape, return
966 	if (!s->has_point(px, py)) return false;
967 
968 	return true;
969 }
970 
find_actor(int mx,int my)971 Container_game_object *Paperdoll_gump::find_actor(int mx, int my) {
972 	ignore_unused_variable_warning(mx, my);
973 	return container;
974 }
975 
get_npc_paperdoll_safe(bool sex) const976 const Paperdoll_npc *Shape_info::get_npc_paperdoll_safe(bool sex) const {
977 	if (npcpaperdoll)
978 		return npcpaperdoll;
979 	int shape = sex ? Shapeinfo_lookup::GetFemaleAvShape() :
980 	            Shapeinfo_lookup::GetMaleAvShape();
981 	const Shape_info &inf = ShapeID::get_info(shape);
982 	return inf.get_npc_paperdoll();
983 }
984