Public Class Form1
    Dim defeated As Integer = 0
    Dim level As Integer = 1
    Dim gamestate As Integer = 0
    Dim drops As List(Of Drop) = New List(Of Drop)
    Dim towers As List(Of Tower) = New List(Of Tower)
    Dim shots As List(Of Shot) = New List(Of Shot)
    Structure Shot
        Dim Location As Point
        Dim Velocity As Point
        Public Sub Move()
            Me.Location.X += Me.Velocity.X
            Me.Location.Y += Me.Velocity.Y
        End Sub
    End Structure
    Structure Drop
        Dim Location As Point
        Dim Hp As Integer
        ReadOnly Points As Integer
        ReadOnly Damage As Integer
        ReadOnly Speed As Integer
        Sub New(ByVal Points As Integer, ByVal Damage As Integer, ByVal Speed As Integer, ByVal Hp As Integer)
            Me.Points = Points
            Me.Damage = Damage
            Me.Speed = Speed
            Me.Hp = Hp
        End Sub
    End Structure
    Structure Tower
        'Dim FireRate As Integer
        'Dim Power As Integer
        'Dim UpgradeCost As Integer
        Dim Range As Integer
        'Dim Up As UpStats 'Nothing = No upgrade
        Dim Location As Point
        'Sub Upgrade(ByRef cash As Integer)
        '    If Up Is Nothing Then Exit Sub
        '    cash -= Me.UpgradeCost
        '    'FireRate += Up.UpFireRate
        '    'Power += Up.UpPower
        '    Range += Up.UpRange
        '    UpgradeCost = Up.NewUpCost
        '    Up = Up.NewUp
        'End Sub
        'Class UpStats
        '    Public UpRange As Integer = 0
        '    'Public UpFireRate As Integer = 0
        '    'Public UpPower As Integer = 0
        '    Public NewUpCost As Integer = 0
        '    Public NewUp As UpStats = Nothing
        '    Public Sub New()

        '    End Sub
        '    Public Sub New(ByVal NewUp As UpStats)
        '        Me.NewUp = NewUp
        '    End Sub
        'End Class
    End Structure
    Public Shared Function CalculateVector(ByVal tower As Point, ByVal enemy As Point) As Point
        Dim x, y As Double
        x = enemy.X - tower.X
        y = (enemy.Y - tower.Y) + 3
        If Math.Abs(x) > Math.Abs(y) Then
            While Math.Abs(x) >= 2
                x = x / 2
                y = y / 2
            End While
        Else
            While Math.Abs(y) >= 2
                x = x / 2
                y = y / 2
            End While
        End If
        x = CDbl(CInt(x))
        y = CDbl(CInt(y))
        Return New Point(CInt(x), CInt(y))
        'If Math.Max(x, y) = x Then
        '    x = 1
        '    y = Math.Floor(y / x)
        'Else
        '    y = 1
        '    x = Math.Ceiling(x / y)
        'End If
    End Function
    Private Sub MoveAllX()
        Dim i As Integer = 0
        While i < drops.Count
            Dim tmp As Drop = drops(i)
            tmp.Location.Y += drops(i).Speed
            If (tmp.Location.Y + My.Resources.raindrop.Height) > Me.pnlBattleArea.Height Then
                Me.lblHealth.Text = CStr(CInt(Me.lblHealth.Text) - tmp.Damage)
                If Me.lblHealth.Text = "0" OrElse Me.lblHealth.Text.Contains("-") Then
                    gamestate = 3
                    Exit While
                End If
                drops.RemoveAt(i)
                Continue While
            End If
            Dim j As Integer = 0
            While j < shots.Count
                If i > 0 Then
                    If New Rectangle(shots(j).Location.X, shots(j).Location.Y, My.Resources.bullet1.Width, My.Resources.bullet1.Height).IntersectsWith(New Rectangle(tmp.Location.X, tmp.Location.Y, My.Resources.raindrop.Width, My.Resources.raindrop.Height)) Then
                        tmp.Hp -= 4
                        shots.RemoveAt(j)
                        If tmp.Hp <= 0 Then
                            Me.lblMoney.Text = CStr(Int(CInt(Me.lblMoney.Text) + drops(j).Points / 10))
                            Me.lblPoints.Text = CInt(Me.lblPoints.Text) + drops(j).Points
                            drops.RemoveAt(i)
                            defeated += 1
                            If defeated = 10 Then
                                defeated = 0
                                level += 1
                            End If
                            GoTo outer_continue
                        End If
                    End If
                Else
                    If New Rectangle(shots(j).Location.X, shots(j).Location.Y, My.Resources.bullet1.Width, My.Resources.bullet1.Height).IntersectsWith(New Rectangle(tmp.Location.X, tmp.Location.Y, My.Resources.raindrop.Width, My.Resources.raindrop.Height)) Then
                        tmp.Hp -= 4
                        shots.RemoveAt(j)
                        If tmp.Hp <= 0 Then
                            Me.lblMoney.Text = CStr(Int(CInt(Me.lblMoney.Text) + drops(j).Points / 10))
                            Me.lblPoints.Text = CInt(Me.lblPoints.Text) + drops(j).Points
                            drops.RemoveAt(i)
                            defeated += 1
                            If defeated = 10 Then
                                defeated = 0
                                level += 1
                            End If
                            GoTo outer_continue
                        End If
                    End If
                    Dim tmpx As Shot = shots(j)
                    tmpx.Move()
                    shots(j) = tmpx
                End If
                j += 1
            End While
            drops(i) = tmp
            tmp = Nothing
            i += 1
