1 #include <stdlib.h>
2 #include <string.h>
3 #include <math.h>
4 #include <limits.h>
5 #include "items.h"
6 #include "asc.h"
7 #include "colors.h"
8 #include "client_serv.h"
9 #include "cursors.h"
10 #include "context_menu.h"
11 #include "text.h"
12 #include "elconfig.h"
13 #include "elwindows.h"
14 #include "errors.h"
15 #include "gamewin.h"
16 #include "gl_init.h"
17 #include "hud.h"
18 #include "init.h"
19 #include "interface.h"
20 #include "item_info.h"
21 #include "item_lists.h"
22 #include "manufacture.h"
23 #include "misc.h"
24 #include "multiplayer.h"
25 #include "platform.h"
26 #include "sound.h"
27 #include "storage.h"
28 #include "textures.h"
29 #include "translate.h"
30 #include "counters.h"
31 #include "widgets.h"
32 #include "spells.h"
33 
34 item item_list[ITEM_NUM_ITEMS];
35 item_extra item_list_extra[ITEM_NUM_ITEMS];
36 
37 struct quantities quantities = {
38 	0,
39 	{
40 		{1, 1, "1"},
41 		{5, 1, "5"},
42 		{10, 2, "10"},
43 		{20, 2, "20"},
44 		{50, 2, "50"},
45 		{100, 3, "100"},
46 		{1, 1, "1"}, // item list preview quantity - not editable or displayed with others
47 	}
48 };
49 int edit_quantity=-1;
50 
51 static int item_action_mode = ACTION_WALK;
52 
53 int item_dragged=-1;
54 int item_quantity=1;
55 int use_item=-1;
56 int item_uid_enabled = 0;
57 const Uint16 unset_item_uid = (Uint16)-1;
58 int items_text[MAX_ITEMS_TEXTURES];
59 int independant_inventory_action_modes = 0;
60 
61 /* title menu options */
62 int allow_equip_swap=0;
63 int use_small_items_window = 0;
64 int manual_size_items_window = 0;
65 int items_mod_click_any_cursor = 1;
66 int items_disable_text_block = 0;
67 int items_buttons_on_left = 0;
68 int items_equip_grid_on_left = 0;
69 
70 /* button options */
71 int items_mix_but_all = 0;
72 int items_stoall_nofirstrow = 0;
73 int items_stoall_nolastrow = 0;
74 int items_dropall_nofirstrow = 0;
75 int items_dropall_nolastrow = 0;
76 int items_list_on_left = 0;
77 
78 /* use standard way to size and position the window components */
79 struct win_component {int pos_x; int pos_y; int len_x; int len_y; int mouse_over; int rows; int cols; int width; int height; };
80 static struct win_component items_grid = {0,0,0,0,-1,0,0,0,0};
81 static struct win_component equip_grid = {0,0,0,0,-1,0,0,0,0};
82 static struct win_component buttons_grid = {0,0,0,0,-1,0,0,0,0};
83 static struct win_component text_arrow = {0,0,0,0,-1,0,0,0,0};
84 static struct win_component unequip_arrow = {0,0,0,0,-1,0,0,0,0};
85 static struct win_component message_box = {0,0,0,0,-1,0,0,0,0};
86 static struct win_component labels_box = {0,0,0,0,-1,0,0,0,0};
87 static struct win_component quantity_grid = {0,0,0,0,-1,0,0,0,0};
88 
89 static char items_string[350] = {0};
90 static size_t last_items_string_id = 0;
91 static const char *item_help_str = NULL;
92 static const char *item_desc_str = NULL;
93 static float button_text_zoom = 0.0f;
94 
95 #define NUMBUT 5
96 static size_t buttons_cm_id[NUMBUT] = {CM_INIT_VALUE, CM_INIT_VALUE, CM_INIT_VALUE, CM_INIT_VALUE, CM_INIT_VALUE};
97 enum { BUT_STORE, BUT_GET, BUT_DROP, BUT_MIX, BUT_ITEM_LIST };
98 
99 /* variables to enable enhanced equipment swapping */
100 static struct { int last_dest; int move_to; int move_from; size_t string_id; Uint32 start_time; } swap_complete = {-1, -1, -1, 0, 0};
101 
102 static int show_items_handler(window_info * win);
103 static void drop_all_handler(void);
104 
105 // Limit external setting of the action mode: Called due to an action keypress or action icon in the icon window.
set_items_action_mode(int new_mode)106 void set_items_action_mode(int new_mode)
107 {
108 	// Only change the action mode if is one used by the window.
109 	if (!independant_inventory_action_modes && ((new_mode == ACTION_WALK) || (new_mode == ACTION_LOOK) || (new_mode == ACTION_USE) || (new_mode == ACTION_USE_WITEM)))
110 		item_action_mode = new_mode;
111 }
112 
set_shown_string(char colour_code,const char * the_text)113 void set_shown_string(char colour_code, const char *the_text)
114 {
115 	if (strlen(the_text) == 0)
116 	{
117 		inventory_item_string[0] = '\0';
118 		inventory_item_string_id++;
119 		return;
120 	}
121 	inventory_item_string[0] = to_color_char(colour_code);
122 	safe_strncpy2(inventory_item_string+1, the_text, sizeof(inventory_item_string)-2, strlen(the_text));
123 	inventory_item_string[sizeof(inventory_item_string)-1] = 0;
124 	inventory_item_string_id++;
125 }
126 
127 
128 /* return index of button or -1 if mouse not over a button */
over_button(window_info * win,int mx,int my)129 static int over_button(window_info *win, int mx, int my)
130 {
131 	if ((mx > buttons_grid.pos_x) && (mx < buttons_grid.pos_x + buttons_grid.len_x) &&
132 		(my > buttons_grid.pos_y) && (my < buttons_grid.pos_y + buttons_grid.len_y)) {
133 		return (my - buttons_grid.pos_y) / buttons_grid.height;
134 	}
135 	return -1;
136 }
137 
138 
gray_out(int x_start,int y_start,int gridsize)139 void gray_out(int x_start, int y_start, int gridsize){
140 
141 	glDisable(GL_TEXTURE_2D);
142 	glEnable(GL_BLEND);
143 	//glBlendFunc(GL_DST_COLOR, GL_ONE); //this brightens up
144 	glBlendFunc(GL_ZERO, GL_SRC_COLOR); //this brightens down
145 	glColor3f(0.4f, 0.2f, 0.2f);
146 	glBegin(GL_QUADS);
147 		glVertex3i(x_start,y_start,0);
148 		glVertex3i(x_start+gridsize,y_start,0);
149 		glVertex3i(x_start+gridsize,y_start+gridsize,0);
150 		glVertex3i(x_start,y_start+gridsize,0);
151 	glEnd();
152 	glDisable(GL_BLEND);
153 	glEnable(GL_TEXTURE_2D);
154 	glColor3f(1.0f, 1.0f, 1.0f);
155 }
156 
157 
rendergrid(int columns,int rows,int left,int top,int width,int height)158 void rendergrid(int columns, int rows, int left, int top, int width, int height)
159 {
160 	int x, y;
161 	int temp;
162 
163 	glBegin(GL_LINES);
164 
165 	for(y=0; y<=rows; y++){
166 		temp = top + y * height;
167 		glVertex2i(left, temp);
168 		glVertex2i(left + width*columns, temp);
169 	}
170 
171 	for(x=0; x<columns+1; x++){
172 		temp = left + x * width;
173 		glVertex2i(temp, top);
174 		glVertex2i(temp, top + height*rows);
175 	}
176 
177 	glEnd();
178 }
179 
get_mouse_pos_in_grid(int mx,int my,int columns,int rows,int left,int top,int width,int height)180 int get_mouse_pos_in_grid(int mx, int my, int columns, int rows, int left, int top, int width, int height)
181 {
182 	int x, y, i;
183 
184 	mx -= left;
185 	my -= top;
186 
187 	i = 0;
188 	for (y = 0; y < rows; y++)
189 	{
190 		for (x = 0; x < columns; x++, i++)
191 		{
192 			if (mx >= x*width && mx <= (x+1)*width && my >= y*height && my <= (y+1)*height)
193 				return i;
194 		}
195 	}
196 
197 	return -1;
198 }
199 
reset_quantity(int pos)200 void reset_quantity (int pos)
201 {
202 	int val;
203 
204 	switch(pos)
205 	{
206 		case 0:
207 			val = 1;
208 			break;
209 		case 1:
210 			val = 5;
211 			break;
212 		case 2:
213 			val = 10;
214 			break;
215 		case 3:
216 			val = 20;
217 			break;
218 		case 4:
219 			val = 50;
220 			break;
221 		case 5:
222 			val = 100;
223 			break;
224 		default:
225 			LOG_ERROR ("Trying to reset invalid element of quantities, pos = %d", pos);
226 			return;
227 	}
228 
229 	safe_snprintf (quantities.quantity[pos].str, sizeof(quantities.quantity[pos].str), "%d", val);
230 	quantities.quantity[pos].len = strlen (quantities.quantity[pos].str);
231 	quantities.quantity[pos].val = val;
232 }
233 
get_item_uv(const Uint32 item,float * u_start,float * v_start,float * u_end,float * v_end)234 void get_item_uv(const Uint32 item, float* u_start, float* v_start,
235 	float* u_end, float* v_end)
236 {
237 	*u_start = (50.0f/256.0f) * (item % 5) + 0.5f / 256.0f;
238 	*u_end = *u_start + (50.0f/256.0f);
239 	*v_start = (50.0f/256.0f) * (item / 5) + 0.5f / 256.0f;
240 	*v_end = *v_start + (50.0f/256.0f);
241 }
242 
drag_item(int item,int storage,int mini)243 void drag_item(int item, int storage, int mini)
244 {
245 	float u_start,v_start,u_end,v_end;
246 	int cur_item,this_texture;
247 	int cur_item_img;
248 	int my_items_grid_size = (int)(0.5 + get_global_scale() * ((use_small_items_window) ?33: 50));
249 	int offset = (mini) ? my_items_grid_size/3 : my_items_grid_size/2;
250 
251 	int quantity=item_quantity;
252 
253 	if(storage) {
254 		if (item < 0 || item >= STORAGE_ITEMS_SIZE)
255 			// oops
256 			return;
257 
258 		cur_item=storage_items[item].image_id;
259 		if(!storage_items[item].quantity) {
260 			use_item = storage_item_dragged=-1;
261 			return;
262 		}
263 		if (quantity > storage_items[item].quantity)
264 			quantity = storage_items[item].quantity;
265 	} else {
266 		if (item < 0 || item >= ITEM_NUM_ITEMS)
267 			// oops
268 			return;
269 
270 		cur_item=item_list[item].image_id;
271 		if(!item_list[item].quantity) {
272 			use_item = item_dragged=-1;
273 			return;
274 		}
275 		if (item >= ITEM_WEAR_START) {
276 			quantity = -1;
277 		} else if (item_list[item].is_stackable) {
278 			if(quantity > item_list[item].quantity)
279 				quantity = item_list[item].quantity;
280 		// The quantity for non-stackable items is misleading so don't show it unless we can reliably count how many we have.
281 		} else if (item_list[item].id == unset_item_uid) {
282 			quantity = -1;
283 		} else {
284 			size_t i;
285 			int count = 0;
286 			for (i = 0; i < ITEM_WEAR_START; i++)
287 				if((item_list[i].quantity > 0) && (item_list[item].image_id == item_list[i].image_id) && (item_list[item].id == item_list[i].id))
288 					count++;
289 			if (quantity > count)
290 				quantity = count;
291 		}
292 	}
293 
294 	cur_item_img=cur_item%25;
295 	get_item_uv(cur_item_img, &u_start, &v_start, &u_end, &v_end);
296 
297 	//get the texture this item belongs to
298 	this_texture=get_items_texture(cur_item/25);
299 
300 	bind_texture(this_texture);
301 	glBegin(GL_QUADS);
302 	draw_2d_thing(u_start, v_start, u_end, v_end, mouse_x - offset, mouse_y - offset, mouse_x + offset, mouse_y + offset);
303 	glEnd();
304 
305 	if (!mini && quantity != -1)
306 	{
307 		int text_height = get_line_height(UI_FONT, get_global_scale() * DEFAULT_SMALL_RATIO);
308 		unsigned char str[20];
309 		safe_snprintf((char*)str, sizeof(str), "%d", quantity);
310 		draw_string_small_zoomed_centered(mouse_x, mouse_y - offset - text_height,
311 			str, 1, get_global_scale());
312 	}
313 }
314 
get_your_items(const Uint8 * data)315 void get_your_items (const Uint8 *data)
316 {
317 	int i,total_items,pos,len;
318 	Uint8 flags;
319 
320 	if (item_uid_enabled)
321 		len=10;
322 	else
323 		len=8;
324 
325 	//data[0] -> num_items
326 	//data[1] -> image_id
327 	//data[3] -> quantity
328 	//data[7] -> pos
329 	//data[8] -> flags
330 	//data[9] -> id
331 
332 
333 	total_items=data[0];
334 
335 	//clear the items first
336 	for(i=0;i<ITEM_NUM_ITEMS;i++){
337 		item_list[i].quantity=0;
338 		item_list_extra[i].slot_busy_start = 0;
339 	}
340 
341 	for(i=0;i<total_items;i++){
342 		pos=data[i*len+1+6];
343 		// try not to wipe out cooldown information if no real change
344 		if(item_list[pos].image_id != SDL_SwapLE16(*((Uint16 *)(data+i*len+1))) ){
345 			item_list[pos].cooldown_time = 0;
346 			item_list[pos].cooldown_rate = 1;
347 		}
348 		item_list[pos].image_id=SDL_SwapLE16(*((Uint16 *)(data+i*len+1)));
349 		item_list[pos].quantity=SDL_SwapLE32(*((Uint32 *)(data+i*len+1+2)));
350 		item_list[pos].pos=pos;
351 #ifdef NEW_SOUND
352 		item_list[pos].action = ITEM_NO_ACTION;
353 		item_list[pos].action_time = 0;
354 #endif // NEW_SOUND
355 		flags=data[i*len+1+7];
356 		if (item_uid_enabled)
357 			item_list[pos].id=SDL_SwapLE16(*((Uint16 *)(data+i*len+1+8)));
358 		else
359 			item_list[pos].id=unset_item_uid;
360 		item_list[pos].is_resource=((flags&ITEM_RESOURCE)>0);
361 		item_list[pos].is_reagent=((flags&ITEM_REAGENT)>0);
362 		item_list[pos].use_with_inventory=((flags&ITEM_INVENTORY_USABLE)>0);
363 		item_list[pos].is_stackable=((flags&ITEM_STACKABLE)>0);
364 	}
365 
366 	build_manufacture_list();
367 	check_castability();
368 }
369 
370 #ifdef NEW_SOUND
check_for_item_sound(int pos)371 void check_for_item_sound(int pos)
372 {
373 	int i, snd = -1;
374 
375 #ifdef _EXTRA_SOUND_DEBUG
376 //	printf("Used item: %d, Image ID: %d, Action: %d\n", pos, item_list[pos].image_id, item_list[pos].action);
377 #endif // _EXTRA_SOUND_DEBUG
378 	if (item_list[pos].action != ITEM_NO_ACTION)
379 	{
380 		// Play the sound that goes with this action
381 		switch (item_list[pos].action)
382 		{
383 			case USE_INVENTORY_ITEM:
384 				snd = get_index_for_inv_use_item_sound(item_list[pos].image_id);
385 				break;
386 			case ITEM_ON_ITEM:
387 				// Find the second item (being used with)
388 				for (i = 0; i < ITEM_NUM_ITEMS; i++)
389 				{
390 					if (i != pos && item_list[i].action == ITEM_ON_ITEM)
391 					{
392 						snd = get_index_for_inv_usewith_item_sound(item_list[pos].image_id, item_list[i].action);
393 						break;
394 					}
395 				}
396 				break;
397 		}
398 		if (snd > -1)
399 			add_sound_object(snd, 0, 0, 1);
400 		// Reset the action
401 		item_list[pos].action = ITEM_NO_ACTION;
402 		item_list[pos].action_time = 0;
403 	}
404 }
405 
update_item_sound(int interval)406 void update_item_sound(int interval)
407 {
408 	int i;
409 	// Iterate through the list of items, checking for out of date item actions (> 2 sec old)
410 	for (i = 0; i < ITEM_NUM_ITEMS; i++)
411 	{
412 		if (item_list[i].action != ITEM_NO_ACTION)
413 		{
414 			item_list[i].action_time += interval;
415 			if (item_list[i].action_time >= 2000)
416 			{
417 				// Item action state is out of date so reset it
418 				item_list[i].action = ITEM_NO_ACTION;
419 				item_list[i].action_time = 0;
420 			}
421 		}
422 	}
423 }
424 #endif // NEW_SOUND
425 
426 static int used_item_counter_initialised = 0;
427 static int used_item_counter_queue[ITEM_NUM_ITEMS];
428 static Uint32 used_item_counter_queue_time[ITEM_NUM_ITEMS];
429 
used_item_counter_init(void)430 void used_item_counter_init(void)
431 {
432 	size_t i;
433 	for (i=0; i<ITEM_NUM_ITEMS; i++)
434 	{
435 		used_item_counter_queue[i] = 0;
436 		used_item_counter_queue_time[i] = 0;
437 	}
438 	used_item_counter_initialised = 1;
439 	//printf("Used item counter code initialised\n");
440 }
441 
used_item_counter_timer(void)442 void used_item_counter_timer(void)
443 {
444 	if (!used_item_counter_initialised)
445 		used_item_counter_init();
446 	else
447 	{
448 		size_t i;
449 		for (i=0; i<ITEM_NUM_ITEMS; i++)
450 		{
451 			/* The timeout value is need in the absence of a server change that would give feedback to
452 			use actions. Too short, and we will miss a valid counter event due to server lag. Too
453 			long, and we will interpret a single dropped item as use of an item that has actually
454 			failed. Hopefully, the server lag will not be as high as the timeout, and a player will
455 			not switch from failed ACTION_USE to dropping an item in less than the timeout. */
456 			if (used_item_counter_queue_time[i] && (SDL_GetTicks() - used_item_counter_queue_time[i]) > 1000u)
457 			{
458 				used_item_counter_queue[i] = 0;
459 				used_item_counter_queue_time[i] = 0;
460 				//printf("Used item counter pos %lu, confirmation timed out\n", i);
461 			}
462 		}
463 	}
464 }
465 
used_item_counter_action_use(int pos)466 void used_item_counter_action_use(int pos)
467 {
468 	if (!used_item_counter_initialised)
469 		used_item_counter_init();
470 	if ((pos < 0) || !(pos < ITEM_NUM_ITEMS))
471 		return;
472 	used_item_counter_queue[pos]++;
473 	used_item_counter_queue_time[pos] = SDL_GetTicks();
474 	//printf("Used item counter pos %d, waiting for confirmation pending %d\n", pos, used_item_counter_queue[pos]);
475 }
476 
477 
used_item_counter_check_confirmation(int pos,int new_quanity)478 static void used_item_counter_check_confirmation(int pos, int new_quanity)
479 {
480 	if (!used_item_counter_initialised)
481 		used_item_counter_init();
482 	if ((pos < 0) || !(pos < ITEM_NUM_ITEMS))
483 		return;
484 	if (used_item_counter_queue[pos])
485 	{
486 		if ((item_list[pos].quantity > 0) && (new_quanity < item_list[pos].quantity))
487 		{
488 			static int sent_unique_message = 0;
489 			static int sent_get_failed_message = 0;
490 			int item_count = get_item_count(item_list[pos].id, item_list[pos].image_id);
491 			used_item_counter_queue[pos]--;
492 			if (!used_item_counter_queue[pos])
493 				used_item_counter_queue_time[pos] = 0;
494 			if (item_count == 1)
495 				increment_used_item_counter(get_basic_item_description(item_list[pos].id, item_list[pos].image_id), item_list[pos].quantity - new_quanity);
496 			else if ((item_count > 1) && !sent_unique_message)
497 			{
498 				LOG_TO_CONSOLE(c_red1, item_use_not_unique_str);
499 				LOG_TO_CONSOLE(c_red1, item_uid_help_str);
500 				sent_unique_message = 1;
501 			}
502 			else if ((item_count < 1) && !sent_get_failed_message)
503 			{
504 				char str[256];
505 				safe_snprintf(str, sizeof(str), "%s id=%d image_id=%d", item_use_get_failed_str, item_list[pos].id, item_list[pos].image_id);
506 				LOG_TO_CONSOLE(c_red1, str);
507 				sent_get_failed_message = 1;
508 			}
509 			//printf("Used item counter confirmed pos %d - item removed [%s] quanity %d\n", pos, get_item_description(item_list[pos].id, item_list[pos].image_id), item_list[pos].quantity - new_quanity);
510 		}
511 		//else
512 		//	printf("Used item counter pos %d issue with quanity\n", pos);
513 	}
514 	//else
515 	//	printf("Used item counter pos %d - not due to use action\n", pos);
516 }
517 
remove_item_from_inventory(int pos)518 void remove_item_from_inventory(int pos)
519 {
520 	used_item_counter_check_confirmation(pos, 0);
521 	item_list[pos].quantity=0;
522 
523 	if (pos == swap_complete.move_from)
524 		swap_complete.move_to = swap_complete.move_from = -1;
525 
526 #ifdef NEW_SOUND
527 	check_for_item_sound(pos);
528 #endif // NEW_SOUND
529 
530 	build_manufacture_list();
531 	check_castability();
532 }
533 
get_new_inventory_item(const Uint8 * data)534 void get_new_inventory_item (const Uint8 *data)
535 {
536 	int pos;
537 	Uint8 flags;
538 	int quantity;
539 	int image_id;
540 	Uint16 id;
541 
542 	if (item_uid_enabled)
543 		id=SDL_SwapLE16(*((Uint16 *)(data+8)));
544 	else
545 		id=unset_item_uid;
546 
547 	pos= data[6];
548 	flags= data[7];
549 	image_id=SDL_SwapLE16(*((Uint16 *)(data)));
550 	quantity=SDL_SwapLE32(*((Uint32 *)(data+2)));
551 
552 	used_item_counter_check_confirmation(pos, quantity);
553 	if (now_harvesting() && (quantity >= item_list[pos].quantity) ) {	//some harvests, eg hydrogenium and wolfram, also decrease an item number. only count what goes up
554 		increment_harvest_counter(item_list[pos].quantity > 0 ? quantity - item_list[pos].quantity : quantity);
555 	}
556 
557 	// don't touch cool down when it's already active
558 	if(item_list[pos].quantity == 0 || item_list[pos].image_id != image_id){
559 		item_list[pos].cooldown_time = 0;
560 		item_list[pos].cooldown_rate = 1;
561 	}
562 	item_list[pos].quantity=quantity;
563 	item_list[pos].image_id=image_id;
564 	item_list[pos].pos=pos;
565 	item_list[pos].id=id;
566 	item_list[pos].is_resource=((flags&ITEM_RESOURCE)>0);
567 	item_list[pos].is_reagent=((flags&ITEM_REAGENT)>0);
568 	item_list[pos].use_with_inventory=((flags&ITEM_INVENTORY_USABLE)>0);
569 	item_list[pos].is_stackable=((flags&ITEM_STACKABLE)>0);
570 
571 #ifdef NEW_SOUND
572 	check_for_item_sound(pos);
573 #endif // NEW_SOUND
574 
575 	build_manufacture_list();
576 	check_castability();
577 
578 	// if we can, complete a swap of equipment by moving the removed item to the slot left by the new
579 	if (pos == swap_complete.move_to)
580 	{
581 		if (item_list[swap_complete.move_from].quantity)
582 		{
583 			if (!move_item(swap_complete.move_from, swap_complete.move_to, -1))
584 				swap_complete.move_from = swap_complete.move_to = -1;
585 		}
586 		else
587 			swap_complete.move_from = swap_complete.move_to = -1;
588 	}
589 }
590 
591 
592 
draw_item(int id,int x_start,int y_start,int gridsize)593 void draw_item(int id, int x_start, int y_start, int gridsize){
594 	float u_start,v_start,u_end,v_end;
595 	int cur_item;
596 	int this_texture;
597 
598 	//get the UV coordinates.
599 	cur_item=id%25;
600 	get_item_uv(cur_item, &u_start, &v_start, &u_end, &v_end);
601 
602 	//get the texture this item belongs to
603 	this_texture=get_items_texture(id/25);
604 
605 	bind_texture(this_texture);
606 	glBegin(GL_QUADS);
607 		draw_2d_thing(u_start,v_start,u_end,v_end,x_start,y_start,x_start+gridsize-1,y_start+gridsize-1);
608 	glEnd();
609 }
610 
display_items_handler(window_info * win)611 static int display_items_handler(window_info *win)
612 {
613 	char str[80];
614 	char my_str[10];
615 	int x,y,i;
616 	int item_is_weared=0;
617 	Uint32 _cur_time = SDL_GetTicks(); /* grab a snapshot of current time */
618 	char *but_labels[NUMBUT] = { sto_all_str, get_all_str, drp_all_str, NULL, itm_lst_str };
619 
620 	glEnable(GL_TEXTURE_2D);
621 
622 	check_for_swap_completion();
623 
624 	/*
625 	* Labrat: I never realised that a store all patch had been posted to Berlios by Awn in February '07
626 	* Thanks to Awn for his earlier efforts (but this is not a derivative of his earlier work)
627 	*
628 	*My next step will be to code an #ifdef STORE_ALL section to save the 0-35 loop in the click handler for future proofing
629 	*  ready for server side implementation
630 	*/
631 	// draw the button labels
632 	but_labels[BUT_MIX] = (items_mix_but_all) ?mix_all_str :mix_one_str;
633 	for (i=0; i<NUMBUT; i++) {
634 		strap_word(but_labels[i],my_str);
635 		glColor3fv(gui_color);
636 		draw_text(buttons_grid.pos_x + buttons_grid.width/2,
637 			buttons_grid.pos_y + buttons_grid.height * i + buttons_grid.height/2,
638 			(const unsigned char*)my_str, strlen(my_str), win->font_category,
639 			TDO_ZOOM, button_text_zoom, TDO_ALIGNMENT, CENTER, TDO_VERTICAL_ALIGNMENT, CENTER_LINE,
640 			TDO_END);
641 	}
642 
643 	x = quantity_grid.pos_x + quantity_grid.width / 2;
644 	y = quantity_grid.pos_y + quantity_grid.height / 2;
645 	for(i = 0; i < ITEM_EDIT_QUANT; x += quantity_grid.width, ++i){
646 		if(i==edit_quantity){
647 			glColor3f(1.0f, 0.0f, 0.3f);
648 		} else if(i==quantities.selected){
649 			glColor3f(0.0f, 1.0f, 0.3f);
650 		} else {
651 			glColor3f(0.3f,0.5f,1.0f);
652 		}
653 		draw_text(x, y, (const unsigned char*)quantities.quantity[i].str,
654 			strlen(quantities.quantity[i].str), win->font_category, TDO_ZOOM, win->current_scale_small,
655 			TDO_ALIGNMENT, CENTER, TDO_VERTICAL_ALIGNMENT, CENTER_DIGITS, TDO_END);
656 	}
657 	glColor3f(0.3f,0.5f,1.0f);
658 	draw_string_small_zoomed_right(labels_box.pos_x + labels_box.len_x, labels_box.pos_y,
659 		(const unsigned char*)quantity_str, 1, win->current_scale);
660 
661 	glColor3f(0.57f,0.67f,0.49f);
662 	draw_text(equip_grid.pos_x + equip_grid.len_x / 2, equip_grid.pos_y, (const unsigned char*)equip_str,
663 		strlen(equip_str), win->font_category, TDO_MAX_WIDTH, equip_grid.len_x,
664 		TDO_ZOOM, win->current_scale_small, TDO_SHRINK_TO_FIT, 1, TDO_ALIGNMENT, CENTER,
665 		TDO_VERTICAL_ALIGNMENT, BOTTOM_LINE, TDO_END);
666 
667 	glColor3f(1.0f,1.0f,1.0f);
668 	//ok, now let's draw the objects...
669 	for(i=ITEM_NUM_ITEMS-1;i>=0;i--){
670 		if(item_list[i].quantity){
671 			int cur_pos;
672 			int x_start,x_end,y_start,y_end;
673 
674 			// don't display an item that is in the proces of being moved after equipment swap
675 			if (item_swap_in_progress(i))
676 				continue;
677 
678 			//get the x and y
679 			cur_pos=i;
680 			if(cur_pos>=ITEM_WEAR_START){//the items we 'wear' are smaller
681 				cur_pos-=ITEM_WEAR_START;
682 				item_is_weared=1;
683 				x_start = equip_grid.pos_x + equip_grid.width * (cur_pos % equip_grid.cols) + 1;
684 				x_end = x_start + equip_grid.width - 1;
685 				y_start = equip_grid.pos_y + equip_grid.height * (cur_pos / equip_grid.cols);
686 				y_end = y_start + equip_grid.height - 1;
687 				draw_item(item_list[i].image_id, x_start, y_start, equip_grid.width - 1);
688 			} else {
689 				item_is_weared=0;
690 				x_start = items_grid.pos_x + items_grid.width * (cur_pos % items_grid.cols) +1;
691 				x_end = x_start + items_grid.width - 1;
692 				y_start = items_grid.pos_y + items_grid.height * (cur_pos / items_grid.cols);
693 				y_end = y_start + items_grid.height - 1;
694 				draw_item(item_list[i].image_id, x_start, y_start, items_grid.width - 1);
695 			}
696 			if ((_cur_time - item_list_extra[i].slot_busy_start) < 250)
697 				gray_out(x_start, y_start, items_grid.width);
698 
699 			if (item_list[i].cooldown_time > _cur_time)
700 			{
701 				float cooldown = ((float)(item_list[i].cooldown_time - _cur_time)) / ((float)item_list[i].cooldown_rate);
702 				float x_center = (x_start + x_end)*0.5f;
703 				float y_center = (y_start + y_end)*0.5f;
704 				float flash_effect_offset = 0.0f;
705 
706 				if (cooldown < 0.0f)
707 					cooldown = 0.0f;
708 				else if (cooldown > 1.0f)
709 					cooldown = 1.0f;
710 
711 				glDisable(GL_TEXTURE_2D);
712 				glEnable(GL_BLEND);
713 
714 				glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
715 				glBegin(GL_TRIANGLE_FAN);
716 					if (cooldown < 1.0f)
717 						flash_effect_offset = sin(pow(1.0f - cooldown, 4.0f) * 2.0f * M_PI * 30.0);
718 					glColor4f(0.14f - flash_effect_offset / 20.0f, 0.35f - flash_effect_offset / 20.0f, 0.82f + flash_effect_offset / 8.0f, 0.48f + flash_effect_offset / 15.0f);
719 
720 					glVertex2f(x_center, y_center);
721 
722 					if (cooldown >= 0.875f) {
723 						float t = tan(2.0f*M_PI*(1.0f - cooldown));
724 						glVertex2f(t*x_end + (1.0f - t)*x_center, y_start);
725 						glVertex2f(x_end, y_start);
726 						glVertex2f(x_end, y_end);
727 						glVertex2f(x_start, y_end);
728 						glVertex2f(x_start, y_start);
729 					} else if (cooldown >= 0.625f) {
730 						float t = 0.5f + 0.5f*tan(2.0f*M_PI*(0.75f - cooldown));
731 						glVertex2f(x_end, t*y_end + (1.0f - t)*y_start);
732 						glVertex2f(x_end, y_end);
733 						glVertex2f(x_start, y_end);
734 						glVertex2f(x_start, y_start);
735 					} else if (cooldown >= 0.375f) {
736 						float t = 0.5f + 0.5f*tan(2.0f*M_PI*(0.5f - cooldown));
737 						glVertex2f(t*x_start + (1.0f - t)*x_end, y_end);
738 						glVertex2f(x_start, y_end);
739 						glVertex2f(x_start, y_start);
740 					} else if (cooldown >= 0.125f) {
741 						float t = 0.5f + 0.5f*tan(2.0f*M_PI*(0.25f - cooldown));
742 						glVertex2f(x_start, t*y_start + (1.0f - t)*y_end);
743 						glVertex2f(x_start, y_start);
744 					} else {
745 						float t = tan(2.0f*M_PI*(cooldown));
746 						glVertex2f(t*x_start + (1.0f - t)*x_center, y_start);
747 					}
748 
749 					glVertex2f(x_center, y_start);
750 				glEnd();
751 
752 				glDisable(GL_BLEND);
753 				glEnable(GL_TEXTURE_2D);
754 			}
755 
756 			if(!item_is_weared){
757 				int use_large = (items_grid.mouse_over == i) && enlarge_text();
758 				safe_snprintf(str, sizeof(str), "%i", item_list[i].quantity);
759 				y_end -= (i & 1) ?items_grid.height - 1 : ((use_large) ?win->default_font_len_y :win->small_font_len_y);
760 				if (use_large)
761 					draw_string_shadowed_zoomed(x_start, y_end, (unsigned char*)str, 1,1.0f,1.0f,1.0f, 0.0f, 0.0f, 0.0f, win->current_scale);
762 				else
763 					draw_string_small_shadowed_zoomed(x_start, y_end, (unsigned char*)str, 1,1.0f,1.0f,1.0f, 0.0f, 0.0f, 0.0f, win->current_scale);
764 			}
765 		}
766 	}
767 	items_grid.mouse_over = -1;
768 
769 	glColor3f(1.0f,1.0f,1.0f);
770 
771 	//draw the load string
772 	safe_snprintf(str, sizeof(str), "%s: %i/%i", attributes.carry_capacity.shortname, your_info.carry_capacity.cur, your_info.carry_capacity.base);
773 	draw_string_small_zoomed(labels_box.pos_x, labels_box.pos_y, (unsigned char*)str, 1, win->current_scale);
774 
775 	//now, draw the inventory text, if any.
776 	if (!items_disable_text_block)
777 	{
778 		if (last_items_string_id != inventory_item_string_id)
779 		{
780 			put_small_text_in_box_zoomed((const unsigned char*)inventory_item_string, strlen(inventory_item_string), message_box.len_x,
781 			(unsigned char*)items_string, win->current_scale);
782 			last_items_string_id = inventory_item_string_id;
783 		}
784 		draw_string_small_zoomed(message_box.pos_x, message_box.pos_y, (unsigned char*)items_string, message_box.rows, win->current_scale);
785 	}
786 
787 	glDisable(GL_TEXTURE_2D);
788 
789 	if (text_arrow.mouse_over != -1)
790 		glColor3fv(gui_bright_color);
791 	else
792 		glColor3fv(gui_color);
793 	text_arrow.mouse_over = -1;
794 	if (items_disable_text_block)
795 	{
796 		glBegin(GL_LINES); /* Dn arrow */
797 			glVertex3i(text_arrow.pos_x, text_arrow.pos_y - text_arrow.len_y, 0);
798 			glVertex3i(text_arrow.pos_x + text_arrow.len_x/2, text_arrow.pos_y, 0);
799 			glVertex3i(text_arrow.pos_x + text_arrow.len_x/2, text_arrow.pos_y, 0);
800 			glVertex3i(text_arrow.pos_x + text_arrow.len_x, text_arrow.pos_y - text_arrow.len_y, 0);
801 		glEnd();
802 		// if we have a coloured message, draw a small dot at the top of the arrow to indicate so, using the colour of the message
803 		if ((strlen(inventory_item_string) > 0) && is_color(inventory_item_string[0]))
804 		{
805 			int colour = from_color_char (inventory_item_string[0]);
806 			if ((colour >= c_lbound) && (colour <= c_ubound))
807 			{
808 				glColor4f((float) colors_list[colour].r1 / 255.0f, (float) colors_list[colour].g1 / 255.0f, (float) colors_list[colour].b1 / 255.0f, 1.0f);
809 				glEnable( GL_POINT_SMOOTH );
810 				glEnable( GL_BLEND );
811 				glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
812 				glPointSize(text_arrow.len_x/3);
813 				glBegin(GL_POINTS);
814 				glVertex2f(text_arrow.pos_x + text_arrow.len_x/2, text_arrow.pos_y - text_arrow.len_y + text_arrow.len_y/6);
815 				glEnd();
816 				glDisable(GL_BLEND);
817 				glDisable(GL_POINT_SMOOTH);
818 			}
819 		}
820 	}
821 	else
822 	{
823 		glBegin(GL_LINES); /* Up arrow */
824 			glVertex3i(text_arrow.pos_x, text_arrow.pos_y, 0);
825 			glVertex3i(text_arrow.pos_x + text_arrow.len_x/2, text_arrow.pos_y - text_arrow.len_y, 0);
826 			glVertex3i(text_arrow.pos_x + text_arrow.len_x/2, text_arrow.pos_y - text_arrow.len_y, 0);
827 			glVertex3i(text_arrow.pos_x + text_arrow.len_x, text_arrow.pos_y, 0);
828 		glEnd();
829 	}
830 
831 	if (unequip_arrow.mouse_over != -1)
832 		glColor3fv(gui_bright_color);
833 	else
834 		glColor3fv(gui_color);
835 	unequip_arrow.mouse_over = -1;
836 	if (items_equip_grid_on_left)
837 	{
838 		int arrow_width = (disable_double_click) ?unequip_arrow.len_x : unequip_arrow.len_x/2;
839 		glBegin(GL_LINES); /* right arrow */
840 			glVertex3i(unequip_arrow.pos_x, unequip_arrow.pos_y, 0);
841 			glVertex3i(unequip_arrow.pos_x + arrow_width, unequip_arrow.pos_y + unequip_arrow.len_y/2, 0);
842 			glVertex3i(unequip_arrow.pos_x + arrow_width, unequip_arrow.pos_y + unequip_arrow.len_y/2, 0);
843 			glVertex3i(unequip_arrow.pos_x, unequip_arrow.pos_y + unequip_arrow.len_y, 0);
844 			if (!disable_double_click)
845 			{
846 				glVertex3i(unequip_arrow.pos_x + arrow_width, unequip_arrow.pos_y, 0);
847 				glVertex3i(unequip_arrow.pos_x + unequip_arrow.len_x, unequip_arrow.pos_y + unequip_arrow.len_y/2, 0);
848 				glVertex3i(unequip_arrow.pos_x + unequip_arrow.len_x, unequip_arrow.pos_y + unequip_arrow.len_y/2, 0);
849 				glVertex3i(unequip_arrow.pos_x + arrow_width, unequip_arrow.pos_y + unequip_arrow.len_y, 0);
850 			}
851 		glEnd();
852 	}
853 	else
854 	{
855 		int arrow_width = (disable_double_click) ?unequip_arrow.len_x : unequip_arrow.len_x/2;
856 		glBegin(GL_LINES); /* left arrow */
857 			glVertex3i(unequip_arrow.pos_x + arrow_width, unequip_arrow.pos_y, 0);
858 			glVertex3i(unequip_arrow.pos_x, unequip_arrow.pos_y + unequip_arrow.len_y/2, 0);
859 			glVertex3i(unequip_arrow.pos_x, unequip_arrow.pos_y + unequip_arrow.len_y/2, 0);
860 			glVertex3i(unequip_arrow.pos_x + arrow_width, unequip_arrow.pos_y + unequip_arrow.len_y, 0);
861 			if (!disable_double_click)
862 			{
863 				glVertex3i(unequip_arrow.pos_x + unequip_arrow.len_x, unequip_arrow.pos_y, 0);
864 				glVertex3i(unequip_arrow.pos_x + arrow_width, unequip_arrow.pos_y + unequip_arrow.len_y/2, 0);
865 				glVertex3i(unequip_arrow.pos_x + arrow_width, unequip_arrow.pos_y + unequip_arrow.len_y/2, 0);
866 				glVertex3i(unequip_arrow.pos_x + unequip_arrow.len_x, unequip_arrow.pos_y + unequip_arrow.len_y, 0);
867 			}
868 		glEnd();
869 	}
870 
871 	// Render the grid *after* the images. It seems impossible to code
872 	// it such that images are rendered exactly within the boxes on all
873 	// cards
874 	glColor3fv(gui_color);
875 
876 	//draw the grids
877 	rendergrid(items_grid.cols, items_grid.rows, items_grid.pos_x, items_grid.pos_y, items_grid.width, items_grid.height);
878 
879 	glColor3f(0.57f,0.67f,0.49f);
880 	rendergrid(equip_grid.cols, equip_grid.rows, equip_grid.pos_x, equip_grid.pos_y, equip_grid.width, equip_grid.height);
881 
882 	// draw the button boxes
883 	glColor3fv(gui_color);
884 	for (i=0; i<NUMBUT; i++) {
885 		glBegin(GL_LINE_LOOP);
886 			glVertex3i(buttons_grid.pos_x + buttons_grid.width, buttons_grid.pos_y + buttons_grid.height * i, 0);
887 			glVertex3i(buttons_grid.pos_x, buttons_grid.pos_y + buttons_grid.height * i, 0);
888 			glVertex3i(buttons_grid.pos_x, buttons_grid.pos_y + buttons_grid.height * i + buttons_grid.height, 0);
889 			glVertex3i(buttons_grid.pos_x + buttons_grid.width, buttons_grid.pos_y + buttons_grid.height * i + buttons_grid.height, 0);
890 		glEnd();
891 	}
892 
893 	// highlight a button with the mouse over
894 	if (buttons_grid.mouse_over != -1)
895 	{
896 		glColor3fv(gui_bright_color);
897 		glBegin(GL_LINE_LOOP);
898 			glVertex3i(buttons_grid.pos_x + buttons_grid.width + 1, buttons_grid.pos_y + buttons_grid.height * buttons_grid.mouse_over - 1, 0);
899 			glVertex3i(buttons_grid.pos_x - 1, buttons_grid.pos_y + buttons_grid.height * buttons_grid.mouse_over - 1, 0);
900 			glVertex3i(buttons_grid.pos_x - 1, buttons_grid.pos_y + buttons_grid.height * buttons_grid.mouse_over + buttons_grid.height + 1, 0);
901 			glVertex3i(buttons_grid.pos_x + buttons_grid.width + 1, buttons_grid.pos_y + buttons_grid.height * buttons_grid.mouse_over + buttons_grid.height + 1, 0);
902 		glEnd();
903 	}
904 
905 	//now, draw the quantity boxes
906 	glColor3f(0.3f,0.5f,1.0f);
907 	rendergrid(quantity_grid.cols, quantity_grid.rows, quantity_grid.pos_x, quantity_grid.pos_y, quantity_grid.width, quantity_grid.height);
908 
909 	glEnable(GL_TEXTURE_2D);
910 
911 	// display help text for button if mouse over one
912 	if ((buttons_grid.mouse_over != -1) && show_help_text) {
913 		char *helpstr[NUMBUT] = { stoall_help_str, getall_help_str, ((disable_double_click) ?drpall_help_str :dcdrpall_help_str), mixoneall_help_str, itmlst_help_str };
914 		show_help(helpstr[buttons_grid.mouse_over], 0, win->len_y+10, win->current_scale);
915 		show_help(cm_help_options_str, 0, win->len_y+10+win->small_font_len_y, win->current_scale);
916 	}
917 	// show help set in the mouse_over handler
918 	else {
919 		int offset = 10;
920 		if (show_help_text && (item_help_str != NULL)) {
921 			show_help(item_help_str, 0, win->len_y+offset, win->current_scale);
922 			offset += win->small_font_len_y;
923 		}
924 		if (item_desc_str != NULL)
925 			show_help(item_desc_str, 0, win->len_y+offset, win->current_scale);
926 		item_help_str = NULL;
927 		item_desc_str = NULL;
928 	}
929 
930 	buttons_grid.mouse_over = -1;
931 
932 #ifdef OPENGL_TRACE
933 CHECK_GL_ERRORS();
934 #endif //OPENGL_TRACE
935 
936 	return 1;
937 }
938 
939 
940 /* return 1 if sent the move command */
move_item(int item_pos_to_mov,int destination_pos,int avoid_pos)941 int move_item(int item_pos_to_mov, int destination_pos, int avoid_pos)
942 {
943 	int drop_on_stack = 0;
944 	swap_complete.last_dest = -1;
945 	/* if the dragged item is equipped and the destintion is occupied, try to find another slot */
946 	if ((item_pos_to_mov >= ITEM_WEAR_START) && (item_list[destination_pos].quantity)){
947 		int i;
948 		int have_free_pos = 0;
949 		/* find first free slot, use a free slot in preference to a stack as the server does the stacking */
950 		for (i = 0; i < ITEM_WEAR_START; i++){
951 			if (!item_list[i].quantity && i != avoid_pos){
952 				destination_pos = i;
953 				have_free_pos = 1;
954 				break;
955 			}
956 		}
957 		/* if no free slot, try to find an existing stack.  But be careful of dupe image ids */
958 		if (!have_free_pos && item_list[item_pos_to_mov].is_stackable){
959 			int num_stacks_found = 0;
960 			int proposed_slot = -1;
961 			for (i = 0; i < ITEM_WEAR_START; i++){
962 				if (item_list[i].is_stackable && (item_list[i].image_id == item_list[item_pos_to_mov].image_id)){
963 					num_stacks_found++;
964 					proposed_slot = i;
965 				}
966 			}
967 			/* only use the stack if we're sure there are no other possibilities */
968 			if (num_stacks_found == 1){
969 				destination_pos = proposed_slot;
970 				drop_on_stack = 1;
971 			}
972 			else
973 				set_shown_string(c_red2, items_stack_str);
974 			/*  This still leaves one possibility for the dreaded server accusation.
975 				If we have no free inventory slots, one or more stackable items
976 				unequipped, and a single, different equipped item with the same id as
977 				the aforementioned stack.  When we try to unequip the single item, the
978 				client tries to place it on that stack. This may mean we have to
979 				abandon this feature; i.e. allowing a stackable item to be unequipping
980 				when there are no free slots. (pjbroad/bluap) */
981 		}
982 	}
983 	/* move item */
984 	if(drop_on_stack || !item_list[destination_pos].quantity){
985 		Uint8 str[20];
986 		//send the drop info to the server
987 		str[0]=MOVE_INVENTORY_ITEM;
988 		str[1]=item_list[item_pos_to_mov].pos;
989 		str[2]=destination_pos;
990 		my_tcp_send(my_socket,str,3);
991 		swap_complete.last_dest = destination_pos;
992 		return 1;
993 	}
994 	else
995 		return 0;
996 }
997 
equip_item(int item_pos_to_equip,int destination_pos)998 static void equip_item(int item_pos_to_equip, int destination_pos)
999 {
1000 	Uint8 str[20];
1001 	//send the drop info to the server
1002 	str[0]=MOVE_INVENTORY_ITEM;
1003 	str[1]=item_list[item_pos_to_equip].pos;
1004 	str[2]=destination_pos;
1005 	my_tcp_send(my_socket,str,3);
1006 }
1007 
prep_move_to_vacated_slot(int destination)1008 static void prep_move_to_vacated_slot(int destination)
1009 {
1010 	swap_complete.move_from = swap_complete.last_dest;
1011 	swap_complete.move_to = destination;
1012 	swap_complete.string_id = inventory_item_string_id;
1013 	swap_complete.start_time = SDL_GetTicks();
1014 }
1015 
1016 // stop expecting swap completion if we get a message, or we timeout (catch all)
check_for_swap_completion(void)1017 void check_for_swap_completion(void)
1018 {
1019 	if (swap_complete.move_from != -1)
1020 	{
1021 		if (swap_complete.string_id != inventory_item_string_id)
1022 			swap_complete.move_from = swap_complete.move_to = -1;
1023 		if (SDL_GetTicks() > swap_complete.start_time + 2000)
1024 			swap_complete.move_from = swap_complete.move_to = -1;
1025 	}
1026 }
1027 
item_swap_in_progress(int item_pos)1028 int item_swap_in_progress(int item_pos)
1029 {
1030 	return (item_pos == swap_complete.move_from) ?1: 0;
1031 }
1032 
1033 // If we double-click an equipable item, and one of that type is already equipped, swap them.
1034 //
swap_equivalent_equipped_item(int from_pos)1035 static int swap_equivalent_equipped_item(int from_pos)
1036 {
1037 	size_t i;
1038 	enum EQUIP_TYPE from_equip_type = get_item_equip_type(item_list[from_pos].id, item_list[from_pos].image_id);
1039 	int same_pos = -1, left_hand_pos = -1, right_hand_pos = -1, both_hands_pos = -1;
1040 	int item_to_swap = -1, extra_item = -1;
1041 
1042 	// stop now if we don't know about the item type
1043 	if (from_equip_type == EQUIP_NONE)
1044 		return 0;
1045 
1046 	// clear any previous "avoid destination" there may have been
1047 	swap_complete.last_dest = -1;
1048 
1049 	// there are several rules about swapping left and right handed items with both handed items
1050 	// find each of the types
1051 	for(i = ITEM_WEAR_START; i<ITEM_WEAR_START + ITEM_NUM_WEAR; i++)
1052 		if (item_list[i].quantity > 0)
1053 		{
1054 			enum EQUIP_TYPE the_type = get_item_equip_type(item_list[i].id, item_list[i].image_id);
1055 			if (the_type == from_equip_type)
1056 				same_pos = i;
1057 			else if (the_type == EQUIP_LEFT_HAND)
1058 				left_hand_pos = i;
1059 			else if (the_type == EQUIP_RIGHT_HAND)
1060 				right_hand_pos = i;
1061 			else if (the_type == EQUIP_BOTH_HANDS)
1062 				both_hands_pos = i;
1063 		}
1064 
1065 	// if a simple, same item type swap
1066 	if (same_pos != -1)
1067 		item_to_swap = same_pos;
1068 	// if equipping a both hands item, swap a right hand item first, any left hand as the extra
1069 	else if (from_equip_type == EQUIP_BOTH_HANDS && right_hand_pos != -1)
1070 	{
1071 		item_to_swap = right_hand_pos;
1072 		if (left_hand_pos != -1)
1073 			extra_item = left_hand_pos;
1074 	}
1075 	// equipping a both hands item, no right hand but swap any left hand item
1076 	else if (from_equip_type == EQUIP_BOTH_HANDS && left_hand_pos != -1)
1077 		item_to_swap = left_hand_pos;
1078 	// if theres an equipped both hands item, allow a right or left hand item to be swapped with it
1079 	else if (both_hands_pos != -1 && (from_equip_type == EQUIP_RIGHT_HAND || from_equip_type == EQUIP_LEFT_HAND))
1080 		item_to_swap = both_hands_pos;
1081 
1082 	// remove any extra item, swap_complete.last_dest will be set the the new slot for the remove item
1083 	if ((extra_item != -1) && !move_item(extra_item, 0, -1))
1084 	{
1085 		do_alert1_sound();
1086 		set_shown_string(c_red2, items_cannot_equip_str);
1087 		return 1;
1088 	}
1089 
1090 	// do the swap if any
1091 	if (item_to_swap != -1)
1092 	{
1093 		// avoid moving to where we put any removed extra item
1094 		if (move_item(item_to_swap, 0, swap_complete.last_dest))
1095 		{
1096 			equip_item(from_pos, item_to_swap);
1097 			prep_move_to_vacated_slot(from_pos);
1098 			do_get_item_sound();
1099 			item_dragged = -1;
1100 		}
1101 		else
1102 		{
1103 			do_alert1_sound();
1104 			set_shown_string(c_red2, items_cannot_equip_str);
1105 		}
1106 		return 1; // there was something to swap, and we tried, don't try other stuff
1107 	}
1108 
1109 	return 0; // there was nothing to swap, try other stuff
1110 }
1111 
1112 // common function for aut equip used by inventory and quickbar
try_auto_equip(int from_item)1113 void try_auto_equip(int from_item)
1114 {
1115 	if (allow_equip_swap && swap_equivalent_equipped_item(from_item)) {
1116 		// all done, don't try direct equip
1117 	} else {
1118 		size_t i;
1119 		for(i = ITEM_WEAR_START; i < ITEM_WEAR_START + ITEM_NUM_WEAR; i++) {
1120 			if(item_list[i].quantity<1) {
1121 				if (!move_item(from_item, i, -1))
1122 				{
1123 					do_alert1_sound();
1124 					set_shown_string(c_red2, items_cannot_equip_str);
1125 				}
1126 				item_dragged=-1;
1127 				break;
1128 			}
1129 		}
1130 	}
1131 }
1132 
click_items_handler(window_info * win,int mx,int my,Uint32 flags)1133 static int click_items_handler(window_info *win, int mx, int my, Uint32 flags)
1134 {
1135 	Uint8 str[100];
1136 	int right_click = flags & ELW_RIGHT_MOUSE;
1137 	int ctrl_on = flags & KMOD_CTRL;
1138 	int shift_on = flags & KMOD_SHIFT;
1139 	int alt_on = flags & KMOD_ALT;
1140 	int pos;
1141 	actor *me;
1142 
1143 	// only handle mouse button clicks, not scroll wheels moves (unless its the mix button)
1144 	if (((flags & ELW_MOUSE_BUTTON) == 0) && (over_button(win, mx, my) != BUT_MIX)) return 0;
1145 
1146 	// ignore middle mouse button presses
1147 	if ((flags & ELW_MID_MOUSE) != 0) return 0;
1148 
1149 	if (!right_click && over_button(win, mx, my) != -1)
1150 		do_click_sound();
1151 
1152 	if ((flags & ELW_LEFT_MOUSE) && (mx > text_arrow.pos_x) && (mx < text_arrow.pos_x + text_arrow.len_x) &&
1153 			(my < text_arrow.pos_y) && (my > text_arrow.pos_y - text_arrow.len_y))
1154 	{
1155 		items_disable_text_block ^= 1;
1156 		show_items_handler(win);
1157 		do_click_sound();
1158 		return 1;
1159 	}
1160 
1161 	if ((flags & ELW_LEFT_MOUSE) && (mx > unequip_arrow.pos_x) && (mx < unequip_arrow.pos_x + unequip_arrow.len_x) &&
1162 		(my > unequip_arrow.pos_y) && (my < unequip_arrow.pos_y + unequip_arrow.len_y))
1163 	{
1164 		size_t i;
1165 		int last_pos = 0;
1166 		int success = 1;
1167 		static Uint32 last_click = 0;
1168 		if (!safe_button_click(&last_click))
1169 			return 1;
1170 		for(i = ITEM_WEAR_START; i < ITEM_WEAR_START + ITEM_NUM_WEAR; i++)
1171 		{
1172 			if(item_list[i].quantity>0)
1173 			{
1174 				size_t j;
1175 				int found_slot = 0;
1176 				if (item_list[i].is_stackable)
1177 				{
1178 					for (j=0; j<ITEM_WEAR_START; j++)
1179 						if ((item_list[j].quantity>0) && (item_list[i].id == item_list[j].id) && (item_list[i].image_id == item_list[j].image_id))
1180 						{
1181 							if (move_item(i, j, -1))
1182 								found_slot = 1;
1183 							break;
1184 						}
1185 				}
1186 				if (!found_slot)
1187 					for (j=last_pos; j<ITEM_WEAR_START; j++)
1188 					{
1189 						if (item_list[j].quantity<1)
1190 						{
1191 							if (move_item(i, j, -1))
1192 								found_slot = 1;
1193 							last_pos = j + 1;
1194 							break;
1195 						}
1196 					}
1197 				if (!found_slot)
1198 					success = 0;
1199 			}
1200 		}
1201 		if (success)
1202 			do_get_item_sound();
1203 		else
1204 			do_alert1_sound();
1205 		item_dragged=-1;
1206 		return 1;
1207 	}
1208 
1209 	if(right_click) {
1210 		if(item_dragged!=-1 || use_item!=-1 || storage_item_dragged!=-1){
1211 			use_item=-1;
1212 			item_dragged=-1;
1213 			storage_item_dragged=-1;
1214 			item_action_mode=ACTION_WALK;
1215 			return 1;
1216 		}
1217 
1218 		if((mx >= equip_grid.pos_x) && (mx < equip_grid.pos_x + equip_grid.len_x) &&
1219 				(my >= equip_grid.pos_y) && (my < equip_grid.pos_y + equip_grid.len_y + 1)) {
1220 			switch(item_action_mode){
1221 				case ACTION_WALK:
1222 					item_action_mode=ACTION_LOOK;
1223 					break;
1224 				case ACTION_LOOK:
1225 				default:
1226 					item_action_mode=ACTION_WALK;
1227 			}
1228 			return 1;
1229 		} else if((mx >= quantity_grid.pos_x) && (mx < quantity_grid.pos_x + quantity_grid.len_x) &&
1230 				(my >= quantity_grid.pos_y) && (my < quantity_grid.pos_y +  + quantity_grid.len_y)){
1231 			//fall through...
1232 		} else {
1233 			switch(item_action_mode) {
1234 			case ACTION_WALK:
1235 				item_action_mode=ACTION_LOOK;
1236 				break;
1237 			case ACTION_LOOK:
1238 				item_action_mode=ACTION_USE;
1239 				break;
1240 			case ACTION_USE:
1241 				item_action_mode=ACTION_USE_WITEM;
1242 				break;
1243 			case ACTION_USE_WITEM:
1244 				item_action_mode=ACTION_WALK;
1245 				break;
1246 			default:
1247 				item_action_mode=ACTION_WALK;
1248 			}
1249 			return 1;
1250 		}
1251 	}
1252 
1253 	if (item_action_mode == ACTION_USE_WITEM)
1254 		set_gamewin_usewith_action();
1255 
1256 	//see if we changed the quantity
1257 	if((mx >= quantity_grid.pos_x) && (mx < quantity_grid.pos_x + quantity_grid.len_x) &&
1258 			(my >= quantity_grid.pos_y) && (my < quantity_grid.pos_y + quantity_grid.len_y)) {
1259 		int pos = get_mouse_pos_in_grid(mx, my, quantity_grid.cols, quantity_grid.rows, quantity_grid.pos_x, quantity_grid.pos_y, quantity_grid.width, quantity_grid.height);
1260 
1261 		if(pos==-1){
1262 		} else if(flags & ELW_LEFT_MOUSE){
1263 			if(edit_quantity!=-1){
1264 				if(!quantities.quantity[edit_quantity].len){
1265 					//Reset the quantity
1266 					reset_quantity(edit_quantity);
1267 				}
1268 				edit_quantity=-1;
1269 			}
1270 
1271 			item_quantity=quantities.quantity[pos].val;
1272 			quantities.selected=pos;
1273 		} else if(right_click){
1274 			//Edit the given quantity
1275 			edit_quantity=pos;
1276 		}
1277 
1278 		return 1;
1279 	}
1280 
1281 	if(edit_quantity!=-1){
1282 		if(!quantities.quantity[edit_quantity].len)reset_quantity(edit_quantity);
1283 		item_quantity=quantities.quantity[edit_quantity].val;
1284 		quantities.selected=edit_quantity;
1285 		edit_quantity=-1;
1286 	}
1287 
1288 	//see if we clicked on any item in the main category
1289 	else if(mx>items_grid.pos_x && (mx < items_grid.pos_x + items_grid.len_x) &&
1290 				my>0 && my < items_grid.len_y) {
1291 		int pos=get_mouse_pos_in_grid(mx, my, items_grid.cols, items_grid.rows, items_grid.pos_x, items_grid.pos_y, items_grid.width, items_grid.height);
1292 
1293 #ifdef NEW_SOUND
1294 		if(pos>-1) {
1295 			item_list[pos].action = ITEM_NO_ACTION;
1296 			item_list[pos].action_time = 0;
1297 		}
1298 #endif // NEW_SOUND
1299 		if(pos==-1) {
1300 		} else if(item_dragged!=-1){
1301 			if(item_dragged == pos){
1302 				try_auto_equip(item_dragged);
1303 			} else {
1304 				if (move_item(item_dragged, pos, -1)){
1305 					do_drop_item_sound();
1306 				}
1307 				else {
1308 					do_alert1_sound();
1309 				}
1310 				item_dragged=-1;
1311 			}
1312 
1313 		}
1314 		else if(storage_item_dragged!=-1){
1315 			str[0]=WITHDRAW_ITEM;
1316 			*((Uint16*)(str+1))=SDL_SwapLE16(storage_items[storage_item_dragged].pos);
1317 			*((Uint32*)(str+3))=SDL_SwapLE32(item_quantity);
1318 			my_tcp_send(my_socket, str, 6);
1319 			do_drop_item_sound();
1320 			if(storage_items[storage_item_dragged].quantity<=item_quantity) storage_item_dragged=-1;
1321 		}
1322 		else if(item_list[pos].quantity){
1323 			if (ctrl_on && (items_mod_click_any_cursor || (item_action_mode==ACTION_WALK))) {
1324 				str[0]=DROP_ITEM;
1325 				str[1]=item_list[pos].pos;
1326 				if(item_list[pos].is_stackable)
1327 					*((Uint32 *)(str+2))=SDL_SwapLE32(item_list[pos].quantity);
1328 				else
1329 					*((Uint32 *)(str+2))=SDL_SwapLE32(36);//Drop all
1330 				my_tcp_send(my_socket, str, 6);
1331 				do_drop_item_sound();
1332 			} else if (alt_on && (items_mod_click_any_cursor || (item_action_mode==ACTION_WALK))) {
1333 				if ((get_id_MW(MW_STORAGE) >= 0) && (get_show_window_MW(MW_STORAGE)) && (view_only_storage == 0)) {
1334 					str[0]=DEPOSITE_ITEM;
1335 					str[1]=item_list[pos].pos;
1336 					*((Uint32*)(str+2))=SDL_SwapLE32(INT_MAX);
1337 					my_tcp_send(my_socket, str, 6);
1338 					do_drop_item_sound();
1339 				} else {
1340 					drop_fail_time = SDL_GetTicks();
1341 					do_alert1_sound();
1342 				}
1343 			} else if(item_action_mode==ACTION_LOOK) {
1344 				click_time=cur_time;
1345 				str[0]=LOOK_AT_INVENTORY_ITEM;
1346 				str[1]=item_list[pos].pos;
1347 				my_tcp_send(my_socket,str,2);
1348 			} else if(item_action_mode==ACTION_USE) {
1349 				if(item_list[pos].use_with_inventory){
1350 					str[0]=USE_INVENTORY_ITEM;
1351 					str[1]=item_list[pos].pos;
1352 					my_tcp_send(my_socket,str,2);
1353 					used_item_counter_action_use(pos);
1354 #ifdef NEW_SOUND
1355 					item_list[pos].action = USE_INVENTORY_ITEM;
1356 #ifdef _EXTRA_SOUND_DEBUG
1357 //					printf("Using item: %d, inv pos: %d, Image ID: %d\n", item_list[pos].pos, pos, item_list[pos].image_id);
1358 #endif // _EXTRA_SOUND_DEBUG
1359 #endif // NEW_SOUND
1360 				}
1361 			} else if(item_action_mode==ACTION_USE_WITEM) {
1362 				if(use_item!=-1) {
1363 					str[0]=ITEM_ON_ITEM;
1364 					str[1]=item_list[use_item].pos;
1365 					str[2]=item_list[pos].pos;
1366 					my_tcp_send(my_socket,str,3);
1367 					used_item_counter_action_use(use_item);
1368 #ifdef NEW_SOUND
1369 					item_list[use_item].action = ITEM_ON_ITEM;
1370 					item_list[pos].action = ITEM_ON_ITEM;
1371 #ifdef _EXTRA_SOUND_DEBUG
1372 //					printf("Using item: %d on item: %d, Image ID: %d\n", pos, use_item, item_list[pos].image_id);
1373 #endif // _EXTRA_SOUND_DEBUG
1374 #endif // NEW_SOUND
1375 					if (!shift_on)
1376 						use_item=-1;
1377 				} else {
1378 					use_item=pos;
1379 				}
1380 			} else {
1381 				item_dragged=pos;
1382 				do_drag_item_sound();
1383 			}
1384 		}
1385 	}
1386 
1387 	// Get All button
1388 	else if(over_button(win, mx, my)==BUT_GET){
1389 		int x,y;
1390 		me = get_our_actor ();
1391 		if(!me)return(1);
1392 		x=me->x_tile_pos;
1393 		y=me->y_tile_pos;
1394 		items_get_bag(x,y);
1395 	}
1396 
1397 	// Sto All button
1398 	else if(over_button(win, mx, my)==BUT_STORE && get_id_MW(MW_STORAGE) >= 0 && view_only_storage == 0 && get_show_window_MW(MW_STORAGE) /*thanks alberich*/){
1399 #ifdef STORE_ALL
1400 		/*
1401 		* Future code to save server load by having one byte to represent the 36 slot inventory loop. Will need server support.
1402 		*/
1403 		str[0]=DEPOSITE_ITEM;
1404 		str[1]=STORE_ALL;
1405 		my_tcp_send(my_socket, str, 2);
1406 #else
1407 		for(pos=((items_stoall_nofirstrow)?6:0);pos<((items_stoall_nolastrow)?30:36);pos++){
1408 			if(item_list[pos].quantity>0){
1409 				str[0]=DEPOSITE_ITEM;
1410 				str[1]=item_list[pos].pos;
1411 				*((Uint32*)(str+2))=SDL_SwapLE32(item_list[pos].quantity);
1412 				my_tcp_send(my_socket, str, 6);
1413 			}
1414 		}
1415 #endif
1416 	}
1417 
1418 	// Drop All button
1419 	else if(over_button(win, mx, my)==BUT_DROP){
1420 		drop_all_handler();
1421 	}
1422 
1423 	// Mix One/All button
1424 	else if(over_button(win, mx, my)==BUT_MIX){
1425 		if (items_mix_but_all)
1426 			mix_handler(255, mixbut_empty_str);
1427 		else
1428 			mix_handler(1, mixbut_empty_str);
1429 	}
1430 
1431 	// Item List button
1432 	else if (over_button(win, mx, my)==BUT_ITEM_LIST)
1433 		toggle_items_list_window(win);
1434 
1435 	//see if we clicked on any item in the wear category
1436 	else if((mx > equip_grid.pos_x) && (mx < equip_grid.pos_x + equip_grid.len_x) &&
1437 				(my > equip_grid.pos_y) && (my < equip_grid.pos_y + equip_grid.len_y)){
1438 		int pos = ITEM_WEAR_START + get_mouse_pos_in_grid(mx, my, equip_grid.cols, equip_grid.rows, equip_grid.pos_x, equip_grid.pos_y, equip_grid.width, equip_grid.height);
1439 
1440 		if(pos < ITEM_WEAR_START) {
1441 		} else if(item_list[pos].quantity){
1442 			if(item_action_mode == ACTION_LOOK) {
1443 				str[0]=LOOK_AT_INVENTORY_ITEM;
1444 				str[1]=item_list[pos].pos;
1445 				my_tcp_send(my_socket, str, 2);
1446 			} else if(item_dragged==-1 && left_click) {
1447 				item_dragged=pos;
1448 				do_drag_item_sound();
1449 			}
1450 			else if(item_dragged!=-1 && left_click) {
1451 				int can_move = (item_dragged == pos) || allow_equip_swap;
1452 				if (can_move && move_item(pos, 0, -1)) {
1453 					equip_item(item_dragged, pos);
1454 					if (item_dragged != pos)
1455 						prep_move_to_vacated_slot(item_dragged);
1456 					do_get_item_sound();
1457 				}
1458 				else {
1459 					do_alert1_sound();
1460 					set_shown_string(c_red2, items_cannot_equip_str);
1461 				}
1462 				item_dragged=-1;
1463 			}
1464 		} else if(item_dragged!=-1){
1465 			equip_item(item_dragged, pos);
1466 			item_dragged=-1;
1467 			do_drop_item_sound();
1468 		}
1469 	}
1470 
1471 	// clear the message area if double-clicked
1472 	else if (!items_disable_text_block && (mx > message_box.pos_x) && (mx < message_box.pos_x + message_box.len_x) &&
1473 			(my > message_box.pos_y) && (my < message_box.pos_y + message_box.len_y)) {
1474 		static Uint32 last_click = 0;
1475 		if (safe_button_click(&last_click)) {
1476 			set_shown_string(0,"");
1477 			return 1;
1478 		}
1479 	}
1480 
1481 	return 1;
1482 }
1483 
set_description_help(int pos)1484 static void set_description_help(int pos)
1485 {
1486 	Uint16 item_id = item_list[pos].id;
1487 	int image_id = item_list[pos].image_id;
1488 	if (show_item_desc_text && item_info_available() && (get_item_count(item_id, image_id) == 1))
1489 		item_desc_str = get_item_description(item_id, image_id);
1490 }
1491 
mouseover_items_handler(window_info * win,int mx,int my)1492 static int mouseover_items_handler(window_info *win, int mx, int my) {
1493 	int pos;
1494 
1495 	// check and record if mouse if over a button
1496 	if ((buttons_grid.mouse_over = over_button(win, mx, my)) != -1)
1497 		return 0; // keep standard cursor
1498 
1499 	if ((mx > text_arrow.pos_x) && (mx < text_arrow.pos_x + text_arrow.len_x) &&
1500 			(my < text_arrow.pos_y) && (my > text_arrow.pos_y - text_arrow.len_y))
1501 	{
1502 		item_help_str = items_text_toggle_help_str;
1503 		text_arrow.mouse_over = 1;
1504 		return 0;
1505 	}
1506 
1507 	if ((mx > unequip_arrow.pos_x) && (mx < unequip_arrow.pos_x + unequip_arrow.len_x) &&
1508 			(my > unequip_arrow.pos_y) && (my < unequip_arrow.pos_y + unequip_arrow.len_y))
1509 	{
1510 		item_help_str = (disable_double_click) ?items_unequip_all_help_str :items_doubleclick_unequip_all_help_str;
1511 		unequip_arrow.mouse_over = 1;
1512 		return 0;
1513 	}
1514 
1515 	if((mx > items_grid.pos_x) && (mx < items_grid.pos_x + items_grid.len_x) && (my > 0) && (my < items_grid.len_y)) {
1516 		pos = get_mouse_pos_in_grid(mx, my, items_grid.cols, items_grid.rows, items_grid.pos_x, items_grid.pos_y, items_grid.width, items_grid.height);
1517 
1518 		if(pos==-1) {
1519 		} else if(item_list[pos].quantity){
1520 			set_description_help(pos);
1521 			if ((item_dragged == -1) && (items_mod_click_any_cursor || (item_action_mode==ACTION_WALK)))
1522 					item_help_str = mod_click_item_help_str;
1523 			if(item_action_mode==ACTION_LOOK) {
1524 				elwin_mouse=CURSOR_EYE;
1525 			} else if(item_action_mode==ACTION_USE) {
1526 				elwin_mouse=CURSOR_USE;
1527 			} else if(item_action_mode==ACTION_USE_WITEM) {
1528 				elwin_mouse=CURSOR_USE_WITEM;
1529 				if (use_item!=-1)
1530 					item_help_str = multiuse_item_help_str;
1531 			} else {
1532 				elwin_mouse=CURSOR_PICK;
1533 			}
1534 			items_grid.mouse_over = pos;
1535 
1536 			return 1;
1537 		}
1538 	} else if((mx > equip_grid.pos_x) && (mx < equip_grid.pos_x + equip_grid.len_x) &&
1539 			(my > equip_grid.pos_y) && (my < equip_grid.pos_y + equip_grid.len_y)) {
1540 		pos = ITEM_WEAR_START + get_mouse_pos_in_grid(mx, my, equip_grid.cols, equip_grid.rows, equip_grid.pos_x, equip_grid.pos_y, equip_grid.width, equip_grid.height);
1541 		item_help_str = equip_here_str;
1542 		if(pos==-1) {
1543 		} else if(item_list[pos].quantity){
1544 			set_description_help(pos);
1545 			if(item_action_mode==ACTION_LOOK) {
1546 				elwin_mouse=CURSOR_EYE;
1547 			} else if(item_action_mode==ACTION_USE) {
1548 				elwin_mouse=CURSOR_USE;
1549 			} else if(item_action_mode==ACTION_USE_WITEM) {
1550 				elwin_mouse=CURSOR_USE_WITEM;
1551 			} else {
1552 				elwin_mouse=CURSOR_PICK;
1553 			}
1554 			return 1;
1555 		}
1556 	} else if(show_help_text && (mx > quantity_grid.pos_x) && (mx < quantity_grid.pos_x +  quantity_grid.len_x) &&
1557 			(my > quantity_grid.pos_y) && (my < quantity_grid.pos_y + quantity_grid.len_y)){
1558 		item_help_str = quantity_edit_str;
1559 	} else if (show_help_text && *inventory_item_string && !items_disable_text_block &&
1560 			(mx > message_box.pos_x) && (mx < message_box.pos_x + message_box.len_x) &&
1561 			(my > message_box.pos_y) && (my < message_box.pos_y + message_box.len_y)) {
1562 		item_help_str = (disable_double_click)?click_clear_str :double_click_clear_str;
1563 	}
1564 
1565 	return 0;
1566 }
1567 
keypress_items_handler(window_info * win,int x,int y,SDL_Keycode key_code,Uint32 key_unicode,Uint16 key_mod)1568 static int keypress_items_handler(window_info * win, int x, int y, SDL_Keycode key_code, Uint32 key_unicode, Uint16 key_mod)
1569 {
1570 	if(edit_quantity!=-1){
1571 		char * str=quantities.quantity[edit_quantity].str;
1572 		int * len=&quantities.quantity[edit_quantity].len;
1573 		int * val=&quantities.quantity[edit_quantity].val;
1574 
1575 		if(key_code == SDLK_DELETE){
1576 			reset_quantity(edit_quantity);
1577 			edit_quantity=-1;
1578 			return 1;
1579 		} else if(key_code == SDLK_BACKSPACE){
1580 			if(*len>0){
1581 				(*len)--;
1582 				str[*len]=0;
1583 				*val=atoi(str);
1584 			}
1585 			return 1;
1586 		} else if(key_code == SDLK_RETURN || key_code == SDLK_KP_ENTER){
1587 			if(!*val){
1588 				reset_quantity(edit_quantity);
1589 			}
1590 			item_quantity=*val;
1591 			quantities.selected=edit_quantity;
1592 			edit_quantity=-1;
1593 			return 1;
1594 		} else if(key_code == SDLK_ESCAPE){
1595 			reset_quantity(edit_quantity);
1596 			edit_quantity=-1;
1597 			return 1;
1598 		} else if(key_unicode >= '0' && key_unicode <= '9'){
1599 			if (*len<5)
1600 			{
1601 				str[*len]=key_unicode;
1602 				(*len)++;
1603 				str[*len]=0;
1604 				*val=atoi(str);
1605 			}
1606 			return 1;
1607 		}
1608 	}
1609 
1610 	return 0;
1611 }
1612 
drop_all_handler(void)1613 static void drop_all_handler (void)
1614 {
1615 	Uint8 str[6] = {0};
1616 	int i;
1617 	int dropped_something = 0;
1618 	static Uint32 last_click = 0;
1619 
1620 	/* provide some protection for inadvertent pressing (double click that can be disabled) */
1621 	if (safe_button_click(&last_click))
1622 	{
1623 		for(i = 0; i < ITEM_NUM_ITEMS; i++)
1624 		{
1625 			if (item_list[i].quantity != 0 &&  // only drop stuff that we're not wearing or not excluded
1626 				item_list[i].pos >= ((items_dropall_nofirstrow)?6:0) &&
1627 				item_list[i].pos < ((items_dropall_nolastrow)?30:ITEM_WEAR_START))
1628 			{
1629 				str[0] = DROP_ITEM;
1630 				str[1] = item_list[i].pos;
1631 				*((Uint32 *)(str+2)) = SDL_SwapLE32(item_list[i].quantity);
1632 				my_tcp_send (my_socket, str, 6);
1633 				dropped_something = 1;
1634 			}
1635 		}
1636 		if (dropped_something)
1637 			do_drop_item_sound();
1638 	}
1639 }
1640 
get_max_but_label_len(window_info * win)1641 static int get_max_but_label_len(window_info * win)
1642 {
1643 	char const *but_labels[] = { sto_all_str, get_all_str, drp_all_str, mix_all_str, mix_one_str, itm_lst_str };
1644 	int max_width = 0;
1645 	char *buf = NULL;
1646 	size_t i;
1647 	if (win == NULL)
1648 		return 0;
1649 	max_width = win->box_size;
1650 	for (i = 0; i < sizeof(but_labels) / sizeof(char *); i++)
1651 	{
1652 		char *saveptr = NULL;
1653 		char *next = NULL;
1654 		buf = realloc(buf, strlen(but_labels[i]) + 1);
1655 		strcpy((char *)buf, but_labels[i]);
1656 		next = strtok_r(buf, " ", &saveptr);
1657 		while (next != NULL)
1658 		{
1659 			max_width = max2i(max_width, get_buf_width_zoom((unsigned char *)next, strlen(next), win->font_category, button_text_zoom));
1660 			next = strtok_r(NULL, " ", &saveptr);
1661 		}
1662 	}
1663 	free(buf);
1664 	return max_width;
1665 }
1666 
show_items_handler(window_info * win)1667 static int show_items_handler(window_info * win)
1668 {
1669 	int i, win_x_len, win_y_len;
1670 	int seperator = (int)(0.5 + win->current_scale * 5);
1671 
1672 	if (!manual_size_items_window)
1673 		use_small_items_window = ((window_height<=600) || (window_width<=800));
1674 
1675 	items_grid.cols = 6;
1676 	items_grid.rows = 6;
1677 	items_grid.width = (int)(0.5 + win->current_scale * ((use_small_items_window) ?33: 50));
1678 	items_grid.height = (int)(0.5 + win->current_scale * ((use_small_items_window) ?33: 50));
1679 	items_grid.len_x = items_grid.width * items_grid.cols;
1680 	items_grid.len_y = items_grid.height * items_grid.rows;
1681 
1682 	equip_grid.cols = 2;
1683 	equip_grid.rows = 4;
1684 	equip_grid.width = (int)(0.5 + win->current_scale * 33);
1685 	equip_grid.height = (int)(0.5 + win->current_scale * 33);
1686 	equip_grid.len_x = equip_grid.width * equip_grid.cols;
1687 	equip_grid.len_y = equip_grid.height * equip_grid.rows;;
1688 
1689 	/* the buttons */
1690 	button_text_zoom = win->current_scale_small
1691 		* min2f(1.0, 0.5 * equip_grid.height / win->small_font_len_y);
1692 	buttons_grid.cols = 1;
1693 	buttons_grid.rows = NUMBUT;
1694 	buttons_grid.width = get_max_but_label_len(win) + (int)(0.5 + 6 * button_text_zoom);
1695 	buttons_grid.height = equip_grid.height;
1696 	buttons_grid.len_x = buttons_grid.width * buttons_grid.cols;
1697 	buttons_grid.len_y = buttons_grid.height * buttons_grid.rows;
1698 
1699 	/* we can have the eqipment grid and or the button bar on the left of right */
1700 	if (items_buttons_on_left)
1701 	{
1702 		buttons_grid.pos_x = 0;
1703 		if (items_equip_grid_on_left)
1704 		{
1705 			equip_grid.pos_x = buttons_grid.pos_x + buttons_grid.len_x + seperator;
1706 			items_grid.pos_x = equip_grid.pos_x + equip_grid.len_x + seperator;
1707 		}
1708 		else
1709 		{
1710 			items_grid.pos_x = buttons_grid.pos_x + buttons_grid.len_x + seperator;
1711 			equip_grid.pos_x = items_grid.pos_x + items_grid.len_x + seperator;
1712 		}
1713 		buttons_grid.pos_y = items_grid.height;
1714 		items_grid.pos_y = 0;
1715 		equip_grid.pos_y = items_grid.height;
1716 	}
1717 	else
1718 	{
1719 		if (items_equip_grid_on_left)
1720 		{
1721 			equip_grid.pos_x = 0;
1722 			items_grid.pos_x = equip_grid.pos_x + equip_grid.len_x + seperator;
1723 			buttons_grid.pos_x = items_grid.pos_x + items_grid.len_x + seperator;
1724 		}
1725 		else
1726 		{
1727 			items_grid.pos_x = 0;
1728 			equip_grid.pos_x = items_grid.pos_x + items_grid.len_x + seperator;
1729 			buttons_grid.pos_x = equip_grid.pos_x + equip_grid.len_x + seperator;
1730 		}
1731 		items_grid.pos_y = 0;
1732 		equip_grid.pos_y = items_grid.height;
1733 		buttons_grid.pos_y = items_grid.height;
1734 	}
1735 
1736 	text_arrow.len_x = unequip_arrow.len_x = equip_grid.width * 0.4;
1737 	text_arrow.len_y = unequip_arrow.len_y = equip_grid.height * 0.4;
1738 	unequip_arrow.pos_x = equip_grid.pos_x + ((items_equip_grid_on_left) ?equip_grid.width :0) + (equip_grid.width - unequip_arrow.len_x) / 2;
1739 	unequip_arrow.pos_y = equip_grid.pos_y + equip_grid.len_y + (equip_grid.height - unequip_arrow.len_y) / 2;
1740 
1741 	/* we can finally calculate the maximum y value so far .... */
1742 	win_y_len = ((items_grid.pos_y + items_grid.len_y) > (equip_grid.pos_y + equip_grid.len_y + text_arrow.len_y + seperator)) ?(items_grid.pos_y + items_grid.len_y) : (equip_grid.pos_y + equip_grid.len_y + text_arrow.len_y + seperator);
1743 	win_y_len = ((buttons_grid.pos_y + buttons_grid.len_y) > win_y_len) ?(buttons_grid.pos_y + buttons_grid.len_y) :win_y_len;
1744 
1745 	/* then we can finally poistion the message box arrow */
1746 	text_arrow.pos_x = equip_grid.pos_x + ((items_equip_grid_on_left) ?0 :equip_grid.width) + (equip_grid.width - text_arrow.len_x) / 2;
1747 	text_arrow.pos_y = win_y_len;
1748 
1749 	/* calculate the window width needed for all the components */
1750 	win_x_len = items_grid.len_x + equip_grid.len_x + buttons_grid.len_x + 2 * seperator + ((items_buttons_on_left && items_equip_grid_on_left) ?win->box_size :0);
1751 
1752 	/* the text message box */
1753 	message_box.rows = 4;
1754 	message_box.cols = 1;
1755 	message_box.len_x = win_x_len - 8;
1756 	message_box.len_y = win->small_font_len_y * message_box.rows;
1757 	message_box.pos_x = 4;
1758 	if (items_disable_text_block)
1759 		message_box.pos_y = 0;
1760 	else
1761 	{
1762 		message_box.pos_y = win_y_len + seperator;
1763 		win_y_len += message_box.len_y;
1764 	}
1765 
1766 	/* the load and quanity labels */
1767 	win_y_len += seperator;
1768 	labels_box.rows = 1;
1769 	labels_box.cols = 1;
1770 	labels_box.len_x = win_x_len - 8;
1771 	labels_box.len_y = win->small_font_len_y * labels_box.rows;
1772 	labels_box.pos_x = 4;
1773 	labels_box.pos_y = win_y_len;
1774 	win_y_len += labels_box.len_y;
1775 
1776 	/* the quanity numbers */
1777 	item_quantity = quantities.quantity[quantities.selected].val;
1778 	quantity_grid.cols = ITEM_EDIT_QUANT;
1779 	quantity_grid.rows = 1;
1780 	quantity_grid.width = (int)((float)win_x_len / (float)quantity_grid.cols);
1781 	quantity_grid.height = win->small_font_len_y + seperator;
1782 	quantity_grid.len_x = quantity_grid.width * quantity_grid.cols;
1783 	quantity_grid.len_y = quantity_grid.height * quantity_grid.rows;
1784 	quantity_grid.pos_x = (win_x_len - quantity_grid.len_x) / 2;
1785 	quantity_grid.pos_y = win_y_len;
1786 	win_y_len += quantity_grid.height;
1787 
1788 	resize_window(win->window_id, win_x_len, win_y_len);
1789 
1790 	cm_remove_regions(win->window_id);
1791 	for (i=0; i<NUMBUT; i++)
1792 		cm_add_region(buttons_cm_id[i], win->window_id, buttons_grid.pos_x, buttons_grid.pos_y + buttons_grid.height * i, buttons_grid.width, buttons_grid.height);
1793 
1794 	/* make sure we redraw any string */
1795 	last_items_string_id = 0;
1796 
1797 	return 1;
1798 }
1799 
context_items_handler(window_info * win,int widget_id,int mx,int my,int option)1800 static int context_items_handler(window_info *win, int widget_id, int mx, int my, int option)
1801 {
1802 	if (option<ELW_CM_MENU_LEN)
1803 		return cm_title_handler(win, widget_id, mx, my, option);
1804 	switch (option)
1805 	{
1806 		case ELW_CM_MENU_LEN+1: manual_size_items_window = 1; show_items_handler(win); break;
1807 		case ELW_CM_MENU_LEN+2: case ELW_CM_MENU_LEN+6: case ELW_CM_MENU_LEN+7: show_items_handler(win); break;
1808 		case ELW_CM_MENU_LEN+9: send_input_text_line("#sto", 4); break;
1809 	}
1810 	return 1;
1811 }
1812 
display_items_menu()1813 void display_items_menu()
1814 {
1815 	int items_win = get_id_MW(MW_ITEMS);
1816 	if(items_win < 0){
1817 		items_win = create_window(win_inventory, (not_on_top_now(MW_ITEMS) ?game_root_win : -1), 0, get_pos_x_MW(MW_ITEMS), get_pos_y_MW(MW_ITEMS),
1818 			0, 0, ELW_USE_UISCALE|ELW_WIN_DEFAULT);
1819 		set_id_MW(MW_ITEMS, items_win);
1820 
1821 		set_window_custom_scale(items_win, MW_ITEMS);
1822 		set_window_handler(items_win, ELW_HANDLER_DISPLAY, &display_items_handler );
1823 		set_window_handler(items_win, ELW_HANDLER_CLICK, &click_items_handler );
1824 		set_window_handler(items_win, ELW_HANDLER_MOUSEOVER, &mouseover_items_handler );
1825 		set_window_handler(items_win, ELW_HANDLER_KEYPRESS, (int (*)())&keypress_items_handler );
1826 		set_window_handler(items_win, ELW_HANDLER_SHOW, &show_items_handler );
1827 		set_window_handler(items_win, ELW_HANDLER_UI_SCALE, &show_items_handler );
1828 		set_window_handler(items_win, ELW_HANDLER_FONT_CHANGE, &show_items_handler);
1829 
1830 		cm_add(windows_list.window[items_win].cm_id, cm_items_menu_str, context_items_handler);
1831 		cm_bool_line(windows_list.window[items_win].cm_id, ELW_CM_MENU_LEN+1, &use_small_items_window, NULL);
1832 		cm_bool_line(windows_list.window[items_win].cm_id, ELW_CM_MENU_LEN+2, &manual_size_items_window, NULL);
1833 		cm_bool_line(windows_list.window[items_win].cm_id, ELW_CM_MENU_LEN+3, &item_window_on_drop, "item_window_on_drop");
1834 		cm_bool_line(windows_list.window[items_win].cm_id, ELW_CM_MENU_LEN+4, &allow_equip_swap, NULL);
1835 		cm_bool_line(windows_list.window[items_win].cm_id, ELW_CM_MENU_LEN+5, &items_mod_click_any_cursor, NULL);
1836 		cm_bool_line(windows_list.window[items_win].cm_id, ELW_CM_MENU_LEN+6, &items_buttons_on_left, NULL);
1837 		cm_bool_line(windows_list.window[items_win].cm_id, ELW_CM_MENU_LEN+7, &items_equip_grid_on_left, NULL);
1838 
1839 		buttons_cm_id[BUT_STORE] = cm_create(inv_keeprow_str, NULL);
1840 		cm_bool_line(buttons_cm_id[BUT_STORE], 0, &items_stoall_nofirstrow, NULL);
1841 		cm_bool_line(buttons_cm_id[BUT_STORE], 1, &items_stoall_nolastrow, NULL);
1842 
1843 		buttons_cm_id[BUT_DROP] = cm_create(inv_keeprow_str, NULL);
1844 		cm_bool_line(buttons_cm_id[BUT_DROP], 0, &items_dropall_nofirstrow, NULL);
1845 		cm_bool_line(buttons_cm_id[BUT_DROP], 1, &items_dropall_nolastrow, NULL);
1846 
1847 		buttons_cm_id[BUT_MIX] = cm_create(mix_all_str, NULL);
1848 		cm_bool_line(buttons_cm_id[BUT_MIX], 0, &items_mix_but_all, NULL);
1849 
1850 		buttons_cm_id[BUT_GET] = cm_create(auto_get_all_str, NULL);
1851 		cm_bool_line(buttons_cm_id[BUT_GET], 0, &items_auto_get_all, NULL);
1852 
1853 		buttons_cm_id[BUT_ITEM_LIST] = cm_create(item_list_but_str, NULL);
1854 		cm_bool_line(buttons_cm_id[BUT_ITEM_LIST], 0, &items_list_on_left, NULL);
1855 
1856 		show_items_handler(&windows_list.window[items_win]);
1857 		check_proportional_move(MW_ITEMS);
1858 
1859 	} else {
1860 		show_window(items_win);
1861 		select_window(items_win);
1862 	}
1863 }
1864 
get_items_cooldown(const Uint8 * data,int len)1865 void get_items_cooldown (const Uint8 *data, int len)
1866 {
1867 	int iitem, nitems, ibyte, pos;
1868 	Uint8 cooldown, max_cooldown;
1869 
1870 	// reset old cooldown values
1871 	for (iitem = 0; iitem < ITEM_NUM_ITEMS; iitem++)
1872 	{
1873 		item_list[iitem].cooldown_time = 0;
1874 		item_list[iitem].cooldown_rate = 1;
1875 	}
1876 
1877 	nitems = len / 5;
1878 	if (nitems <= 0) return;
1879 
1880 	ibyte = 0;
1881 	for (iitem = 0; iitem < nitems; iitem++)
1882 	{
1883 		pos = data[ibyte];
1884 		max_cooldown = SDL_SwapLE16 (*((Uint16*)(&data[ibyte+1])));
1885 		cooldown = SDL_SwapLE16 (*((Uint16*)(&data[ibyte+3])));
1886 		ibyte += 5;
1887 
1888 		item_list[pos].cooldown_rate = 1000 * (Uint32)max_cooldown;
1889 		item_list[pos].cooldown_time = cur_time + 1000 * (Uint32)cooldown;
1890 	}
1891 }
1892