using System;
using System.Collections;
using System.Drawing;
using System.IO;
using System.Threading;
using System.Windows.Forms;
using Microsoft.DirectX.DirectDraw;
using Global;
using Global.GlobalClass;
using KillerInstinct.DirectX;
using KillerInstinct.Sprites;
namespace KillerInstinct{
public class LayerCompareClass : IComparer
{
int IComparer.Compare(Object x, Object y)
{
return ((new CaseInsensitiveComparer()).Compare(((SpriteClass)x).layer.ToString(), ((SpriteClass)y).layer.ToString()));
}
}
public class SpriteListClass
{
public const int MAX_MESSAGES = 10; // maximum number of messages to allow on screen at one time
public const int TEXT_SPACE = 12; // space to leave between text chat messages
private const int SORT_TIMER = 500; // how long to wait before sorting sprite list
private const int DEFAULT_DELETE_TIME = 10000; // default time to wait before deleting text object
private ArrayList List, TextList;
public ulong spriteCount = 0; // increments on add, used to set unique playerId
private bool ListChanged = false;
private int lastSortTime = 0; // how long since we last sorted (TickCount)
private int m_enemyEnergy = 0; // how much energy ALL of the current enemies have
public Resource[] resourceList;
public struct Resource
{
public string file;
public Surface surface; // bitmap attributes
public SurfaceDescription description; // bitmap attributes
public Bitmap bmp; // bitmap attributes
}
public int EnemyEnergy
{
get
{ return m_enemyEnergy; }
set
{
m_enemyEnergy = value;
if (m_enemyEnergy < 0)
{
global.dbg.Out(Debug.WARN, "SpriteListClass.EnemyEnergy < 0 [" + m_enemyEnergy + "] val=[" + value + "]");
m_enemyEnergy = 0;
}
global.dbg.Out(Debug.TRACE, "SpriteListClass.EnemyEnergy=[" + m_enemyEnergy + "]");
}
}
public struct TextObject
{
private int m_createTime;
private int m_deleteTime;
private string m_text;
public int m_x, m_y;
public Color m_color;
public Color Color
{
get
{ return m_color; }
}
public int CreateTime
{
get
{ return m_createTime; }
}
public int DeleteTime
{
get
{ return m_deleteTime; }
}
public string Text
{
get
{ return m_text; }
}
public int X
{
get
{ return m_x; }
}
public int Y
{
get
{ return m_y; }
}
// TODO: OVERLOADED: add text object using default time
/*public TextObject(string text)
{
TextObject(text, DEFAULT_DELETE_TIME);
}*/
public TextObject(string text, int deleteTime)
{
m_text = text;
m_x = 10;
m_y = global.options.SCR_HEIGHT-150;
m_color = Color.Black;
// use default deletion (cleanup) time if none specified
if (deleteTime <= 0)
m_deleteTime = DEFAULT_DELETE_TIME;
else
m_deleteTime = deleteTime;
// save our creation time so we know when to delete (based on m_delay)
m_createTime = System.Environment.TickCount;
}
}
public SpriteListClass()
{
// do a one-time load of all resources (*.bmp, etc)
if (LoadResources() == true)
{
List = new ArrayList();
TextList = new ArrayList();
}
}
private void SortList()
{
if (System.Environment.TickCount - lastSortTime > SORT_TIMER)
{
global.dbg.Out(Debug.DBG1, "Sorting sprite list.");
// re-sort the list
IComparer layerComparer = new LayerCompareClass();
List.Sort(0, List.Count, layerComparer);
// reset time since last sort
lastSortTime = System.Environment.TickCount;
}
}
// add to sprite list
public void Add(SpriteClass s)
{
global.dbg.Out(Debug.DBG2, "SpriteListClass.Add called [" + s.file + "]");
if (s != null)
{
lock (List)
{
ListChanged = true;
++spriteCount;
List.Add(s);
global.dbg.Out(Debug.DBG1, "Added sprite [" + s.globalId + "] file=[" + s.file + "]");
SortList();
}
}
else
{
global.dbg.Out(Debug.WARN, "SpriteListClass.Add: can't add a NULL sprite");
}
}
// add to sprite list
private void Add(SpriteClass[] s)
{
global.dbg.Out(Debug.DBG2, "SpriteListClass.Add[] called.");
for (int i=0; i<s.Length; ++i)
{
lock (List)
{
Add(s[i]);
}
}
}
// delete list
public void Clear()
{
global.dbg.Out(Debug.DBG2, "SpriteListClass.Clear called.");
// reset enemy energy
global.spriteList.EnemyEnergy = 0;
List.Clear();
}
// delete from sprite list
public void Delete(SpriteClass s)
{
if (s != null)
{
global.dbg.Out(Debug.DBG2, "SpriteListClass.Delete called [" + s.file + "]");
lock (List)
{
global.dbg.Out(Debug.DBG1, "Removed sprite [" + s.globalId + "] file=[" + s.file + "]");
ListChanged = true;
List.Remove(s);
SortList();
}
}
else
{
//TODO:avoid else tree ;)
global.dbg.Out(Debug.DBG1, "SpriteListClass.Delete: can't remove a NULL sprite");
}
}
// returns how many POWER-UP objects we have
public int CountPowerups()
{
int count = 0;
lock (List)
{
IEnumerator Index = List.GetEnumerator();
while (Index.MoveNext())
{
if (Index.Current.GetType() == typeof(PowerupClass))
{
++count;
}
}
}
return (count);
}
// returns how many MINE objects we have
public int CountMines()
{
int count = 0;
lock (List)
{
IEnumerator Index = List.GetEnumerator();
while (Index.MoveNext())
{
if (Index.Current.GetType() == typeof(MineClass))
{
++count;
}
}
}
return (count);
}
public void SetAISpeed(int speed)
{
lock (List)
{
IEnumerator Index = List.GetEnumerator();
// look through all the sprites
while (Index.MoveNext())
{
// only look at tank sprites
if (Index.Current.GetType() == typeof(Sprites.TankClass))
{
// only look at AI tanks
if (((Sprites.TankClass)Index.Current).AI == true)
{
// set speed
global.dbg.Out(Debug.DBG2, "SetAISpeed: setting speed of [" + ((Sprites.TankClass)Index.Current).playerName + "] to [" + speed + "]");
((Sprites.TankClass)Index.Current).speed = speed;
}
}
}
}
}
// returns nearest tank to given tank
public Sprites.TankClass GetNearestEnemy(Sprites.TankClass thisTank)
{
Sprites.TankClass tank = null;
// looking for tanks CLOSER than this value
int nearestDistance = global.options.SCR_WIDTH + global.options.SCR_HEIGHT;
// ensure nobody screws with the sprite list while we're looking at it
lock (List)
{
IEnumerator Index = List.GetEnumerator();
// look through all the sprites
while (Index.MoveNext())
{
// only look at tank sprites
if (Index.Current.GetType() == typeof(Sprites.TankClass))
{
// only look at OTHER tanks
if (Object.Equals((Sprites.TankClass)Index.Current, thisTank) == false)
{
// only look at ENEMY tanks
if (thisTank.PlayerTeam != ((Sprites.TankClass)Index.Current).PlayerTeam)
{
int dist = GetDistance(((Sprites.SpriteClass)Index.Current), thisTank);
// if closer...
if (dist < nearestDistance)
{
// ...use this tank, it's the closest (so far)...
tank = (Sprites.TankClass)Index.Current;
// ...and remember this as being the closest so far
nearestDistance = dist;
}
}
}
}
}
}
// if no tank was found to be the closest, and there was at least one other tank found...
if (tank == null)
{
global.dbg.Out(Debug.WARN, "SpriteListClass.GetNearestTank: Unable to find a nearest tank.");
}
return (tank);
}
// returns disatance in pixels between 'sprite1' and 'sprite2'
public int GetDistance(Sprites.SpriteClass sprite1, Sprites.SpriteClass sprite2)
{
int xRes = (int)(sprite1.posX - sprite2.posX) * (int)(sprite1.posX - sprite2.posX);
int yRes = (int)(sprite1.posY - sprite2.posY) * (int)(sprite1.posY - sprite2.posY);
return ((int)Math.Sqrt( xRes + yRes ));
}
// draw all sprites (tanks, powerups, etc) and text objects on-screen
public void Draw()
{
global.dbg.Out(Debug.DBG3, "SpriteListClass.Draw: drawing [" + List.Count + "] sprites...");
lock (List)
{
IEnumerator Index = List.GetEnumerator();
while (Index.MoveNext())
{
// if tank, use overloaded Draw()
if (Index.Current.GetType() == typeof(TankClass))
((TankClass)Index.Current).Draw();
// otherwise use normal SpriteClass.Draw()
else
((SpriteClass)Index.Current).Draw();
// if the list has been changed (add, delete)...
if (ListChanged == true)
{
// ...reposition iterator to valid position
Index = List.GetEnumerator();
ListChanged = false;
}
}
}
// draw text objects; delete any expired if necessary
global.dbg.Out(Debug.DBG3, "SpriteListClass.Draw: drawing [" + TextList.Count + "] text objects...");
lock (TextList)
{
IEnumerator Index = TextList.GetEnumerator();
while (Index.MoveNext())
{
TextObject textObj = (TextObject)Index.Current;
// if... current time (1005) - create time (1000) > delay (4) ...delete! (maybe)
if (System.Environment.TickCount - textObj.CreateTime > textObj.DeleteTime)
{
TextList.Remove(textObj);
break;
}
else
global.DC.DrawText(textObj.X, textObj.Y, textObj.Text, textObj.Color);
}
}
}
public void Restore()
{
IEnumerator Index = List.GetEnumerator();
while (Index.MoveNext())
{
SpriteClass temp = (SpriteClass)Index.Current;
temp.Restore();
}
}
public bool Collision()
{
bool collision = false;
try
{
lock (List)
{
int i=0, j=0, k;
global.dbg.Out(Debug.DBG3, "SpriteListClass.Collision: [" + List.Count + " sprites]");
// test collision of list objects
IEnumerator Index = List.GetEnumerator();
ArrayList tempList = new ArrayList();
tempList = (ArrayList)List.Clone();
while (Index.MoveNext())
{
i++;
j=0;
//global.dbg.Out(Debug.DBG3, " SpriteList.DetectCollision with sprite [" + (i) + "]");
SpriteClass temp = (SpriteClass)Index.Current;
IEnumerator Index2 = tempList.GetEnumerator();
for (k=0; k<i; ++k)
{
j++;
Index2.MoveNext();
}
while (Index2.MoveNext())
{
j++;
//global.dbg.Out(Debug.DBG3, " and with sprite [" + (j) + "]");
//global.dbg.Out(Debug.DBG3, "SpriteListClass.Collision: [" + i + ", " + (j)+ "]");
SpriteClass temp2 = (SpriteClass)Index2.Current;
//global.dbg.Out(Debug.DBG3, " Sprite.Collision[" + temp.PlayerId + " - " + temp.PlayerName + ", " + temp2.PlayerId + " - " + temp2.PlayerName + "]");
// if we shot and hit somebody else
// globalId=20 != enemyParentId=1 AND parentId=0 != enemyGlobalId=100
// if tank hits another tank
// globalId=0 != enemyParentId=1 AND parentId=MAX != enemyGlobalId=100
// globalId=0 != enemyParentId=1
if (temp.globalId != temp2.parentId && temp.parentId != temp2.globalId)
{
if (temp.Collision(temp2))
{
global.dbg.Out(Debug.DBG2, "SpriteListClass.Collision: reacting to collision. [" + temp.playerName + ", " + temp2.playerName + "]");
collision = true;
temp.ReactToCollision(temp2);
temp2.ReactToCollision(temp);
}
}
// if the list has changed, break out and reset the enumerators
if (ListChanged == true)
break;
}
if (ListChanged == true)
{
Index = List.GetEnumerator();
tempList = (ArrayList)List.Clone();
ListChanged = false;
}
}
}
}
catch (Exception e)
{
global.dbg.Out(Debug.ERR, "SpriteListClass.Collision: Exception caught [" + e + "]");
}
return collision;
}
public int HighestScore()
{
int score = 0;
IEnumerator Index = List.GetEnumerator();
while (Index.MoveNext())
{
SpriteClass temp = (SpriteClass)Index.Current;
// only count energy of OTHER players
if (temp.m_score > score)
score = temp.m_score;
}
return score;
}
public void AddTank(int id, string bmp, string name)
{
global.dbg.Out(Debug.DBG2, "SpriteListClass.AddTank called.");
try
{
int x = global.random(100, global.options.SCR_WIDTH-100);
int y = global.random(100, global.options.SCR_HEIGHT-100);
//TODO: set the player's team
Add(new Sprites.TankClass(bmp, x, y, id, name, false, 3, 0));
}
catch (Exception e)
{
global.dbg.Out(Debug.ERR, "SpriteList.AddTank: " + e);
}
}
public void MoveTank(int id, float x, float y, float direction, float speed)
{
global.dbg.Out(Debug.DBG2, "MoveTank called");
lock (List)
{
IEnumerator Index = List.GetEnumerator();
while (Index.MoveNext())
{
SpriteClass temp = (SpriteClass)Index.Current;
if (temp.playerId == id)
{
lock (List)
{
global.dbg.Out(Debug.DBG2, "MoveTank found tank for changes");
temp.posX = x;
temp.posY = y;
temp.whichFrame = (int)direction;
temp.Speed = speed;
break;
}
}
}
}
}
// sets ALL sprites to active = true
public void SetActive()
{
SetActiveState(true);
}
// sets ALL sprites to active = false
public void SetInactive()
{
SetActiveState(false);
}
// sets ALL sprites to active
private void SetActiveState(bool active)
{
global.dbg.Out(Debug.DBG3, "SetActiveState called");
lock (List)
{
IEnumerator Index = List.GetEnumerator();
while (Index.MoveNext())
{
global.dbg.Out(Debug.DBG3, "Setting sprite [" + ((SpriteClass)Index.Current).playerName + "] active = " + active);
((SpriteClass)Index.Current).active = active;
}
}
}
// called by PlayClass when a player leaves the game
public void RemoveTank(int id)
{
global.dbg.Out(Debug.DBG2, "RemoveTank called. id=[" + id + "]");
try
{
lock (List)
{
IEnumerator Index = List.GetEnumerator();
while (Index.MoveNext())
{
SpriteClass sprite = (SpriteClass)Index.Current;
if (sprite.playerId == id)
{
global.dbg.Out(Debug.DBG1, "Removed tank, id=[" + id + "]");
List.Remove(sprite);
break;
}
}
}
}
catch (Exception e)
{
global.dbg.Out(Debug.ERR, "SpriteList.RemoveTank: " + e);
}
}
// called by PlayClass when a shot packet is received
public void AddShot(ShotClass.ShotTypes shotType, float x, float y, float direction)
{
global.dbg.Out(Debug.DBG2, "AddSprite called. shotType=[" + shotType + "] xy=[" + x + "," + y + "] dir=[" + direction + "]");
try
{
//TODO: PASS THE PROPER PARENT ID!!!!
//FIXME!!!!!!!!!!!!
/*ShotClass shot = new ShotClass(shotType, x, y, 0);
shot.frameAngle = 1;
shot.direction = direction;
Add(shot);
// play corresponding sound
global.SC.Play(SoundClass.SOUND_SHOT+shotType+".wav", false);*/
}
catch (Exception e)
{
global.dbg.Out(Debug.ERR, "SpriteList.AddShot: " + e);
}
}
// called by PlayClass when a text message is received
public void AddText(TextObject obj)
{
global.dbg.Out(Debug.DBG2, "SpriteListClass.AddText called.");
try
{
// move all other CHAT messages up one a few pixels and if MAX_CHAT_MESSAGES
// is reached, erase the oldest (first) message in the list
CycleChatText(MAX_MESSAGES+1, TEXT_SPACE);
global.dbg.Out(Debug.DBG2, "SpriteListClass.AddText: inserting new text.");
// insert our new message at position 0
lock (TextList)
TextList.Insert(0, obj);
}
catch (Exception e)
{
global.dbg.Out(Debug.ERR, "SpriteList.AddText: " + e);
}
}
// move all other CHAT messages up one a few pixels and if MAX_CHAT_MESSAGES
// is reached, erase the oldest (first) message in the list
private void CycleChatText(int limit, int space)
{
global.dbg.Out(Debug.DBG2, "SpriteListClass.CycleText called. limit=[" + limit + "] space=[" + space + "]");
try
{
lock (TextList)
{
// if we have too many messages...
if (TextList.Count > limit)
{
global.dbg.Out(Debug.DBG2, "SpriteListClass.CycleText erasing first element.");
// ...erase the oldest (last) to make room for new messages
TextList.RemoveAt(TextList.Count-1);
}
// cycle through all messages and update y coordinate so messages scroll
for (int i=0; i < TextList.Count; ++i)
{
TextObject obj = (TextObject) TextList[i];
obj.m_y += space;
TextList[i] = obj;
global.dbg.Out(Debug.DBG2, "SpriteListClass.CycleText set obj #" + i + " y=[" + obj.m_y + "]");
}
}
}
catch (Exception e)
{
global.dbg.Out(Debug.ERR, "SpriteList.CycleChatText: " + e);
}
}
public int GetResourceIdx(string file)
{
int foundIdx = -1;
for (int i=0; i<resourceList.Length; ++i)
{
// if matching file, return position (idx)
if (resourceList[i].file == file)
{
// if file actually exists...
if (File.Exists(file))
{
foundIdx = i;
break;
}
else
{
global.dbg.Out(Debug.ERR, "GetResourceIdx:: Cannot load sprite [" + file + "]. Exiting...");
MessageBox.Show("GetResourceIdx:: Cannot load sprite [" + file + "]. Exiting...");
global.options.initialized = false;
break;
}
}
}
return (foundIdx);
}
// call this ONETIME to load all possible resources into memory
public bool LoadResources()
{
// get array of *.bmp files
string[] files = Directory.GetFiles(CONST.DIR_BITMAP, "*.bmp");
if (files.Length > 0)
{
// create our static list of resources
resourceList = new Resource[files.Length];
for (int i=0; i<files.Length; ++i)
{
resourceList[i].file = files[i];
resourceList[i].bmp = new Bitmap(resourceList[i].file);
resourceList[i].description = new SurfaceDescription();
resourceList[i].surface = new Surface(resourceList[i].file, resourceList[i].description, global.DC.Draw);
}
return true;
}
// nothing found
return false;
}
}
}
|