using System;
using System.Drawing;
using Microsoft.DirectX.DirectDraw;
using Global;
using Global.GlobalClass;
using KillerInstinct.DirectX;
namespace KillerInstinct.Sprites{
public class TankClass : SpriteClass
{
// tank constants
private const int SHOTDELAY = 50; // how often to allow a shot to be fired
private const int ENERGYLOW_DELAY = 200; // how often to play "energy low" sound
private const int ENERGYLOW_START = 15; // when to start warning player that energy is low
private const int MAX_BAR_ENERGY = 100; // maximum energy displayed in bar
private const int MAX_SHIELD = 100; // maximum allowed shields
private const int MAX_WEAPONS = 20; // maximum weapons we are allowed to have
private const int START_ENERGY = 100; // starting (unless handicapped?) and maximum tank energy
// ...
private ProgressBarClass m_energyBar = null; // our energy/health meter
private ShotClass.Weapon[] m_weapons = new ShotClass.Weapon[MAX_WEAPONS]; // store information about each weapon
private ShotClass.ShotTypes m_shotType = ShotClass.ShotTypes.Basic; // default to shot #1 (Shot0.bmp / Shot0.wav)
private ShotClass.ShotTypes m_altShotType = ShotClass.ShotTypes.AltBasic; // default to alternate shot #1 (Shot10.bmp, Shot10.wav)
private int m_shield = 0; // current shield level
private int m_lowDelay = 0;
private int m_shotDelay = 0; // time till next shot allowed
private int m_shotAltDelay = 0; // time till next alternative shot allowed
private int m_maxShotAltDelay= 0;
private int m_playerTeam = 0;
private bool m_readyToShoot = true; // flag to indicate if ready to shoot again
private bool m_readyToShootAlt= true;// flag to indicate if ready to shoot (alternative) again
private bool m_ai = false;// if this tank is artificial intelligence
//private SpriteListClass m_spriteList; // to get information about other tank sprites
public bool AI
{
get
{ return m_ai; }
}
// returns ammunition count for current PRIMARY weapon
public int AmmoCount
{
get
{ return m_weapons[(int)m_shotType].ammo; }
set
{
m_weapons[(int)m_shotType].ammo = value;
// if an unlimited "regen" weapon, reset regeneneration time
if (m_weapons[(int)m_shotType].unlimited == true)
m_weapons[(int)m_shotType].lastRegen = System.Environment.TickCount;
// ensure max ammunition is not over-stepped
if (m_weapons[(int)m_shotType].ammo > m_weapons[(int)m_shotType].maxAmmo)
m_weapons[(int)m_shotType].ammo = m_weapons[(int)m_shotType].maxAmmo;
}
}
// returns ammunition count for current SECONDARY weapon
public int AltAmmoCount
{
get
{ return m_weapons[(int)m_altShotType].ammo; }
set
{
m_weapons[(int)m_altShotType].ammo = value;
// if an unlimited "regen" weapon, reset regeneneration time
if (m_weapons[(int)m_altShotType].unlimited == true)
m_weapons[(int)m_altShotType].lastRegen = System.Environment.TickCount;
// ensure max ammunition is not over-stepped
if (m_weapons[(int)m_altShotType].ammo > m_weapons[(int)m_altShotType].maxAmmo)
m_weapons[(int)m_altShotType].ammo = m_weapons[(int)m_altShotType].maxAmmo;
}
}
// returns maximum allowed ammunition for this PRIMARY weapon
public int AmmoMax
{
get
{ return m_weapons[(int)m_shotType].maxAmmo; }
}
// returns maximum allowed ammunition for this SECONDARY weapon
public int AltAmmoMax
{
get
{ return m_weapons[(int)m_altShotType].maxAmmo; }
}
// get or set whether we are ready to shoot
public bool ReadyToShoot
{
get
{ return m_readyToShoot; }
set
{ m_readyToShoot = value; }
}
// get or set whether we are ready to shoot alternative weapon
public bool ReadyToShootAlt
{
get
{ return m_readyToShootAlt; }
set
{ m_readyToShootAlt = value; }
}
// returns constant resource string with current shot type + ".bmp"
public string ShotBitmap
{
get
{ return (CONST.BMP_SHOT + (int)m_shotType + ".bmp"); }
}
// returns constant resource string with current alternate shot type + ".bmp"
public string AltShotBitmap
{
get
{ return (CONST.BMP_SHOT + (int)m_altShotType + ".bmp"); }
}
// returns constant resource string with current shot type + ".wav"
public string ShotSound
{
get
{ return (SoundClass.SOUND_SHOT + (int)m_shotType + ".wav"); }
}
// returns constant resource string with current alternate shot type + ".wav"
public string AltShotSound
{
get
{ return (SoundClass.SOUND_SHOT + (int)m_altShotType + ".wav"); }
}
// returns MINIMUM amount of damage the current PRIMARY shot can do
public int MinShotDamage
{
get
{ return (m_weapons[(int)m_shotType].minDamage); }
}
// returns MINIMUM amount of damage the current SECONDARY shot can do
public int MinAltShotDamage
{
get
{ return (m_weapons[(int)m_altShotType].minDamage); }
}
// returns MAXIMUM amount of damage the current PRIMARY shot can do
public int MaxShotDamage
{
get
{ return (m_weapons[(int)m_shotType].maxDamage); }
}
// returns MAXIMUM amount of damage the current SECONDARY shot can do
public int MaxAltShotDamage
{
get
{ return (m_weapons[(int)m_altShotType].maxDamage); }
}
// returns CURRENT range of currently selected PRIMARY weapon
public int ShotRange
{
get
{ return m_weapons[(int)m_shotType].range; }
}
// returns CURRENT range of currently selected SECONDARY weapon
public int ShotAltRange
{
get
{ return m_weapons[(int)m_altShotType].range; }
}
// sets PRIMARY shot number which is used to return resource file names, e.g. .bmp or .wav files
public ShotClass.ShotTypes ShotType
{
get
{ return m_shotType; }
set
{
// set weapon type only if we actually have it (i.e. ammo > -1)
if (m_weapons[(int)value].ammo > -1)
m_shotType = value;
}
}
// sets SECONDARY shot number which is used to return resource file names, e.g. .bmp or .wav files
public ShotClass.ShotTypes AltShotType
{
get
{ return m_altShotType; }
set
{
// set weapon type only if we actually have it (i.e. ammo > -1)
if (m_weapons[(int)value].ammo > -1)
m_altShotType = value;
}
}
public int Shield
{
get
{ return m_shield; }
set
{
m_shield = value;
// if we over-stepped max allowed energy, top off at maximum
if (m_shield > MAX_SHIELD)
m_shield = MAX_SHIELD;
// or if we went negative (that's bad), set to 0
else if (m_shield < 0)
m_shield = 0;
global.dbg.Out(Debug.DBG1, "TankClass.Shield set to [" + m_shield + "]");
}
}
public int PlayerTeam
{
get
{ return m_playerTeam; }
}
// returns ammunition for given shot type
public int GetAmmoCount(int shotType)
{
return (m_weapons[shotType].ammo);
}
// returns maximum ammunition for given shot type
public int GetAmmoMax(int shotType)
{
return (m_weapons[shotType].maxAmmo);
}
// returns weapon color
public Color GetWeaponColor(int shotType)
{
return (m_weapons[shotType].color);
}
// handles changing an AI's weapon
private void ChangeAIWeapon()
{/*
ShotClass.ShotTypes lastIdx = ShotClass.ShotTypes.Basic;
int lastAmmoCount = 0;
for(int i=0; i < m_weapons.Length; ++i)
{
if (m_weapons[i].ammo > lastAmmoCount)
{
lastAmmoCount = m_weapons[i].ammo;
lastIdx = (ShotClass.ShotTypes)i;
}
}
// don't change weapons if weapon is already selected
if (ShotType != lastIdx)
ShotType = lastIdx;*/
ShotType = ShotClass.ShotTypes.Basic;
}
public int GetMinDamage(int shotType)
{
return (m_weapons[shotType].minDamage);
}
public int GetMaxDamage(int shotType)
{
return (m_weapons[shotType].maxDamage);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////
// TANK ///////////////////////////////////////////////////////////////////////////////////////////////
public TankClass(string file, float x, float y, int playerId, string name, bool useAI, int playerTeam, int skill)
{
global.dbg.Out(Debug.DBG2, "TankClass called, file=[" + file + "] x=[" + x + "] y=[" + y + "] id=[" + playerId + "] name=[" + name + "] useAI=[" + useAI + "] team=[" + playerTeam + "] skill=[" + skill + "]");
// set our parent ID to ourself (to avoid our own shots colliding with ourself!)
this.parentId = (ulong)playerId;
this.globalId = (ulong)playerId;
// sprite attributes
frame.Width = 64;
frame.Height = 64;
framesCount = 40;
framesPerRow = 10;
layer = LAYER_TANK;
// starting direction
whichFrame = global.random(0, framesCount-1);
// vehicle attributes
maxSpeed = 1.5f;
maxSpeedBack = 0.75f;
acceleration = 0.25f;
// starting Speed
Speed = 0;
// type of collision
collisionType = CollisionType.Pixel;
// player identity for DirectPlay
playerId = playerId;
playerName = name;
m_ai = useAI;
m_playerTeam = playerTeam;
Energy = 100;
Skill = skill;
m_maxShotAltDelay = 50;
// initialize ammunition for all weapon types
// weapon #1 REGENERATE (slow, low damage, middle range)
m_weapons[(int)ShotClass.ShotTypes.Basic].shotType = ShotClass.ShotTypes.Basic;
m_weapons[(int)ShotClass.ShotTypes.Basic].ammo = 5;
m_weapons[(int)ShotClass.ShotTypes.Basic].maxAmmo = 5;
m_weapons[(int)ShotClass.ShotTypes.Basic].unlimited = true;
m_weapons[(int)ShotClass.ShotTypes.Basic].regenRate = 3000;
m_weapons[(int)ShotClass.ShotTypes.Basic].minDamage = 15;
m_weapons[(int)ShotClass.ShotTypes.Basic].maxDamage = 25;
m_weapons[(int)ShotClass.ShotTypes.Basic].range = 300;
m_weapons[(int)ShotClass.ShotTypes.Basic].maxRange = 300;
m_weapons[(int)ShotClass.ShotTypes.Basic].speed = 3.0f;
m_weapons[(int)ShotClass.ShotTypes.Basic].color = Color.DarkGreen;
// weapon #2 NORMAL (fast, middle damage, middle range)
m_weapons[(int)ShotClass.ShotTypes.L2].shotType = ShotClass.ShotTypes.L2;
m_weapons[(int)ShotClass.ShotTypes.L2].ammo = 10;
m_weapons[(int)ShotClass.ShotTypes.L2].maxAmmo = 25;
m_weapons[(int)ShotClass.ShotTypes.L2].unlimited = false;
m_weapons[(int)ShotClass.ShotTypes.L2].minDamage = 5;
m_weapons[(int)ShotClass.ShotTypes.L2].maxDamage = 20;
m_weapons[(int)ShotClass.ShotTypes.L2].range = 300;
m_weapons[(int)ShotClass.ShotTypes.L2].maxRange = 300;
m_weapons[(int)ShotClass.ShotTypes.L2].speed = 4.0f;
m_weapons[(int)ShotClass.ShotTypes.L2].color = Color.OrangeRed;
// weapon #3 DAMAGE (slow, hard damage, short range)
m_weapons[(int)ShotClass.ShotTypes.L3].shotType = ShotClass.ShotTypes.L3;
m_weapons[(int)ShotClass.ShotTypes.L3].ammo = 10;
m_weapons[(int)ShotClass.ShotTypes.L3].maxAmmo = 25;
m_weapons[(int)ShotClass.ShotTypes.L3].unlimited = false;
m_weapons[(int)ShotClass.ShotTypes.L3].minDamage = 35;
m_weapons[(int)ShotClass.ShotTypes.L3].maxDamage = 55;
m_weapons[(int)ShotClass.ShotTypes.L3].range = 250;
m_weapons[(int)ShotClass.ShotTypes.L3].maxRange = 250;
m_weapons[(int)ShotClass.ShotTypes.L3].speed = 3.5f;
m_weapons[(int)ShotClass.ShotTypes.L3].color = Color.PowderBlue;
// weapon #4 RANGE (very fast, low damage, wide range)
m_weapons[(int)ShotClass.ShotTypes.L4].shotType = ShotClass.ShotTypes.L4;
m_weapons[(int)ShotClass.ShotTypes.L4].ammo = 10;
m_weapons[(int)ShotClass.ShotTypes.L4].maxAmmo = 25;
m_weapons[(int)ShotClass.ShotTypes.L4].unlimited = false;
m_weapons[(int)ShotClass.ShotTypes.L4].minDamage = 15;
m_weapons[(int)ShotClass.ShotTypes.L4].maxDamage = 25;
m_weapons[(int)ShotClass.ShotTypes.L4].range = 400;
m_weapons[(int)ShotClass.ShotTypes.L4].maxRange = 400;
m_weapons[(int)ShotClass.ShotTypes.L4].speed = 6.0f;
m_weapons[(int)ShotClass.ShotTypes.L4].color = Color.RoyalBlue;
// alternate basic weapon (NORMAL)
m_weapons[(int)ShotClass.ShotTypes.AltBasic].shotType = ShotClass.ShotTypes.AltBasic;
m_weapons[(int)ShotClass.ShotTypes.AltBasic].ammo = 10;
m_weapons[(int)ShotClass.ShotTypes.AltBasic].maxAmmo = 10;
m_weapons[(int)ShotClass.ShotTypes.AltBasic].unlimited = true;
m_weapons[(int)ShotClass.ShotTypes.AltBasic].regenRate = 500;
m_weapons[(int)ShotClass.ShotTypes.AltBasic].minDamage = 1;
m_weapons[(int)ShotClass.ShotTypes.AltBasic].maxDamage = 5;
m_weapons[(int)ShotClass.ShotTypes.AltBasic].range = 175;
m_weapons[(int)ShotClass.ShotTypes.AltBasic].maxRange = 175;
m_weapons[(int)ShotClass.ShotTypes.AltBasic].speed = 3.0f;
// alternate weapon level 2 (COUNT)
m_weapons[(int)ShotClass.ShotTypes.AltL2].shotType = ShotClass.ShotTypes.AltL2;
m_weapons[(int)ShotClass.ShotTypes.AltL2].ammo = 50;
m_weapons[(int)ShotClass.ShotTypes.AltL2].maxAmmo = 100;
m_weapons[(int)ShotClass.ShotTypes.AltL2].unlimited = false;
m_weapons[(int)ShotClass.ShotTypes.AltL2].minDamage = 5;
m_weapons[(int)ShotClass.ShotTypes.AltL2].maxDamage = 10;
m_weapons[(int)ShotClass.ShotTypes.AltL2].range = 200;
m_weapons[(int)ShotClass.ShotTypes.AltL2].maxRange = 200;
m_weapons[(int)ShotClass.ShotTypes.AltL2].speed = 4.0f;
// alternate basic level 3 (DAMAGE)
m_weapons[(int)ShotClass.ShotTypes.AltL3].shotType = ShotClass.ShotTypes.AltL3;
m_weapons[(int)ShotClass.ShotTypes.AltL3].ammo = 25;
m_weapons[(int)ShotClass.ShotTypes.AltL3].maxAmmo = 100;
m_weapons[(int)ShotClass.ShotTypes.AltL3].unlimited = false;
m_weapons[(int)ShotClass.ShotTypes.AltL3].minDamage = 7;
m_weapons[(int)ShotClass.ShotTypes.AltL3].maxDamage = 15;
m_weapons[(int)ShotClass.ShotTypes.AltL3].range = 250;
m_weapons[(int)ShotClass.ShotTypes.AltL3].maxRange = 250;
m_weapons[(int)ShotClass.ShotTypes.AltL3].speed = 5.0f;
// alternate basic level 4 (FLAME)
m_weapons[(int)ShotClass.ShotTypes.AltL4].shotType = ShotClass.ShotTypes.AltL4;
m_weapons[(int)ShotClass.ShotTypes.AltL4].ammo = 25;
m_weapons[(int)ShotClass.ShotTypes.AltL4].maxAmmo = 100;
m_weapons[(int)ShotClass.ShotTypes.AltL4].unlimited = false;
m_weapons[(int)ShotClass.ShotTypes.AltL4].minDamage = 1;
m_weapons[(int)ShotClass.ShotTypes.AltL4].maxDamage = 25;
m_weapons[(int)ShotClass.ShotTypes.AltL4].range = 350;
m_weapons[(int)ShotClass.ShotTypes.AltL4].maxRange = 350;
m_weapons[(int)ShotClass.ShotTypes.AltL4].speed = 2.5f;
// add a health status bar above our tank
m_energyBar = new ProgressBarClass(56, 4, START_ENERGY, START_ENERGY, Color.Black, Color.Red, Color.Black);
// draw our tank
Init(file, CONST.COLORKEY_WHITE, x, y);
// set again in case SpriteClass.Init() over-wrote it with a debug name
playerName = name;
}
// this gets called every frame
public override void Update()
{
// calculate our new coordinates
posX = posX + (Speed * (float)Math.Sin(((180-whichFrame*frameAngle)*Math.PI)/180));
posY = posY - (Speed * (float)Math.Cos(((180-whichFrame*frameAngle)*Math.PI)/180));
// if our new coordinates are out of the playing field, stop the tank!
if ((posX<=0) || (posX>=global.options.SCR_WIDTH-frame.Width) ||
(posY<=0) || (posY>=global.options.SCR_HEIGHT-frame.Height))
Speed = 0;
// bounce off a wall?
BounceScreenEdge();
// ready for next shot?
if (m_shotDelay < SHOTDELAY)
{
m_shotDelay++;
}
else
{
m_shotDelay = 0;
m_readyToShoot = true;
}
// don't allow alternative shot #4 to shoot too often
if (AltShotType == ShotClass.ShotTypes.AltL4)
{
// ready for next shot?
if (m_shotAltDelay < m_maxShotAltDelay)
{
m_shotAltDelay++;
}
else
{
m_shotAltDelay = 0;
m_readyToShootAlt = true;
}
}
else
{
m_readyToShootAlt = true;
}
// if this is a bot (aritificial intelligence) then handle their next move
if (m_ai)
{
DoAI();
}
// regenerates any weapons with "unlimited" ammo according to their 'regenRate'
for(int i=0; i < m_weapons.Length; ++i)
{
// if this weapon is unlimited...
if (m_weapons[i].unlimited == true)
{
// ...AND the weapon is NOT already full...
if (m_weapons[i].ammo < m_weapons[i].maxAmmo)
{
// if current TickCount (1000) - last regen TickCount (990) > regen Rate (10)...
if ((System.Environment.TickCount - m_weapons[i].lastRegen) > m_weapons[i].regenRate)
{
// ...regenerate ammo...
++m_weapons[i].ammo;
// ...and remember our last regeneration time for next time
m_weapons[i].lastRegen = System.Environment.TickCount;
}
}
}
}
}
// 1. draw tank's energy bar
// 2. draw tank's shields
// 3. draw tank's name
// 4. call base-class SpriteClass.Draw()
public new void Draw()
{
// redraw remaining energy/health bar
// health bar only displays 100, if over 100, draw 2nd, brighter bar over 1st bar
if (Energy > MAX_BAR_ENERGY)
{
if (global.options.showEnergyBar)
{
m_energyBar.Draw((int)posX+1, (int)posY-3, MAX_BAR_ENERGY);
m_energyBar.Draw((int)posX+1, (int)posY-3, Energy-MAX_BAR_ENERGY, Color.Gold);
}
}
else
{
if (global.options.showEnergyBar)
m_energyBar.Draw((int)posX+1, (int)posY-3, Energy);
}
// shield color
int colorRed = 55;
int colorGreen = 85;
int colorBlue = 185;
int radius = 22;
// draw multiple levels of shields (if we have any)
for(int level=0; level <= MAX_SHIELD; level += 20)
{
if (Shield > level)
{
// draw a shield layer
global.DC.DrawCircle((int)posX+(frame.Width/2), (int)posY+(frame.Height/2), radius, Color.FromArgb(colorRed, colorGreen, colorBlue));
// re-adjust color and radius for next time
colorRed += 20;
colorGreen += 20;
colorBlue += 15;
radius += 1;
}
else
break;
}
// display name of sprite
if ((playerName.Length>0) && (global.options.showNames))
{
global.DC.DrawText((int)posX, (int)(posY), playerName, Color.Black);
}
// draw the sprite
base.Draw();
}
// determines if tank's Energy is low and plays a warning sound occasionally if it is
public void IsEnergyLow()
{
// if we are still alive...
if (active)
{
// play low-energy sound if tank's energy is "low" (and is NOT '0')
if ((Energy<=ENERGYLOW_START) && (Energy>0))
{
// only play the sound every "ENERGYLOW_DELAY" game ticks
if (m_lowDelay < ENERGYLOW_DELAY)
{
m_lowDelay++;
}
else
{
m_lowDelay = 0;
global.SC.Play(SoundClass.SOUND_LOWENERGY, false);
}
}
}
}
// primary shot (e.g. left-click)
public void Shoot()
{
global.dbg.Out(Debug.DBG2, "TankClass.Shoot: " + playerName);
// if we are still alive...
if (active)
{
if (ReadyToShoot)
{
ReadyToShoot = false;
// if there is still some ammo left...
if (AmmoCount > 0)
{
// fire a shot!
ShotClass shot = new ShotClass( m_weapons[(int)m_shotType],
posX+(frame.Width/2-9/2)+ 25*(float)Math.Sin(((180-whichFrame*frameAngle)*Math.PI)/180),
posY+(frame.Height/2-9/2)-25*(float)Math.Cos(((180-whichFrame*frameAngle)*Math.PI)/180),
this.globalId);
if (shot != null)
{
// draw proper shot sprite based on tank direction
shot.direction = whichFrame;
// send shot packet
if (global.options.multiplayer)
global.PC.Shoot(shot);
global.SC.Play(ShotSound, false);
// add new sprite to the global sprite list
global.spriteList.Add(shot);
// decrement amount of ammo we have left
--AmmoCount;
}
}
// play out of ammo sound
else
{
//TODO: play different out of ammo sound per weapon???
global.SC.Play(SoundClass.SOUND_SHOTEMPTY, false);
}
}
}
}
// secondary shot (e.g. right-click)
public void ShootAlternativ(Point p)
{
global.dbg.Out(Debug.DBG2, "TankClass.ShootAlternativ: " + playerName);
if (active)
{
if (ReadyToShootAlt)
{
ReadyToShootAlt = false;
// if there is still some ammo left...
if (AltAmmoCount > 0)
{
ShotClass shot = new ShotClass( m_weapons[(int)m_altShotType],
posX+frame.Width/2-2,
posY+frame.Height/2-2,
this.globalId);
if (shot != null)
{
shot.frameAngle = 1;
if ((p.X>=(posX+frame.Height/2)) && (p.Y>=(posY+frame.Width/2)))
shot.direction = (float)(Math.Atan2((p.Y-posY-frame.Height/2),(p.X-posX-frame.Width/2))*180/Math.PI);
else if ((p.X>=(posX+frame.Height/2)) && (p.Y<=(posY+frame.Width/2)))
shot.direction = (float)(Math.Atan2((p.Y-posY-frame.Height/2),(p.X-posX-frame.Width/2))*180/Math.PI);
else if ((p.X<=(posX+frame.Height/2)) && (p.Y<=(posY+frame.Width/2)))
shot.direction = (float)(Math.Atan2((p.Y-posY-frame.Height/2),(p.X-posX-frame.Width/2))*180/Math.PI);
else if ((p.X<=(posX+frame.Height/2)) && (p.Y>=(posY+frame.Width/2)))
shot.direction = (float)(Math.Atan2((p.Y-posY-frame.Height/2),(p.X-posX-frame.Width/2))*180/Math.PI);
// send shot packet
if (global.options.multiplayer)
global.PC.Shoot(shot);
// play a sound
global.SC.Play(AltShotSound, false);
global.spriteList.Add(shot);
// decrement amount of ammo we have left
--AltAmmoCount;
}
}
// play out of ammo sound
else
{
//TODO: play different out of ammo sound per weapon???
global.SC.Play(SoundClass.SOUND_SHOTEMPTY, false);
}
}
}
else
{
global.dbg.Out(Debug.DBG2, "TankClass.ShootAlternativ: couldn't add alternativ shot for " + playerName + ", cause not active");
}
}
// if we were hit by something, calculate shield/energy losses/gains
public override void ReactToCollision(SpriteClass s)
{
// if we are still alive...
if (active)
{
// hit by a shot of something
if (s.GetType() == typeof(ShotClass))
{
global.dbg.Out(Debug.DBG2, "TankClass.ReactToCollision: player=[" + this.playerName + "] shot [" + s.file + "]");
// "calculate" damage
int damage = global.random(GetMinDamage((int)((ShotClass)s).ShotType), GetMaxDamage((int)((ShotClass)s).ShotType));
// if the calculated shield damage wasn't enough, take away some energy as well
if (Shield < damage)
{
Energy -= (damage - Shield);
Shield -= 0;
}
// otherwise just remove shields
else
{
Shield -= damage;
}
global.dbg.Out(Debug.DBG3, " -- damage [" + damage + "]");
// handle further specifics based on each shot type
switch (((ShotClass)s).ShotType)
{
case ShotClass.ShotTypes.Basic:
Speed -= 1.0f;
break;
case ShotClass.ShotTypes.L2:
Speed -= 0.5f;
break;
case ShotClass.ShotTypes.L3:
Speed = 0.0f;
break;
case ShotClass.ShotTypes.L4:
Speed = 0.0f;
break;
case ShotClass.ShotTypes.AltBasic:
case ShotClass.ShotTypes.AltL2:
Speed -= 0.1f;
break;
case ShotClass.ShotTypes.AltL3:
Speed -= 0.15f;
break;
case ShotClass.ShotTypes.AltL4:
Speed -= 0.35f;
break;
default:
global.dbg.Out(Debug.ERR, "TankClass.ReactToCollision: player=[" + this.playerName + "] unknown shot type [" + ((ShotClass)s).ShotType + "]");
break;
}
}
// crashed into a tank
else if (s.GetType() == typeof(TankClass))
{
global.dbg.Out(Debug.DBG2, "TankClass.ReactToCollision: player=[" + this.playerName + "] tank [" + s + "]");
//global.dbg.Out(Debug.TRACE, "TankClass.ReactToCollision: Speed=[" + Speed + "] maxS=[" + maxSpeed + "]");
// bounce off the other tank
BounceObject(s);
// stop tank
Speed = 0;
}
// crashed into a tree
else if (s.GetType() == typeof(TreeClass))
{
global.dbg.Out(Debug.DBG2, "TankClass.ReactToCollision: player=[" + this.playerName + "] tank [" + s + "]");
// bounce off the other tank
BounceObject(s);
// stop tank
Speed = 0;
}
// picked up a power-up
else if (s.GetType() == typeof(PowerupClass))
{
global.dbg.Out(Debug.DBG2, "TankClass.ReactToCollision: player=[" + this.playerName + "] powerup [" + s + "]");
switch (((PowerupClass)s).Powerup)
{
//TODO: handle secondary weapon pick-ups...
case PowerupClass.PowerupType.Energy:
global.dbg.Out(Debug.DBG2, "TankClass picked up energy powerup, amount=[" + ((PowerupClass)s).Amount + "]");
Energy += ((PowerupClass)s).Amount;
break;
case PowerupClass.PowerupType.Shield:
global.dbg.Out(Debug.DBG2, "TankClass picked up shield powerup, amount=[" + ((PowerupClass)s).Amount + "]");
Shield += ((PowerupClass)s).Amount;
break;
case PowerupClass.PowerupType.Weapon:
global.dbg.Out(Debug.DBG2, "TankClass picked up weapon powerup, weapon=[" + ((PowerupClass)s).Weapon + "]");
break;
case PowerupClass.PowerupType.Ammo:
global.dbg.Out(Debug.DBG2, "TankClass picked up ammo powerup, weapon=[" + ((PowerupClass)s).Weapon + "] amount=[" + ((PowerupClass)s).Amount + "]");
AmmoCount += ((PowerupClass)s).Amount;
break;
default:
global.dbg.Out(Debug.ERR, "TankClass picked up unknown powerup, powerup=[" + ((PowerupClass)s).Powerup + "] amount=[" + ((PowerupClass)s).Amount + "] weapon=[" + ((PowerupClass)s).Weapon + "]");
break;
}
}
else
{
global.dbg.Out(Debug.WARN, "TankClass.ReactToCollision: player=[" + this.playerName + "] SOMETHING ELSE?? [" + s + "]");
}
// picked up a power-down!
//else if (s.GetType() == typeof(PowerdownClass))
//{
//}
// are we dead?
//if (Energy < 1)
}
}
private void DoAI()
{
// set speed
speed = ((float)Skill)*0.25f;
TankClass enemy = global.spriteList.GetNearestEnemy(this);
// if a tank was found...
if (enemy != null)
{
global.dbg.Out(Debug.DBG3, "Nearest enemy tank, name=[" + enemy.playerName + "] x=[" + enemy.posX + "/" + posX + "] y=[" + enemy.posY + "/" + posY + "]");
//
//enemy.whichFrame
//
// which direction to turn
if (posX>enemy.posX && posY>enemy.posY)
{
global.dbg.Out(Debug.DBG3, "Tank.AIMove(1)");
if (whichFrame>=0 && whichFrame<9)
{
global.dbg.Out(Debug.DBG3, "Tank.AIMove(1.1)");
if (global.random(0,1) == 0)
TurnLeft();
else
TurnRight();
}
else if (whichFrame>=9 && whichFrame<19)
{
global.dbg.Out(Debug.DBG3, "Tank.AIMove(1.2)");
TurnLeft();
}
else if (whichFrame>=19 && whichFrame<29)
{
global.dbg.Out(Debug.DBG3, "Tank.AIMove(1.3)");
// TODO: more exactly decision for turning
}
else if (whichFrame>=29 && whichFrame<39)
{
global.dbg.Out(Debug.DBG3, "Tank.AIMove(1.4)");
TurnRight();
}
}
else if (posX>enemy.posX && posY<enemy.posY)
{
global.dbg.Out(Debug.DBG3, "Tank.AIMove(2)");
if (whichFrame>=0 && whichFrame<9)
{
TurnRight();
}
else if (whichFrame>=9 && whichFrame<19)
{
if (global.random(0,1) == 0)
TurnLeft();
else
TurnRight();
}
else if (whichFrame>=19 && whichFrame<29)
{
TurnLeft();
}
else if (whichFrame>=29 && whichFrame<39)
{
// TODO: more exactly decision for turning
}
}
else if (posX<enemy.posX && posY<enemy.posY)
{
global.dbg.Out(Debug.DBG3, "Tank.AIMove(3)");
if (whichFrame>=0 && whichFrame<9)
{
// TODO: more exactly decision for turning
}
else if (whichFrame>=9 && whichFrame<19)
{
TurnRight();
}
else if (whichFrame>=19 && whichFrame<29)
{
if (global.random(0,1) == 0)
TurnLeft();
else
TurnRight();
}
else if (whichFrame>=29 && whichFrame<39)
{
TurnLeft();
}
}
else if (posX<enemy.posX && posY>enemy.posY)
{
global.dbg.Out(Debug.DBG3, "Tank.AIMove(4)");
if (whichFrame>=0 && whichFrame<9)
{
TurnLeft();
}
else if (whichFrame>=9 && whichFrame<19)
{
// TODO: more exactly decision for turning
}
else if (whichFrame>=19 && whichFrame<29)
{
if (global.random(0,1) == 0)
TurnLeft();
else
TurnRight();
}
else if (whichFrame>=29 && whichFrame<39)
{
TurnRight();
}
}
}
// turn if we are too close to the LEFT-...
if (posX < 100)
{
if (posY < 100) // ...UPPER corner
TurnLeft();
else if (posY > global.options.SCR_HEIGHT-200) // ...BOTTOM corner
TurnRight();
else // ...wall...
TurnRight();
}
// turn if we are too close to the RIGHT-...
else if (posX > global.options.SCR_WIDTH-100)
{
if (posY < 150) // ...UPPER corner
TurnLeft();
else if (posY > global.options.SCR_HEIGHT-150) // ...BOTTOM corner
TurnLeft();
else // ...wall...
TurnRight();
}
// turn if we are too close to the TOP
else if (posY < 150)
{
TurnLeft();
}
// turn if we are too close to the BOTTOM
else if (posY > global.options.SCR_HEIGHT-150)
{
TurnRight();
}
// if we are not near a wall, use this speed
else
{
// turn randomly left or right
int dir = global.random(0, 100);
if (dir == 0 || dir == 1)
TurnLeft();
if (dir == 2)
TurnRight();
}
// if command-line parameter '-noAIShoot' was NOT used...
if (global.options.noAIShoot == false)
{
// choose weapon with the most ammunition
if (global.random(0, 50) == 0)
ChangeAIWeapon();
// choose secondary weapon with the most ammunition
//else if (global.random(0, 30) == 0)
//ChangeAIWeapon();
// fire a PRIMARY shot every once in a while
if ((global.random(0, 100)==0))
Shoot();
// fire a SECONDARY shot every once in a while
int enemyDistance = global.spriteList.GetDistance(this, enemy);
if (enemyDistance<ShotAltRange)
{
if (enemyDistance<ShotAltRange/2)
{
if ((global.random(0, 3)==0))
ShootAlternativ(new Point((int)enemy.posX, (int)enemy.posY));
}
else
{
if ((global.random(0, 10)==0))
ShootAlternativ(new Point((int)enemy.posX, (int)enemy.posY));
}
}
}
}
private void BounceObject(SpriteClass s)
{
Rectangle rIntersect = new Rectangle((int)posX, (int)posY, CollisionBox.Width, CollisionBox.Height);
Rectangle r2 = new Rectangle((int)s.posX, (int)s.posY, s.CollisionBox.Width, s.CollisionBox.Height);
// get intersecting rectangle
rIntersect.Intersect(r2);
// if object is on top-left, bounce down-right
if (rIntersect.X < posX && rIntersect.Y < posY)
{
//global.dbg.Out(Debug.TRACE, "BounceObject: bouncing down-right (xy=" + posX + "," + posY + " s.xy=" + s.posX + "," + s.posY + ")");
posX += 0.5f;
posY += 0.5f;
}
// if object is on top-right, bounce down-left
if (rIntersect.X >= posX && rIntersect.Y < posY)
{
//global.dbg.Out(Debug.TRACE, "BounceObject: bouncing down-left (xy=" + posX + "," + posY + " s.xy=" + s.posX + "," + s.posY + ")");
posX -= 0.5f;
posY += 0.5f;
}
// if object is on bottom-left, bounce up-right
if (rIntersect.X < posX && rIntersect.Y >= posY)
{
//global.dbg.Out(Debug.TRACE, "BounceObject: bouncing up-right (xy=" + posX + "," + posY + " s.xy=" + s.posX + "," + s.posY + ")");
posX += 0.5f;
posY -= 0.5f;
}
// if object is on bottom-right, bounce up-left
if (rIntersect.X >= posX && rIntersect.Y >= posY)
{
//global.dbg.Out(Debug.TRACE, "BounceObject: bouncing up-left (xy=" + posX + "," + posY + " s.xy=" + s.posX + "," + s.posY + ")");
posX -= 0.5f;
posY -= 0.5f;
}
}
private void BounceScreenEdge()
{
// Top
if (posY <= InfoBarClass.BAR_HEIGHT)
{
posY = InfoBarClass.BAR_HEIGHT;
// head into right direction
if (whichFrame>0 && whichFrame<20)
if (posX+frame.Width < global.options.SCR_WIDTH-1)
posX += 0.25f;
// head into left direction
else if (whichFrame>20)
if (posX>1)
posX -= 0.25f;
}
// Bottom
else if (posY+frame.Height >= global.options.SCR_HEIGHT-1)
{
posY = global.options.SCR_HEIGHT-frame.Height-1;
// head into right direction
if (whichFrame>0 && whichFrame<20)
if (posX+frame.Width < global.options.SCR_WIDTH-1)
posX += 0.25f;
// head into left direction
else if (whichFrame>20)
if (posX>1)
posX -= 0.25f;
}
// Left
else if (posX <= 1)
{
posX = 1;
// head up
if (whichFrame<10 || whichFrame>30)
if (posY+frame.Width < global.options.SCR_WIDTH-1)
posY += 0.25f;
// head down
else if (whichFrame>20 && whichFrame<30)
if (posY > InfoBarClass.BAR_HEIGHT)
posY -= 0.25f;
}
// Right
else if (posX+frame.Width >= global.options.SCR_WIDTH-1)
{
posX = global.options.SCR_WIDTH-frame.Width-1;
// head up
if (whichFrame<10 || whichFrame>30)
if (posY+frame.Width < global.options.SCR_WIDTH-1)
posY += 0.25f;
// head down
else if (whichFrame>20 && whichFrame<30)
posY -= 0.25f;
}
}
}
}
|