Tower defence games

Miscellaneous Forums/General Discussion/Tower defence games

Hey, i've been in the process of making a tower defence like game for a while now, and i'm in the middle of designing field upgrades (Upgrades that will take place amoungst the action.

My question is, do you think it would be wise to:

Pause the game for a brief moment and let the user choose which aspect of the tower they would like to upgrade,

Just have set upgrades, with no user interaction,

Or, let the user choose which parts of the tower they wish to upgrade, but leave the enemies attacking and have no pause.

I'm aiming for a micro management type game here.

Let him pause anytime he wants to and let him adjust his towers in realtime as well as in pause mode. He also should be in charge on how to upgrade towers but it could be an option that this is done automatically for him.

In your opinion, wouldn't this slow up the action slightly?

No, the action comes from the strategy aspect on were to build which towers as well as if something goes wrong on how to react. Believe me this is already enough trouble. :O)

A large part of the fun also comes through the aquarium effect which you would destroy by making it too hectic.

ah ok. Well thanks for your thoughts. I will proceed in that direction :o)

No problem, looking forward to your efforts in the showcase forum.

As a TD fanatic i would suggest:

- no upgrade/build during pause but instead 3 speed settings (slow/normal/fast) (seperate highscore-lists)
- cummulative interest on money not spent every round

I'm looking forward to seeing your first screenshots.

Make the player upgrade in real time, have the pause stop everything.

Here's a simple TD game for BMax I made a while ago.
SuperStrict
AppTitle = "Defense"
Const GfxWidth:Int = 640,GfxHeight:Int = 480
Graphics GfxWidth,GfxHeight,0

Const Grid:Int=32
Const MapWidth:Int=GfxWidth/Grid-3
Const MapHeight:Int=GfxHeight/Grid-2
Const EnemySize:Int = Grid*0.6

Global Gold:Int=5,Shooting:Int=0
Global Map:Int[MapWidth,MapHeight]
Global MX:Int,MY:Int
Global Level:Int=0,StartTime:Int,SpawnCount:Int,SpawnTimer:Int,Score:Int,Lives:Int=10,STimer:Int
Global MouseMode:Int=0,SelectedTower:Tower
'0 = nothing
'1 = selected tower

For Local y:Int = 0 To MapHeight-1 Step 2
	For Local x:Int = 0 To MapWidth-1
		Map[x,y] = 1
	Next
Next
For Local y:Int = 0 To MapHeight-1 Step 4
	Map[0,y] = 0
Next
For Local y:Int = 2 To MapHeight-1 Step 4
	Map[MapWidth-1,y] = 0
Next
Map[0,0] = 3
Map[0,MapHeight-1] = 2
Type Tower
	Field X:Int,Y:Int
	Field Damage:Int
	Field Rate:Int
	Field Level:Int
	Field Angle:Int
	Field ShootingTimer:Int
	Global List:TList

	Method New()
		Level = 1
		Damage = 1
		Rate = 800
		ShootingTimer = MilliSecs()
		ListAddLast(Tower.List,Self)
	End Method

	Function Shoot()
		For Local T:Tower = EachIn Tower.List
			If MilliSecs()-T.ShootingTimer>T.Rate Then
				T.ShootingTimer = MilliSecs()
				If Shooting
					Local A:Arrow = New Arrow
					A.X = T.X+Grid*0.5
					A.Y = T.Y+Grid*0.5
					A.Angle = T.Angle+Rand(-4,4)
					A.Damage = T.Damage
				EndIf
			EndIf
		Next
	End Function

	Function Draw()
		Local DrawColor:Int
		For Local T:Tower = EachIn Tower.List
			'Update angle
			If Shooting Then
				T.Angle = ATan2(MY-(T.Y+Grid*0.5),MX-(T.X+Grid*0.5))
			EndIf
			DrawColor = 128+T.Level
			If DrawColor>255 Then DrawColor = 255
			SetColor DrawColor,30,30
			DrawLine T.X+Grid*0.5+Cos(T.Angle)*Grid*0.3,T.Y+Grid*0.5+Sin(T.Angle)*Grid*0.3,T.X+Grid*0.5,T.Y+Grid*0.5
			SetScale(0.7,0.7)
			SetColor(255,255,255)
			DrawText T.Level,T.X+Grid-TextWidth(T.Level),T.Y+Grid-11
			SetScale(1,1)
		Next
	End Function

	Function NotFound:Int(x:Int,y:Int)
		For Local T:Tower = EachIn Tower.List
			If Floor(x/Grid) = T.X/Grid And Floor(y/Grid) = T.Y/Grid Then Return False
		Next
		Return True
	End Function

	Function GetTower:Tower(x:Int,y:Int)
		For Local T:Tower = EachIn Tower.List
			If Floor(x/Grid) = T.X/Grid And Floor(y/Grid) = T.Y/Grid Then Return T
		Next
		Return Null
	End Function
