using System;
using System.Data;
using System.Text;
using System.Threading;
using System.Collections.ObjectModel;
using Holo.Virtual.Users;
using Holo.Virtual.Rooms.Pathfinding;
using Ion.Storage;
namespace Holo.Virtual.Rooms.Games
{
internal class Game
{
#region Declares
///
/// The gameLobby object where this game is hosted in.
///
internal gameLobby Lobby;
///
/// Indicates if this game is of the type 'BattleBall'. If false, then the game is of the type 'SnowStorm'.
///
internal bool isBattleBall;
///
/// The ID of this game.
///
internal int ID;
///
/// The display name of this game.
///
internal string Name;
///
/// The ID of the map that is played with this game.
///
internal int mapID;
///
/// The total amount of gametime in seconds.
///
internal int totalTime;
///
/// The amount of gametime is seconds that is still left on this game.
///
internal int leftTime;
internal gameState State;
///
/// BattleBall only. The numbers of the enabled powerups for this game.
///
internal int[] enabledPowerups;
///
/// The gamePlayer object (bbPlayer or ssPlayer) of the virtual user that has created this game.
///
internal gamePlayer Owner;
///
/// Collection with the gamePlayer objects of the virtual users that have 'checked in' to this game sub and should receive updates about team status etc.
///
internal Collection Subviewers = new Collection();
internal Collection[] Teams;
#region Map declares
#region Enumerators etc
internal enum gameState { Waiting = 0, Started = 1, Ended = 2 };
///
/// Represents the state of a tile on the gamemap in a game of 'BattleBall'.
///
private enum bbTileState { Default = 0, Touched = 1, Clicked = 2, Pressed = 3, Sealed = 4 }
///
/// Represents the color of the team that owns a tile on the gamemap in a game of 'BattleBall'.
///
private enum bbTileColor { Disabled = -2, Default = -1, Red = 0, Blue = 1, Yellow = 2, Green = 3 }
#endregion
///
/// The amount of seconds that the 'loading game' bar still has to be displayed.
///
private int leftCountdownSeconds = Config.Game_Countdown_Seconds;
///
/// The length of the arena.
private int bX;
///
/// The max width of the arena.
///
private int bY;
///
/// The heightmap of the arena.
///
internal string Heightmap;
private byte[,] Height;
private bool[,] Blocked;
private bbTileState[,] bbTState;
private bbTileColor[,] bbTColor;
private Thread updateHandler;
#endregion
#endregion
#region Constructors/destructor
///
/// Initializes a 'BattleBall' game.
///
/// The gameLobby object of the lobby where this game is hosted in.
/// The ID of this game.
/// The display name of this game.
/// The ID of the map that is played with this game.
/// The amount of teams that is used with the game.
/// The numbers of the enabled powerups for this game.
/// The gamePlayer instance of the virtual user that has created this game.
internal Game(gameLobby Lobby, int ID, string Name, int mapID, int teamAmount, int[] enabledPowerups, gamePlayer Owner)
{
this.Lobby = Lobby;
this.ID = ID;
this.Name = Name;
this.mapID = mapID;
this.Owner = Owner;
this.enabledPowerups = enabledPowerups;
this.totalTime = Config.Game_BattleBall_gameLength_Seconds;
this.leftTime = Config.Game_BattleBall_gameLength_Seconds;
this.isBattleBall = true;
// Dimension teams
this.Teams = new Collection[teamAmount];
for (int i = 0; i < teamAmount; i++)
Teams[i] = new Collection();
}
///
/// Initializes a 'SnowStorm' game.
///
/// The gameLobby object of the lobby where this game is hosted in.
/// The ID of this game.
/// The display name of this game.
/// The ID of the map that is played with this game.
/// The amount of teams that is used with the game.
/// The total amount of gametime in seconds.
/// The gamePlayer instance of the virtual user that has created this game.
internal Game(gameLobby Lobby, int ID, string Name, int mapID, int teamAmount, int totalTime, gamePlayer Owner)
{
this.Lobby = Lobby;
this.ID = ID;
this.Name = Name;
this.mapID = mapID;
this.Owner = Owner;
this.totalTime = totalTime;
this.leftTime = totalTime;
this.isBattleBall = false;
// Dimension teams
this.Teams = new Collection[teamAmount];
for (int i = 0; i < teamAmount; i++)
Teams[i] = new Collection();
}
///
/// Stops the game, and removes all players and subviewers from the game.
///
internal void Abort()
{
try { updateHandler.Abort(); }
catch { }
string Data = "Cm" + "H";
for (int i = 0; i < Teams.Length; i++)
foreach (gamePlayer Member in Teams[i])
{
if (Member.User != null)
{
if (State == gameState.Waiting)
Member.User.sendData(Data);
Member.User.gamePlayer = null;
}
}
foreach (gamePlayer subViewer in Subviewers)
{
if (subViewer.User != null)
{
if (State == gameState.Waiting)
subViewer.User.sendData(Data);
subViewer.User.gamePlayer = null;
}
}
}
#endregion
#region Player management
///
/// Moves a player to a certain team, or removes a player from the game.
///
/// The gamePlayer object of the player to move.
/// The ID of the team where the player was in before. If the player is new to the game, then this parameter should be -1.
/// The ID of the team where the player is moving to. If the player is leaving the game, then this parameter should be -1.
internal void movePlayer(gamePlayer Player, int fromTeamID, int toTeamID)
{
try
{
if (fromTeamID != -1) // Remove player from previous team
Teams[fromTeamID].Remove(Player);
if (toTeamID != -1) // User added to team
{
if (Teams[toTeamID].Contains(Player) == false)
Teams[toTeamID].Add(Player);
Player.teamID = toTeamID;
}
else
{
Teams[Player.teamID].Remove(Player);
Player.User.gamePlayer = null;
}
sendData("Ci" + this.Sub);
}
catch { }
}
///
/// Indicates if the game can be launched. Games are 'launchable' when there are atleast two teams with players.
///
internal bool Launchable
{
get
{
int activeTeamCount = 0;
for (int i = 0; i < Teams.Length; i++)
if (Teams[i].Count != 0) // This team has got players
activeTeamCount++;
return (activeTeamCount > 1); // Atleast two teams with players
}
}
///
/// Returns a boolean that indicates if a certain team has space left for new team members. On error, false is returned.
///
/// The ID of the team to check.
internal bool teamHasSpace(int teamID)
{
try
{
int m = 0;
int l = Teams.Length;
if (l == 2)
m = 6;
else if (l == 3)
m = 4;
else if (l == 4)
m = 3;
return (Teams[teamID].Count < m);
}
catch { return false; }
}
///
/// Sends a single packet to all players, spectators and subviewers of this game.
///
/// The packet to send.
internal void sendData(string Data)
{
for (int i = 0; i < Teams.Length; i++)
foreach (gamePlayer Member in Teams[i])
Member.sendData(Data);
if (State == gameState.Waiting) // Game hasn't started yet
{
foreach (gamePlayer subViewer in Subviewers)
subViewer.sendData(Data);
}
}
#endregion
#region Properties
internal string Sub
{
get
{
StringBuilder Entry = new StringBuilder(Encoding.encodeVL64((int)State));
if (Launchable) // Atleast two teams with waiting players
Entry.Append("I"); // 1 = Game can be started
else
Entry.Append("H"); // 0 = Game can't be started yet
Entry.Append(Name + Convert.ToChar(2) + Encoding.encodeVL64(Owner.roomUID) + Owner.User._Username + Convert.ToChar(2));
if (isBattleBall == false) // SnowStorm game
Entry.Append(Encoding.encodeVL64(totalTime));
Entry.Append(Encoding.encodeVL64(mapID) + Encoding.encodeVL64(0) + Encoding.encodeVL64(Teams.Length));
for (int i = 0; i < Teams.Length; i++)
{
Entry.Append(Encoding.encodeVL64(Teams[i].Count));
foreach (gamePlayer Member in Teams[i])
Entry.Append(Encoding.encodeVL64(Member.roomUID) + Member.User._Username + Convert.ToChar(2));
}
if (isBattleBall) // BattleBall game
{
for (int i = 0; i < enabledPowerups.Length; i++)
Entry.Append(enabledPowerups[i] + ",");
Entry.Append("9" + Convert.ToChar(2));
}
else // SnowStorm game
Entry.Append(Encoding.encodeVL64(leftTime) + Encoding.encodeVL64(mapID));
return Entry.ToString();
}
}
#endregion
#region Arena
internal void startGame()
{
State = gameState.Started;
foreach (gamePlayer subViewer in Subviewers)
subViewer.sendData(this.Sub);
using (DatabaseClient dbClient = frmMain.dbManager.GetClient())
{
this.Heightmap = dbClient.getString("SELECT heightmap FROM games_maps WHERE type = '" + Lobby.Type + "' AND id = '" + mapID + "'");
}
string[] _H = Heightmap.Split(Convert.ToChar(13));
this.bX = _H[0].Length;
this.bY = _H.Length - 1;
string[] _T = null;
if (isBattleBall)
{
this.bbTState = new bbTileState[bX, bY];
this.bbTColor = new bbTileColor[bX, bY];
using (DatabaseClient dbClient = frmMain.dbManager.GetClient())
{
_T = dbClient.getString("SELECT bb_tilemap FROM games_maps WHERE type = 'bb' AND id = '" + mapID + "'").Split(Convert.ToChar(13));
}
}
this.Height = new byte[bX, bY];
this.Blocked = new bool[bX, bY];
for (int y = 0; y < bY; y++)
{
for (int x = 0; x < bX; x++)
{
string q = _H[y].Substring(x, 1);
if (q == "x")
{
Blocked[x, y] = true;
if (isBattleBall)
{
bbTColor[x, y] = bbTileColor.Disabled;
bbTState[x, y] = bbTileState.Sealed;
}
}
else
{
Height[x, y] = Convert.ToByte(q);
if (isBattleBall)
{
if (_T[y].Substring(x, 1) == "1")
bbTColor[x, y] = bbTileColor.Default;
else
{
bbTColor[x, y] = bbTileColor.Disabled;
bbTState[x, y] = bbTileState.Sealed;
}
}
}
}
}
int j = 0;
for (int i = 0; i < Teams.Length; i++)
{
if (Teams[i].Count == 0) // Empty team
continue;
DataRow dRow;
using (DatabaseClient dbClient = frmMain.dbManager.GetClient())
{
dRow = dbClient.getRow("SELECT x,y,z FROM games_maps_playerspawns WHERE type = '" + Lobby.Type + "' AND mapid = '" + mapID + "' AND teamid = '" + i + "'");
}
int spawnX = Convert.ToInt32(dRow["x"]);
int spawnY = Convert.ToInt32(dRow["y"]);
byte spawnZ = Convert.ToByte(dRow["z"]);
bool Flip = false;
foreach (gamePlayer Player in Teams[i])
{
Player.roomUID = j;
rs:
try
{
while (Blocked[spawnX, spawnY])
{
if (spawnZ == 0 || spawnZ == 2)
{
if (Flip)
spawnX -= 1;
else
spawnX += 1;
}
else if (spawnZ == 4 || spawnZ == 6)
{
if (Flip)
spawnY -= 1;
else
spawnY += 1;
}
}
Flip = (Flip != true);
}
catch { Flip = (Flip != true); goto rs; } // Out of range of map, reverse spawn
Blocked[spawnX, spawnY] = true;
Player.X = spawnX;
Player.Y = spawnY;
Player.Z = spawnZ;
Player.H = Height[spawnX, spawnY];
Player.goalX = -1;
Player.enteringGame = true;
j++;
//Out.WriteLine("Assigned spawnpoint [" + spawnX + "," + spawnY + "] to player [" + j + ", team id: " + Player.teamID + "]");
}
}
sendData("Cq" + Encoding.encodeVL64(-1)); // Send players to arena
new Eucalypt.commonDelegate(countDownTicker).BeginInvoke(null, null); // Start countdown bar
}
///
/// Counts
///
private void countDownTicker()
{
while (leftCountdownSeconds > 0)
{
leftCountdownSeconds--;
Thread.Sleep(1000);
}
using (DatabaseClient dbClient = frmMain.dbManager.GetClient())
{
for (int i = 0; i < Teams.Length; i++)
foreach (gamePlayer Player in Teams[i])
{
Player.User._Tickets -= 2;
Player.User.sendData("A|" + Player.User._Tickets);
dbClient.runQuery("UPDATE users SET tickets = '" + Player.User._Tickets + "' WHERE id = '" + Player.User.userID + "' LIMIT 1");
}
}
// Start game
updateHandler = new Thread(new ThreadStart(updateLoop));
updateHandler.Start();
sendData("Cw" + Encoding.encodeVL64(totalTime));
}
internal string getMap()
{
StringBuilder Setup = new StringBuilder();
if (isBattleBall)
{
Setup.Append("I" + Encoding.encodeVL64(leftCountdownSeconds) + Encoding.encodeVL64(Config.Game_Countdown_Seconds) + "H" + Encoding.encodeVL64(bX) + Encoding.encodeVL64(bY));
for (int y = 0; y < bY; y++)
for (int x = 0; x < bX; x++)
Setup.Append(Encoding.encodeVL64((int)bbTColor[x, y]) + Encoding.encodeVL64((int)bbTState[x, y]));
Setup.Append("IH");
}
else
Setup.Append("holo.cast.not_finished");
return Setup.ToString();
}
internal string getPlayers()
{
StringBuilder Setup = null;
StringBuilder Helper = new StringBuilder();
Random RND = new Random(DateTime.Now.Millisecond * totalTime);
if (isBattleBall)
{
int p = 0;
for (int i = 0; i < Teams.Length; i++)
foreach (gamePlayer Player in Teams[i])
{
//Out.WriteLine(Player.roomUID + "!");
Helper.Append("H" + Encoding.encodeVL64(p) + Encoding.encodeVL64(Player.X) + Encoding.encodeVL64(Player.Y) + Encoding.encodeVL64(Player.H) + Encoding.encodeVL64(Player.Z) + "HM" + Player.User._Username + Convert.ToChar(2) + Player.User._Mission + Convert.ToChar(2) + Player.User._Figure + Convert.ToChar(2) + Player.User._Sex + Convert.ToChar(2) + Encoding.encodeVL64(Player.teamID) + Encoding.encodeVL64(Player.roomUID));
p++;
}
Setup = new StringBuilder("I" + Encoding.encodeVL64(leftCountdownSeconds) + Encoding.encodeVL64(Config.Game_Countdown_Seconds) + Encoding.encodeVL64(p));
Setup.Append(Helper.ToString());
Helper = null;
Setup.Append(Encoding.encodeVL64(bX) + Encoding.encodeVL64(bY));
for (int y = 0; y < bY; y++)
for (int x = 0; x < bX; x++)
Setup.Append(Encoding.encodeVL64((int)bbTColor[x, y]) + Encoding.encodeVL64((int)bbTState[x, y]));
Setup.Append("IH");
}
else
Setup.Append("holo.cast.not_finished");
return Setup.ToString();
}
private void updateLoop()
{
if (isBattleBall)
{
int teamAmount = Teams.Length;
int[] teamScores = new int[teamAmount];
bool subtrSecond = false;
int activeTeamAmount = 0;
for (int i = 0; i < teamAmount; i++)
{
if (Teams[i].Count > 0)
activeTeamAmount++;
else
teamScores[i] = -1;
}
Random RND = new Random(DateTime.Now.Millisecond * teamAmount);
while (leftTime > 0)
{
int[] Amounts = new int[3]; // [0] = unit amount, [1] = updated tile amount, [2] = moving units amount
StringBuilder Players = new StringBuilder();
StringBuilder updatedTiles = new StringBuilder();
StringBuilder Movements = new StringBuilder();
for (int i = 0; i < teamAmount; i++)
foreach (gamePlayer Player in Teams[i])
{
Players.Append("H" + Encoding.encodeVL64(Player.roomUID) + Encoding.encodeVL64(Player.X) + Encoding.encodeVL64(Player.Y) + Encoding.encodeVL64(Player.H) + Encoding.encodeVL64(Player.Z) + "HM");
if (Player.bbColorTile)
{
Player.bbColorTile = false;
updatedTiles.Append(Encoding.encodeVL64(Player.X) + Encoding.encodeVL64(Player.Y) + Encoding.encodeVL64((int)bbTColor[Player.X, Player.Y]) + Encoding.encodeVL64((int)bbTState[Player.X, Player.Y]));
Amounts[1]++;
}
if (Player.goalX != -1)
{
Coord Next = gamePathfinder.getNextStep(Player.X, Player.Y, Player.goalX, Player.goalY);
if (Next.X != -1 && Blocked[Next.X, Next.Y] == false)
{
Amounts[2]++;
Movements.Append("J" + Encoding.encodeVL64(Player.roomUID) + Encoding.encodeVL64(Next.X) + Encoding.encodeVL64(Next.Y));
if (Next.X == Player.goalX && Next.Y == Player.goalY)
Player.goalX = -1;
Blocked[Player.X, Player.Y] = false;
Blocked[Next.X, Next.Y] = true;
Player.Z = Rotation.Calculate(Player.X, Player.Y, Next.X, Next.Y);
Player.X = Next.X;
Player.Y = Next.Y;
Player.H = Height[Next.X, Next.Y];
if (bbTState[Next.X, Next.Y] != bbTileState.Sealed)
{
if (bbTColor[Next.X, Next.Y] == (bbTileColor)Player.teamID) // Already property of this team, increment the state
bbTState[Next.X, Next.Y]++;
else
{
bbTState[Next.X, Next.Y] = bbTileState.Touched;
bbTColor[Next.X, Next.Y] = (bbTileColor)Player.teamID;
}
int extraPoints = 0;
switch ((int)bbTState[Next.X, Next.Y]) // Check the new status and calculate extra points
{
case 1:
extraPoints = activeTeamAmount;
break;
case 2:
extraPoints = activeTeamAmount * 3;
break;
case 3:
extraPoints = activeTeamAmount * 5;
break;
case 4:
extraPoints = activeTeamAmount * 7;
break;
}
teamScores[Player.teamID] += extraPoints;
Player.Score += extraPoints;
Player.bbColorTile = true;
}
}
else
Player.goalX = -1; // Stop moving
}
Amounts[0]++;
}
string scoreString = "H" + Encoding.encodeVL64(teamAmount);
for (int i = 0; i < teamAmount; i++)
scoreString += Encoding.encodeVL64(teamScores[i]);
sendData("Ct" + Encoding.encodeVL64(Amounts[0]) + Players.ToString() + Encoding.encodeVL64(Amounts[1]) + updatedTiles.ToString() + scoreString + "I" + Encoding.encodeVL64(Amounts[2]) + Movements.ToString());
if (subtrSecond)
leftTime--;
subtrSecond = (subtrSecond != true);
Thread.Sleep(470);
}
State = gameState.Ended;
StringBuilder Scores = new StringBuilder("Cx" + Encoding.encodeVL64(Config.Game_scoreWindow_restartGame_Seconds) + Encoding.encodeVL64(teamAmount));
for (int i = 0; i < teamAmount; i++)
{
int memberAmount = Teams[i].Count;
if (memberAmount > 0)
{
Scores.Append(Encoding.encodeVL64(memberAmount));
using (DatabaseClient dbClient = frmMain.dbManager.GetClient())
{
foreach (gamePlayer Player in Teams[i])
{
Scores.Append(Encoding.encodeVL64(Player.roomUID));
if (Player.User != null)
{
dbClient.runQuery("UPDATE users SET bb_playedgames = bb_playedgames + 1,bb_totalpoints = bb_totalpoints + " + Player.Score + " WHERE id = '" + Player.User.userID + "' LIMIT 1");
Scores.Append(Player.User._Username + Convert.ToChar(2));
}
else
Scores.Append("M");
Scores.Append(Encoding.encodeVL64(Player.Score));
}
}
Scores.Append(Encoding.encodeVL64(teamScores[i]));
}
else
Scores.Append("M");
}
sendData(Scores.ToString());
}
}
private Coord[] getSurfaceTiles(Coord Start)
{
return null;
}
#endregion
}
}