outer_continue:
        End While
    End Sub
    Private Function Max(ByVal ParamArray args() As Integer) As Integer
        Array.Sort(args)
        Return args(args.Length - 1)
    End Function
    Private Sub TowerShoot(ByVal tower As Tower)
        Dim i As Integer = 0
        Dim closest As Drop
        While i < drops.Count
            If i > 0 Then
                If Closeness(tower.Location, drops(i).Location) < Closeness(tower.Location, closest.Location) Then
                    closest = drops(i)
                End If
            Else
                closest = drops(i)
            End If
            i += 1
        End While
        Dim empty As Drop
        If closest.Equals(empty) Then Exit Sub
        Dim newshot As Shot
        newshot.Velocity = CalculateVector(tower.Location, New Point(closest.Location.X, closest.Location.Y + My.Resources.raindrop.Height))
        newshot.Location = tower.Location
        shots.Add(newshot)
    End Sub
    Public Shared Function Closeness(ByVal pt1 As Point, ByVal pt2 As Point) As Integer
        Return Math.Abs(pt1.X - pt2.X) + Math.Abs(pt1.Y - pt2.Y)
    End Function
    Public Shared Function CloseTo(ByVal pt1 As Point, ByVal pt2 As Point, ByVal factor As Integer) As Boolean
        If pt1.X > (pt2.X - factor) AndAlso pt1.Y > (pt2.Y - factor) Then
            If pt1.X < (pt2.X + factor) AndAlso pt1.Y < (pt2.Y + factor) Then
                Return True
            End If
        End If
        Return False
    End Function
    Private Sub tmrMain_Tick()
        Static tickcount As Long = 0
        tickcount += 1
        If tickcount Mod 10 - IIf(level <= 10, level \ 2, 4) = 0 Then
            Dim drp As New Drop(level * 10, Math.Ceiling(level / 9), IIf(level < 7, level * 2, 15), level)
            drp.Location = New Point(Int(Rnd() * (Me.pnlBattleArea.Width - 50)) + 25, -(My.Resources.raindrop.Height - 1))
            drops.Add(drp)
        End If
        If tickcount Mod 40 = 0 Then
            Dim twr As Tower
            For Each twr In towers
                TowerShoot(twr)
            Next
            tickcount = 0
        End If
        MoveAllX()
        Me.pnlBattleArea.Invalidate()
    End Sub

    Private Sub Form1_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
        t.Abort()
    End Sub

    Private Sub Form1_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles Me.KeyDown
        Select Case gamestate
            Case 1
                gamestate = 2
                t.Start()
                Me.pnlBattleArea.Invalidate()
            Case 3
                Me.Close()
        End Select
    End Sub

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        gamestate = 1
    End Sub
    Private Sub Timer_Timer()
        Try
            While True
                Threading.Thread.Sleep(100)
                Me.Invoke(tmr)
            End While
        Catch ex As Threading.ThreadAbortException
            'Let the Sub exit.
        End Try
    End Sub

    Private Sub pnlBattleArea_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles pnlBattleArea.MouseDown
        If (CInt(Me.lblMoney.Text) >= 20) AndAlso (MsgBox("Would you like to build a tower here?", MsgBoxStyle.YesNo Or MsgBoxStyle.Question, "Build Tower") = MsgBoxResult.Yes) Then
            Me.lblMoney.Text = CStr(CInt(Me.lblMoney.Text) - 20)
            Dim twr As Tower
            twr.Location = e.Location
            twr.Range = 150
            towers.Add(twr)
            Me.pnlBattleArea.Invalidate()
        End If
    End Sub

    Private Sub pnlBattleArea_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles pnlBattleArea.Paint
        Dim g As Graphics = e.Graphics
        Dim centered As New StringFormat()
        centered.Alignment = StringAlignment.Center
        centered.LineAlignment = StringAlignment.Center
        Select Case gamestate
            Case 1
                g.DrawString("Clouds TD", New Font("Arial", 51), Brushes.White, Me.pnlBattleArea.ClientRectangle, centered)
            Case 2
                Dim d As Drop
                For Each d In drops
                    g.DrawImage(My.Resources.raindrop, d.Location)
                Next
                Dim s As Shot
                For Each s In shots
                    g.DrawImage(My.Resources.bullet1, s.Location)
                Next
                Dim t As Tower
                For Each t In towers
                    g.DrawImage(My.Resources.tower1, t.Location)
                Next
            Case 3
                g.DrawString("Oops... flooded. :-(", New Font("Arial", 32), Brushes.White, Me.pnlBattleArea.ClientRectangle, centered)
        End Select
    End Sub
    'Delegates
    Delegate Sub InvokeTimer()
    Dim tmr As InvokeTimer = New InvokeTimer(AddressOf tmrMain_Tick)
    Dim t As Threading.Thread = New Threading.Thread(AddressOf Timer_Timer)
End Class
