using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Threading; using WiimoteLib; namespace Copter { enum ColumnType { Normal, Slow, Fast, MoreHilly, LessHilly } struct Column { public Column ( int height, int top, ColumnType type ) { this.height = height; this.top = top; this.type = type; } public Column ( Column original ) { this.height = original.height; this.top = original.top; this.type = original.type; } public int height; public int top; public ColumnType type; } public partial class frmMain : Form { private const int block_size = 8, column = 5, initial_wall_height = 10, special_wall_interval = 200, // should be greater than width max_fall_speed = 6, max_rise_speed = 7, rising_accel = -2, falling_accel = 2, message_show_time = 50; private Brush wall_brush = Brushes.Red, ship_brush = Brushes.Yellow, score_shadow_brush = Brushes.Red, score_text_brush = Brushes.Black, message_shadow_brush = Brushes.Blue, message_text_brush = Brushes.White; private bool using_wiimote; private Wiimote wiimote; private Mutex wiimote_callback_mutex = new Mutex (); private Mutex wiimote_light_mutex = new Mutex (); private Brush[] background_brushes = { Brushes.Black, // Normal Brushes.DarkRed, // Slow Brushes.Green, // Fast Brushes.Yellow, // More Hilly Brushes.Purple // Less Hilly }; private Bitmap buffer; private Random rand; private double real_current_height; private double velocity, acceleration; private Column[] grid; private int frames; private int max_wall_change; private string message; private int message_start_frame; private int gates_passed; private int current_led; private int height = 50, width = 80; public frmMain () { InitializeComponent (); } private void frmMain_Load ( object sender, EventArgs e ) { init_game (); } private void frmMain_MouseDown ( object sender, MouseEventArgs e ) { acceleration = rising_accel; } private void frmMain_MouseUp ( object sender, MouseEventArgs e ) { acceleration = falling_accel; } private void btnStart_Click ( object sender, EventArgs e ) { reset_game (); start_game (); } private void wiimote_event ( object sender, WiimoteChangedEventArgs e ) { if ( wiimote.WiimoteState.ButtonState.A && btnStart.Visible == true ) { wiimote_callback_mutex.WaitOne (); BeginInvoke ( (MethodInvoker) delegate () { btnStart.PerformClick (); } ); wiimote_callback_mutex.ReleaseMutex (); } // Control acceleration with A button //acceleration = e.WiimoteState.ButtonState.A ? rising_accel : falling_accel; // Control acceleration with Y rotation velocity = 10 * e.WiimoteState.AccelState.Y; // Control position with Y rotation //real_current_height = height * block_size / 2 + ( height * block_size / 2 ) * e.WiimoteState.AccelState.Y; } private void cycle_wiimote_lights () { if ( !using_wiimote ) return; if ( current_led == 1 ) wiimote.SetLEDs ( false, true, false, false ); else if ( current_led == 2 ) wiimote.SetLEDs ( false, false, true, false ); else if ( current_led == 3 ) wiimote.SetLEDs ( false, false, false, true ); else if ( current_led == 4 ) wiimote.SetLEDs ( true, false, false, false ); current_led = current_led % 4 + 1; } private void tmrGame_Tick ( object sender, EventArgs e ) { frames++; shift_walls (); do_powerup (); do_physics (); redraw (); paint (); //cycle_wiimote_lights (); } private void size_window () { this.ClientSize = new Size ( block_size * width, block_size * height ); buffer = new Bitmap ( block_size * width, block_size * height ); btnStart.Left = width * block_size - btnStart.Width - 15; btnStart.Top = height * block_size - btnStart.Height - 15; grid = new Column[width]; } private void init_game () { size_window (); wiimote = new Wiimote (); try { wiimote.Connect (); using_wiimote = true; // connect didn't fail! wiimote.OnWiimoteChanged += new WiimoteChangedEventHandler ( wiimote_event ); wiimote.SetReportType ( Wiimote.InputReport.ButtonsAccel, true ); wiimote.SetLEDs ( false, false, false, false ); } catch ( Exception ) { using_wiimote = false; } } private void reset_game () { width -= gates_passed; size_window (); rand = new Random (); frames = 0; max_wall_change = using_wiimote ? 4 : 2; gates_passed = 0; current_led = 1; real_current_height = block_size * height * 0.5; tmrGame.Interval = using_wiimote ? 20 : 40; velocity = 0; acceleration = falling_accel; for ( int i = 0; i < width; i++ ) grid[i] = new Column ( height - initial_wall_height * 2, initial_wall_height, ColumnType.Normal ); message = null; redraw (); paint (); } private void start_game () { btnStart.Visible = false; tmrGame.Enabled = true; show_message ( "Go!" + ( using_wiimote ? " (Wiimote!)" : "" ) ); } private void stop_game () { tmrGame.Enabled = false; btnStart.Visible = true; } private void shift_walls () { // Shift each column to the left for ( int col = 0; col < width - 1; col++ ) { grid[col].height = grid[col + 1].height; grid[col].top = grid[col + 1].top; grid[col].type = grid[col + 1].type; } // Generate new values for right-most column generate_frame ( frames ); } private void generate_frame ( int frame ) { int col = width - ( frames - frame ) - 1; grid[col].top = grid[col - 1].top + rand.Next ( max_wall_change * 2 + 1 ) - max_wall_change; grid[col].height = Math.Max ( 5, height - initial_wall_height * 2 - (int) Math.Pow ( frame, 0.3 ) ); if ( frame % special_wall_interval == 0 ) grid[col].type = (ColumnType) rand.Next ( 4 ) + 1; else grid[col].type = ColumnType.Normal; // Keep cave from going off the edge if ( grid[col].top < 1 ) grid[col].top = 1; if ( grid[col].top + grid[col].height > height - 1 ) grid[col].top = height - grid[col].height - 1; } private void do_physics () { // Translate X based on current velocity real_current_height += (int) velocity; // Update velocity according to current acceleration velocity = velocity + acceleration; // Apply rising and falling terminal velocity if ( velocity < -max_rise_speed ) velocity = -max_rise_speed; if ( velocity > max_fall_speed ) velocity = max_fall_speed; int current_height = (int) ( real_current_height / block_size ); if ( current_height < grid[column].top || current_height + 1 >= grid[column].top + grid[column].height ) { show_message ( "Game Over! Final Score: " + frames.ToString () ); stop_game (); } } private void do_powerup () { if ( grid[column].type == ColumnType.Normal ) return; gates_passed++; switch ( grid[column].type ) { case ColumnType.Fast: tmrGame.Interval = (int) Math.Max ( 10, tmrGame.Interval * 0.9 ); show_message ( "Faster!" ); break; case ColumnType.Slow: tmrGame.Interval = (int) Math.Min ( 120, tmrGame.Interval * 1.11 ); show_message ( "Slower!" ); break; case ColumnType.LessHilly: max_wall_change = Math.Max ( 2, max_wall_change - 1 ); for ( int f = frames - width + column + 20; f <= frames; f++ ) generate_frame ( f ); show_message ( "Less craggy!" ); break; case ColumnType.MoreHilly: max_wall_change += 1; for ( int f = frames - width + column + 20; f <= frames; f++ ) generate_frame ( f ); show_message ( "More craggy!" ); break; } } private void redraw () { Graphics g = Graphics.FromImage ( buffer ); // Draw the walls (draw everything as a wall, then cut out the background) g.FillRectangle ( wall_brush, 0, 0, block_size * width, block_size * height ); for ( int col = 0; col < width; col++ ) { g.FillRectangle ( background_brushes[(int) grid[col].type], block_size * col, block_size * grid[col].top, block_size, block_size * grid[col].height ); } // Draw the ship (green square) g.FillRectangle ( ship_brush, block_size * column, (int) real_current_height, block_size, block_size ); // Draw the score draw_text ( frames.ToString (), width * block_size - 100, 10, g, this.Font, score_shadow_brush, score_text_brush ); if ( grid[column].type != ColumnType.Normal ) { g.FillRectangle ( Brushes.White, block_size * ( column - 1 ), block_size * grid[column].top, block_size * 3, block_size * grid[column].height ); } // Draw the message if ( message != null && frames - message_start_frame < message_show_time ) { draw_text ( message, 30, height * block_size - 40, g, this.Font, message_shadow_brush, message_text_brush ); } } private void paint () { // Draw backbuffer to screen this.CreateGraphics ().DrawImageUnscaled ( buffer, new Point ( 0, 0 ) ); } private void show_message ( string text ) { message = text; message_start_frame = frames; } private void draw_text ( string text, int x, int y, Graphics graphics, Font font, Brush shadow_brush, Brush text_brush ) { graphics.DrawString ( text, font, shadow_brush, x + 2, y + 2 ); graphics.DrawString ( text, font, shadow_brush, x - 2, y - 2 ); graphics.DrawString ( text, font, shadow_brush, x + 2, y - 2 ); graphics.DrawString ( text, font, shadow_brush, x - 2, y + 2 ); graphics.DrawString ( text, font, text_brush, x, y ); } private void frmMain_FormClosed ( object sender, FormClosedEventArgs e ) { if ( using_wiimote ) wiimote.SetLEDs ( false, false, false, false ); } } }