1 /*
2 Copyright (C) 2000 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 "Actor_gump.h"
24 #include "actors.h"
25 #include "array_size.h"
26 #include "gamewin.h"
27 #include "misc_buttons.h"
28 #include "ignore_unused_variable_warning.h"
29 
30 #include <cstdio>
31 
32 using std::size_t;
33 
34 #define TWO_HANDED_BROWN_SHAPE  48
35 #define TWO_HANDED_BROWN_FRAME  0
36 #define TWO_FINGER_BROWN_SHAPE  48
37 #define TWO_FINGER_BROWN_FRAME  1
38 
39 /*
40  *  Statics:
41  */
42 
43 Actor_gump::Position Actor_gump::disk   = {124, 115};
44 Actor_gump::Position Actor_gump::heart  = {124, 132};
45 Actor_gump::Position Actor_gump::combat = {52, 100};
46 Actor_gump::Position Actor_gump::halo   = {47, 110};
47 Actor_gump::Position Actor_gump::cmode  = {48, 132};
48 Actor_gump::Position Actor_gump::coords[12] = {
49 	{114, 10},    /* head */      {115, 24},    /* back */
50 	{115, 37},    /* belt */      {115, 55},    /* lhand */
51 	{115, 71},    /* lfinger */   {114, 85},    /* legs */
52 	{76, 98},     /* feet */      {35, 70},     /* rfinger */
53 	{37, 56},     /* rhand */     {37, 37},     /* torso */
54 	{37, 24},     /* neck */      {37, 11}      /* ammo */
55 };
56 
57 /*
58  *  Find the index of the closest 'spot' to a mouse point.
59  *
60  *  Output: Index, or -1 if unsuccessful.
61  */
62 
find_closest(int mx,int my,int only_empty)63 int Actor_gump::find_closest(
64     int mx, int my,         // Mouse point in window.
65     int only_empty          // Only allow empty spots.
66 ) {
67 	mx -= x;
68 	my -= y;       // Get point rel. to us.
69 	long closest_squared = 1000000; // Best distance squared.
70 	int closest = -1;       // Best index.
71 	for (size_t i = 0; i < array_size(coords); i++) {
72 		int dx = mx - coords[i].x;
73 		int dy = my - coords[i].y;
74 		long dsquared = dx * dx + dy * dy;
75 		// Better than prev.?
76 		if (dsquared < closest_squared && (!only_empty ||
77 		                                   !container->get_readied(i))) {
78 			closest_squared = dsquared;
79 			closest = i;
80 		}
81 	}
82 	return closest;
83 }
84 
85 /*
86  *  Create the gump display for an actor.
87  */
88 
Actor_gump(Container_game_object * cont,int initx,int inity,int shnum)89 Actor_gump::Actor_gump(
90     Container_game_object *cont,    // Container it represents.  MUST
91     //   be an Actor.
92     int initx, int inity,       // Coords. on screen.
93     int shnum           // Shape #.
94 ) : Gump(cont, initx, inity, shnum) {
95 	set_object_area(TileRect(26, 0, 104, 132), 6, 136);
96 	Actor *npc = cont->as_actor();
97 	add_elem(new Heart_button(this, heart.x, heart.y));
98 	if (npc->get_npc_num() == 0) {
99 		add_elem(new Disk_button(this, disk.x, disk.y));
100 		add_elem(new Combat_button(this, combat.x, combat.y));
101 	}
102 	add_elem(new Halo_button(this, halo.x, halo.y, npc));
103 	add_elem(new Combat_mode_button(this, cmode.x, cmode.y, npc));
104 
105 	for (size_t i = 0; i < array_size(coords); i++) {
106 		// Set object coords.
107 		Game_object *obj = container->get_readied(i);
108 		if (obj)
109 			set_to_spot(obj, i);
110 	}
111 }
112 
113 /*
114  *  Add an object.
115  *
116  *  Output: false if cannot add it.
117  */
118 
add(Game_object * obj,int mx,int my,int sx,int sy,bool dont_check,bool combine)119 bool Actor_gump::add(
120     Game_object *obj,
121     int mx, int my,         // Screen location of mouse.
122     int sx, int sy,         // Screen location of obj's hotspot.
123     bool dont_check,        // Skip volume check.
124     bool combine            // True to try to combine obj.  MAY
125     //   cause obj to be deleted.
126 )
127 {
128 	ignore_unused_variable_warning(sx, sy);
129 	Game_object *cont = find_object(mx, my);
130 
131 	if (cont && cont->add(obj, false, combine))
132 		return true;
133 
134 	int index = find_closest(mx, my, 1);
135 
136 	if (index != -1 && container->add_readied(obj, index))
137 		return true;
138 
139 	if (container->add(obj, dont_check, combine))
140 		return true;
141 
142 	return false;
143 }
144 
145 /*
146  *  Set object's coords. to given spot.
147  */
148 
set_to_spot(Game_object * obj,int index)149 void Actor_gump::set_to_spot(
150     Game_object *obj,
151     int index           // Spot index.
152 ) {
153 	// Get shape info.
154 	Shape_frame *shape = obj->get_shape();
155 	if (!shape)
156 		return;         // Not much we can do.
157 	int w = shape->get_width();
158 	int h = shape->get_height();
159 	// Set object's position.
160 	obj->set_shape_pos(
161 	    coords[index].x + shape->get_xleft() - w / 2 - object_area.x,
162 	    coords[index].y + shape->get_yabove() - h / 2 - object_area.y);
163 	// Shift if necessary.
164 	int x0 = obj->get_tx() - shape->get_xleft();
165 	int y0 = obj->get_ty() - shape->get_yabove();
166 	int newcx = obj->get_tx();
167 	int newcy = obj->get_ty();
168 	if (x0 < 0)
169 		newcx -= x0;
170 	if (y0 < 0)
171 		newcy -= y0;
172 	int x1 = x0 + w;
173 	int y1 = y0 + h;
174 	if (x1 > object_area.w)
175 		newcx -= x1 - object_area.w;
176 	if (y1 > object_area.h)
177 		newcy -= y1 - object_area.h;
178 	obj->set_shape_pos(newcx, newcy);
179 }
180 
181 /*
182  *  Paint on screen.
183  */
184 
paint()185 void Actor_gump::paint(
186 ) {
187 	// Watch for any newly added objs.
188 	for (size_t i = 0; i < array_size(coords); i++) {
189 		// Set object coords.
190 		Game_object *obj = container->get_readied(i);
191 		if (obj)//&& !obj->get_tx() && !obj->get_ty())
192 			set_to_spot(obj, i);
193 	}
194 
195 	Gump::paint();          // Paint gump & objects.
196 
197 	// Paint over blue lines for 2 handed
198 	Actor *actor = container->as_actor();
199 	if (actor) {
200 		if (actor->is_two_fingered()) {
201 			int sx = x + 36;
202 			int // Note this is the right finger slot shifted slightly
203 			    sy = y + 70;
204 			ShapeID sid(TWO_FINGER_BROWN_SHAPE, TWO_FINGER_BROWN_FRAME, SF_GUMPS_VGA);
205 			sid.paint_shape(sx, sy);
206 		}
207 		if (actor->is_two_handed()) {
208 			int sx = x + 36;
209 			int // Note this is the right hand slot shifted slightly
210 			    sy = y + 55;
211 			ShapeID sid(TWO_HANDED_BROWN_SHAPE, TWO_HANDED_BROWN_FRAME, SF_GUMPS_VGA);
212 			sid.paint_shape(sx, sy);
213 		}
214 	}
215 	// Show weight.
216 	int max_weight = container->get_max_weight();
217 	int weight = container->get_weight() / 10;
218 	char text[20];
219 	snprintf(text, 20, "%d/%d", weight, max_weight);
220 	int twidth = sman->get_text_width(2, text);
221 	const int boxw = 102;
222 	sman->paint_text(2, text, x + 28 + (boxw - twidth) / 2, y + 120);
223 }
224 
find_actor(int mx,int my)225 Container_game_object *Actor_gump::find_actor(int mx, int my) {
226 	ignore_unused_variable_warning(mx, my);
227 	return container;
228 }
229