1 /*
2  * Seven Kingdoms: Ancient Adversaries
3  *
4  * Copyright 1997,1998 Enlight Software Ltd.
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 2 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  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  */
20 
21 //Filename    : OBULLET.CPP
22 //Description : Object Bullet
23 //Owner		  : Alex
24 
25 #include <OVGA.h>
26 #include <OUNIT.h>
27 #include <OBULLET.h>
28 #include <OWORLD.h>
29 #include <OSERES.h>
30 #include <OU_CART.h>
31 #include <OTOWN.h>
32 #include <ONATIONA.h>
33 
34 // -------- Define constant ---------//
35 
36 const int SCAN_RADIUS = 2;
37 const int SCAN_RANGE = SCAN_RADIUS * 2 + 1;
38 
39 // from the closet to the far
40 static char spiral_x[SCAN_RANGE*SCAN_RANGE] =
41 	{ 0, 0,-1, 0, 1,-1,-1, 1, 1, 0,-2, 0, 2, -1,-2,-2,-1, 1, 2, 2, 1,-2,-2, 2, 2};
42 static char spiral_y[SCAN_RANGE*SCAN_RANGE] =
43 	{ 0,-1, 0, 1, 0,-1, 1, 1,-1,-2, 0, 2, 0, -2,-1, 1, 2, 2, 1,-1,-2,-2, 2, 2,-2};
44 
45 //--------- Begin of function Bullet::Bullet -------//
46 
Bullet()47 Bullet::Bullet()
48 {
49 	sprite_id = 0;
50 }
51 //--------- End of function Bullet::Bullet -------//
52 
53 
54 //--------- Begin of function Bullet::init ---------//
55 //
56 // <char> parentType		- the type of object emits the bullet
57 // <short> parentRecno	- the recno of the object
58 // <short> targetXLoc	- the x loc of the target
59 // <short> targetYLoc	- the y loc of the target
60 //	<char> targetMobileType - target mobile type
61 //
init(char parentType,short parentRecno,short targetXLoc,short targetYLoc,char targetMobileType)62 void Bullet::init(char parentType, short parentRecno, short targetXLoc, short targetYLoc, char targetMobileType)
63 {
64 	parent_type = parentType;
65 	parent_recno = parentRecno;
66 	target_mobile_type = targetMobileType;
67 
68 	//**** BUGHERE, using parentType and parentRecno to allow bullet by firm, town, etc.
69 	//**** BUGHERE, only allow bullet by unit for this version
70 	err_when(parent_type!=BULLET_BY_UNIT);
71 	Unit *parentUnit = unit_array[parentRecno];
72 
73 	//---------- copy attack info from the parent unit --------//
74 
75 	AttackInfo* attackInfo = parentUnit->attack_info_array+parentUnit->cur_attack;
76 
77 	attack_damage  = parentUnit->actual_damage();
78 	damage_radius  = attackInfo->bullet_radius;
79 	nation_recno   = parentUnit->nation_recno;
80 	// ###### begin Gilbert 26/6 ########## //
81 	fire_radius    = attackInfo->fire_radius;
82 	// ###### end Gilbert 26/6 ########## //
83 
84 	//----- clone vars from sprite_res for fast access -----//
85 
86 	sprite_id 	= attackInfo->bullet_sprite_id;
87 	sprite_info = sprite_res[sprite_id];
88 
89 	sprite_info->load_bitmap_res();		// the sprite bitmap will be freed by ~Sprite(), so we don't have to add ~Bullet() to free it.
90 
91 	//--------- set the starting position of the bullet -------//
92 
93 	cur_action = SPRITE_MOVE;
94 	cur_frame  = 1;
95 	set_dir(parentUnit->attack_dir);
96 
97 	SpriteFrame* spriteFrame = cur_sprite_frame();
98 
99 	origin_x = cur_x = parentUnit->cur_x;
100 	origin_y = cur_y = parentUnit->cur_y;
101 
102 	//------ set the target position and bullet mobile_type -------//
103 
104 	target_x_loc = targetXLoc;
105 	target_y_loc = targetYLoc;
106 
107 	go_x = target_x_loc * ZOOM_LOC_WIDTH  + ZOOM_LOC_WIDTH/2  - spriteFrame->offset_x - spriteFrame->width/2;			// -spriteFrame->offset_x to make abs_x1 & abs_y1 = original x1 & y1. So the bullet will be centered on the target
108 	go_y = target_y_loc * ZOOM_LOC_HEIGHT + ZOOM_LOC_HEIGHT/2 - spriteFrame->offset_y - spriteFrame->height/2;
109 
110 	mobile_type = parentUnit->mobile_type;
111 
112 	//---------- set bullet movement steps -----------//
113 
114 	int xStep 	= (go_x - cur_x)/attackInfo->bullet_speed;
115 	int yStep 	= (go_y - cur_y)/attackInfo->bullet_speed;
116 
117 	total_step  = MAX(1, MAX(abs(xStep), abs(yStep)));
118 	cur_step    = 0;
119 
120 	err_when( total_step < 0 );		// number overflow
121 }
122 //----------- End of function Bullet::init -----------//
123 
124 
125 //--------- Begin of function Bullet::process_move --------//
126 
process_move()127 void Bullet::process_move()
128 {
129 	//-------------- update position -----------------//
130 	//
131 	// If it gets very close to the destination, fit it
132 	// to the destination ingoring the normal vector.
133 	//
134 	//------------------------------------------------//
135 
136 	cur_x = origin_x + (int)(go_x-origin_x) * cur_step / total_step;
137 	cur_y = origin_y + (int)(go_y-origin_y) * cur_step / total_step;
138 
139 	//cur_step++;
140 
141 	//------- update frame id. --------//
142 
143 	if( ++cur_frame > cur_sprite_move()->frame_count )
144 		cur_frame = 1;
145 
146 	//----- if the sprite has reach the destintion ----//
147 
148 	//if( cur_step > total_step )
149 	if( ++cur_step > total_step )
150 	{
151 		check_hit();
152 
153 		cur_action = SPRITE_DIE;		// Explosion
154 
155 		// ###### begin Gilbert 17/5 #########//
156 		// if it has die frame, adjust cur_x, cur_y to be align with the target_x_loc, target_y_loc
157 		if( sprite_info->die.first_frame_recno )
158 		{
159 			next_x = cur_x = target_x_loc * ZOOM_LOC_WIDTH;
160 			next_y =cur_y = target_y_loc * ZOOM_LOC_HEIGHT;
161 		}
162 		// ###### end Gilbert 17/5 #########//
163 
164 		cur_frame = 1;
165 	}
166 	else if( total_step - cur_step == 1 )
167 	{
168 		warn_target();
169 	}
170 }
171 //---------- End of function Bullet::process_move ----------//
172 
173 
174 //--------- Begin of function Bullet::process_die --------//
175 //
176 // return : <int> 1 - dying animation completes.
177 //					   0 - still dying
178 //
process_die()179 int Bullet::process_die()
180 {
181 
182 	// ------- sound effect --------//
183 	se_res.sound(cur_x_loc(), cur_y_loc(), cur_frame, 'S',sprite_id,"DIE");
184 
185 	//--------- next frame ---------//
186 	if( ++cur_frame > sprite_info->die.frame_count )
187 	// ####### begin Gilbert 28/6 ########//
188 	if( ++cur_frame > sprite_info->die.frame_count )
189 	{
190 		// ------- set fire on the target area --------//
191 		if( fire_radius > 0)
192 		{
193 			Location *locPtr;
194 			if( fire_radius == 1)
195 			{
196 				locPtr = world.get_loc(target_x_loc, target_y_loc);
197 				if( locPtr->can_set_fire() && locPtr->fire_str() < 30 )
198 					locPtr->set_fire_str(30);
199 				if( locPtr->fire_src() > 0 )
200 					locPtr->set_fire_src(1);		// such that the fire will be put out quickly
201 			}
202 			else
203 			{
204 				short x, y, x1, y1, x2, y2;
205 				// ##### begin Gilbert 2/10 ######//
206 				x1 = target_x_loc - fire_radius + 1;
207 				if( x1 < 0 )
208 					x1 = 0;
209 				y1 = target_y_loc - fire_radius + 1;
210 				if( y1 < 0 )
211 					y1 = 0;
212 				x2 = target_x_loc + fire_radius - 1;
213 				if( x2 >= world.max_x_loc )
214 					x2 = world.max_x_loc-1;
215 				y2 = target_y_loc + fire_radius - 1;
216 				if( y2 >= world.max_y_loc )
217 					y2 = world.max_y_loc-1;
218 				// ##### end Gilbert 2/10 ######//
219 				for( y = y1; y <= y2; ++y)
220 				{
221 					locPtr = world.get_loc(x1, y);
222 					for( x = x1; x <= x2; ++x, ++locPtr)
223 					{
224 						// ##### begin Gilbert 30/10 ######//
225 						int dist = abs(x-target_x_loc) + abs(y-target_y_loc);
226 						if( dist > fire_radius)
227 							continue;
228 						int fl = 30 - dist * 7;
229 						if( fl < 10 )
230 							fl = 10;
231 						if( locPtr->can_set_fire() && locPtr->fire_str() < fl )
232 							locPtr->set_fire_str(fl);
233 						if( locPtr->fire_src() > 0 )
234 							locPtr->set_fire_src(1);		// such that the fire will be put out quickly
235 						// ##### begin Gilbert 30/10 ######//
236 					}
237 				}
238 			}
239 		}
240 		return 1;
241 	}
242 	// ####### end Gilbert 28/6 ########//
243 	return 0;
244 }
245 //--------- End of function Bullet::process_die --------//
246 
247 
248 //--------- Begin of function Bullet::hit_target --------//
249 
250 // ####### begin Gilbert 14/5 #########//
hit_target(short x,short y)251 void Bullet::hit_target(short x, short y)
252 {
253 	//---- check if there is any unit in the target location ----//
254 
255 	Location* locPtr = world.get_loc(x, y);
256 // ####### end Gilbert 14/5 #########//
257 
258 	short	targetUnitRecno = locPtr->unit_recno(target_mobile_type);
259 	if(unit_array.is_deleted(targetUnitRecno))
260 		return; // the target unit is deleted
261 
262 	Unit* targetUnit = unit_array[targetUnitRecno];
263 
264 	Unit* parentUnit;
265 	if(unit_array.is_deleted(parent_recno))
266 	//### begin alex 26/9 ###//
267 	//	parentUnit = NULL;	// parent is dead
268 	{
269 		parentUnit = NULL;	// parent is dead
270 		if(nation_array.is_deleted(nation_recno))
271 			return;
272 	}
273 	//#### end alex 26/9 ####//
274 	else
275 	{
276 		parentUnit = unit_array[parent_recno];
277 		nation_recno = parentUnit->nation_recno;
278 	}
279 
280 	float attackDamage = attenuated_damage(targetUnit->cur_x, targetUnit->cur_y);
281 
282 	// -------- if the unit is guarding reduce damage ----------//
283 	err_when(unit_array.is_deleted(locPtr->unit_recno(target_mobile_type)));
284 	// ##### begin Gilbert 14/5 #########//
285 	if( attackDamage == 0 )
286 		return;
287 
288 	if( targetUnit->nation_recno == nation_recno )
289 	{
290 		if( targetUnit->unit_id == UNIT_EXPLOSIVE_CART )
291 			((UnitExpCart *)targetUnit)->trigger_explode();
292 		return;
293 	}
294 	// ##### end Gilbert 14/5 #########//
295 
296 	// ##### begin Gilbert 3/9 #########//
297 	if( !nation_array.should_attack(nation_recno, targetUnit->nation_recno) )
298 		return;
299 	// ##### end Gilbert 3/9 #########//
300 	if(targetUnit->is_guarding())
301 	{
302 		switch(targetUnit->cur_action)
303 		{
304 		case SPRITE_IDLE:
305 		case SPRITE_READY_TO_MOVE:
306 		case SPRITE_TURN:
307 		case SPRITE_MOVE:
308 		case SPRITE_ATTACK:
309 			// ####### begin Gilbert 9/9 #######//
310 			// check if on the opposite direction
311 			if( (targetUnit->cur_dir & 7)== ((cur_dir + 4 ) & 7)
312 				|| (targetUnit->cur_dir & 7)== ((cur_dir + 3 ) & 7)
313 				|| (targetUnit->cur_dir & 7)== ((cur_dir + 5 ) & 7) )
314 			// ####### end Gilbert 9/9 #######//
315 			{
316 				attackDamage = attackDamage > (float)10/ATTACK_SLOW_DOWN ? attackDamage - (float)10/ATTACK_SLOW_DOWN : 0;
317 				se_res.sound( targetUnit->cur_x_loc(), targetUnit->cur_y_loc(), 1,
318 				'S', targetUnit->sprite_id, "DEF", 'S', sprite_id );
319 			}
320 			break;
321 		}
322 	}
323 
324 	targetUnit->hit_target(parentUnit, targetUnit, attackDamage, nation_recno);
325 }
326 //---------- End of function Bullet::hit_target ----------//
327 
328 
329 //------- Begin of function Bullet::hit_building -----//
330 //	building means firm or town
331 //
332 // ###### begin Gilbert 14/5 #########//
hit_building(short x,short y)333 void Bullet::hit_building(short x, short y)
334 {
335 	Location* locPtr = world.get_loc(x, y);
336 
337 	if(locPtr->is_firm())
338 	{
339 		Firm *firmPtr = firm_array[locPtr->firm_recno()];
340 		// ##### begin Gilbert 3/9 #########//
341 		if( !firmPtr || !nation_array.should_attack(nation_recno, firmPtr->nation_recno) )
342 		// ##### end Gilbert 3/9 #########//
343 			return;
344 	}
345 	else if(locPtr->is_town())
346 	{
347 		Town *townPtr = town_array[locPtr->town_recno()];
348 		// ##### begin Gilbert 3/9 #########//
349 		if( !townPtr || !nation_array.should_attack(nation_recno, townPtr->nation_recno) )
350 		// ##### end Gilbert 3/9 #########//
351 			return;
352 	}
353 	else
354 		return;
355 
356 	float attackDamage = attenuated_damage(x * ZOOM_LOC_WIDTH, y * ZOOM_LOC_HEIGHT );
357 	// BUGHERE : hit building of same nation?
358 	if( attackDamage == 0)
359 		return;
360 
361 	Unit *virtualUnit = NULL, *parentUnit;
362 	if(unit_array.is_deleted(parent_recno))
363 	{
364 		parentUnit = NULL;
365 		//### begin alex 26/9 ###//
366 		if(nation_array.is_deleted(nation_recno))
367 			return;
368 		//#### end alex 26/9 ####//
369 
370 		for(int i=unit_array.size(); i>0; i--)
371 		{
372 			if(unit_array.is_deleted(i))
373 				continue;
374 
375 			virtualUnit = unit_array[i];
376 			break;
377 		}
378 
379 		if(!virtualUnit)
380 			return; //**** BUGHERE
381 	}
382 	else
383 		virtualUnit = parentUnit = unit_array[parent_recno];
384 
385 	virtualUnit->hit_building(parentUnit, target_x_loc, target_y_loc, attackDamage, nation_recno);
386 	// ####### end Gilbert 14/5 ########//
387 }
388 //---------- End of function Bullet::hit_building ----------//
389 
390 
391 //------- Begin of function Bullet::hit_wall -----//
392 // ###### begin Gilbert 14/5 #########//
hit_wall(short x,short y)393 void Bullet::hit_wall(short x, short y)
394 {
395 	Location* locPtr = world.get_loc(x, y);
396 
397 	if(!locPtr->is_wall())
398 		return;
399 
400 	float attackDamage = attenuated_damage(x * ZOOM_LOC_WIDTH, y * ZOOM_LOC_HEIGHT );
401 	if( attackDamage == 0)
402 		return;
403 // ###### end Gilbert 14/5 #########//
404 
405 	Unit *virtualUnit, *parentUnit;
406 	if(unit_array.is_deleted(parent_recno))
407 	{
408 		parentUnit = NULL;
409 		//### begin alex 26/9 ###//
410 		if(nation_array.is_deleted(nation_recno))
411 			return;
412 		//#### end alex 26/9 ####//
413 
414 		for(int i=unit_array.size(); i>0; i--)
415 		{
416 			if(unit_array.is_deleted(i))
417 				continue;
418 
419 			virtualUnit = unit_array[i];
420 			break;
421 		}
422 
423 		if(!virtualUnit)
424 			return; //**** BUGHERE
425 	}
426 	else
427 		virtualUnit = parentUnit = unit_array[parent_recno];
428 
429 	// ###### begin Gilbert 14/5 #########//
430 	virtualUnit->hit_wall(parentUnit, target_x_loc, target_y_loc, attackDamage, nation_recno);
431 	// ###### end Gilbert 14/5 ########//
432 }
433 //---------- End of function Bullet::hit_wall ----------//
434 
435 
436 //--------- Begin of function Bullet::check_hit -------//
437 // check if the bullet hit a target
438 // return true if hit
check_hit()439 int Bullet::check_hit()
440 {
441 	err_when(SCAN_RANGE != 5);
442 
443 	short x,y;
444 	short townHit[SCAN_RANGE*SCAN_RANGE];
445 	short firmHit[SCAN_RANGE*SCAN_RANGE];
446 	int hitCount = 0;
447 	int townHitCount = 0;
448 	int firmHitCount = 0;
449 
450 	for( int c = 0; c < SCAN_RANGE*SCAN_RANGE; ++c )
451 	{
452 		x = target_x_loc + spiral_x[c];
453 		y = target_y_loc + spiral_y[c];
454 		if( x >= 0 && x < world.max_x_loc && y >= 0 && y < world.max_y_loc )
455 		{
456 			Location *locPtr = world.get_loc(x, y);
457 			if(target_mobile_type==UNIT_AIR)
458 			{
459 				if(locPtr->has_unit(UNIT_AIR))
460 				{
461 					hit_target(x,y);
462 					hitCount++;
463 				}
464 			}
465 			else
466 			{
467 				if(locPtr->is_firm())
468 				{
469 					short firmRecno = locPtr->firm_recno();
470 					// check this firm has not been attacked
471 					short *firmHitPtr;
472 					for( firmHitPtr = firmHit+firmHitCount-1; firmHitPtr >= firmHit; --firmHitPtr )
473 					{
474 						if( *firmHitPtr == firmRecno )
475 							break;
476 					}
477 					if( firmHitPtr < firmHit )				// not found
478 					{
479 						firmHit[firmHitCount++] = firmRecno;
480 						hit_building(x,y);
481 						hitCount++;
482 					}
483 				}
484 				else if( locPtr->is_town() )
485 				{
486 					short townRecno = locPtr->town_recno();
487 					// check this town has not been attacked
488 					short *townHitPtr;
489 					for( townHitPtr = townHit+townHitCount-1; townHitPtr >= townHit; --townHitPtr )
490 					{
491 						if( *townHitPtr == townRecno )
492 							break;
493 					}
494 					if( townHitPtr < townHit )				// not found
495 					{
496 						townHit[townHitCount++] = townRecno;
497 						hit_building(x,y);
498 						hitCount++;
499 					}
500 				}
501 				else if(locPtr->is_wall())
502 				{
503 					hit_wall(x,y);
504 					hitCount++;
505 				}
506 				else
507 				{
508 					hit_target(x,y);	// note: no error checking here because mobile_type should be taken into account
509 					hitCount++;
510 				}
511 			}
512 		}
513 	}
514 
515 	return hitCount;
516 }
517 //--------- End of function Bullet::check_hit -------//
518 
519 
520 //--------- Begin of function Bullet::warn_target -------//
521 //
522 // warn a unit before hit
523 // return true if a unit is warned
warn_target()524 int Bullet::warn_target()
525 {
526 	err_when(SCAN_RANGE != 5);
527 
528 	short x,y;
529 	int warnCount = 0;
530 
531 	for( int c = 0; c < SCAN_RANGE*SCAN_RANGE; ++c )
532 	{
533 		x = target_x_loc + spiral_x[c];
534 		y = target_y_loc + spiral_y[c];
535 		if( x >= 0 && x < world.max_x_loc && y >= 0 && y < world.max_y_loc )
536 		{
537 			Location *locPtr = world.get_loc(x, y);
538 			//char targetMobileType;
539 			//if( (targetMobileType = locPtr->has_any_unit()) != 0)
540 			//{
541 			//	short unitRecno = locPtr->unit_recno(UNIT_LAND);
542 				short unitRecno = locPtr->unit_recno(target_mobile_type);
543 				if( !unit_array.is_deleted(unitRecno) )
544 				{
545 					Unit *unitPtr = unit_array[unitRecno];
546 					// ####### begin Gilbert 9/9 ########//
547 					if( attenuated_damage( unitPtr->cur_x, unitPtr->cur_y) > 0 )
548 					// ####### end Gilbert 9/9 ########//
549 					{
550 						warnCount++;
551 						switch(unitPtr->cur_action)
552 						{
553 						case SPRITE_IDLE:
554 						case SPRITE_READY_TO_MOVE:
555 						//case SPRITE_TURN:
556 							if( unitPtr->can_stand_guard() && !unitPtr->is_guarding() )
557 							{
558 								unitPtr->set_dir( (cur_dir + 4 ) & 7);  // opposite direction of arrow
559 								unitPtr->set_guard_on();
560 							}
561 							break;
562 						case SPRITE_MOVE:
563 							if( unitPtr->can_move_guard() && !unitPtr->is_guarding()
564 								// ###### begin Gilbert 9/9 #######//
565 								&& (	(unitPtr->cur_dir & 7)== ((cur_dir + 4 ) & 7)
566 									||	(unitPtr->cur_dir & 7)== ((cur_dir + 5 ) & 7)
567 									||	(unitPtr->cur_dir & 7)== ((cur_dir + 3 ) & 7)
568 									)
569 								)
570 								// ###### end Gilbert 9/9 #######//
571 							{
572 								unitPtr->set_guard_on();
573 							}
574 							break;
575 						case SPRITE_ATTACK:
576 							if( unitPtr->can_attack_guard() && !unitPtr->is_guarding()
577 								&& unitPtr->remain_attack_delay >= GUARD_COUNT_MAX
578 								&& (	(unitPtr->cur_dir & 7)== ((cur_dir + 4 ) & 7)
579 									||	(unitPtr->cur_dir & 7)== ((cur_dir + 5 ) & 7)
580 									||	(unitPtr->cur_dir & 7)== ((cur_dir + 3 ) & 7)
581 									)
582 								)
583 							{
584 								unitPtr->set_guard_on();
585 							}
586 							break;
587 						}
588 					}
589 				}
590 			//}
591 		}
592 	}
593 
594 	return warnCount;
595 }
596 //--------- End of function Bullet::warn_target -------//
597 
598 
599 //--------- Begin of function Bullet::display_layer -------//
display_layer()600 char Bullet::display_layer()
601 {
602 	if( mobile_type == UNIT_AIR || target_mobile_type == UNIT_AIR )
603 		return 8;
604 	else
605 		return 1;
606 }
607 //--------- End of function Bullet::display_layer -------//
608 
609 
610 //------- Begin of function Bullet::attenuated_damage -----//
attenuated_damage(short curX,short curY)611 float	Bullet::attenuated_damage(short curX, short curY)
612 {
613 	short d = misc.points_distance(curX, curY, target_x_loc * ZOOM_LOC_WIDTH, target_y_loc * ZOOM_LOC_HEIGHT);
614 	// damage drops from attack_damage to attack_damage/2, as range drops from 0 to damage_radius
615 	err_when(damage_radius == 0);
616 	if( d > damage_radius)
617 		return (float) 0;
618 	else
619 		//return ((attack_damage * (2*damage_radius-d) + 2*damage_radius-1)/ (2*damage_radius) );		// ceiling
620 		return attack_damage - attack_damage*d/(2*damage_radius);
621 }
622 //------- End of function Bullet::attenuated_damage -----//
623 
624 #ifdef DYNARRAY_DEBUG_ELEMENT_ACCESS
625 
626 //------- Begin of function BulletArray::operator[] -----//
627 
operator [](int recNo)628 Bullet* BulletArray::operator[](int recNo)
629 {
630 	Bullet* bulletPtr = (Bullet*) get_ptr(recNo);
631 
632 	if( !bulletPtr )
633 		err.run( "BulletArray[] is deleted" );
634 
635 	return bulletPtr;
636 }
637 
638 //--------- End of function BulletArray::operator[] ----//
639 
640 #endif
641 
642 
643