context: /* This source is provided under the GNU AGPLv3  license. You are free to modify and distribute this source and any containing work (such as sound files) provided that:
  * - You make available complete source code of modifications, even if the modifications are part of a larger project, and make the modified work available under the same license (GNU AGPLv3).
  * - You include all copyright and license notices on the modified source.
  * - You state which parts of this source were changed in your work
  * Note that containing works (such as SharpDX) may be available under a different license.
  * Copyright (C) Munawar Bijani
  */
  using System;
  using System.IO;
  using System.Text;
  using System.Collections;
  using System.Collections.Generic;
  using System.Windows.Forms;
  using BPCSharedComponent.ExtendedAudio;
  using BPCSharedComponent.VectorCalculation;
  using BPCSharedComponent.ExtendedAudio;
  namespace TDV
  {
  	#region delegates
  	//strike is fired if a weapon contacts target, but doesn't destroy
  	//destroy is fired if target.damage <= 0
  	public delegate void readyToDisposeHandler(WeaponBase sender);
  	public delegate void hitHandler(WeaponBase sender, Projector target, int damageAmount, WeaponTypes type);
  	public delegate void strikeEventHandler(bool isLCS);
  	public delegate void destroyEventHandler(Projector target);
  	#endregion
  	#region interfaces
  	public enum WeaponTypes : byte
  	{
  		guns,
  		missile,
  		laserCannonSystem,
  		cruiseMissile,
  		landingBeaconLock,
  		samMissile,
  		explosiveMissile,
  		missileInterceptor,
  		tankMissile,
  		battleShipGuns
  	}
  	public interface Explosive
  	{
  		String id
  		{
  			get;
  		}
  		Projector origTarget
  		{
  			get;
  		}
  		void lockOn(Projector target);
  		void save(BinaryWriter w);
  		bool load();
  		void use();
  		void serverSideHit(Projector target, int remainingDamage);
  		void onTick();
  		void free();
  		void initRange(Range r); //This method is implemented through WeaponsBase and inherited by all explosives.
  																											//this event will be raised by a weapon that has done its work and is ready to be deleted from memory
  		event readyToDisposeHandler readyToDispose;
  		//this event is implemented by all weapons to notify the launcher craft that a target has been struck
  		event hitHandler eventHit;
  	}
  	#endregion
  
  	public class Weapons
  	{
  		public event strikeEventHandler strike;
  		public event destroyEventHandler destroy;
  
  		/// <summary>
  		/// Set this property to true if on the next tick, weapons should stop themselves.
  		/// </summary>
  		public static bool requestedClear
  		{
  			get;
  			set;
  		}
  
  		private List<WeaponBase> validWeaponsArray;
  		private long chunkPos;
  		private long numArgsPos = 0;
  		private const int infiniteAmmunition = 2000000000;
  		private long next;
  		private short weaponEvents; //number of weapon events in this tick
  		private String m_nextID;
  		private MemoryStream serverData;
  		private BinaryWriter writer;
  		private MemoryStream incomingData; //holds info about an update sent by server
  		private bool isWaitingForLoad;
  		private bool executingServerData;
  		private List<WeaponTypes> validIndecies;
  		private byte m_LTSCount;
  		private Projector m_creator;
  		private WeaponTypes m_weaponIndex;
  		private int currentIndex;
  		private string m_lockIndex;
  		private int m_lastMaxIndex;
  		private int[] m_ammunition;
  		private Projector m_LCSTarget;
  		private static ExtendedAudioBuffer lockSound, lockAlertSound;
  		private ExtendedAudioBuffer gunSound;
  		private Projector m_target;
  		private bool explosiveDeleted; //is true if a weapon was deleted
  																																	//on its call to Explosive.onTick()
  		private Range m_radarRange;
  		private string[] locks;
  		private int nextPos;
  		private bool m_stoppedStrafe;
  
  		/// <summary>
  		/// Signals to the creator to send a stop strafe command to the server on the next go-around.
  		/// We don't send the command here ourselves since stop strafe will be run on a separate thread for player input, and will crash the stream
  		/// when it writes to a closed stream, or it will
  		/// yield unpredictable results since two processes will be writing to
  		/// the same stream.
  		/// </summary>
  		public bool stoppedStrafe
  		{
  			get { return m_stoppedStrafe; }
  			set { m_stoppedStrafe = value; }
  		}
  
  		private List<WeaponBase> validWeapons
  		{
  			get { return (validWeaponsArray); }
  		}
  
  		private WeaponBase weaponAt(int index)
  		{
  			return validWeaponsArray[index];
  		}
  
  		private int maxIndex
  		{
  			get { return (validWeapons.Count - 1); }
  		}
  
  		/// <summary>
  		/// Checks whether this Weapons class is still operating projectiles.
  		/// </summary>
  		/// <returns>True if there are still live projectiles, false otherwise.</returns>
  		public bool isUsing()
  		{
  			lock (validWeapons)
  				return (validWeapons.Count > 0);
  		}
  
  		/// <summary>
  		/// This property should be set with the ID to assign to the next created projectile.
  		/// If no ID is specified, the class will use nextPosition + next(String) as the ID.
  		/// </summary>
  		public String nextID
  		{
  			get { return m_nextID; }
  			set { m_nextID = value; }
  		}
  
  		public static int maxHRange
  		{
  			get { return (2000000); }
  		}
  
  		public static int maxVRange
  		{
  			get { return (2000000); }
  		}
  
  
  		/// <summary>
  		/// Returns the weapon index in the specified array slot
  		/// </summary>
  		/// <param name="i">Array index</param>
  		/// <returns>The weapon index</returns>
  		public WeaponTypes getWeaponAt(int i)
  		{
  			return validIndecies[currentIndex = i];
  		}
  
  		////determines if an LTS is currently projecting toward its target.
  		////used to limit number of LTS that are active.
  		private byte LTSCount
  		{
  			get { return (m_LTSCount); }
  			set { m_LTSCount = value; }
  		}
  
  		//returns true if the cruise missile is active (viz. switched to) and a tag exists,
  		//false otherwise
  		public bool cruiseMissileLocked()
  		{
  			return (lCSTarget != null && isValidLock(lCSTarget));
  			//lcsTarget could have a lock but the target could have been  destroyed,
  			//in which case the cruise missile would fire at a dead object.
  			//This method only determines if a cruise can be fired.
  		}
  
  		////An object should call this method if it is wanting to cycle through a list of weapons.
  		////This method will update the weaponIndex and match it with the validIndex method.
  		////Therefore, an update by this method will never fail.
  		//returns new weaponIndex
  		public WeaponTypes increaseWeaponIndex()
  		{
  			int wMIndex = getMaxWeaponIndex() + 1;
  			weaponIndex = validIndecies[currentIndex = (currentIndex + 1) % wMIndex];
  			return weaponIndex;
  		}
  
  		private int getMaxWeaponIndex()
  		{
  			return validIndecies.Count - 1;
  		}
  
  		public Projector lCSTarget
  		{
  			get
  			{
  				if (m_LCSTarget == null) {
  					return (null);
  				} else if (m_LCSTarget.isTerminated) {
  					//short circuit evaluation does not work :(
  					return (null);
  				}
  				return (m_LCSTarget);
  			}
  			set { m_LCSTarget = value; }
  		}
  
  
  		public void setAmmunitionFor(WeaponTypes w, int value)
  		{
  			m_ammunition[(int)w] = value;
  		}
  
  		public void setInfiniteAmmunition()
  		{
  			for (int i = 0; i < getMaxWeaponIndex(); i++)
  				setAmmunitionFor((WeaponTypes)i, infiniteAmmunition);
  		}
  		public void decreaseAmmunitionFor(WeaponTypes w)
  		{
  			if (Options.isLoading)
  				return; //Since load already registers proper ammo count don't allow decrease.
  			if (m_ammunition[(int)w] == infiniteAmmunition)
  				return;
  			m_ammunition[(int)w]--;
  		}
  
  		public int ammunitionFor(WeaponTypes w)
  		{
  			return m_ammunition[(int)w];
  		}
  
  		//Returns a Range structure populated with data to define the locking range.
  		//This is the range at which a radar can detect and lock onto an object.
  		//Note: this value does not necessarily represent
  		//the distance from which a weapon can be fired.
  		//See firingRange for this value.
  		public Range lockingRange
  		{
  			get { return (m_radarRange); }
  		}
  		public Range radarRange
  		{
  			get
  			{
  				if (creator is Aircraft)
  					return (new Range(creator.z / 10000f * 20f, maxVRange));
  				else
  					return (m_radarRange);
  			}
  		}
  
  		//Returns a Range structure populated with the range
  		//from which a weapon can be fired.
  		//Note: this value does not necessarily represent the range
  		//from which an object can lock onto another object.
  		//See lockingRange for this value.
  		public Range firingRange
  		{
  			get
  			{
  				Range r = new Range(0f, 0f);
  				switch (weaponIndex) {
  					case WeaponTypes.missile:
  					case WeaponTypes.explosiveMissile:
  						r = new Range(15f, (creator is GuardTower) ? 50000f : 300f);
  						break;
  					case WeaponTypes.guns:
  						r = new Range(4f, 300f);
  						break;
  					case WeaponTypes.laserCannonSystem:
  						r = new Range(10f, 300f);
  						break;
  					case WeaponTypes.cruiseMissile:
  					case WeaponTypes.missileInterceptor:
  						r = new Range(30f, 1000f);
  						break;
  					case WeaponTypes.tankMissile:
  						r = new Range(30f, 50000f);
  						break;
  					case WeaponTypes.battleShipGuns:
  						r = new Range(5f, 50000f);
  						break;
  					case WeaponTypes.samMissile:
  						r = new Range(5f, 50000f);
  						break;
  					case WeaponTypes.landingBeaconLock:
  						return new Range(maxHRange, maxVRange);
  				} //switch
  
  				//Next, if firing at object
  				//increase v range so we can drop missiles
  				if (Options.mode == Options.Modes.mission
  								&& isValidLock()
  								&& getLockedTarget().isObject)
  					r.verticalDistance = 50000f;
  				return r;
  			}
  		}
  
  		public WeaponTypes weaponIndex
  		{
  			get { return (m_weaponIndex); }
  
gt: 			set { m_weaponIndex = value; }