Yeah, it looks a little daunting at first, I've taken out the bit relevant to shooting and stripped all the online related stuff from it, also I've added a lot of annotation.
`=> Player clicked mouse, weapon is ready to fire, player is not meele attacking, plyer is not switching weapons and weapon is idling (not playing an aimation)
if MOUSE=1 and (total_time#>last_shot#+WeaponStats#(HeldWeapons(ChosenWeapon),4)) and MeleeAttack=0 and WeaponSwap=0 and WeaponIdle=1
`=> Check the player has enough ammo to shoot.
if PlayerClip(HeldWeapons(ChosenWeapon))>0
`Update HUD
UpdateWeaponHUD=1
last_shot_time#=total_time#
burst_shot_count=burst_shot_count+1
if burst_shot_count>Burst_Shot_Max then MOUSE=0 : burst_shot_count=0
`Reduce ammo
dec PlayerClip(HeldWeapons(ChosenWeapon)) ` = PlayerClip(HeldWeapons(ChosenWeapon))-1
`Play the shooting animation
play object WeaponObjects#(HeldWeapons(ChosenWeapon),1),WeaponStats#(HeldWeapons(ChosenWeapon),22),WeaponStats#(HeldWeapons(ChosenWeapon),23)
if WeaponStats#(HeldWeapons(ChosenWeapon),33)=0 `=> Is weapon shotgun? NO.
inc BulletsFired ` = BulletsFired+1 `=> This variable is used for the getting the correct id of the next bullet/bullet hole pair
if BulletsFired>NumberOfBullets then BulletsFired = 1 `=> This is used to re-set the above variable when needed
`Position bullet at weapon position => end of barrel, changes depending on weapon, hence the array.
position object BulletObject(BulletsFired,1),WeaponObjects#(HeldWeapons(ChosenWeapon),2),WeaponObjects#(HeldWeapons(ChosenWeapon),3),WeaponObjects#(HeldWeapons(ChosenWeapon),4)
`Calculate distance from player to target to give realistic spread
`=> This is due to the amount the bullet moves from it's ideal hit point being larger when the player is further away.
'=> The cast_x#,cast_y# and cast_z# is the point of contact of a raycast (done with Newton, not shown in this snippet)
`=> pointing the bullet at this point will give perfect accuracy, I call this the 'ideal point' or 'ideal direction'.
dist# = GetDistance(px#,py#,pz#,cast_x#,cast_y#,cast_z#)
if CurrentPlayerSpecial=4 `Twice the accuracy
ActualSpreadX# = dist#*((rnd(WeaponSpread#/2)-WeaponSpread#/4)/10)
ActualSpreadY# = dist#*((rnd(WeaponSpread#/2)-WeaponSpread#/4)/10)
ActualSpreadZ# = dist#*((rnd(WeaponSpread#/2)-WeaponSpread#/4)/10)
else
ActualSpreadX# = dist#*((rnd(WeaponSpread#)-WeaponSpread#/2)/10)
ActualSpreadY# = dist#*((rnd(WeaponSpread#)-WeaponSpread#/2)/10)
ActualSpreadZ# = dist#*((rnd(WeaponSpread#)-WeaponSpread#/2)/10)
endif
`Add spread => this points the bullet in the ideal direction modified by the inaccuracy of the weapon
point object BulletObject(BulletsFired,1), cast_x#+ActualSpreadX#, cast_y#+ActualSpreadY#, cast_z#+ActualSpreadZ#
`Add damage
BulletObject(BulletsFired,3)=WeaponStats#(HeldWeapons(ChosenWeapon),7)
`Fire bullet => This variable is very important; it's a flag to check if the bullet is 'alive' or not.
=> When looping the bullets later (this will happen every frame) this variable is checked before any other
=> calculations are carried out, saving you loads of FPS if most of the bullets are dead (not moving).
BulletObject(BulletsFired,2)=1
`Shooter =>This is simply so that the message pump can display who got a kill, it's also useful for stopping friendly fire.
BulletObject(BulletsFired,4)=LocalPlayer
else => WEAPON IS A SHOTGUN
`Getting distance to target, this is done outside the for loop for optimisation
dist# = GetDistance(px#,py#,pz#,cast_x#,cast_y#,cast_z#)
for i=1 to 12 => this loop does the same calculations for placing and aiming a bullet as above but the loop lets us shoot 12 bullets at a time, like a shotgun.
inc BulletsFired `= BulletsFired+1
if BulletsFired>NumberOfBullets then BulletsFired = 1
`Position bullet at weapon position
position object BulletObject(BulletsFired,1),WeaponObjects#(HeldWeapons(ChosenWeapon),2),WeaponObjects#(HeldWeapons(ChosenWeapon),3),WeaponObjects#(HeldWeapons(ChosenWeapon),4)
`Random real value for the bullet's inaccuracy
ActualSpreadX# = dist#*((rnd(WeaponSpread#)-WeaponSpread#/2)/10)
ActualSpreadY# = dist#*((rnd(WeaponSpread#)-WeaponSpread#/2)/10)
ActualSpreadZ# = dist#*((rnd(WeaponSpread#)-WeaponSpread#/2)/10)
`Add spread
point object BulletObject(BulletsFired,1), cast_x#+ActualSpreadX#, cast_y#+ActualSpreadY#, cast_z#+ActualSpreadZ#
`Add damage
BulletObject(BulletsFired,3)=WeaponStats#(HeldWeapons(ChosenWeapon),7)
`Fire bullet
BulletObject(BulletsFired,2)=1
next i
endif
else `=> Player doesn't have enough ammo to shoot so auto reload.
`Handle ammo
RELOAD: `=> This is the label the code goes to when the player presses the 'reload' key
`=> This re-sets the shooting variables, don't worry about it too much.
SingleShot=1
Recoil#=0.0
MOUSE=0
burst_shot_count=0
`If the player has some ammo
if PlayerAmmo(HeldWeapons(ChosenWeapon))>0
`=> Update HUD
UpdateWeaponHUD=1
`=> Play the reloading animation
play object WeaponObjects#(HeldWeapons(ChosenWeapon),1),WeaponStats#(HeldWeapons(ChosenWeapon),24),WeaponStats#(HeldWeapons(ChosenWeapon),25)
`=> This variable stops the player from shooting, changing weapon, meele-ing or reloading while the current reload is taking place
RELOADING=1
`=> This bit of code just fills the clip and deducts the correct amount of ammo from the ammo count:
if PlayerAmmo(HeldWeapons(ChosenWeapon))>=WeaponStats#(HeldWeapons(ChosenWeapon),8) and PlayerClip(HeldWeapons(ChosenWeapon))=0
PlayerClip(HeldWeapons(ChosenWeapon))= WeaponStats#(HeldWeapons(ChosenWeapon),8)
PlayerAmmo(HeldWeapons(ChosenWeapon))= PlayerAmmo(HeldWeapons(ChosenWeapon))-PlayerClip(HeldWeapons(ChosenWeapon))
else
BulletsToReplace = WeaponStats#(HeldWeapons(ChosenWeapon),8) - PlayerClip(HeldWeapons(ChosenWeapon))
if PlayerAmmo(HeldWeapons(ChosenWeapon))>=BulletsToReplace and PlayerClip(HeldWeapons(ChosenWeapon))>0
PlayerClip(HeldWeapons(ChosenWeapon))= WeaponStats#(HeldWeapons(ChosenWeapon),8)
PlayerAmmo(HeldWeapons(ChosenWeapon))= PlayerAmmo(HeldWeapons(ChosenWeapon))-BulletsToReplace
else
PlayerClip(HeldWeapons(ChosenWeapon))=PlayerAmmo(HeldWeapons(ChosenWeapon))
PlayerAmmo(HeldWeapons(ChosenWeapon))= PlayerAmmo(HeldWeapons(ChosenWeapon))-PlayerClip(HeldWeapons(ChosenWeapon))
endif
endif
else
`If the player has no ammo left
print "OUT OF AMMO"
endif
endif
You will also need code to handle your bullets (again stripped down and annotated):
_HandleBullets:
for t=1 to NumberOfBullets => GO THROUGH ALL BULLETS
if BulletObject(t,2)=1 => IF BULLET IS ALIVE (SEE HOW USEFUL THIS IS NOW?)
`Cast a ray with Newton and see if we've hit something
x1# = object position x(BulletObject(t,1)) : y1# = object position y(BulletObject(t,1)) : z1# = object position z(BulletObject(t,1))
`Get a unit vector in the direction the player is facing, by moving the camera 1 unit!
move object BulletObject(t,1),1.0
x2# = object position x(BulletObject(t,1)) : y2# = object position y(BulletObject(t,1)) : z2# = object position z(BulletObject(t,1))
move object BulletObject(t,1),-1.0
`Get the diffrence to find direction vector=> THIS IS THE DIRECTION THE BULLET IS MOVING
bdx# = x2# - x1#
bdy# = y2# - y1#
bdz# = z2# - z1#
`Then multiply by BulletSpeed#*time# to get a vector BulletSpeed#*time# long => THIS IS FOR TIME-BASED MOVEMENT
x2# = x1# + (bdx# * BulletSpeed#*time#)
y2# = y1# + (bdy# * BulletSpeed#*time#)
z2# = z1# + (bdz# * BulletSpeed#*time#)
`Cast the ray. Put start point in vector 1, end point in vector 2 => CASTING A RAY USING NEWTON
NDB_SetVector 1, x1#, y1#, z1#
NDB_SetVector 2, x2#, y2#, z2#
`Get distance to object hit as a unit vector of BulletSpeed#*time#
dist# = NDB_NewtonWorldrayCast()
if dist#<1.0 `=> ALTHOUGH THIS SAYS <1 IT'S NEWTON SO IT REALLY MEANS < (1*BulletSpeed#*time#) DUE TO THE HIT DISTANCE
`=> BEING RETURNED AS A FRACTION OF THE RAYCAST LENGTH
HitBody = NDB_RayCastGetBody() `=> GET THE NUMBER OF THE BODY HIT BY THE RAYCAST
if HitBody = Player `IF THE PLAYER HAS BEEN HIT
`=> THE PLAYER'S BODY HAS BEEN HIT! DO STUFF!
`Hurt the player
Player_Health# = Player_Health# - BulletObject(BulletsFired,3)
`Set the headshot variable
PlayerKilledByHeadShot=0
`Set the last impact time => THIS VARIABLE IS USED FOR PLAYER HEALTH REGENERATION
last_impact# = total_time#
last_bullet_hit_by = t
`Replace the bullet => move the bullet out of sight to be used again
position object BulletObject(t,1),1000,-1000+(5*t),1000
`Bullet no longer being fired => this is important, the bullet has hit something, we don't want it to keep moving.
BulletObject(t,2)=0
else `=> OBJECT HIT WAS NOT PLAYER
HitMass# = NDB_NewtonBodyGetMassMatrix( HitBody ) `=> CHECK THE NEWTON MASS OF THE OBJECT HIT
if HitMass# = 0.0 `=> MASS IS ZERO, OBJECT HIT IS SCENERY
`=> GET POINT AT END OF RAYCAST
castdist# = dist# * BulletSpeed#*time#
cast_x# = x1# + (bdx# * castdist#)
cast_y# = y1# + (bdy# * castdist#)
cast_z# = z1# + (bdz# * castdist#)
`Replace the bullet
position object BulletObject(t,1),1000,-1000+(5*t),1000
`Add a bullethole
`=> POSITION IT JUST BEFORE THE OBJECT HIT (OTHERWISE IT WON'T SHOW AS THE BULLETHOLE WILL BE BEHIND THE OBJECT HIT BY THE BULLET)
position object BulletHoleObject(t), cast_x#-(bdx#*0.01), cast_y#-(bdy#*0.01), cast_z#-(bdz#*0.01)
`=> GET THE NORMALS (ANGLE AT 90 DEGREES TO THE SURFACE THE BULLET HIT)
NDB_RayCastGetNormal
nx# = NDB_GetVector_X() : ny# = NDB_GetVector_Y() : nz# = NDB_GetVector_Z()
`=> THIS NEXT BIT JUST POINTS THE BULLETHOLE PLANE IN THE RIGHT DIRECTION
x# = cast_x# + nx# : y# = cast_y# + ny# : z# = cast_z# + nz#
point object BulletHoleObject(t), x#, y#, z#
`=> RANDOMLY ORIENTATE THE BULLETHOLE SO THEY DON'T ALL LOOK THE SAME EVEN IF YOU USE THE SAME IMAGE
roll object left BulletHoleObject(t),rnd(359)
`Bullet no longer being fired
BulletObject(t,2)=0
else `=> OBJECT HIT WAS NOT PLAYER OR SCENERY, PROBABLY SKYBOX, STOP THE BULLET, DON'T USE A BULLETHOLE
`Replace the bullet => move the bullet away from the level to be used again
position object BulletObject(t,1),1000,-1000,1000
`Bullet no longer being fired
BulletObject(t,2)=0
endif
endif
else `=> NOTHING WAS HIT IN THE RAYCAST SO MOVE THE BULLET BulletSpeed#*time# UNITS IN THE DIRECTION OF TRAVEL
move object BulletObject(t,1),BulletSpeed#*time#
endif
endif
next t
I hope this helps,
Broken_Code