**************************************************************************** **************************************************************************** ** ** Platinum (Enemy firing) ** ** This software is in the public domain. There is no warranty. ** ** by Patrick Davidson (pad@calc.org, http://pad.calc.org/) ** ** Last updated August 4, 2000 ** **************************************************************************** **************************************************************************** ******************************************** FIRE ENEMY BUBBLE FROM (D1,D2) * * This routine fires the aimed "bubble" bullets. It is called D1 being * the X coordinate to fire from, and D2 holding the Y coordinate. These * coordinates are where the bullet will appear, not the upper-right corner * of the enemy. As this routine just calls the next one, refer to it for * details on how it works. * ******** fire_enemy_bubble: lea esdata_bubble(pc),a1 ;image bra.s enemy_cannon_common ******************************************** ENEMY BULLET DATA BLOCKS * * These are short data blocks describing each type of bullet for the aimed * firing routine. They consists of damage, size, and image number. * ******** esdata_bubble: dc.w 1,5,in_Bubble_Weapon esdata_normal: dc.w 1,3,in_Enemy_Bullet esdata_ball: dc.w 3,7,in_Ball ******************************************** FIRE ENEMY CANNON FROM (D1,D2) * * This routine fires an aimed enemy bullet. Call "fire_enemy_cannon_top" * with A4 pointing to an enemy, or "fire_enemy_cannon" with D1 being the X * coordinate the bullet should be launched from and D2 containing the Y * coordinate. The routine will change A0, A1, D0, D3, D4, and D6. If you * use "fire_enemy_cannon_top", D1 and D2 are also altered. * * In the discussion below, note the following: * * DX = DeltaX = Target X coordinate - Origin X coordinate * DY = DeltaY = Target Y coordinate - Origin Y coordinate * XV = X Velocity = Distance traveled right in a unit of time * YV = Y Velocity = Distance traveled down in a unit of time * * Unlike the other enemy firing routines, this one fires a bullet aimed * directly toward the player. This requires getting the bullet to move in a * direction approximately the same as toward the player. Of course, the most * obvious (and easy) way to implement this would be to give the bullet a * constant YV, and set the XV to DX/DY. * * That method, while very simple, is also not very good. The problem with * is that bullets at different rates would travel at different. Compare a * bullet which goes straight down with one moving at a high angle. They * both move down at the same speed, but since one is also moving sideways * at a high rate, it is much faster. * * Ideally, all bullets should travel at the same, or approximately the same, * speed toward the player. This basically means that the value of * SQR(XV^2 + YV^2) should be a constant (the value essentially the magnitude * of a velocity vector; if you don't know what that is, just consider it the * conversion of the distance formula to deal with speed). However, XV/YV * should always equal (approximaltey) DX/DY so that the bullet travels in * the correct direction. One formula that could be used is: * * XV = C * DX / SQR(DX^2 + DY^2) * YV = C * DY / SQR(DX^2 + DY^2) * * This formula basically takes each component of the distance, and divides * it by the actual distance to get each component of the velocity (which is * also multiplied by a constant). This will make the overall speed always * equal to C (if you don't believe me, just evaluate the speed formula with * these values put in). However, this still isn't very convenient for our * purposes as it requires computing square roots. However, you can also * obtain the following formulas by either using a little trigonometry with * the above, or by thinking about it a different way: * * XV = C * COS(ARCTAN(DY/DX)) * YV = C * SIN(ARCTAN(DY/DX)) * * This first computes the angle (DY/DX) and then uses sine and cosine to get * the coordinates of that on a circle (which are also the velocities to go * in that direction at a speed of 1 (in whatever unit is used)) and then * multiplies them by the speed value. Of course, computing trig functions * does initially seem even worse than the above. However, since we are only * dealing with DY/DX (which has, or can at least be trimmed to, a narrow * range of values), a table can be used to obtain XV and YV from DY/DX. * * In the case of this routine, the value actually obtained is DX/DY, from * which the contangent must be computed. If the value of this is greater * than 3, it is decreased to 3, so that the table can be kept to a reasonable * length (beyond that, the difference in angle is small, and really oblique * shots aren't terribly important anyway). This is stored as a fixed point * value with two bits for the fractional part; that is, it is used as 4 * times the actual number. This value is used to look up the velocity from * a table, which is then stored in the bullet structure. * ******** fire_enemy_cannon_top: move.w e_x(a4),d1 addq.w #3,d1 move.w e_y(a4),d2 addq.w #3,d2 fire_enemy_cannon: lea esdata_normal(pc),a1 enemy_cannon_common: bsr find_enemy_bullet enemy_cannon_located: move.w player_yc(a5),d3 sub.w d2,d3 addq.w #3,d3 ;D3 = DeltaY ble.s fec_toolow move.w player_xc(a5),d4 addq.w #4,d4 sub.w d1,d4 ;D4 = DeltaX move.w d4,d6 ;D6 = DeltaX bge.s fec_positive_dx_1 neg.w d6 fec_positive_dx_1: add.w d6,d6 add.w d6,d6 ext.l d6 divu d3,d6 ;D3 = 4DeltaX/DeltaY cmp.w #12,d6 bgt.s fec_toolow tst.w d4 bgt.s fec_positive_dx_2 neg.w d6 fec_positive_dx_2: move.w #EB_AIMED,(a0)+ ;type move.w (a1)+,(a0)+ ;damage move.w (a1)+,d4 move.w d4,eb_w-4(a0) move.w d4,eb_h-4(a0) lsr.w #1,d4 subq.w #1,d4 sub.w d4,d1 move.w d1,(a0)+ ;X-coord sub.w d4,d2 move.w d2,eb_y-6(a0) move.w (a1)+,d5 lea eb_image-6(a0),a0 move.w d5,(a0)+ ;image add.w d6,d6 ;direction cmp.w #in_Ball,d5 beq.s aimed_fast_bullet move.w edir_table(pc,d6.w),(a0) fec_toolow: rts aimed_fast_bullet: move.b edir_table(pc,d6.w),d2 add.b d2,d2 move.b d2,(a0)+ move.b edir_table+1(pc,d6.w),d2 add.b d2,d2 move.b d2,(a0) rts dc.b 1,-5,1,-5,1,-5,2,-5,2,-4,2,-4,2,-4,3,-4,3,-4,4,-3,4,-2,5,-1 edir_table: dc.b 6,0 dc.b 5,1,4,2,4,3,3,4,3,4,2,4,2,4,2,4,2,5,1,5,1,5,1,5 ******************************************** LOCATE AN ENEMY BULLET * * This routine finds the first unused enemy bullet structure, and returns a * pointer to the beginning of it in A0. This routine modifies only A0 and * D0. If there are no free enemy bullets, this routine will return two * levels, exiting the routine that called it. For this reason, it is not * necessary for the routines using this to check if it finds anything, as * they will only continue if a bullet has been found. * ******** find_enemy_bullet: lea enemy_bullets(a5),a0 moveq #15,d0 loop_find_enemy_bullet: tst.w (a0) beq.s found_enemy_bullet lea eb_size(a0),a0 dbra d0,loop_find_enemy_bullet addq.l #4,sp found_enemy_bullet: rts ******************************************** FIRE STANDARD WEAPONS * * These routines drop the standard "bomb" and "missile" types of enemy * bullets. Both of them expect A4 to point to the launching enemy. They * change the A0 and D0 registers only. * ******** drop_bomb: bsr find_enemy_bullet move.w #EB_NORMAL,(a0)+ ;type move.w #1,(a0)+ ;damage move.w e_x(a4),(a0) addq.w #3,(a0)+ ;X-coord move.w #3,(a0)+ ;width move.w e_y(a4),(a0) addq.w #8,(a0)+ ;Y-coord move.w #5,(a0)+ ;height move.w #in_Enemy_Bomb,(a0) ;image rts shoot_missile: bsr find_enemy_bullet move.w #EB_ARROW,(a0)+ ;type move.w #1,(a0)+ ;damage move.w e_x(a4),(a0) addq.w #3,(a0)+ ;X-coord move.w #3,(a0)+ ;width move.w e_y(a4),(a0) addq.w #8,(a0)+ ;Y-coord move.w #7,(a0)+ ;height move.w #in_Missile,(a0) ;image rts drop_arrow: bsr find_enemy_bullet move.w #EB_ARROW,(a0)+ ;type move.w #1,(a0)+ ;damage move.w e_x(a4),(a0) addq.w #1,(a0)+ ;X-coord move.w #5,(a0)+ ;width move.w e_y(a4),(a0) addq.w #3,(a0)+ ;Y-coord move.w #11,(a0)+ ;height move.w #in_Arrow_Bullet,(a0) ;image rts ******************************************** FIRE SEMI-GUIDED MISSILE * * This routine fires the semi-guided missiles. These missile will travel * diagonally (in the direction of the player) until they are above the * player when they will turn downward. This routine is used the same as the * one above, except that D3 is also changed. * ******** drop_guided: bsr find_enemy_bullet move.w player_xc(a5),d3 sub.w e_x(a4),d3 addq.w #4,d3 ble.s guided_going_left move.w #EB_RIGHT,(a0)+ ;type move.w #1,(a0)+ ;damage move.w e_x(a4),(a0) addq.w #2,(a0)+ ;X-coord move.w #5,(a0)+ ;width move.w e_y(a4),(a0) addq.w #3,(a0)+ ;Y-coord move.w #5,(a0)+ ;height move.w #in_Guided_Right,(a0) ;image rts guided_going_left: move.w #EB_LEFT,(a0)+ ;type move.w #1,(a0)+ ;damage move.w e_x(a4),(a0) addq.w #2,(a0)+ ;X-coord move.w #5,(a0)+ ;width move.w e_y(a4),(a0) addq.w #3,(a0)+ ;Y-coord move.w #5,(a0)+ ;height move.w #in_Guided_Left,(a0) ;image rts ******************************************** DEPLOY CASH BONUSES * * This routine deploys a cash bonus. The bonuses themselves actually are * stored as enemy bullets, but with a negative amount of damage so they * actually give you money. This routine is called as the regular dropping * routine, except with D5 holding the negative of the amount you drop, D6 * holding the image, and D3 holding the width. Normally, the enemy would * already be destroyed when this is called. However, A4 should still point * to the "remnant" of its structure that still has the X and Y coordinates * stored in it. * ******** deploy_bonus: bsr find_enemy_bullet move.w #EB_NORMAL,(a0)+ ;type move.w d5,(a0)+ ;damage move.w e_x(a4),(a0) addq.w #3,(a0)+ ;X-coord move.w d3,(a0)+ ;width move.w e_y(a4),(a0) addq.w #8,(a0)+ ;Y-coord move.w #7,(a0)+ ;height move.w d6,(a0) ;image rts