1 /*
2 GAME_WINDOW.C
3
4 Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
5 and the "Aleph One" developers.
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 This license is contained in the file "COPYING",
18 which is included with this source code; it is available online at
19 http://www.gnu.org/licenses/gpl.html
20
21 Thursday, December 30, 1993 9:51:24 PM
22
23 Tuesday, May 24, 1994 6:42:32 PM
24 functions beginning with underscores are mac-specific and always have a corresponding portable
25 function which sets up game-specific information (see update_compass and _update_compass for
26 an example).
27 Friday, June 10, 1994 3:56:57 AM
28 gutted, rewritten. Much cleaner now.
29 Sunday, September 4, 1994 6:32:05 PM
30 made scroll_inventory() non-static so that shell.c can call it.
31 Saturday, September 24, 1994 10:33:19 AM
32 fixed some things, twice...
33 Friday, October 7, 1994 3:13:22 PM
34 New interface. Draws panels from PICT resources for memory consumption. Inventory is
35 different panels, which are switched to whenever you grab an item. There is no scrolling.
36 Tuesday, June 6, 1995 3:37:50 PM
37 Marathon II modifications.
38 Tuesday, August 29, 1995 4:02:15 PM
39 Reestablishing a level of portablility...
40
41 Feb 4, 2000 (Loren Petrich):
42 Added SMG display stuff
43
44 Apr 30, 2000 (Loren Petrich): Added XML parser object for all the screen_drawing data,
45 and also essentiall all the data here.
46
47 May 28, 2000 (Loren Petrich): Added support for buffering the Heads-Up Display
48
49 Jul 2, 2000 (Loren Petrich):
50 The HUD is now always buffered
51
52 Jul 16, 2001 (Loren Petrich):
53 Using "temporary" as storage space for count_text and weapon_name;
54 it is 256 bytes long, which should be more than enough for most text.
55 This fixes the long-weapon-name bug.
56
57 Mar 08, 2002 (Woody Zenfell):
58 SDL network microphone support
59 */
60
61 #include "cseries.h"
62
63 #ifdef __WIN32__
64 #include <windows.h>
65 #endif
66
67 #ifdef HAVE_OPENGL
68 #include "OGL_Headers.h"
69 #endif
70
71 #include "HUDRenderer_SW.h"
72 #include "game_window.h"
73
74 // LP addition: color and font parsers
75 #include "FontHandler.h"
76 #include "screen.h"
77
78 #include "shell.h"
79 #include "preferences.h"
80 #include "screen.h"
81 #include "screen_definitions.h"
82 #include "images.h"
83 #include "InfoTree.h"
84
85 #include "network_sound.h"
86
87 extern void draw_panels(void);
88 extern void validate_world_window(void);
89 static void set_current_inventory_screen(short player_index, short screen);
90
91 /* --------- globals */
92
93 // LP addition: whether or not the motion sensor is active
94 bool MotionSensorActive = true;
95
96 struct interface_state_data interface_state;
97
98 #define NUMBER_OF_WEAPON_INTERFACE_DEFINITIONS 10
99 struct weapon_interface_data weapon_interface_definitions[NUMBER_OF_WEAPON_INTERFACE_DEFINITIONS] =
100 {
101 /* Mac, the knife.. */
102 {
103 _i_knife,
104 UNONE,
105 433, 432,
106 NONE, NONE,
107 0, 0,
108 false,
109 {
110 { _unused_interface_data, 0, 0, 0, 0, 0, 0, UNONE, UNONE, true},
111 { _unused_interface_data, 0, 0, 0, 0, 0, 0, UNONE, UNONE, true}
112 },
113 UNONE, UNONE,
114 0, 0
115 },
116
117 /* Harry, the .44 */
118 {
119 _i_magnum,
120 BUILD_DESCRIPTOR(_collection_interface, _magnum_panel),
121 432, 444,
122 420, NONE,
123 366, 517,
124 true,
125 {
126 { _uses_bullets, 517, 412, 8, 1, 5, 14, BUILD_DESCRIPTOR(_collection_interface, _magnum_bullet), BUILD_DESCRIPTOR(_collection_interface, _magnum_casing), false},
127 { _uses_bullets, 452, 412, 8, 1, 5, 14, BUILD_DESCRIPTOR(_collection_interface, _magnum_bullet), BUILD_DESCRIPTOR(_collection_interface, _magnum_casing), true}
128 },
129 BUILD_DESCRIPTOR(_collection_interface, _left_magnum),
130 BUILD_DESCRIPTOR(_collection_interface, _left_magnum_unusable),
131 -97, 0
132 },
133
134 /* Ripley, the plasma pistol. */
135 {
136 _i_plasma_pistol,
137 BUILD_DESCRIPTOR(_collection_interface, _zeus_panel),
138 431, 443,
139 401, NONE,
140 366, 475,
141 false,
142 {
143 { _uses_energy, 414, 366, 20, 0, 38, 57, _energy_weapon_full_color, _energy_weapon_empty_color, true},
144 { _unused_interface_data, 450, 410, 50, 0, 62, 7, _energy_weapon_full_color, _energy_weapon_empty_color, true}
145 },
146 UNONE, UNONE,
147 0, 0
148 },
149
150 /* Arnold, the assault rifle */
151 {
152 _i_assault_rifle,
153 BUILD_DESCRIPTOR(_collection_interface, _assault_panel),
154 430, 452,
155 439, NONE, //��
156 366, 460,
157 false,
158 {
159 { _uses_bullets, 391, 368, 13, 4, 4, 10, BUILD_DESCRIPTOR(_collection_interface, _assault_rifle_bullet), BUILD_DESCRIPTOR(_collection_interface, _assault_rifle_casing), true},
160 { _uses_bullets, 390, 413, 7, 1, 8, 12, BUILD_DESCRIPTOR(_collection_interface, _assault_rifle_grenade), BUILD_DESCRIPTOR(_collection_interface, _assault_rifle_grenade_casing), true},
161 },
162 UNONE, UNONE,
163 0, 0
164 },
165
166 /* John R., the missile launcher */
167 {
168 _i_missile_launcher,
169 BUILD_DESCRIPTOR(_collection_interface, _missile_panel),
170 433, 445,
171 426, NONE,
172 365, 419,
173 false,
174 {
175 { _uses_bullets, 385, 376, 2, 1, 16, 49, BUILD_DESCRIPTOR(_collection_interface, _missile), BUILD_DESCRIPTOR(_collection_interface, _missile_casing), true},
176 { _unused_interface_data, 0, 0, 0, 0, 0, 0, UNONE, UNONE, true }
177 },
178 UNONE, UNONE,
179 0, 0
180 },
181
182 /* ???, the flame thrower */
183 {
184 _i_flamethrower,
185 BUILD_DESCRIPTOR(_collection_interface, _flamethrower_panel),
186 433, 445,
187 398, NONE,
188 363, 475,
189 false,
190 {
191 /* This weapon has 7 seconds of flamethrower carnage.. */
192 { _uses_energy, 427, 369, 7*TICKS_PER_SECOND, 0, 38, 57, _energy_weapon_full_color, _energy_weapon_empty_color, true},
193 { _unused_interface_data, 450, 410, 50, 0, 62, 7, _energy_weapon_full_color, _energy_weapon_empty_color, true}
194 },
195 UNONE, UNONE,
196 0, 0
197 },
198
199 /* Predator, the alien shotgun */
200 {
201 _i_alien_shotgun,
202 BUILD_DESCRIPTOR(_collection_interface, _alien_weapon_panel),
203 418, 445,
204 395, 575,
205 359, 400,
206 false,
207 {
208 { _unused_interface_data, 425, 411, 50, 0, 96, 7, _energy_weapon_full_color, _energy_weapon_empty_color, true},
209 { _unused_interface_data, 450, 410, 50, 0, 62, 7, _energy_weapon_full_color, _energy_weapon_empty_color, true}
210 },
211 UNONE, UNONE,
212 0, 0
213 },
214
215 /* Shotgun */
216 {
217 _i_shotgun,
218 BUILD_DESCRIPTOR(_collection_interface, _single_shotgun),
219 432, 444,
220 420, NONE,
221 373, 451,
222 true,
223 {
224 { _uses_bullets, 483, 411, 2, 1, 12, 16, BUILD_DESCRIPTOR(_collection_interface, _shotgun_bullet), BUILD_DESCRIPTOR(_collection_interface, _shotgun_casing), true},
225 { _uses_bullets, 451, 411, 2, 1, 12, 16, BUILD_DESCRIPTOR(_collection_interface, _shotgun_bullet), BUILD_DESCRIPTOR(_collection_interface, _shotgun_casing), true}
226 },
227 BUILD_DESCRIPTOR(_collection_interface, _double_shotgun),
228 UNONE,
229 0, -12
230 },
231
232 /* Ball */
233 {
234 _i_red_ball, // statistically unlikely to be valid (really should be SKULL)
235 BUILD_DESCRIPTOR(_collection_interface, _skull),
236 432, 444,
237 402, NONE,
238 366, 465,
239 false,
240 {
241 { _unused_interface_data, 451, 411, 2, 1, 12, 16, BUILD_DESCRIPTOR(_collection_interface, _shotgun_bullet), BUILD_DESCRIPTOR(_collection_interface, _shotgun_casing), true},
242 { _unused_interface_data, 483, 411, 2, 1, 12, 16, BUILD_DESCRIPTOR(_collection_interface, _shotgun_bullet), BUILD_DESCRIPTOR(_collection_interface, _shotgun_casing), true}
243 },
244 UNONE, UNONE,
245 0, 0
246 },
247
248 /* LP addition: SMG (clone of assault rifle) */
249 {
250 _i_smg,
251 BUILD_DESCRIPTOR(_collection_interface, _smg),
252 430, 452,
253 439, NONE, //��
254 366, 460,
255 false,
256 {
257 { _uses_bullets, 405, 382, 8, 4, 5, 10, BUILD_DESCRIPTOR(_collection_interface, _smg_bullet), BUILD_DESCRIPTOR(_collection_interface, _smg_casing), true},
258 { _unused_interface_data, 390, 413, 7, 1, 8, 12, BUILD_DESCRIPTOR(_collection_interface, _assault_rifle_grenade), BUILD_DESCRIPTOR(_collection_interface, _assault_rifle_grenade_casing), true},
259 },
260 UNONE, UNONE,
261 0, 0
262 },
263 };
264
265 // Software rendering
266 HUD_SW_Class HUD_SW;
267
268 /* --------- code */
269
initialize_game_window(void)270 void initialize_game_window(void)
271 {
272 initialize_motion_sensor(
273 BUILD_DESCRIPTOR(_collection_interface, _motion_sensor_mount),
274 BUILD_DESCRIPTOR(_collection_interface, _motion_sensor_virgin_mount),
275 BUILD_DESCRIPTOR(_collection_interface, _motion_sensor_alien),
276 BUILD_DESCRIPTOR(_collection_interface, _motion_sensor_friend),
277 BUILD_DESCRIPTOR(_collection_interface, _motion_sensor_enemy),
278 BUILD_DESCRIPTOR(_collection_interface, _network_compass_shape_nw),
279 MOTION_SENSOR_SIDE_LENGTH);
280 }
281
282 /* draws the entire interface */
draw_interface(void)283 void draw_interface(void)
284 {
285 if (alephone::Screen::instance()->openGL())
286 return;
287
288 if (!game_window_is_full_screen())
289 {
290 /* draw the frame */
291 draw_panels();
292 }
293
294 validate_world_window();
295 }
296
297 /* updates only what needs changing (time_elapsed==NONE means redraw everything no matter what,
298 but skip the interface frame) */
update_interface(short time_elapsed)299 void update_interface(short time_elapsed)
300 {
301 if (time_elapsed == NONE)
302 reset_motion_sensor(current_player_index);
303 if (alephone::Screen::instance()->openGL() || alephone::Screen::instance()->lua_hud())
304 return;
305
306 if (!game_window_is_full_screen())
307 {
308 // LP addition: don't force an update unless explicitly requested
309 bool force_update = (time_elapsed == NONE);
310
311 ensure_HUD_buffer();
312
313 // LP addition: added support for HUD buffer;
314 _set_port_to_HUD();
315 if (HUD_SW.update_everything(time_elapsed))
316 force_update = true;
317 _restore_port();
318
319 // Draw the whole thing if doing so is requested
320 // (may need some smart way of drawing only what has to be drawn)
321 if (force_update)
322 RequestDrawingHUD();
323 }
324 }
325
mark_interface_collections(bool loading)326 void mark_interface_collections(bool loading)
327 {
328 loading ? mark_collection_for_loading(_collection_interface) :
329 mark_collection_for_unloading(_collection_interface);
330 }
331
mark_weapon_display_as_dirty(void)332 void mark_weapon_display_as_dirty(void)
333 {
334 interface_state.weapon_is_dirty = true;
335 }
336
mark_ammo_display_as_dirty(void)337 void mark_ammo_display_as_dirty(void)
338 {
339 interface_state.ammo_is_dirty = true;
340 }
341
mark_shield_display_as_dirty(void)342 void mark_shield_display_as_dirty(void)
343 {
344 interface_state.shield_is_dirty = true;
345 }
346
mark_oxygen_display_as_dirty(void)347 void mark_oxygen_display_as_dirty(void)
348 {
349 interface_state.oxygen_is_dirty = true;
350 }
351
mark_player_inventory_screen_as_dirty(short player_index,short screen)352 void mark_player_inventory_screen_as_dirty(short player_index, short screen)
353 {
354 struct player_data *player= get_player_data(player_index);
355
356 set_current_inventory_screen(player_index, screen);
357 SET_INVENTORY_DIRTY_STATE(player, true);
358 }
359
mark_player_inventory_as_dirty(short player_index,short dirty_item)360 void mark_player_inventory_as_dirty(short player_index, short dirty_item)
361 {
362 struct player_data *player= get_player_data(player_index);
363
364 /* If the dirty item is not NONE, then goto that item kind display.. */
365 if(dirty_item != NONE)
366 {
367 short item_kind= get_item_kind(dirty_item);
368 short current_screen= GET_CURRENT_INVENTORY_SCREEN(player);
369
370 /* Don't change if it is a powerup, or you are in the network statistics screen */
371 if(item_kind != _powerup && item_kind != current_screen) // && current_screen!=_network_statistics)
372 {
373 /* Goto that type of item.. */
374 set_current_inventory_screen(player_index, item_kind);
375 }
376 }
377 SET_INVENTORY_DIRTY_STATE(player, true);
378 }
379
mark_player_network_stats_as_dirty(short player_index)380 void mark_player_network_stats_as_dirty(short player_index)
381 {
382 if (GET_GAME_OPTIONS()&_live_network_stats)
383 {
384 struct player_data *player= get_player_data(player_index);
385
386 set_current_inventory_screen(player_index, _network_statistics);
387 SET_INVENTORY_DIRTY_STATE(player, true);
388 }
389 }
390
set_interface_microphone_recording_state(bool state)391 void set_interface_microphone_recording_state(bool state)
392 {
393 #if !defined(DISABLE_NETWORKING)
394 set_network_microphone_state(state);
395 #endif // !defined(DISABLE_NETWORKING)
396 }
397
scroll_inventory(short dy)398 void scroll_inventory(short dy)
399 {
400 short mod_value, index, current_inventory_screen, section_count, test_inventory_screen = 0;
401 short section_items[NUMBER_OF_ITEMS];
402 short section_counts[NUMBER_OF_ITEMS];
403
404 current_inventory_screen= GET_CURRENT_INVENTORY_SCREEN(current_player);
405
406 if(dynamic_world->player_count>1)
407 {
408 mod_value= NUMBER_OF_ITEM_TYPES+1;
409 } else {
410 mod_value= NUMBER_OF_ITEM_TYPES;
411 }
412
413 if(dy>0)
414 {
415 for(index= 1; index<mod_value; ++index)
416 {
417 test_inventory_screen= (current_inventory_screen+index)%mod_value;
418
419 assert(test_inventory_screen>=0 && test_inventory_screen<NUMBER_OF_ITEM_TYPES+1);
420 if(test_inventory_screen != NUMBER_OF_ITEM_TYPES)
421 {
422 calculate_player_item_array(current_player_index, test_inventory_screen,
423 section_items, section_counts, §ion_count);
424 if(section_count) break; /* Go tho this one! */
425 } else {
426 /* Network statistics! */
427 break;
428 }
429 }
430
431 current_inventory_screen= test_inventory_screen;
432 } else {
433 /* Going down.. */
434 for(index= mod_value-1; index>0; --index)
435 {
436 test_inventory_screen= (current_inventory_screen+index)%mod_value;
437
438 assert(test_inventory_screen>=0 && test_inventory_screen<NUMBER_OF_ITEM_TYPES+1);
439 if(test_inventory_screen != NUMBER_OF_ITEM_TYPES)
440 {
441 calculate_player_item_array(current_player_index, test_inventory_screen,
442 section_items, section_counts, §ion_count);
443 if(section_count) break; /* Go tho this one! */
444 } else {
445 /* Network statistics! */
446 break;
447 }
448 }
449
450 current_inventory_screen= test_inventory_screen;
451 }
452 set_current_inventory_screen(current_player_index, current_inventory_screen);
453
454 SET_INVENTORY_DIRTY_STATE(current_player, true);
455 }
456
set_current_inventory_screen(short player_index,short screen)457 static void set_current_inventory_screen(
458 short player_index,
459 short screen)
460 {
461 struct player_data *player= get_player_data(player_index);
462
463 assert(screen>=0 && screen<7);
464
465 player->interface_flags&= ~INVENTORY_MASK_BITS;
466 player->interface_flags|= screen;
467 player->interface_decay= 5*TICKS_PER_SECOND;
468 }
469
470 // From sreen_sdl.cpp
471 extern SDL_Surface *HUD_Buffer;
472 extern void build_sdl_color_table(const color_table *color_table, SDL_Color *colors);
473
474 // From game_window.cpp
475 extern HUD_SW_Class HUD_SW;
476
477 extern void draw_panels(void);
478
ensure_HUD_buffer(void)479 void ensure_HUD_buffer(void) {
480
481 // Allocate surface for HUD if not present
482 if (HUD_Buffer == NULL) {
483 HUD_Buffer = SDL_CreateRGBSurface(SDL_SWSURFACE, 640, 480, 32,
484 0x00ff0000,
485 0x0000ff00,
486 0x000000ff,
487 0xff000000);
488 if (HUD_Buffer == NULL)
489 alert_out_of_memory();
490 }
491 }
492
493 /*
494 * Draw HUD (to HUD surface)
495 */
496
497 extern int LuaTexturePaletteSize();
498
draw_panels(void)499 void draw_panels(void)
500 {
501 if (alephone::Screen::instance()->openGL())
502 return;
503
504 ensure_HUD_buffer();
505
506 // Draw static HUD picture
507 static SDL_Surface *static_hud_pict = NULL;
508 static bool hud_pict_not_found = false;
509 if (static_hud_pict == NULL && !hud_pict_not_found) {
510 LoadedResource rsrc;
511 if (get_picture_resource_from_images(INTERFACE_PANEL_BASE, rsrc))
512 static_hud_pict = picture_to_surface(rsrc);
513 else
514 hud_pict_not_found = true;
515 }
516
517 if (!hud_pict_not_found) {
518 SDL_Rect dst_rect = {0, 320, 640, 160};
519 if (!LuaTexturePaletteSize())
520 SDL_BlitSurface(static_hud_pict, NULL, HUD_Buffer, &dst_rect);
521 else
522 SDL_FillRect(HUD_Buffer, &dst_rect, 0);
523 }
524
525 // Add dynamic elements
526 _set_port_to_HUD();
527 HUD_SW.update_everything(NONE);
528 _restore_port();
529
530 // Tell main loop to render the HUD in the next run
531 RequestDrawingHUD();
532 }
533
534 extern short vidmasterStringSetID; // shell.cpp
535 struct weapon_interface_data *original_weapon_interface_definitions = NULL;
536
reset_mml_interface()537 void reset_mml_interface()
538 {
539 if (original_weapon_interface_definitions) {
540 for (unsigned i = 0; i < NUMBER_OF_WEAPON_INTERFACE_DEFINITIONS; i++)
541 weapon_interface_definitions[i] = original_weapon_interface_definitions[i];
542 free(original_weapon_interface_definitions);
543 original_weapon_interface_definitions = NULL;
544 }
545 }
546
parse_mml_interface(const InfoTree & root)547 void parse_mml_interface(const InfoTree& root)
548 {
549 // back up old values first
550 if (!original_weapon_interface_definitions) {
551 original_weapon_interface_definitions = (struct weapon_interface_data *) malloc(sizeof(struct weapon_interface_data) * NUMBER_OF_WEAPON_INTERFACE_DEFINITIONS);
552 assert(original_weapon_interface_definitions);
553 for (unsigned i = 0; i < NUMBER_OF_WEAPON_INTERFACE_DEFINITIONS; i++)
554 original_weapon_interface_definitions[i] = weapon_interface_definitions[i];
555 }
556
557 root.read_attr("motion_sensor", MotionSensorActive);
558
559 BOOST_FOREACH(InfoTree rect, root.children_named("rect"))
560 {
561 int16 index;
562 if (!rect.read_indexed("index", index, NUMBER_OF_INTERFACE_RECTANGLES))
563 continue;
564 screen_rectangle *r = get_interface_rectangle(index);
565
566 int16 top, left, bottom, right;
567 if (rect.read_attr("top", top) &&
568 rect.read_attr("left", left) &&
569 rect.read_attr("bottom", bottom) &&
570 rect.read_attr("right", right))
571 {
572 r->top = top;
573 r->left = left;
574 r->bottom = bottom;
575 r->right = right;
576 }
577 }
578
579 BOOST_FOREACH(InfoTree color, root.children_named("color"))
580 {
581 int16 index;
582 if (!color.read_indexed("index", index, NUMBER_OF_INTERFACE_COLORS))
583 continue;
584 color.read_color(get_interface_color(index));
585 }
586 BOOST_FOREACH(InfoTree font, root.children_named("font"))
587 {
588 int16 index;
589 if (!font.read_indexed("index", index, NUMBER_OF_INTERFACE_FONTS))
590 continue;
591 font.read_font(get_interface_font(index));
592 }
593
594 BOOST_FOREACH(InfoTree vid, root.children_named("vidmaster"))
595 {
596 vidmasterStringSetID = -1;
597 vid.read_attr_bounded<int16>("stringset_index", vidmasterStringSetID, -1, SHRT_MAX);
598 }
599
600 BOOST_FOREACH(InfoTree weapon, root.children_named("weapon"))
601 {
602 int16 index;
603 if (!weapon.read_indexed("index", index, MAXIMUM_WEAPON_INTERFACE_DEFINITIONS))
604 continue;
605 weapon_interface_data& def = weapon_interface_definitions[index];
606
607 weapon.read_attr("shape", def.weapon_panel_shape);
608 weapon.read_attr("start_y", def.weapon_name_start_y);
609 weapon.read_attr("end_y", def.weapon_name_end_y);
610 weapon.read_attr("start_x", def.weapon_name_start_x);
611 weapon.read_attr("end_x", def.weapon_name_end_x);
612 weapon.read_attr("top", def.standard_weapon_panel_top);
613 weapon.read_attr("left", def.standard_weapon_panel_left);
614 weapon.read_attr("multiple", def.multi_weapon);
615 weapon.read_attr("multiple_shape", def.multiple_shape);
616 weapon.read_attr("multiple_unusable_shape", def.multiple_unusable_shape);
617 weapon.read_attr("multiple_delta_x", def.multiple_delta_x);
618 weapon.read_attr("multiple_delta_y", def.multiple_delta_y);
619
620 BOOST_FOREACH(InfoTree ammo, weapon.children_named("ammo"))
621 {
622 int16 index;
623 if (!ammo.read_indexed("index", index, NUMBER_OF_WEAPON_INTERFACE_ITEMS))
624 continue;
625 weapon_interface_ammo_data& adef = def.ammo_data[index];
626
627 ammo.read_attr("type", adef.type);
628 ammo.read_attr("left", adef.screen_left);
629 ammo.read_attr("top", adef.screen_top);
630 ammo.read_attr("across", adef.ammo_across);
631 ammo.read_attr("down", adef.ammo_down);
632 ammo.read_attr("delta_x", adef.delta_x);
633 ammo.read_attr("delta_y", adef.delta_y);
634 ammo.read_attr("bullet_shape", adef.bullet);
635 ammo.read_attr("empty_shape", adef.empty_bullet);
636 ammo.read_attr("right_to_left", adef.right_to_left);
637 }
638 }
639 }
640