Click here to Skip to main content
15,895,709 members
Articles / Mobile Apps

Nerdkill game for PocketPC

Rate me:
Please Sign up or sign in to vote.
4.46/5 (29 votes)
28 Jun 2004CPOL13 min read 101.8K   1.2K   35  
A shoot'em up platform in C# for the .NET Compact Framework.
//*******************************************************************
/*

	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.
//	
//---------------------------------------------------------------

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
United States United States
Raphael is a senior software engineer with a background in electronics engineering.
He enjoys programming in C++ since 1994.
He developed professional software for the BeOS and now focuses on Windows, MacOS and Linux software development.
He uses C++, C#, Java,VB.Net, PHP, Bash and Perl on a regular basis. He is familiar with C, Objective-C, VB6, Python, ML, Haskell, Lisp, Scheme, some obsolete languages (Basic and Pascal) and x86/Motorola assembly languages.
Raphael is a big fan of the .Net platform.
A number of open source personal projects can be found on his web site.

Comments and Discussions