1 /*
2 
3 	Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
4 	and the "Aleph One" developers.
5 
6 	This program is free software; you can redistribute it and/or modify
7 	it under the terms of the GNU General Public License as published by
8 	the Free Software Foundation; either version 3 of the License, or
9 	(at your option) any later version.
10 
11 	This program is distributed in the hope that it will be useful,
12 	but WITHOUT ANY WARRANTY; without even the implied warranty of
13 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 	GNU General Public License for more details.
15 
16 	This license is contained in the file "COPYING",
17 	which is included with this source code; it is available online at
18 	http://www.gnu.org/licenses/gpl.html
19 
20 */
21 
22 /*
23  *  HUDRenderer.cpp - HUD rendering base class and data
24  *
25  *  Written in 2001 by Christian Bauer
26  */
27 
28 #include "HUDRenderer.h"
29 #include "lua_script.h" // texture palette
30 
31 using namespace std;
32 
33 /*
34  *  Move rectangle
35  */
36 
offset_rect(screen_rectangle * rect,short dx,short dy)37 static inline void offset_rect(screen_rectangle *rect, short dx, short dy)
38 {
39 	rect->top += dy;
40 	rect->left += dx;
41 	rect->bottom += dy;
42 	rect->right += dx;
43 }
44 
45 
46 /*
47  *  Update all HUD elements
48  */
49 
50 extern bool shapes_file_is_m1();
51 
update_everything(short time_elapsed)52 bool HUD_Class::update_everything(short time_elapsed)
53 {
54 	ForceUpdate = false;
55 
56 	if (!LuaTexturePaletteSize())
57 	{
58 		if (!shapes_file_is_m1())
59 		{
60 			update_motion_sensor(time_elapsed);
61 			update_inventory_panel((time_elapsed == NONE) ? true : false);
62 			update_weapon_panel((time_elapsed == NONE) ? true : false);
63 			update_ammo_display((time_elapsed == NONE) ? true : false);
64 			update_suit_energy(time_elapsed);
65 			update_suit_oxygen(time_elapsed);
66 
67 			// Draw the message area if the player count is greater than one
68 			if (dynamic_world->player_count > 1)
69 				draw_message_area(time_elapsed);
70 		}
71 	}
72 	else
73 	{
74 		int size;
75 		// some good looking break points, based on 640x160
76 		if (LuaTexturePaletteSize() <= 5)
77 			size = 128;
78 		else if (LuaTexturePaletteSize() <= 16)
79 			size = 80;
80 		else if (LuaTexturePaletteSize() <= 36)
81 			size = 53;
82 		else if (LuaTexturePaletteSize() <= 64)
83 			size = 40;
84 		else if (LuaTexturePaletteSize() <= 100)
85 			size = 32;
86 		else if (LuaTexturePaletteSize() <= 144)
87 			size = 26;
88 		else
89 			size = 20;
90 
91 		int rows = 160 / size;
92 		int cols = 640 / size;
93 
94 		int x_offset = (640 - cols * size) / 2;
95 		int y_offset = (160 - rows * size) / 2;
96 
97 		for (int i = 0; i < LuaTexturePaletteSize(); ++i)
98 		{
99 			if (LuaTexturePaletteTexture(i) != UNONE)
100 				DrawTexture(LuaTexturePaletteTexture(i), LuaTexturePaletteTextureType(i), (i % cols) * size + x_offset, 320 + y_offset + (i / cols) * size, size - 1);
101 		}
102 
103 		if (LuaTexturePaletteSelected() >= 0)
104 		{
105 			int i = LuaTexturePaletteSelected();
106 			screen_rectangle r;
107 			r.left = (i % cols) * size + x_offset;
108 			r.right = r.left + size;
109 			r.top = 320 + y_offset + (i / cols) * size;
110 			r.bottom = r.top + size;
111 			FrameRect(&r, _inventory_text_color);
112 		}
113 
114 		ForceUpdate = true;
115 	}
116 
117 	return ForceUpdate;
118 }
119 
120 
121 /*
122  *  Update energy bar
123  */
124 
update_suit_energy(short time_elapsed)125 void HUD_Class::update_suit_energy(short time_elapsed)
126 {
127 	/* time_elapsed==NONE means force redraw */
128 	if (time_elapsed==NONE || (interface_state.shield_is_dirty))
129 	{
130 		// LP addition: display needs to be updated
131 		ForceUpdate = true;
132 
133 		screen_rectangle *shield_rect= get_interface_rectangle(_shield_rect);
134 		short width= shield_rect->right-shield_rect->left;
135 		short actual_width, suit_energy;
136 		short background_shape_id, bar_shape_id, bar_top_shape_id;
137 
138 		suit_energy = current_player->suit_energy%PLAYER_MAXIMUM_SUIT_ENERGY;
139 
140 		if(	!suit_energy &&
141 		   (current_player->suit_energy==PLAYER_MAXIMUM_SUIT_ENERGY ||
142 			current_player->suit_energy==2*PLAYER_MAXIMUM_SUIT_ENERGY ||
143 			current_player->suit_energy==3*PLAYER_MAXIMUM_SUIT_ENERGY))
144 		{
145 			suit_energy= PLAYER_MAXIMUM_SUIT_ENERGY;
146 		}
147 
148 		actual_width= (suit_energy*width)/PLAYER_MAXIMUM_SUIT_ENERGY;
149 
150 		/* Setup the bars.. */
151 		if(current_player->suit_energy>2*PLAYER_MAXIMUM_SUIT_ENERGY)
152 		{
153 			background_shape_id= _double_energy_bar;
154 			bar_shape_id= _triple_energy_bar;
155 			bar_top_shape_id= _triple_energy_bar_right;
156 		}
157 		else if(current_player->suit_energy>PLAYER_MAXIMUM_SUIT_ENERGY)
158 		{
159 			background_shape_id= _energy_bar;
160 			bar_shape_id= _double_energy_bar;
161 			bar_top_shape_id= _double_energy_bar_right;
162 		}
163 		else
164 		{
165 			background_shape_id= _empty_energy_bar;
166 			bar_shape_id= _energy_bar;
167 			bar_top_shape_id= _energy_bar_right;
168 			if(current_player->suit_energy<0) actual_width= 0;
169 		}
170 
171 		draw_bar(shield_rect, actual_width,
172 			BUILD_DESCRIPTOR(_collection_interface, bar_top_shape_id),
173 			BUILD_DESCRIPTOR(_collection_interface, bar_shape_id),
174 			BUILD_DESCRIPTOR(_collection_interface, background_shape_id));
175 
176 		interface_state.shield_is_dirty= false;
177 	}
178 }
179 
180 
181 /*
182  *  Update oxygen bar
183  */
184 
update_suit_oxygen(short time_elapsed)185 void HUD_Class::update_suit_oxygen(short time_elapsed)
186 {
187 	static short delay_time= 0;
188 
189 	/* Redraw the oxygen only if the interface is visible and only if enough delay has passed.. */
190 	if(((delay_time-= time_elapsed)<0) || time_elapsed==NONE || interface_state.oxygen_is_dirty)
191 	{
192 		// LP addition: display needs to be updated
193 		ForceUpdate = true;
194 
195 		screen_rectangle *oxygen_rect= get_interface_rectangle(_oxygen_rect);
196 		short width, actual_width;
197 		short suit_oxygen;
198 
199 		suit_oxygen= MIN(current_player->suit_oxygen, PLAYER_MAXIMUM_SUIT_OXYGEN);
200 		width= oxygen_rect->right-oxygen_rect->left;
201 		actual_width= (suit_oxygen*width)/PLAYER_MAXIMUM_SUIT_OXYGEN;
202 
203 		draw_bar(oxygen_rect, actual_width,
204 			BUILD_DESCRIPTOR(_collection_interface, _oxygen_bar_right),
205 			BUILD_DESCRIPTOR(_collection_interface, _oxygen_bar),
206 			BUILD_DESCRIPTOR(_collection_interface, _empty_oxygen_bar));
207 
208 		delay_time= DELAY_TICKS_BETWEEN_OXYGEN_REDRAW;
209 		interface_state.oxygen_is_dirty= false;
210 	}
211 }
212 
213 
214 /*
215  *  A change of weapon has occurred, change the weapon display panel
216  */
217 
update_weapon_panel(bool force_redraw)218 void HUD_Class::update_weapon_panel(bool force_redraw)
219 {
220 	if(force_redraw || interface_state.weapon_is_dirty)
221 	{
222 		// LP addition: display needs to be updated
223 		ForceUpdate = true;
224 
225 		char *weapon_name = temporary;
226 		struct weapon_interface_data *definition;
227 		screen_rectangle *destination= get_interface_rectangle(_weapon_display_rect);
228 		screen_rectangle source;
229 		short desired_weapon= get_player_desired_weapon(current_player_index);
230 
231 		/* Now we have to erase, because the panel won't do it for us.. */
232 		FillRect(destination, _inventory_background_color);
233 
234 		if(desired_weapon != NONE)
235 		{
236 			assert(desired_weapon>=0 && desired_weapon<short(MAXIMUM_WEAPON_INTERFACE_DEFINITIONS));
237 
238 			definition= weapon_interface_definitions+desired_weapon;
239 
240 			/* Check if it is a multi weapon */
241 			if(definition->multi_weapon)
242 			{
243 				if (definition->multiple_unusable_shape != UNONE)
244 				{
245 					/* always draw the single */
246 					if (definition->weapon_panel_shape != UNONE)
247 						DrawShapeAtXY(definition->weapon_panel_shape,
248 							      definition->standard_weapon_panel_left,
249 							      definition->standard_weapon_panel_top);
250 
251 					if(current_player->items[definition->item_id]>1)
252 					{
253 						if (definition->multiple_shape != UNONE)
254 							DrawShapeAtXY(
255 								definition->multiple_shape,
256 								definition->standard_weapon_panel_left + definition->multiple_delta_x,
257 								definition->standard_weapon_panel_top + definition->multiple_delta_y);
258 					}
259 					else
260 					{
261 						/* Draw the empty one.. */
262 						DrawShapeAtXY(
263 							definition->multiple_unusable_shape,
264 							definition->standard_weapon_panel_left + definition->multiple_delta_x,
265 							definition->standard_weapon_panel_top + definition->multiple_delta_y);
266 					}
267 				}
268 				else
269 				{
270 					if(current_player->items[definition->item_id]>1)
271 					{
272 						if (definition->multiple_shape != UNONE)
273 							DrawShapeAtXY(
274 								definition->multiple_shape,
275 								definition->standard_weapon_panel_left + definition->multiple_delta_x,
276 								definition->standard_weapon_panel_top + definition->multiple_delta_y);
277 					} else {
278 						if (definition->weapon_panel_shape != UNONE)
279 							DrawShapeAtXY(definition->weapon_panel_shape,
280 								      definition->standard_weapon_panel_left,
281 								      definition->standard_weapon_panel_top);
282 					}
283 				}
284 			} else {
285 				/* Slam it to the screen! */
286 				if(definition->weapon_panel_shape != UNONE)
287 				{
288 					DrawShapeAtXY(definition->weapon_panel_shape,
289 						definition->standard_weapon_panel_left,
290 						definition->standard_weapon_panel_top);
291 				}
292 			}
293 
294 			/* Get the weapon name.. */
295 			if(desired_weapon != _weapon_ball)
296 			{
297 #define strWEAPON_NAME_LIST 137
298 				getcstr(weapon_name, strWEAPON_NAME_LIST, desired_weapon);
299 			} else {
300 				short item_index;
301 
302 				/* Which ball do they actually have? */
303 				for(item_index= BALL_ITEM_BASE; item_index<BALL_ITEM_BASE+MAXIMUM_NUMBER_OF_PLAYERS; ++item_index)
304 				{
305 					if(current_player->items[item_index]>0) break;
306 				}
307 				assert(item_index != BALL_ITEM_BASE+MAXIMUM_NUMBER_OF_PLAYERS);
308 				get_item_name(weapon_name, item_index, false);
309 			}
310 
311 			/* Draw the weapon name.. */
312 			source= *destination;
313 			source.top= definition->weapon_name_start_y;
314 			source.bottom= definition->weapon_name_end_y;
315 			if(definition->weapon_name_start_x != NONE)
316 			{
317 				source.left= definition->weapon_name_start_x;
318 			}
319 
320 			if(definition->weapon_name_end_x != NONE)
321 			{
322 				source.right= definition->weapon_name_end_x;
323 			}
324 
325 			DrawText(weapon_name, &source, _center_horizontal|_center_vertical|_wrap_text,
326 				_weapon_name_font, _inventory_text_color);
327 
328 			/* And make sure that the ammo knows it needs to update */
329 			interface_state.ammo_is_dirty= true;
330 		}
331 		interface_state.weapon_is_dirty= false;
332 	}
333 }
334 
335 
336 /*
337  *  Update ammunition display
338  */
339 
update_ammo_display(bool force_redraw)340 void HUD_Class::update_ammo_display(bool force_redraw)
341 {
342 	if(force_redraw || interface_state.ammo_is_dirty)
343 	{
344 		// LP addition: display needs to be updated
345 		ForceUpdate = true;
346 
347 		draw_ammo_display_in_panel(_primary_interface_ammo);
348 		draw_ammo_display_in_panel(_secondary_interface_ammo);
349 		interface_state.ammo_is_dirty= false;
350 	}
351 }
352 
353 
354 /*
355  *  Update inventory display
356  */
357 
update_inventory_panel(bool force_redraw)358 void HUD_Class::update_inventory_panel(bool force_redraw)
359 {
360 	/* changed_item gets erased first.. */
361 	/* This should probably go to a gworld first, or something */
362 	short section_items[NUMBER_OF_ITEMS];
363 	short section_counts[NUMBER_OF_ITEMS];
364 	short section_count, loop;
365 	short item_type, current_row;
366 
367 	if(INVENTORY_IS_DIRTY(current_player) || force_redraw)
368 	{
369 		// LP addition: display needs to be updated
370 		ForceUpdate = true;
371 
372 		screen_rectangle *destination= get_interface_rectangle(_inventory_rect);
373 		screen_rectangle text_rectangle;
374 		short max_lines= max_displayable_inventory_lines();
375 
376 		/* Recalculate and redraw.. */
377 		item_type= GET_CURRENT_INVENTORY_SCREEN(current_player);
378 
379 		/* Reset the row.. */
380 		current_row= 0;
381 		if(item_type!=_network_statistics)
382 		{
383 			/* Get the types and names */
384 			calculate_player_item_array(current_player_index, item_type,
385 				section_items, section_counts, &section_count);
386 		}
387 
388 		/* Draw the header. */
389 		get_header_name(temporary, item_type);
390 		draw_inventory_header(temporary, current_row++);
391 
392 		/* Erase the panel.. */
393 		text_rectangle= *destination;
394 		text_rectangle.top+= _get_font_line_height(_interface_font);
395 		FillRect(&text_rectangle, _inventory_background_color);
396 
397 #if !defined(DISABLE_NETWORKING)
398 		if (item_type==_network_statistics)
399 		{
400 			char remaining_time[16];
401 			int seconds = dynamic_world->game_information.game_time_remaining / TICKS_PER_SECOND;
402 			if (seconds / 60 < 1000) // start counting down at 999 minutes
403 			{
404 				sprintf(remaining_time, "%d:%02d", seconds/60, seconds%60);
405 				draw_inventory_time(remaining_time, current_row-1); // compensate for current_row++ above
406 			} else if (GET_GAME_OPTIONS() & _game_has_kill_limit)
407 			{
408 				switch (GET_GAME_TYPE())
409 				{
410 					case _game_of_kill_monsters:
411 					case _game_of_cooperative_play:
412 					case _game_of_king_of_the_hill:
413 					case _game_of_kill_man_with_ball:
414 					case _game_of_tag:
415 
416 						short player_index;
417 						int kill_limit = INT_MAX;
418 						for (player_index = 0; player_index < dynamic_world->player_count;++player_index)
419 						{
420 							struct player_data *player = get_player_data(player_index);
421 
422 							int kills_left = dynamic_world->game_information.kill_limit - (player->total_damage_given.kills - player->damage_taken[player_index].kills);
423 							if (kills_left < kill_limit) kill_limit = kills_left;
424 						}
425 						char kills_left[4];
426 						sprintf(kills_left, "%d", kill_limit);
427 						draw_inventory_time(kills_left, current_row-1);
428 						break;
429 				}
430 			}
431 
432 			struct player_ranking_data rankings[MAXIMUM_NUMBER_OF_PLAYERS];
433 
434 			calculate_player_rankings(rankings);
435 
436 			/* Calculate the network statistics. */
437 			for(loop= 0; loop<dynamic_world->player_count; ++loop)
438 			{
439 				screen_rectangle dest_rect;
440 				struct player_data *player= get_player_data(rankings[loop].player_index);
441 				short width;
442 
443 				calculate_inventory_rectangle_from_offset(&dest_rect, current_row++);
444 				calculate_ranking_text(temporary, rankings[loop].ranking);
445 
446 				/* Draw the player name.. */
447 				width= _text_width(temporary, _interface_font);
448 				dest_rect.right-= width;
449 				dest_rect.left+= TEXT_INSET;
450 				DrawText(player->name, &dest_rect, _center_vertical,
451 					 _interface_font, PLAYER_COLOR_BASE_INDEX+player->color);
452 
453 				/* Now draw the ranking_text */
454 				dest_rect.right+= width;
455 				dest_rect.left= dest_rect.right-width;
456 				DrawText(temporary, &dest_rect, _center_vertical,
457 					 _interface_font, PLAYER_COLOR_BASE_INDEX+player->color);
458 			}
459 		}
460 		else
461 #endif // !defined(DISABLE_NETWORKING)
462 		{
463 			/* Draw the items. */
464 			for(loop= 0; loop<section_count && current_row<max_lines; ++loop)
465 			{
466 				bool valid_in_this_environment;
467 
468 				/* Draw the item */
469 				get_item_name(temporary, section_items[loop], (section_counts[loop]!=1));
470 				valid_in_this_environment= item_valid_in_current_environment(section_items[loop]);
471 				draw_inventory_item(temporary, section_counts[loop], current_row++, false, valid_in_this_environment);
472 			}
473 		}
474 
475 		SET_INVENTORY_DIRTY_STATE(current_player, false);
476 	}
477 }
478 
479 
480 /*
481  *  Draw the text in the rectangle, starting at the given offset, on the
482  *  far left.  Headers also have their backgrounds erased first.
483  */
484 
draw_inventory_header(char * text,short offset)485 void HUD_Class::draw_inventory_header(char *text, short offset)
486 {
487 	screen_rectangle destination;
488 
489 	calculate_inventory_rectangle_from_offset(&destination, offset);
490 
491 	/* Erase.. */
492 	FillRect(&destination, _inventory_header_background_color);
493 
494 	/* Now draw the text. */
495 	destination.left+= TEXT_INSET;
496 	DrawText(text, &destination, _center_vertical, _interface_font,
497 		_inventory_text_color);
498 }
499 
500 /*
501  * Draw the time in the far right of the rectangle; does not erase the background...should always be called after
502  * draw_inventory_header
503  */
draw_inventory_time(char * text,short offset)504 void HUD_Class::draw_inventory_time(char *text, short offset)
505 {
506 
507     screen_rectangle destination;
508 
509     calculate_inventory_rectangle_from_offset(&destination, offset);
510 
511     destination.left = destination.right - _text_width(text, _interface_font) - TEXT_INSET;
512     DrawText(text, &destination, _center_vertical, _interface_font, _inventory_text_color);
513 }
514 
515 
516 /*
517  *  Calculate inventory rectangle
518  */
519 
calculate_inventory_rectangle_from_offset(screen_rectangle * r,short offset)520 void HUD_Class::calculate_inventory_rectangle_from_offset(screen_rectangle *r, short offset)
521 {
522 	screen_rectangle *inventory_rect= get_interface_rectangle(_inventory_rect);
523 	short line_height= _get_font_line_height(_interface_font);
524 
525 	*r= *inventory_rect;
526 	r->top += offset*line_height;
527 	r->bottom= r->top + line_height;
528 }
529 
530 
531 /*
532  *  Calculate number of visible inventory lines
533  */
534 
max_displayable_inventory_lines(void)535 short HUD_Class::max_displayable_inventory_lines(void)
536 {
537 	screen_rectangle *destination= get_interface_rectangle(_inventory_rect);
538 	return (destination->bottom-destination->top)/_get_font_line_height(_interface_font);
539 }
540 
541 
542 /*
543  *  Draw health/oxygen bar
544  */
545 
draw_bar(screen_rectangle * rectangle,short width,shape_descriptor top_piece,shape_descriptor full_bar,shape_descriptor background_texture)546 void HUD_Class::draw_bar(screen_rectangle *rectangle, short width,
547 	shape_descriptor top_piece, shape_descriptor full_bar,
548 	shape_descriptor background_texture)
549 {
550 	screen_rectangle destination= *rectangle;
551 	screen_rectangle source;
552 	screen_rectangle bar_section;
553 
554 	/* Draw the background (right). */
555 	destination.left+= width;
556 	source= destination;
557 
558 	offset_rect(&source, -rectangle->left, -rectangle->top);
559 	DrawShape(background_texture, &destination, &source);
560 
561 	bar_section= *rectangle;
562 	bar_section.right= bar_section.left+width-TOP_OF_BAR_WIDTH;
563 
564 	/* Draw the top bit.. */
565 	if(width>2*TOP_OF_BAR_WIDTH)
566 	{
567 		DrawShapeAtXY(top_piece, rectangle->left+width-TOP_OF_BAR_WIDTH,
568 			rectangle->top);
569 	} else {
570 		destination= *rectangle;
571 
572 		/* Gotta take lines off the top, so that the bottom stuff is still visible.. */
573 		destination.left= rectangle->left+width/2+width%2;
574 		bar_section.right= destination.left;
575 		destination.right= destination.left+width/2;
576 
577 		source= destination;
578 		offset_rect(&source, -source.left+TOP_OF_BAR_WIDTH-width/2, -source.top);
579 		DrawShape(top_piece, &destination, &source);
580 	}
581 
582 	/* Copy the bar.. */
583 	if(bar_section.left<bar_section.right)
584 	{
585 		screen_rectangle bar_section_source= bar_section;
586 
587 		offset_rect(&bar_section_source, -rectangle->left, -rectangle->top);
588 		DrawShape(full_bar, &bar_section, &bar_section_source);
589 	}
590 }
591 
592 
593 /*
594  *  Draw ammo display
595  */
596 
draw_ammo_display_in_panel(short trigger_id)597 void HUD_Class::draw_ammo_display_in_panel(short trigger_id)
598 {
599 	struct weapon_interface_data *current_weapon_data;
600 	struct weapon_interface_ammo_data *current_ammo_data;
601 	short ammunition_count;
602 	short desired_weapon= get_player_desired_weapon(current_player_index);
603 
604 	/* Based on desired weapon, so we can get ammo updates as soon as we change */
605 	if(desired_weapon != NONE)
606 	{
607 		current_weapon_data= weapon_interface_definitions+desired_weapon;
608 		current_ammo_data= &current_weapon_data->ammo_data[trigger_id];
609 
610 		if(trigger_id==_primary_interface_ammo)
611 		{
612 			ammunition_count= get_player_weapon_ammo_count(current_player_index, desired_weapon, _primary_weapon);
613 		} else {
614 			ammunition_count= get_player_weapon_ammo_count(current_player_index, desired_weapon, _secondary_weapon);
615 		}
616 
617 		/* IF we have ammo for this trigger.. */
618 		if(current_ammo_data->type!=_unused_interface_data && ammunition_count!=NONE)
619 		{
620 			if(current_ammo_data->type==_uses_energy)
621 			{
622 				/* Energy beam weapon-> progress bar type.. */
623 				short  fill_height;
624 				screen_rectangle bounds;
625 
626 				/* Pin it.. */
627 				ammunition_count= PIN(ammunition_count, 0, current_ammo_data->ammo_across);
628 				fill_height= (ammunition_count*(current_ammo_data->delta_y-2))/current_ammo_data->ammo_across;
629 
630 				/* Setup the energy left bar... */
631 				bounds.left= current_ammo_data->screen_left;
632 				bounds.right= current_ammo_data->screen_left+current_ammo_data->delta_x;
633 				bounds.bottom= current_ammo_data->screen_top+current_ammo_data->delta_y;
634 				bounds.top= current_ammo_data->screen_top;
635 
636 				/* Draw the full rectangle */
637 				FillRect(&bounds, current_ammo_data->bullet);
638 
639 				/* Inset the rectangle.. */
640 				bounds.left+=1; bounds.right-=1; bounds.bottom-= 1; bounds.top+=1;
641 
642 				/* ...and erase the empty part of the rectangle */
643 				bounds.bottom-= fill_height;
644 				bounds.top= current_ammo_data->screen_top+1;
645 
646 				/* Fill it. */
647 				FillRect(&bounds, current_ammo_data->empty_bullet);
648 
649 				/* We be done.. */
650 			} else {
651 				/* Uses ammunition, a little trickier.. */
652 				short row, x, y;
653 				screen_rectangle destination, source;
654 				short max, partial_row_count;
655 
656 				x= current_ammo_data->screen_left;
657 				y= current_ammo_data->screen_top;
658 
659 				destination.left= x;
660 				destination.top= y;
661 
662 				/* Pin it.. */
663 				max= current_ammo_data->ammo_down*current_ammo_data->ammo_across;
664 				ammunition_count= PIN(ammunition_count, 0, max);
665 
666 				/* Draw all of the full rows.. */
667 				for(row=0; row<(ammunition_count/current_ammo_data->ammo_across); ++row)
668 				{
669 					DrawShapeAtXY(current_ammo_data->bullet,
670 						x, y);
671 					y+= current_ammo_data->delta_y;
672 				}
673 
674 				/* Draw the partially used row.. */
675 				partial_row_count= ammunition_count%current_ammo_data->ammo_across;
676 				if(partial_row_count)
677 				{
678 					/* If we use ammo from right to left.. */
679 					if(current_ammo_data->right_to_left)
680 					{
681 						/* Draw the unused part of the row.. */
682 						destination.left= x, destination.top= y;
683 						destination.right= x+(partial_row_count*current_ammo_data->delta_x);
684 						destination.bottom= y+current_ammo_data->delta_y;
685 						source= destination;
686 						offset_rect(&source, -source.left, -source.top);
687 						DrawShape(current_ammo_data->bullet, &destination, &source);
688 
689 						/* Draw the used part of the row.. */
690 						destination.left= destination.right,
691 						destination.right= destination.left+(current_ammo_data->ammo_across-partial_row_count)*current_ammo_data->delta_x;
692 						source= destination;
693 						offset_rect(&source, -source.left, -source.top);
694 						DrawShape(current_ammo_data->empty_bullet, &destination, &source);
695 					} else {
696 						/* Draw the used part of the row.. */
697 						destination.left= x, destination.top= y;
698 						destination.right= x+(current_ammo_data->ammo_across-partial_row_count)*current_ammo_data->delta_x;
699 						destination.bottom= y+current_ammo_data->delta_y;
700 						source= destination;
701 						offset_rect(&source, -source.left, -source.top);
702 						DrawShape(current_ammo_data->empty_bullet, &destination, &source);
703 
704 						/* Draw the unused part of the row */
705 						destination.left= destination.right;
706 						destination.right= destination.left+(partial_row_count*current_ammo_data->delta_x);
707 						source= destination;
708 						offset_rect(&source, -source.left, -source.top);
709 						DrawShape(current_ammo_data->bullet, &destination, &source);
710 					}
711 					y+= current_ammo_data->delta_y;
712 				}
713 
714 				/* Draw the remaining rows. */
715 				x= current_ammo_data->screen_left;
716 				for(row=0; row<(max-ammunition_count)/current_ammo_data->ammo_across; ++row)
717 				{
718 					DrawShapeAtXY(current_ammo_data->empty_bullet,
719 						x, y);
720 					y+= current_ammo_data->delta_y;
721 				}
722 			}
723 		}
724 	}
725 }
726 
727 
728 /*
729  *  Draw inventory item
730  */
731 
draw_inventory_item(char * text,short count,short offset,bool erase_first,bool valid_in_this_environment)732 void HUD_Class::draw_inventory_item(char *text, short count,
733 	short offset, bool erase_first, bool valid_in_this_environment)
734 {
735 	screen_rectangle destination, text_destination;
736 	char *count_text = temporary;
737 	short color;
738 
739 	calculate_inventory_rectangle_from_offset(&destination, offset);
740 
741 	/* Select the color for the text.. */
742 	color= (valid_in_this_environment) ? (_inventory_text_color) : (_invalid_weapon_color);
743 
744 	/* Erase on items that changed only in count.. */
745 	if(erase_first)
746 	{
747 		FillRect(&destination, _inventory_background_color);
748 	} else {
749 		/* Unfortunately, we must always erase the numbers.. */
750 		text_destination= destination;
751 		text_destination.right= text_destination.left+NAME_OFFSET+TEXT_INSET;
752 		FillRect(&text_destination, _inventory_background_color);
753 	}
754 
755 	/* Draw the text name.. */
756 	text_destination= destination;
757 	text_destination.left+= NAME_OFFSET+TEXT_INSET;
758 	DrawText(text, &text_destination, _center_vertical, _interface_font, color);
759 
760 	/* Draw the text count-> Change the font!! */
761 	text_destination= destination;
762 	text_destination.left+= TEXT_INSET;
763 	sprintf(count_text, "%3d", count);
764 	DrawText(count_text, &text_destination, _center_vertical, _interface_item_count_font, color);
765 }
766 
767 
768 /*
769  *  Draw player name
770  */
771 
draw_player_name(void)772 void HUD_Class::draw_player_name(void)
773 {
774 	struct player_data *player= get_player_data(current_player_index);
775 	screen_rectangle *player_name_rect= get_interface_rectangle(_player_name_rect);
776 
777 	DrawText(player->name, player_name_rect,
778 		_center_vertical | _center_horizontal, _player_name_font,
779 		player->color+PLAYER_COLOR_BASE_INDEX);
780 }
781 
782 
783 /*
784  * Draw the message area, and then put messages or player name in the buffer
785  */
786 
787 #define MESSAGE_AREA_X_OFFSET -9
788 #define MESSAGE_AREA_Y_OFFSET -5
789 
draw_message_area(short time_elapsed)790 void HUD_Class::draw_message_area(short time_elapsed)
791 {
792 	if(time_elapsed == NONE)
793 	{
794         screen_rectangle *player_name_rect = get_interface_rectangle(_player_name_rect);
795 		DrawShapeAtXY(
796 			BUILD_DESCRIPTOR(_collection_interface, _network_panel),
797 			player_name_rect->left + MESSAGE_AREA_X_OFFSET, player_name_rect->top + MESSAGE_AREA_Y_OFFSET);
798 		draw_player_name();
799 	}
800 }
801