End Type
Tower.List = CreateList()

Type Arrow
	Field X:Float,Y:Float,Angle:Float
	Field Damage:Int
	Global List:TList

	Method New()
		ListAddLast(List,Self)
	End Method

	Function Update()
		For Local A:Arrow = EachIn Arrow.List
			A.X = A.X + Cos(A.Angle)*2.0
			A.Y = A.Y + Sin(A.Angle)*2.0
			If A.X < 0 Or A.Y < 0 Or A.X > MapWidth*Grid Or A.Y > MapHeight*Grid Then
				ListRemove(List,A)
			Else
				For Local E:Enemy = EachIn Enemy.List
					If Distance(A.X,A.Y,E.X,E.Y) < EnemySize*0.5 Then
						E.HP = E.HP - A.Damage
						If E.HP < 0 Then
							ListRemove(Enemy.List,E)
							Score = Score+E.MaxHP/10
							Gold = Gold + Level
						EndIf
						ListRemove(List,A)
						Exit
					EndIf
				Next
			EndIf
		Next
	End Function

	Function Draw()
		SetColor 128,128,255
		For Local A:Arrow = EachIn Arrow.List
			DrawLine A.X,A.Y,A.X-Cos(A.Angle)*5,A.Y-Sin(A.Angle)*5
		Next
	End Function
End Type
Arrow.List = CreateList()

Type Enemy
	Field HP:Int,MaxHP:Int
	Field X:Int,Y:Int
	Global List:TList

	Function Draw()
		For Local E:Enemy = EachIn Enemy.List
			If (Floor((E.Y)/Grid)+3) Mod 4 = 0 And E.Y>=Grid*1.5 And E.X < (MapWidth-0.5)*Grid Then E.X = E.X + 1
			If (Floor((E.Y)/Grid)+1) Mod 4 = 0 And E.Y>=Grid*1.5 And E.X > Grid*0.5 Then E.X = E.X - 1
			If Map[Floor(E.X/Grid),Floor((E.Y+Grid*0.5)/Grid)] <> 1  Then E.Y = E.Y+1
			SetColor Float(E.HP)/Float(E.MaxHP)*255.0,Float(E.HP)/Float(E.MaxHP)*255.0,Float(E.HP)/Float(E.MaxHP)*255.0
			DrawOval E.X-EnemySize*0.5,E.Y-EnemySize*0.5,EnemySize,EnemySize
			Local FX:Int = Floor(E.X/Grid),FY:Int = Floor(E.Y/Grid)
			If FX>=0 And FY=>0 And FX<MapWidth And FY<MapHeight Then
				If Map[FX,FY] = 2 Then
					Lives = Lives - 1
					If Lives = 0 Then RuntimeError "Game over!"
					ListRemove(Enemy.List,E)
				EndIf
			EndIf
		Next
	End Function

	Method New()
		ListAddLast(List,Self)
	End Method
End Type
Enemy.List = CreateList()

StartTime = MilliSecs()

