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, §ion_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= ¤t_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