//*******************************************************************
/*
Solution: NerdkillPocket
Project: NerdkillPocket
File: RGameNerdkill.cs
Copyright 2003, 2004, Raphael MOLL.
This file is part of NerdkillPocket.
NerdkillPocket is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
NerdkillPocket is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with NerdkillPocket; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
//*******************************************************************
using System;
using System.Drawing;
using System.Collections;
using Alfray.Nerdkill.Engine;
//********************************
namespace Alfray.Nerdkill.Gameplay
{
//******************************************************
/// <summary>
/// Implementation of the nerdkill game
/// </summary>
//******************************************************
public sealed class RGameNerdkill: Engine.IEngineProcess
{
//-------------------------------------------
//----------- Public Constants --------------
//-------------------------------------------
//-------------------------------------------
//----------- Public Properties -------------
//-------------------------------------------
//*******************
public bool IsRunning
{
get
{
return mIsRunning;
}
set
{
mIsRunning = value;
}
}
//******************
public bool IsPaused
{
get
{
return mIsPaused;
}
set
{
mIsPaused = value;
}
}
//*******************
public bool IsSoundOn
{
get
{
return mIsSoundOn;
}
set
{
mIsSoundOn = value;
}
}
//********************
public bool IsFinished
{
get
{
return mNbNerds <= 0;
}
}
//-------------------------------------------
//----------- Public Methods ----------------
//-------------------------------------------
//********************
public RGameNerdkill()
{
}
#region IEngineProcess Members
//********************************************
public bool Initialize(IEngineContext context)
{
// keep context
mContext = context;
// initialize everything
init();
restart();
return true;
}
//*******************************************
public bool Terminate(IEngineContext context)
{
// keep context
mContext = context;
// Stop looping sounds if any
stopBulldozerSound();
// Play and wait for completion of the exit sound
// Disabled for PocketPC -- RM 20040525
// RSound sound = context.Resources.GetSound(RConstants.SoundId.NerdOut);
// context.Sound.Play(sound);
// while waiting display a congrats box
if (this.IsFinished)
{
// RM 20040512 .Net Compact Framework
System.Windows.Forms.MessageBox.Show("Congratulations! All the little pesky nerds are gone!\nThank you for playing!",
"Nerdkill C#");
}
// Disabled for PocketPC -- RM 20040525
// context.Sound.Wait(sound);
return true;
}
//*******************
/// <summary>
/// Inherited from IDisposable
/// </summary>
//*******************
public void Dispose()
{
// Nothing to dispose here
}
//*******************************
public bool PostEvent(REvent evt)
{
// Note: right now this is synchronous with UI and stuff
// If UI is made asynchronous, needs to use the Synchronise
// method of the queue before accessing it
mEventQueue.Enqueue(evt);
return true;
}
//***********************************************
public bool ProcessEvents(IEngineContext context)
{
// keep context
mContext = context;
// consumes all new EngineInput events
processInputEvents();
// the fun sound timer will kick in if the game is not pause
// and no action is being done (and sound is enabled)
bool fun_timer = (!IsPaused) && (IsSoundOn) && (mEventQueue.Count == 0);
// now process all events queued here
while(mEventQueue.Count > 0)
{
REvent evt = (REvent) mEventQueue.Dequeue();
// process events that can occur during a pause
switch(evt.Id)
{
case REvent.EventId.EndPause:
IsPaused = false;
mContext.Render2D.Pause = false;
break;
case REvent.EventId.SoundOn:
IsSoundOn = true;
break;
case REvent.EventId.SoundOff:
IsSoundOn = false;
break;
case REvent.EventId.Restart:
// Stop looping sounds if any
stopBulldozerSound();
// reset everything
restart();
// Play the intro sound
if (mIsSoundOn)
mContext.Sound.Play(mContext.Resources.GetSound(RConstants.SoundId.NerdIn));
break;
case REvent.EventId.Quit:
IsRunning = false;
break;
}
// the following events are discarded during a pause
if (IsPaused)
continue;
switch(evt.Id)
{
case REvent.EventId.ChangeWeapon:
mCurrentWeapon = evt.Code;
if (mCurrentWeapon == RConstants.SpriteId.WeaponFlag)
PostEvent(new REvent(REvent.EventId.Quit));
if (IsSoundOn)
mContext.Sound.Play(mContext.Resources.GetSound(RConstants.SoundId.Select));
break;
//---------------------
case REvent.EventId.FireWeapon:
fireWeapon(evt.Position);
break;
//---------------------
case REvent.EventId.MouseClick:
if (evt.Screen == RConstants.ScreenId.GameScreen)
{
mMousePressed = true;
mMousePos = evt.Position;
PostEvent(new REvent(REvent.EventId.FireWeapon, evt.Position));
}
else if (evt.Screen == RConstants.ScreenId.MenuScreen)
{
mMousePressed = false;
mMousePos = Point.Empty;
menuClicked(evt.Position);
}
break;
//---------------------
case REvent.EventId.MouseUp:
mMousePressed = false;
if (evt.Screen == RConstants.ScreenId.GameScreen)
mMousePos = evt.Position;
else
mMousePos = Point.Empty;
break;
//---------------------
case REvent.EventId.MouseIn:
case REvent.EventId.MouseOut:
// these events do not hold the mouse position under .Net WinForms
mMousePos = Point.Empty;
break;
//---------------------
case REvent.EventId.MouseMoved:
if (evt.Screen == RConstants.ScreenId.GameScreen)
mMousePos = evt.Position;
else
mMousePos = Point.Empty;
break;
//---------------------
case REvent.EventId.Scroll:
manualScrolling(evt.Position);
break;
//---------------------
case REvent.EventId.Pause:
IsPaused = true;
mContext.Render2D.Pause = true;
break;
}
} // while dequeue
// uzi weapon runs outside of the loop
if (mCurrentWeapon == RConstants.SpriteId.WeaponUzi)
{
if (mMousePressed && !mMousePos.IsEmpty)
{
fun_timer = false;
fireUzi(mMousePos);
playUziSound(true);
}
else
{
playUziSound(false);
}
}
// scroll game screen has necessary
if (autoScrolling())
fun_timer = false;
// if nothing happens... let's make the fun sound
if (fun_timer)
continueFunTimer();
else
stopFunTimer();
return true;
}
//**********************************************
public bool ProcessFrame(IEngineContext context)
{
if (IsPaused)
return true;
// keep context
mContext = context;
// update nerds
moveNerds();
triggerMines();
moveBulldozer();
moveCokeCan();
// render in offscreen buffer
renderBackground();
renderMenu();
renderSprites();
// update screen title
if (mNbNerds == 0)
context.Render2D.DisplayTitle("Congrats! No nerd left!");
else if (mNbNerds == 1)
context.Render2D.DisplayTitle(mNbNerds.ToString() + " tiny nerd left");
else
context.Render2D.DisplayTitle(mNbNerds.ToString() + " nerds left");
return true;
}
#endregion
//-------------------------------------------
//----------- Private Methods ---------------
//-------------------------------------------
//****************
public void init()
{
mIsRunning = false;
mIsSoundOn = true;
mEventQueue = new Queue();
}
//*******************
public void restart()
{
// reset all tables and structures
mMineTable = new ArrayList();
mBullTable = new ArrayList();
mCokeTable = new ArrayList();
mRandom = new Random(System.Environment.TickCount);
mCurrentWeapon = RConstants.SpriteId.WeaponGun;
mShootPos = Point.Empty;
mMousePos = Point.Empty;
mMousePressed = false;
mFunTimerLimit = 0;
mAhSoundVariant = 0;
mFunSoundVariant = 0;
mSpriteToggle = 0;
mNbActions = 0;
mIsPaused = false;
initNerds();
}
//*********************
public void initNerds()
{
mNbNerds = RGameConstants.NbNerds;
mNerdTable = new ArrayList(RGameConstants.NbNerds);
for(int i = 0; i < RGameConstants.NbNerds; i++)
{
int x = mRandom.Next(RGameConstants.NerdMinPosX, RGameConstants.NerdMaxPosX);
int y = mRandom.Next(RGameConstants.NerdMinPosY, RGameConstants.NerdMaxPosY);
double speed = RGameConstants.MaxSpeedBase + RGameConstants.MaxSpeedDelta * mRandom.NextDouble();
double dir = Math.PI * 2.0 * mRandom.NextDouble();
RNerd nerd = new RNerd(new Point(x, y), speed, dir);
mNerdTable.Add(nerd);
}
}
//******************************
public void processInputEvents()
{
// consumes all new EngineInput events
while(true)
{
REvent evt = mContext.Input.GetNextEvent();
if (evt == null)
break;
// interpret Input events
switch(evt.Id)
{
case REvent.EventId.KeyPress:
switch(evt.Key)
{
case "F": case "1": case "F1":
PostEvent(new REvent(REvent.EventId.ChangeWeapon,
RConstants.SpriteId.WeaponGun));
break;
case "U": case "2": case "F2":
PostEvent(new REvent(REvent.EventId.ChangeWeapon,
RConstants.SpriteId.WeaponUzi));
break;
case "C": case "3": case "F3":
PostEvent(new REvent(REvent.EventId.ChangeWeapon,
RConstants.SpriteId.WeaponMine));
break;
case "R": case "4": case "F4":
PostEvent(new REvent(REvent.EventId.ChangeWeapon,
RConstants.SpriteId.WeaponElec));
break;
case "E": case "5": case "F5":
PostEvent(new REvent(REvent.EventId.ChangeWeapon,
RConstants.SpriteId.WeaponBull));
break;
case "M": case "6": case "F6":
PostEvent(new REvent(REvent.EventId.ChangeWeapon,
RConstants.SpriteId.WeaponCoke));
break;
case "D": case "7": case "F7":
PostEvent(new REvent(REvent.EventId.ChangeWeapon,
RConstants.SpriteId.WeaponDynamite));
break;
case "L": case "8": case "F8":
case "esc":
PostEvent(new REvent(REvent.EventId.ChangeWeapon,
RConstants.SpriteId.WeaponFlag));
break;
case "P": case "pause":
if (IsPaused)
PostEvent(new REvent(REvent.EventId.EndPause));
else
PostEvent(new REvent(REvent.EventId.Pause));
break;
}
break; // case REvent.EventId.KeyPress
}
} // while DInput
}
//-------------------------------------------
//**********************
private void moveNerds()
{
// TBDL: use moving window
double max_speed = RGameConstants.MaxSpeedBase + RGameConstants.MaxSpeedDelta * mRandom.NextDouble();
foreach(RNerd nerd in mNerdTable)
{
if (!nerd.Visible)
continue;
if (nerd.IsAlive && nerd.IsElec)
{
// it's being electroctued
if (nerd.Damage(1))
{
nerd.DeadSpriteId = RConstants.SpriteId.NerdDeadElec;
makeAhSound();
}
}
if (nerd.IsAlive)
{
// current speed... walking or running
double speed = nerd.Speed;
double dir = nerd.Direction;
int x = nerd.Position.X;
int y = nerd.Position.Y;
// if going too fast...
if (speed > max_speed)
{
// breathless, let's slow down
speed--;
nerd.Speed = (speed < 0.0 ? 0.0 : speed);
}
else if (speed < RGameConstants.LowSpeedLimit)
{
// it managed to get some rest, go run again!
speed += RGameConstants.LowSpeedBump;
nerd.Speed = (speed < 0.0 ? 0.0 : speed);
}
// if moving at all...
if (speed > 0.0)
{
// movement vector
double vx = Math.Cos(dir);
double vy = Math.Sin(dir);
// new position
x = nerd.Position.X + (int)(vx * speed);
y = nerd.Position.Y + (int)(vy * speed);
// bounce on walls
const int mx = RGameConstants.NerdMaxPosX;
const int my = RGameConstants.NerdMaxPosY;
if (x < 0 || y < 0 || x >= mx || y >= my)
{
if (x < 0 || x >= mx)
vx = -vx;
if (y < 0 || y >= my)
vy = -vy;
// update direction
dir = Math.Atan2(vy, vx);
// and position
x = nerd.Position.X + (int)(vx * speed);
y = nerd.Position.Y + (int)(vy * speed);
}
else
{
// randomize direction a tiny bit
// 10 degres = 0.174 radians
if (mRandom.Next(10) >= 8)
{
const double ten_degres = Math.PI / 18.0;
dir += mRandom.NextDouble() * ten_degres - ten_degres / 2.0;
}
// normalize direction
const double pi2 = Math.PI * 2.0;
if (dir < 0.0)
dir += pi2;
else if (dir > pi2)
dir -= pi2;
}
} // speed > 0
// update nerd
nerd.Direction = dir;
nerd.SetPosition(x, y);
}
else
{
// dead nerd but still visible
// this happens when the nerd has been killed in an explosion
// and its skeleton is still flying away...
double speed = nerd.Speed;
if (speed > 0.0)
{
double dir = nerd.Direction;
// movement vector
double vx = Math.Cos(dir);
double vy = Math.Sin(dir);
// new position
int x = nerd.Position.X + (int)(vx * speed);
int y = nerd.Position.Y + (int)(vy * speed);
// decrease speed
speed--;
// the skeleton does not bounce on walls
if (x >= 0 && y >= 0 && x < RGameConstants.NerdMaxPosX && y < RGameConstants.NerdMaxPosY)
{
// still within boundaries, update nerd
nerd.Speed = speed;
nerd.Direction = dir;
nerd.SetPosition(x, y);
}
}
else if (nerd.DeadTick == 0)
{
nerd.DeadTick = DateTime.Now.Ticks;
}
else
{
long delay = (DateTime.Now.Ticks - nerd.DeadTick) / TimeSpan.TicksPerSecond;
if (nerd.IsExploded)
{
if (delay == 10)
nerd.DeadSpriteId = RConstants.SpriteId.NerdDeadShade;
else if (delay == 50)
nerd.Visible = false;
}
else if (!nerd.IsElec)
{
if (delay == 10)
nerd.DeadSpriteId = RConstants.SpriteId.NerdDeadExploded;
else if (delay == 30)
nerd.DeadSpriteId = RConstants.SpriteId.NerdDeadShade;
else if (delay == 60)
nerd.Visible = false;
}
}
}
}
}
//*************************
private void triggerMines()
// Check for nerds that walk on mines
// Each mine also triggers proximity mines
{
// A mine with direct hit explodes right away
long direct_tick = DateTime.Now.Ticks;
// A mine with indirect hit (i.e. triggered by another mine) explodes with a delay
long delay_tick = direct_tick + (long)(RGameConstants.MineDelay * TimeSpan.TicksPerSecond);
// -1- Trigger mines walked over by nerds
foreach(RMine mine in mMineTable)
{
// no need to trigger a mine twice...
if (mine.IsExploded || mine.Trigerred)
continue;
// let's find a clueless nerd to walk on the mine...
foreach(RNerd nerd in mNerdTable)
{
if (!nerd.Visible || !nerd.IsAlive)
continue;
if (mine.Trigger(nerd))
{
mine.TargetTick = direct_tick;
// a nerd only trigger one mine at once
break;
}
}
}
// -2- Explode mines as necessary
foreach(RMine mine in mMineTable)
{
// can't explode a mine twice
if (mine.IsExploded)
continue;
// only deal with triggered mines here
// which target tick is past currently time
if (!mine.Trigerred || mine.TargetTick >= direct_tick)
continue;
// note this mine as exploding
mine.Explode();
// explosion sound
if (IsSoundOn)
mContext.Sound.Play(mContext.Resources.GetSound(RConstants.SoundId.Explode));
// explode all nerds around
int px = mine.Position.X + RGameConstants.MineWidth / 2;
int py = mine.Position.Y + RGameConstants.MineHeight / 2;
foreach(RNerd nerd in mNerdTable)
{
if (!nerd.Visible || !nerd.IsAlive)
continue;
if (nerd.Explode(px, py))
{
nerd.DeadSpriteId = RConstants.SpriteId.NerdDeadExploded;
makeAhSound();
}
}
// trigger surrounding mines
foreach(RMine mine2 in mMineTable)
{
// can't explode or trigger a mine twice
if (mine2.IsExploded || mine2.Trigerred)
continue;
if (mine2.TriggerRange(px, py))
{
mine2.TargetTick = delay_tick;
// select sound
if (IsSoundOn)
mContext.Sound.Play(mContext.Resources.GetSound(RConstants.SoundId.Select));
}
} // for each mine2
} // for each mine
}
//**************************
private void moveBulldozer()
// Check for nerds smashed by bulldozer
{
foreach(RBulldozer bdz in mBullTable)
{
// no need to trigger a bdz twice...
if (!bdz.IsVisible)
continue;
// advance the thing
bdz.Advance();
// let's find a clueless nerd to be smashed...
foreach(RNerd nerd in mNerdTable)
{
if (!nerd.Visible || !nerd.IsAlive)
continue;
if (bdz.Hit(nerd))
{
nerd.Die();
nerd.DeadSpriteId = RConstants.SpriteId.NerdDeadNormal;
makeAhSound();
}
}
}
}
//************************
private void moveCokeCan()
// Check for nerds smashed by coke cans exploding
{
foreach(RCokeCan coke in mCokeTable)
{
// no need to trigger a bdz twice...
if (coke.IsExploded)
continue;
// advance the thing
coke.Advance();
// it can be triggered only once
if (coke.IsExploded)
{
// play sound
if (IsSoundOn)
mContext.Sound.Play(mContext.Resources.GetSound(RConstants.SoundId.Explode));
// let's find a clueless nerd to be smashed...
foreach(RNerd nerd in mNerdTable)
{
if (!nerd.Visible || !nerd.IsAlive)
continue;
if (coke.Hit(nerd))
{
nerd.Die();
nerd.DeadSpriteId = RConstants.SpriteId.NerdDeadExploded;
makeAhSound();
}
}
}
}
}
//********************************
private void fireWeapon(Point pos)
{
switch(mCurrentWeapon)
{
case RConstants.SpriteId.WeaponGun:
fireGun(pos);
// play sound
if (IsSoundOn)
mContext.Sound.Play(mContext.Resources.GetSound(RConstants.SoundId.Gun));
break;
case RConstants.SpriteId.WeaponUzi:
// play sound
playUziSound(true);
break;
case RConstants.SpriteId.WeaponElec:
fireElec(pos);
// play sound
if (IsSoundOn)
mContext.Sound.Play(mContext.Resources.GetSound(RConstants.SoundId.Elec));
break;
case RConstants.SpriteId.WeaponDynamite:
fireExplode(pos);
// play sound
if (IsSoundOn)
mContext.Sound.Play(mContext.Resources.GetSound(RConstants.SoundId.Explode));
break;
case RConstants.SpriteId.WeaponMine:
addMine(pos);
break;
case RConstants.SpriteId.WeaponBull:
addBulldozer(pos);
// play sound
startBulldozerSound();
break;
case RConstants.SpriteId.WeaponCoke:
addCokeCan(pos);
break;
}
}
//*****************************
private void fireGun(Point pos)
{
bool shoot_a_nerd = true;
mNbActions++;
// set shoot impact
mShootPos = pos;
// kill some and scare the others
foreach(RNerd nerd in mNerdTable)
{
if (!nerd.Visible || !nerd.IsAlive)
continue;
// kill a nerd?
if (shoot_a_nerd && nerd.IsHit(pos.X, pos.Y))
{
nerd.Die();
makeAhSound();
// kill only one nerd at a time
shoot_a_nerd = false;
}
else
{
nerd.Scare(pos.X, pos.Y);
}
} // foreach
}
//******************************
private void fireElec(Point pos)
{
bool shoot_a_nerd = true;
mNbActions++;
// kill some and scare the others
foreach(RNerd nerd in mNerdTable)
{
if (!nerd.Visible || !nerd.IsAlive)
continue;
// kill a nerd?
if (shoot_a_nerd && nerd.IsHit(pos.X, pos.Y))
{
if (nerd.IsElec)
{
// if a nerd is already being electrified, unelectrify it
nerd.IsElec = false;
}
else
{
// damage handling is done in the moveNerd function
nerd.IsElec = true;
// damage only one nerd at a time
shoot_a_nerd = false;
continue;
}
}
nerd.Scare(pos.X, pos.Y);
} // foreach
}
//*****************************
private void addMine(Point pos)
{
mNbActions++;
// set shoot impact
mShootPos = pos;
// set the mine
pos.X -= RGameConstants.NerdWidth / 2;
pos.Y -= RGameConstants.NerdHeight / 2;
mMineTable.Add(new RMine(pos));
}
//**********************************
private void addBulldozer(Point pos)
{
mNbActions++;
// set shoot impact
mShootPos = pos;
// set the start position
pos.X -= RGameConstants.BulldozerWidth / 2;
pos.Y -= RGameConstants.BulldozerHeight / 2;
mBullTable.Add(new RBulldozer(pos, new Point(RConstants.BackWidth, pos.Y)));
}
//********************************
private void addCokeCan(Point pos)
{
mNbActions++;
// set shoot impact
mShootPos = pos;
// set the start position
pos.X -= RGameConstants.CokeCanWidth / 2;
pos.Y -= RGameConstants.CokeCanHeight / 2;
// set the end position
Point end = pos;
end.X = pos.X;
end.Y = mRandom.Next(pos.Y, Math.Max(pos.Y, RConstants.BackHeight));
mCokeTable.Add(new RCokeCan(pos, end));
}
//*********************************
private void fireExplode(Point pos)
{
mNbActions++;
// set shoot impact
mShootPos = pos;
// kill some and scare the others
foreach(RNerd nerd in mNerdTable)
{
if (!nerd.Visible || !nerd.IsAlive)
continue;
if (nerd.Explode(pos.X, pos.Y))
{
nerd.DeadSpriteId = RConstants.SpriteId.NerdDeadExploded;
makeAhSound();
}
} // foreach
}
//*****************************
private void fireUzi(Point pos)
{
fireGun(pos);
}
//**********************************
private void playUziSound(bool play)
{
RSound snd = mContext.Resources.GetSound(RConstants.SoundId.UziRepeat);
if (play && IsSoundOn)
mContext.Sound.Play(snd, false);
else
mContext.Sound.Stop(snd);
}
//******************************
private bool autoScrolling()
{
// scroll game screen has necessary
// returns true if scrolled, false if nothing happened
if (!mMousePos.IsEmpty)
{
// get the scroll offset
Point scroll = mContext.Render2D.GetScreenScroll(RConstants.ScreenId.GameScreen);
// get the physical mouse position
// (note: this works because the game screen is located in 0,0)
int px = mMousePos.X - scroll.X;
int py = mMousePos.Y - scroll.Y;
int sx = scroll.X;
int sy = scroll.Y;
const int wx = RConstants.WindowWidth;
const int wy = RConstants.WindowHeight - RConstants.MenuHeight;
if (px < RGameConstants.ScrollArea)
sx -= RGameConstants.ScrollIncr;
if (py < RGameConstants.ScrollArea)
sy -= RGameConstants.ScrollIncr;
if (px > wx - RGameConstants.ScrollArea)
sx += RGameConstants.ScrollIncr;
if (py > wy - RGameConstants.ScrollArea)
sy += RGameConstants.ScrollIncr;
const int maxScrollX = RConstants.BackWidth - wx;
const int maxScrollY = RConstants.BackHeight - wy;
sx = Math.Max(0, Math.Min(sx, maxScrollX));
sy = Math.Max(0, Math.Min(sy, maxScrollY));
if (scroll.X != sx || scroll.Y != sy)
{
mContext.Render2D.ScrollScreen(RConstants.ScreenId.GameScreen, sx, sy);
return true;
}
}
return false;
}
//**********************************************
private void manualScrolling(Point relative_pos)
{
// scrolling position is relative: -1/+1 on X or Y axis that
// directly represent the pixel direction to be scrolled
// that is:
// -1 X => scroll left, +1 X => scroll right,
// -1 Y => scroll up, +1 Y => scroll down,
if (!relative_pos.IsEmpty)
{
// get the scroll offset
Point scroll = mContext.Render2D.GetScreenScroll(RConstants.ScreenId.GameScreen);
int px = (relative_pos.X < 0 ? -1 * RGameConstants.ScrollIncr : (relative_pos.X > 0 ? RGameConstants.ScrollIncr : 0));
int py = (relative_pos.Y < 0 ? -1 * RGameConstants.ScrollIncr : (relative_pos.Y > 0 ? RGameConstants.ScrollIncr : 0));
int sx = scroll.X + px;
int sy = scroll.Y + py;
const int wx = RConstants.WindowWidth;
const int wy = RConstants.WindowHeight - RConstants.MenuHeight;
const int maxScrollX = RConstants.BackWidth - wx;
const int maxScrollY = RConstants.BackHeight - wy;
sx = Math.Max(0, Math.Min(sx, maxScrollX));
sy = Math.Max(0, Math.Min(sy, maxScrollY));
if (scroll.X != sx || scroll.Y != sy)
mContext.Render2D.ScrollScreen(RConstants.ScreenId.GameScreen, sx, sy);
}
}
//*****************************
private void continueFunTimer()
{
// start counting...
if (mFunTimerLimit == 0)
{
mFunTimer = DateTime.Now;
mFunTimerLimit = mRandom.Next(RGameConstants.FunTimerMin, RGameConstants.FunTimerMax);
}
else
{
TimeSpan ts = DateTime.Now - mFunTimer;
if (ts.Seconds >= mFunTimerLimit)
{
makeFunSound();
mFunTimerLimit = 0;
}
}
}
//*************************
private void stopFunTimer()
{
mFunTimerLimit = 0;
}
//*********************************
private void menuClicked(Point pos)
{
const int sx1 = 1 * RGameConstants.WeaponItemWidth;
const int sx2 = 2 * RGameConstants.WeaponItemWidth;
const int sx3 = 3 * RGameConstants.WeaponItemWidth;
const int sx4 = 4 * RGameConstants.WeaponItemWidth;
const int sy = RGameConstants.WeaponItemHeight;
if (pos.Y < sy)
{
// top row
if (pos.X < sx1)
PostEvent(new REvent(REvent.EventId.ChangeWeapon,
RConstants.SpriteId.WeaponGun));
else if (pos.X < sx2)
PostEvent(new REvent(REvent.EventId.ChangeWeapon,
RConstants.SpriteId.WeaponUzi));
else if (pos.X < sx3)
PostEvent(new REvent(REvent.EventId.ChangeWeapon,
RConstants.SpriteId.WeaponMine));
else if (pos.X < sx4)
PostEvent(new REvent(REvent.EventId.ChangeWeapon,
RConstants.SpriteId.WeaponElec));
}
else
{
// low row
if (pos.X < sx1)
PostEvent(new REvent(REvent.EventId.ChangeWeapon,
RConstants.SpriteId.WeaponBull));
else if (pos.X < sx2)
PostEvent(new REvent(REvent.EventId.ChangeWeapon,
RConstants.SpriteId.WeaponCoke));
else if (pos.X < sx3)
PostEvent(new REvent(REvent.EventId.ChangeWeapon,
RConstants.SpriteId.WeaponDynamite));
else if (pos.X < sx4)
PostEvent(new REvent(REvent.EventId.ChangeWeapon,
RConstants.SpriteId.WeaponFlag));
}
}
//-------------------------------------------
//*****************************
private void renderBackground()
{
RSprite sp = mContext.Resources.GetSprite(RConstants.SpriteId.MainBackground);
Size sz = mContext.Render2D.GetScreenSize(RConstants.ScreenId.GameScreen);
for(int y = 0; y < sz.Height; y += sp.Size.Height)
for(int x = 0; x < sz.Width; x += sp.Size.Width)
mContext.Render2D.DrawSprite(RConstants.ScreenId.GameScreen, sp, x, y);
}
//**************************
private void renderSprites()
{
RSprite sprite;
// RM 20021106 TBDL: create an array of all sprites to render, sort by Y
// and then render all at once using a DrawSprite(Array[]).
// render all mines
for(int i = mMineTable.Count-1; i >= 0; i--)
{
RMine mine = (RMine) mMineTable[i];
sprite = mContext.Resources.GetSprite(mine.SpriteId);
mContext.Render2D.DrawSprite(RConstants.ScreenId.GameScreen,
sprite,
mine.Position.X, mine.Position.Y);
// remove exploded mines
if (mine.IsExploded)
mMineTable.RemoveAt(i);
}
// render all *dead* nerds
foreach(RNerd nerd in mNerdTable)
{
if (nerd.IsAlive)
continue;
sprite = mContext.Resources.GetSprite(nerd.DeadSpriteId);
mContext.Render2D.DrawSprite(RConstants.ScreenId.GameScreen,
sprite,
nerd.Position.X, nerd.Position.Y);
}
// render all bulldozers
for(int i = mBullTable.Count-1; i >= 0; i--)
{
RBulldozer bdz = (RBulldozer) mBullTable[i];
sprite = mContext.Resources.GetSprite(bdz.SpriteId, mSpriteToggle);
mContext.Render2D.DrawSprite(RConstants.ScreenId.GameScreen,
sprite,
bdz.Position.X, bdz.Position.Y);
// remove non visible bulldozers
if (!bdz.IsVisible)
{
mBullTable.RemoveAt(i);
// stop bulldozer sound with last one
// (messy to do this here but it's easy so let's do it)
if (mBullTable.Count == 0)
stopBulldozerSound();
}
}
// render all coke cans
for(int i = mCokeTable.Count-1; i >= 0; i--)
{
RCokeCan coke = (RCokeCan) mCokeTable[i];
if (coke.IsExploded)
{
sprite = mContext.Resources.GetSprite(coke.SpriteId, 1);
// remove non exploded cans
mCokeTable.RemoveAt(i);
}
else
{
sprite = mContext.Resources.GetSprite(coke.SpriteId, 0);
}
mContext.Render2D.DrawSprite(RConstants.ScreenId.GameScreen,
sprite,
coke.Position.X, coke.Position.Y);
}
// render all *live* nerds
// also update the number of nerds left
ArrayList nerd_points = new ArrayList(mNerdTable.Count);
mNbNerds = 0;
foreach(RNerd nerd in mNerdTable)
{
if (!nerd.IsAlive)
continue;
mNbNerds++;
nerd_points.Add(new Point(nerd.Position.X / RGameConstants.NerdFinderScaleX,
nerd.Position.Y / RGameConstants.NerdFinderScaleY));
int frame = mSpriteToggle;
RConstants.SpriteId spid;
if (nerd.IsElec)
{
spid = RConstants.SpriteId.NerdElec;
}
else if (nerd.Speed > 0.0)
{
int dir = (int)((nerd.Direction + Math.PI / 4.0) * (2.0 / Math.PI));
spid = mSpriteDir[dir & 0x3];
}
else
{
spid = RConstants.SpriteId.NerdFix;
}
sprite = mContext.Resources.GetSprite(spid, frame);
mContext.Render2D.DrawSprite(RConstants.ScreenId.GameScreen,
sprite,
nerd.Position.X, nerd.Position.Y);
}
// render nerd points in nerd finder
mContext.Render2D.DrawPoints(RConstants.ScreenId.MenuScreen,
new Point(RGameConstants.NerdFinderX, RGameConstants.NerdFinderY),
nerd_points,
mContext.Resources.GetSprite(RConstants.SpriteId.NerdFinderPoint));
// display the shoot position if any
if (!mShootPos.IsEmpty)
{
mContext.Render2D.DrawSprite(RConstants.ScreenId.GameScreen,
mContext.Resources.GetSprite(RConstants.SpriteId.ShootImpact),
mShootPos.X - RGameConstants.NerdWidth / 2,
mShootPos.Y - RGameConstants.NerdHeight / 2);
mShootPos = Point.Empty;
}
// toggle sprite animation
mSpriteToggle = 1-mSpriteToggle;
}
//***********************
private void renderMenu()
{
// render whole menu area
mContext.Render2D.DrawSprite(RConstants.ScreenId.MenuScreen,
mContext.Resources.GetSprite(RConstants.SpriteId.MenuBackground),
0, 0);
// render nerd finder background based on current scrolling
// get the scroll offset
Point scroll = mContext.Render2D.GetScreenScroll(RConstants.ScreenId.GameScreen);
mContext.Render2D.DrawSprite(RConstants.ScreenId.MenuScreen,
mContext.Resources.GetSprite(RConstants.SpriteId.NerdFinderBack),
RGameConstants.NerdFinderX + scroll.X / RGameConstants.NerdFinderScaleX,
RGameConstants.NerdFinderY + scroll.Y / RGameConstants.NerdFinderScaleY);
// render each weapon in its current state
const int sx0 = 0 * RGameConstants.WeaponItemWidth;
const int sx1 = 1 * RGameConstants.WeaponItemWidth;
const int sx2 = 2 * RGameConstants.WeaponItemWidth;
const int sx3 = 3 * RGameConstants.WeaponItemWidth;
const int sy = RGameConstants.WeaponItemHeight;
mContext.Render2D.DrawSprite(RConstants.ScreenId.MenuScreen,
mContext.Resources.GetSprite(RConstants.SpriteId.WeaponGun,
mCurrentWeapon == RConstants.SpriteId.WeaponGun ? 1 : 0),
sx0, 0);
mContext.Render2D.DrawSprite(RConstants.ScreenId.MenuScreen,
mContext.Resources.GetSprite(RConstants.SpriteId.WeaponUzi,
mCurrentWeapon == RConstants.SpriteId.WeaponUzi ? 1 : 0),
sx1, 0);
mContext.Render2D.DrawSprite(RConstants.ScreenId.MenuScreen,
mContext.Resources.GetSprite(RConstants.SpriteId.WeaponMine,
mCurrentWeapon == RConstants.SpriteId.WeaponMine ? 1 : 0),
sx2, 0);
mContext.Render2D.DrawSprite(RConstants.ScreenId.MenuScreen,
mContext.Resources.GetSprite(RConstants.SpriteId.WeaponElec,
mCurrentWeapon == RConstants.SpriteId.WeaponElec ? 1 : 0),
sx3, 0);
mContext.Render2D.DrawSprite(RConstants.ScreenId.MenuScreen,
mContext.Resources.GetSprite(RConstants.SpriteId.WeaponBull,
mCurrentWeapon == RConstants.SpriteId.WeaponBull ? 1 : 0),
sx0, sy);
mContext.Render2D.DrawSprite(RConstants.ScreenId.MenuScreen,
mContext.Resources.GetSprite(RConstants.SpriteId.WeaponCoke,
mCurrentWeapon == RConstants.SpriteId.WeaponCoke ? 1 : 0),
sx1, sy);
mContext.Render2D.DrawSprite(RConstants.ScreenId.MenuScreen,
mContext.Resources.GetSprite(RConstants.SpriteId.WeaponDynamite,
mCurrentWeapon == RConstants.SpriteId.WeaponDynamite ? 1 : 0),
sx2, sy);
mContext.Render2D.DrawSprite(RConstants.ScreenId.MenuScreen,
mContext.Resources.GetSprite(RConstants.SpriteId.WeaponFlag,
mCurrentWeapon == RConstants.SpriteId.WeaponFlag ? 1 : 0),
sx3, sy);
}
//-------------------------------------------
//************************
private void makeAhSound()
{
if (!IsSoundOn)
return;
// Pick a sound variation
// There are up to NbSoundDeadAh (currently 10) variations
// and we randomly play them all before starting all over.
// Memorize which have already been played by setting a bit
// in the sound variation integer.
// select an random free variation
int variation;
int mask;
do
{
variation = mRandom.Next(RConstants.NbSoundDeadAh);
mask = (1 << variation);
}
while((mAhSoundVariant & mask) != 0);
// play the variation
mContext.Sound.Play(mContext.Resources.GetSound(RConstants.SoundId.DeadAhVariant, variation));
// memorize the variation has been used
mAhSoundVariant |= mask;
// reset mask if all bits have been used
const int full_mask = (1 << RConstants.NbSoundDeadAh)-1;
if (mAhSoundVariant == full_mask)
mAhSoundVariant = 0;
}
//*************************
private void makeFunSound()
{
if (!IsSoundOn)
return;
// Pick a sound variation
// There are up to NbSoundFun (currently 10) variations
// and we randomly play them all before starting all over.
// Memorize which have already been played by setting a bit
// in the sound variation integer.
// select an random free variation
int variation;
int mask;
do
{
variation = mRandom.Next(RConstants.NbSoundFun);
mask = (1 << variation);
}
while((mFunSoundVariant & mask) != 0);
// play the variation
mContext.Sound.Play(mContext.Resources.GetSound(RConstants.SoundId.FunVariant, variation));
// memorize the variation has been used
mFunSoundVariant |= mask;
// reset mask if all bits have been used
const int full_mask = (1 << RConstants.NbSoundFun)-1;
if (mFunSoundVariant == full_mask)
mFunSoundVariant = 0;
}
//********************************
private void startBulldozerSound()
{
if (!IsSoundOn)
return;
Engine.RSound snd = mContext.Resources.GetSound(RConstants.SoundId.BullRepeat);
mContext.Sound.Play(snd, false); // continue if looping sound is started
}
//*******************************
private void stopBulldozerSound()
{
Engine.RSound snd = mContext.Resources.GetSound(RConstants.SoundId.BullRepeat);
mContext.Sound.Stop(snd);
}
//-------------------------------------------
//----------- Private Attributes ------------
//-------------------------------------------
private IEngineContext mContext;
private bool mIsRunning;
private bool mIsPaused;
private bool mIsSoundOn;
private Queue mEventQueue;
private ArrayList mNerdTable;
private Random mRandom;
private int mSpriteToggle;
private int mAhSoundVariant;
private int mFunSoundVariant;
private RConstants.SpriteId mCurrentWeapon;
private Point mShootPos; // Empty if no last shoot
private Point mMousePos; // Empty if not in window
private bool mMousePressed;
private DateTime mFunTimer;
private int mFunTimerLimit;
private ArrayList mMineTable; // Array of RMine
private ArrayList mBullTable; // Array of RBulldozer
private ArrayList mCokeTable; // Array of RCokeCan
// stats
private int mNbNerds;
private int mNbActions;
private RConstants.SpriteId [] mSpriteDir =
new RConstants.SpriteId[]
{
RConstants.SpriteId.NerdMovRight,
RConstants.SpriteId.NerdMovDown,
RConstants.SpriteId.NerdMovLeft,
RConstants.SpriteId.NerdMovUp
};
} // class RGameNerdkill
} // namespace Alfray.Nerdkill.Gameplay
//---------------------------------------------------------------
//
// $Log: RGameNerdkill.cs,v $
// Revision 1.3 2004/05/27 17:25:06 ralf
// Scroll event for directional keys
//
// Revision 1.2 2004/05/26 08:49:18 ralf
// Polishing.
//
// Revision 1.1.1.1 2004/05/24 14:21:57 ralf
// Stable port to PockerPC/.Net Compact Framework.
// Uses .Net/GDI+ for graghic. Uses WinCE's SoundPlay for sound.
//
//---------------------------------------------------------------