Repeat
	If MilliSecs()>SpawnTimer+45000 Then
		SpawnTimer = MilliSecs()
		SpawnCount = SpawnCount + 60
		Level = Level + 1
	EndIf
	If SpawnCount>0 And MilliSecs()>STimer+400 Then
		STimer = MilliSecs()
		SpawnCount = SpawnCount - 1
		Local E:Enemy = New enemy
		E.X = Grid*0.5
		E.Y = 0
		E.HP = Level * Level * Level
		E.MaxHP = Level * Level * Level
		If E.HP <= 0 Then E.HP = 1
		If E.MaxHP <= 0 Then E.MaxHP = 1
	EndIf


	MX = MouseX()
	MY = MouseY()
	If MouseHit(2) Then Shooting=1-Shooting

	If MouseHit(1) Then
		If MouseMode = 0 Then
			If Floor(MX/Grid) >= 0 And Floor(MY/Grid) >= 0 And Floor(MX/Grid) < MapWidth And Floor(MY/Grid) < MapHeight And Tower.NotFound(MX,MY) Then
				If Map[Floor(MX/Grid),Floor(MY/Grid)] = 1 And Gold>=2 Then
					Gold = Gold - 2
					Local T:Tower = New Tower
					T.X = Floor(MX/Grid)*Grid
					T.Y = Floor(MY/Grid)*Grid
				EndIf
			ElseIf Tower.NotFound(MX,MY) = False Then
				Local T:Tower = Tower.GetTower(MX,MY)
				MouseMode = 1
				SelectedTower:Tower = T
			EndIf
		ElseIf MouseMode = 1 Then
			If MX>=MapWidth*Grid+5 And MY>=190 And MX<MapWidth*Grid+5+TextWidth("Upgrade for") And MY<190+TextHeight("!")*2 Then
					If Gold>=SelectedTower.Level*SelectedTower.Level Then
					Gold=Gold-SelectedTower.Level*SelectedTower.Level
					SelectedTower.Level = SelectedTower.Level+1
					SelectedTower.Rate = SelectedTower.Rate*0.98
					If SelectedTower.Rate<200 Then SelectedTower.Rate = 200
					SelectedTower.Damage = (SelectedTower.Damage+1)*1.25
				EndIf
			Else
				MouseMode = 0
				SelectedTower = Null
			EndIf
		EndIf
	EndIf
	Tower.Shoot()
	Arrow.Update()

	DrawMap()
	Tower.Draw()
	Enemy.Draw()
	Arrow.Draw()
	SetColor 255,255,0
	DrawText "Gold: "+Gold,MapWidth*Grid+5,5
	SetColor 128,255,128
	DrawText "Level: "+Level,MapWidth*Grid+5,20
	SetColor 255,128,128
	DrawText "Score: "+Score,MapWidth*Grid+5,35
	SetColor 128,128,255
	DrawText "Time: "+((MilliSecs()-StartTime)/1000),MapWidth*Grid+5,50
	SetColor 255,255,255
	DrawText "Lives: "+Lives,MapWidth*Grid+5,65
	SetScale(0.9,1)
	DrawText ((SpawnTimer+45000-MilliSecs())/1000)+"s to spawn",MapWidth*Grid+5,80
	SetScale(1,1)


	If MouseMode = 1 And SelectedTower <> Null Then
		DrawText "Tower:",MapWidth*Grid+5,130
		DrawText "Level "+SelectedTower.Level,MapWidth*Grid+5,145
		DrawText "Dmg "+SelectedTower.Damage,MapWidth*Grid+5,160
		DrawText "Rate "+SelectedTower.Rate,MapWidth*Grid+5,175
		DrawRect MapWidth*Grid+5,190,TextWidth("Upgrade for"),TextHeight("!")*2
		SetColor 0,0,0
		DrawText "Upgrade for",MapWidth*Grid+5,190
		DrawText SelectedTower.Level*SelectedTower.Level+"g",MapWidth*Grid+5,205
	EndIf

	Flip
	Cls

	If KeyHit(KEY_P) Then
		Repeat
		Until KeyHit(KEY_P)
	EndIf
Until KeyHit(KEY_ESCAPE)

Function DrawMap()
	SetColor 255,255,255
	DrawLine MapWidth*Grid,0,MapWidth*Grid,MapHeight*Grid,0
	DrawLine 0,MapHeight*Grid,MapWidth*Grid,MapHeight*Grid,0
	For Local x:Int = 0 To MapWidth-1
		For Local y:Int = 0 To MapHeight-1
			If Map[x,y] = 0 Then
				SetColor 0,0,0
			ElseIf Map[x,y] = 1
				SetColor 255,255,255
			ElseIf Map[x,y] = 2
				SetColor 255,0,0
			ElseIf Map[x,y] = 3
				SetColor 0,255,0
			EndIf
			DrawRect x*Grid,y*Grid,Grid,Grid
			SetColor 0,0,0
			DrawRect x*Grid+1,y*Grid+1,Grid-2,Grid-2
		Next
	Next
End Function

Function Distance:Float(x1:Int,y1:Int,x2:Int,y2:Int)
	x1 = x1-x2
	y1 = y1-y2
	Return Sqr(x1*x1+y1*y1)
End Function


Hmmm. I will tinker with both ideas then. My version will include shields for towers as the passing monsters will be able to inflict damage on the towers, and you can also purchase a repair bot, that will float about the map and repair towers.

That will be part of the upgrade system hopefully.