'#################################################
' Project Name: Editor
' Owner: Fobsi
' Date: 16.03.2019
' Description: Editor for Age of Empires like Game
'#################################################
Strict

Include "lib/astarlib.bmx"
AppTitle = "Age of Civilizations - Editor"

Const USERCONTROLLED = 1
Const CHASE = 2

Const TILESIZE = 36'108
Global TILESIZEW = 36'108
Global TILESIZEH = TILESIZEW/2
Global MAPWIDTH = 10
Global MAPHEIGHT= 10

' Variablen für Pixel/Tile Koordinaten
Global pixX:Int
Global pixY:Int

'Editor Variabeln
Global tiletoselect:Int = 72
Global activeLayer:Int = 1

'input routine
Global ccstring$=""
Global cccursorblink 
Global ccursorblinkrate = 30

Global pathmap[][]
Global isomapL1[][]
Global isomapL2[][]

Global autotilemap[][]
Global tilewertmap[]

Type Tgame 
	Field cursor:TImage
	Field grid:TImage
	Field redBlock:TImage
	Field wallBlock:TImage
	Field unitsprite:TImage
	Field chaser:TImage
	Field tiles:TImage
	Field unitspriteminimap:TImage
	Field buildingsprite:TImage
	Field Drawing, Erasing, Started = True
	Field unit:Tunit
 	Field Astar:Tastar
	Global unitList:TList
	Global buildingList:TList
	Global pickedX:Int,pickedY:Int,pickedXTile:Int,pickedYTile:Int
	Global mapX:Float = 540, mapY:Float = 0
	Field mXs:Int
	Field mYs:Int
	Field recttmpx:Int,recttmpy:Int,recttmpwidth:Int,recttmpheight:Int,recttmpwidthX:Int,recttmpheightY:Int
	
	Field collision = True 
	
	Function Create:tgame()
		Local game:tgame = New Tgame
		If Not GraphicsModeExists(1024,768) RuntimeError "Grafikkarte nicht unterstützt!"	
		Graphics 1024,768 ; SetMaskColor 255,255,255 ;	HideMouse() 
		
		game.cursor = LoadImage("graphics/red_pointer.bmp",MASKEDIMAGE)
		game.grid = LoadImage("graphics/grid.bmp",MASKEDIMAGE)		
		game.redBlock = LoadImage("graphics/end.bmp") 'target location
		game.wallBlock = LoadImage("graphics/wall.bmp") 'wall	
		game.unitsprite= LoadAnimImage("graphics/units/worker/worker.png",72,72,0,60)
		MidHandleImage game.unitsprite	
		game.chaser = LoadAnimImage("graphics/units/warrior/warrior.png",72,72,0,60)
		game.tiles = LoadAnimImage("graphics/TILEMAP_LARGE.png",108,108,0,288)
		game.unitspriteminimap = LoadImage("graphics/unit.bmp",MASKEDIMAGE)
		game.buildingsprite= LoadAnimImage("graphics/buildings/worker/buildings.png",108,64,0,2)
		MidHandleImage game.chaser
		SetBlend(ALPHABLEND)	
		Local tempunit:tunit
		pathmap=pathmap[..mapwidth*3+1]
		For Local z = 0 To mapwidth*3
			pathmap[z]=pathmap[z][..mapHeight*3]
		Next
		isomapL1=isomapL1[..mapwidth+1]
		For Local z = 0 To mapwidth
			isomapL1[z]=isomapL1[z][..mapHeight]
		Next
		isomapL2=isomapL2[..mapwidth+1]
		For Local z = 0 To mapwidth
			isomapL2[z]=isomapL2[z][..mapHeight]
		Next
		
		autotilemap=autotilemap[..mapwidth+1]
		For Local z = 0 To mapwidth
			autotilemap[z]=autotilemap[z][..mapHeight]
		Next
		tilewertmap=tilewertmap[..256]
		For Local c = 0 To 255
			Select c
				Case 11,15
					tilewertmap[c] = 105
				Case 22,23,150,151
					tilewertmap[c] = 87
				Case 31,63,159,253,30
					tilewertmap[c] = 86
				Case 43
					tilewertmap[c] = 84
				Case 104,105,110,232
					tilewertmap[c] = 90
				Case 107,111,235
					tilewertmap[c] = 89
				Case 127
					tilewertmap[c] = 92
				Case 208,212,240
					tilewertmap[c] = 81
				Case 214,215,246
					tilewertmap[c] = 79
				Case 223
					tilewertmap[c] = 93
				Case 248,249,216,252
					tilewertmap[c] = 82
				Case 251
					tilewertmap[c] = 95
				Case 254
					tilewertmap[c] = 94
				Case 255,247
					tilewertmap[c] = 72
			End Select 
		Next

		
		For Local x:Int = 0 To mapwidth-1
			For Local y:Int = 0 To mapheight-1
				isomapL1[x][y] = Rand(0,4)
				isomapL2[x][y] = 286
			Next
		Next

		game.Astar = Tastar.Create(TILESIZE,MAPWIDTH*3,MAPHEIGHT*3,pathmap)
		For Local x = 1 To 10	'Create And initialize 3 units
			game.unit = New Tunit ; game.unit.ID = x
			If Not game.UnitList Then game.unitList = CreateList()
			If Not game.buildingList Then game.buildingList = CreateList()
			game.unit.pathBank = CreateBank(1) 'data bank that unit's path data is stored	in	
			game.unitList.addlast(game.unit)
			If x = 1 'smily
				game.unit.xLoc = 32
				game.unit.yLoc = 32
				game.unit.speed = 5
				game.unit.pathAI = userControlled
				game.unit.sprite = game.unitsprite ;
				tempunit = game.unit	
			Else 'If x = 2 'chaser
				game.unit.xLoc = 32+Rand(400)
				game.unit.yLoc = 32+Rand(400)
				game.unit.speed = 3
				game.unit.target = tempunit
				game.unit.pathAI = chase
				game.unit.sprite = game.chaser 'First unit 
			End If
		Next
		Return game
	End Function
	Method RunProgram()'Main program loop	
		While Not KeyHit(KEY_ESCAPE) And Not AppTerminate() 'And Not Confirm("Beenden?")
			'UserInput()
			MoveUnits()
			RenderScreen()
			UserInput()
		Wend
	End Method
	Method UserInput()'This Function handles most user mouse And keyboard Input		
		If MouseHit(1)=True Then 
		
			If recttmpx = 0 Then recttmpx = MouseX()
			If recttmpy = 0 Then recttmpy = MouseY()
			
			'For Local t:tunit = EachIn unitList
				't.pathAI = Null
			'Next
			
		Else 
			If MouseDown(1)=True Then 
				recttmpwidth = MouseX()-recttmpx
				recttmpheight= MouseY()-recttmpy
				recttmpwidthX = MouseX() 
				recttmpheightY = MouseY()

				
			ElseIf MouseDown(1)=False 
				For Local r:tunit = EachIn unitList 

					Local x = recttmpx 
					Local y = recttmpy
					Local w = recttmpwidthX 
					Local h = recttmpheightY 
					
					Local px = ((r.xLoc/(tilesize) - r.yLoc/(tilesize)) * (tilesizeW/2) ) + mapX
					Local py = ((r.xLoc/(tilesize) + r.yLoc/(tilesize)) * (tilesizeH/2) ) + mapY -tilesizeH/2
					
					If RectsOverlap(recttmpx ,recttmpy,recttmpwidth ,recttmpheight,px,py,1,1)=True Then
						r.pathAI = userControlled
						'r.pathStatus = Found 
					''astar.FindPath(r,pickedX*tilesize,pickedY*tilesize)
					EndIf 
				Next

				recttmpx = 0
				recttmpy = 0
				recttmpwidth = 0
				recttmpheight = 0
			EndIf 
		EndIf
		 
	End Method
	Method RenderScreen() 'This Function draws stuff on the screen.
	
		'Tile Auswahl 1*1
		pickedX = TileX(MouseX(), MouseY(), tilesizeW, tilesizeH, mapX, mapY)
		pickedY = TileY(MouseX(), MouseY(), tilesizeW, tilesizeH, mapX, mapY)
		
		'Tile Auswahl 3*3
		pickedXTile = TileX(MouseX(), MouseY(), tilesizeW*3, tilesizeH*3, mapX, mapY)
		pickedYTile = TileY(MouseX(), MouseY(), tilesizeW*3, tilesizeH*3, mapX, mapY)
		
		'Mausrad
		tiletoselect :+ MouseZSpeed()
		mXs = MouseXSpeed()
		mYs = MouseYSpeed()	
		
		'Runtime Error protektion
		If tiletoselect < 0 Then tiletoselect = 287
		If tiletoselect > 287 Then tiletoselect = 0
		
		'Aktiviere/Deaktiviere Kollisions zeichnen
		If MouseHit(3)  Then 
			If collision = True  Then 
				collision = False 
			Else 
				collision = True
			EndIf
		EndIf
		
		' Mousegeschwindigkeit aktualisieren
		If MouseDown(2)
			MapX = mapX + mXs
			MapY = mapY + mYs
		End If
		
		Cls	
		
		DrawMaps()
		If KeyDown(KEY_F4) Then DrawTextInfos()
		
		SetColor 255,0,0
		DrawBox(recttmpx,recttmpy,recttmpwidth,recttmpheight)
		DrawText "X: "+MouseX()+" Y:"+MouseY(), MouseX()+10,MouseY()+10
		SetColor 255,255,255
		
		DrawImage cursor,MouseX(),MouseY() 'Draw the mouse

		Flip
	End Method
	Method DrawTextinfos()
	
		'mapX / mapY
		DrawText "mapX:"+mapX+"|mapY:"+mapY,0,0
	
		' Die "gepickten" Tilekoordinaten neben dem Mauszeiger ausgeben
		SetColor 255,0,0
		DrawText "X:"+ pickedX + "|Y:" + pickedY + "|TX:" + pickedXTile  + "|TY:" + pickedYTile , MouseX()+8, MouseY()-8
		SetColor 255,255,255
		
		'Editor Tile ID und Anzeige des Aktuell ausgewählten Tiles
		SetColor 0,0,0
		DrawRect 914,36,tilesizeW+2,tilesizeH*2+2
		SetColor 255,255,255
		DrawImage(tiles, 915,37 ,tiletoselect)
		If collision = True Then SetColor 0,255,0 Else SetColor 255,0,0
		DrawRect 991,2,32,32
		SetColor 255,0,0
		DrawText "Tile ID:"+tiletoselect,915,146
		SetColor 255,255,255
		
		' Edit Modus oder nicht
		SetColor 0,0,0
		DrawText "K",1002,12
		If Started = False Then SetColor 0,255,0 Else SetColor 255,0,0
		DrawRect 957,2,32,32
		SetColor 0,0,0
		DrawText "Edit",958,12
		
		' Aktiver Layer 1
		If activeLayer = 1 Then SetColor 0,255,0 Else SetColor 255,0,0
		DrawRect 905,2,50,15
		SetColor 0,0,0
		DrawText "Layer1",906,3
		
		' Aktiver Layer 2
		If activeLayer = 2 Then SetColor 0,255,0 Else SetColor 255,0,0
		DrawRect 905,18,50,15
		SetColor 0,0,0
		DrawText "Layer2",906,18
		
		' RAM
		SetColor 0,128,0
		Local txt$="MemAllocd:"+GCMemAlloced()+"kb"
		DrawText txt,1024-TextWidth(txt),768-TextHeight(txt)
		SetColor 255,255,255
		
	End Method
	Method DrawMaps()
		'Iso Map Layer 1
		For Local y = 0 To mapwidth-1 'Draw the walls And the grid 
			For Local x = 0 To mapheight-1
				pixX = PixelX(x, y, tilesizeW*3)
				pixY = PixelY(x, y, tilesizeH*3)
						
				DrawImage(tiles, pixX-(tilesizeW*3)/2 + mapX, pixY + mapY-(tilesizeH*3),isomapL1[x][y])
				If pathmap[x][y] = unwalkable Then 
					DrawImage(tiles, pixX-tilesizeW/2 + mapX, pixY + mapY-(tilesizeH),15)
				EndIf 
			Next
		Next
		
		'Einheiten
		Local test:Int 
		test = 0
		For Local r:tunit = EachIn unitList
			test = test + 15			
			Local px = ((r.xLoc/(tilesize) - r.yLoc/(tilesize)) * (tilesizeW/2) ) + mapX
			Local py = ((r.xLoc/(tilesize) + r.yLoc/(tilesize)) * (tilesizeH/2) ) + mapY -tilesizeH/2
			
			'DrawText "px:"+ px+" py:"+py,500,500+test
			DrawImage r.sprite,px,py,r.richtung
			'DrawText "PX:"+px+"|PY:"+py+"|C:"+r.pathAI+"|anim:"+r.anim+"|pathStatus:"+r.pathStatus,px,py+12
			DrawText "|C:"+r.pathAI+"pathStatus:"+r.pathStatus,px,py+12 
		Next
		
		'Iso Map + Pathmap
		For Local y = 0 To mapwidth-1  
			For Local x = 0 To mapheight-1
				pixX = PixelX(x, y, tilesizeW)
				pixY = PixelY(x, y, tilesizeH)
						
				DrawImage(tiles, pixX-tilesizeW/2 + mapX, pixY + mapY-(tilesizeH),isomapL2[x][y])
				If pathmap[x][y] = unwalkable Then 
					DrawImage(tiles, pixX-tilesizeW/2 + mapX, pixY + mapY-(tilesizeH),15)
				EndIf 
			Next
		Next
		
		'Pathmap - Kollisionmap
		For Local y = 0 To (mapwidth*3)-1  
			For Local x = 0 To (mapheight*3)-1
				pixX = PixelX(x, y, tilesizeW)
				pixY = PixelY(x, y, tilesizeH)
				SetAlpha .25
				DrawImage(tiles, (pixX-tilesizeH) + mapX, (pixY-(tilesizeH*3)) + mapY,25)
				If pathmap[x][y] = unwalkable Then 
					SetColor 255,0,0
					DrawImage(tiles, (pixX-tilesizeH) + mapX, (pixY-(tilesizeH*3)) + mapY,25)
					SetColor 255,255,255
				EndIf
				SetAlpha 1 
			Next
		Next

		'Minimap
		For Local x = 0 To mapwidth*3-1 'Draw the walls And the grid 
			For Local y = 0 To mapheight*3-1
				DrawImage grid,x*3,y*3
				If pathmap[x][y] = unwalkable Then DrawImage wallBlock,x*3,y*3
				
			Next
		Next
		
		'Units on Minimap
		For Local r:tunit = EachIn unitList
			DrawImage unitspriteminimap,((r.xLoc/tilesizeW)*3)-1,((r.yLoc/(tilesizeH*2))*3)-1
		Next
		
		'Autotile MAP 
		For Local y = 0 To mapwidth-1 'Draw the walls And the grid 
			For Local x = 0 To mapheight-1
				pixX = PixelX(x+2, y+1, tilesizeW)
				pixY = PixelY(x+2, y+1, tilesizeH)
				'DrawText autotilemap[x][y],pixX-tilesizeW/2 + mapX,pixY + mapY-(tilesizeH)	
			Next
		Next
		
		'Tile Annzeige
		If collision = False Then 
			DrawImage(tiles, PixelX(pickedXTile, pickedYTile, tilesizeW*3)-tilesizeW*3/2 + mapX, PixelY(pickedXTile, pickedYTile, tilesizeH*3) + mapY-(tilesizeH*3),15)
 		Else
			SetColor 255,0,0
			DrawImage(tiles, PixelX(pickedX, pickedY, tilesizeW)-tilesizeW/2 + mapX, PixelY(pickedX, pickedY, tilesizeH) + mapY-(tilesizeH*3),25)
 			SetColor 255,255,255
		EndIf
				
	End Method
	Method MoveUnits() 'This Function performs pathfinding And moves the units.
		If Started = True 	
			For unit = EachIn unitList
				UpdatePath(unit)
				If unit.pathStatus = found Then MoveUnit(unit) Else unit.anim = 0
			Next
		EndIf
	End Method 
	Method UpdatePath(unit:Tunit) 'This Function checks For path updates And calls the FindPath() Function when needed.
		If unit.pathAI = userControlled Then'If the unit is the smiley, trigger New paths using the mouse
		
			If MouseHit(2) Then 
				If pickedX >= 0 And pickedY >= 0	And pickedX <= mapwidth*3 And pickedY <= mapheight*3 Then 			
					unit.pathStatus = astar.FindPath(unit,pickedX*tilesize,pickedY*tilesize)
				EndIf 
			EndIf 
		Else If unit.pathAI = chase	'Step Until it reaches the smiley. If smiley And chaser aren't at same loc. on screen And no active path, find a New path.
			If Not (unit.xLoc = unit.target.xLoc And unit.yLoc = unit.target.yLoc) 'If not generating pad, generate one. Update it when 'the chaser reaches its third Step on path.	
			If unit.pathStatus <> found Or unit.pathlocation = 3  
				unit.pathStatus = astar.FindPath(unit,unit.target.xLoc,unit.target.yLoc)
			End If
			End If	
		End If 
	End Method
	Method  MoveUnit(unit:Tunit)'This Function moves the sprites around on the screen.
		astar.CheckPathStepAdvance(unit) 'Check For pathStep advances.
		Local xVector# = unit.xPath-unit.xLoc  
		Local yVector# = unit.yPath-unit.yLoc  
		Local angle# = ATan2(yVector#,xVector#)
		Local xSpeed#= Cos(angle)*unit.speed 
		Local ySpeed#= Sin(angle)*unit.speed
		'animation
		'Print xSpeed+"|"+ySpeed
		If unit.pathStatus = Started Then 
			If unit.startzeit+100 <= MilliSecs() Then 
				If unit.anim < 4
					unit.startzeit = MilliSecs()
					unit.anim = unit.anim + 1
				Else
					'unit.startzeit = MilliSecs()
					unit.anim = 0
				EndIf 
			EndIf
		EndIf 		
		If xSpeed > 0 And ySpeed <= 0 Then unit.richtung = 3+(unit.anim*8)
		
		If xSpeed > 0 And ySpeed > 0 Then unit.richtung = 6+(unit.anim*8)
		If xSpeed > 0 And ySpeed < 0 Then unit.richtung = 1+(unit.anim*8)
		If xSpeed < 0 And ySpeed > 0 Then unit.richtung = 7+(unit.anim*8)
		
		If xSpeed < 0 And ySpeed < 0 And xSpeed = ySpeed Then unit.richtung = 0+(unit.anim*8)
		If xSpeed > 0 And ySpeed > 0 And xSpeed = ySpeed Then unit.richtung = 4+(unit.anim*8)
		
		If xSpeed < 0 And ySpeed > 0 And xSpeed <> ySpeed Then unit.richtung = 5+(unit.anim*8)
		If xSpeed > 0 And ySpeed < 0 And xSpeed <> ySpeed Then unit.richtung = 2+(unit.anim*8)
		
		If Abs(unit.xLoc - unit.xPath) < Abs(xSpeed)  unit.xLoc = unit.xPath Else unit.xLoc = unit.xLoc + xSpeed 
		If Abs(unit.yLoc - unit.yPath) < Abs(ySpeed)  unit.yLoc = unit.yPath Else unit.yLoc = unit.yLoc + ySpeed
	End Method

End Type


SeedRnd MilliSecs()

Local game:tgame = Tgame.Create()
game.RunProgram()
End 

' Funktionen für das Umrechnen von normalen Pixel-Koordinaten in Tile-Koordinaten und umgekehrt

Function PixelX:Float(tileX:Int, tileY:Int, tileW:Int)
	Return ( tileX * (tileW/2) ) - ( tileY * (tileW/2) )
End Function

Function PixelY:Float(tileX:Int, tileY:Int, tileH:Int)
	Return ( tileX * (tileH/2) ) + ( tileY * (tileH/2) )
End Function

Function TileX:Int(PixelX:Float, PixelY:Float, tileW:Int, tileH:Int, offsetX:Float = 0, offsetY:Float = 0)
	pixelX = pixelX - offsetX
	pixelY = pixelY - offsetY
	Return ( pixelX / (tileW/2) + pixelY / (tileH/2) ) / 2
End Function

Function TileY:Int(PixelX:Float, PixelY:Float, tileW:Int, tileH:Int, offsetX:Float = 0, offsetY:Float = 0)
	pixelX = pixelX - offsetX
	pixelY = pixelY - offsetY
	Return ( pixelY / (tileH/2) - pixelX / (tileW/2) ) / 2
End Function

Function TileIndex:Int(x:Int,y:Int)
		Local le=0,ri=0 ,up=0 ,do=0 ,leup=0,ledo=0,riup=0,rido=0
		'Print "Check: "+x+""+y
		If x >= 1 And y >= 1 And x <= (mapwidth-2) And y <= (mapheight-2) Then 
			
			If autotilemap[x-1][y] > 0 Then  le = 1
			If autotilemap[x+1][y] > 0 Then  ri = 1
			If autotilemap[x][y-1] > 0 Then  up = 1
			If autotilemap[x][y+1] > 0 Then  do = 1
			If autotilemap[x-1][y-1] > 0 Then  leup = 1
			If autotilemap[x-1][y+1] > 0 Then  ledo = 1
			If autotilemap[x+1][y-1] > 0 Then  riup = 1
			If autotilemap[x+1][y+1] > 0 Then  rido = 1
			
			Local te = (leup + up*2 + riup*4 + le*8 + ri*16 + ledo*32 + do*64 + rido*128) 
			'Print te
			Return te
		EndIf 
		Return 			
End Function 

' Map Arrays Laden
Function LoadMap:Int(ordner:String,name:String)
	If Confirm("Mapname: "+ordner+"/"+name+".map Laden?")=True Then 
		Local file:TStream
		Local existent = FileType(ordner+"/"+name+".map")
		If existent = True Then 
			file = OpenFile(ordner+"/"+name+".map")
			mapwidth = ReadShort(file)
			mapheight = ReadShort(file)
			ClearAutoMap()
			For Local x:Int = 0 To mapwidth-1
				For Local y:Int = 0 To mapheight-1
					isomapL1[x][y] = ReadShort(file)
					isomapL2[x][y] = ReadShort(file)
					autotilemap[x][y] = ReadShort(file)
				Next
			Next
			
			For Local x:Int = 0 To mapwidth*3-1
				For Local y:Int = 0 To mapheight*3-1
					pathmap[x][y] = ReadShort(file)
				Next
			Next
			
			CloseFile(file)
		EndIf 
	EndIf 
	Return True 
End Function 

'Map Arrays Speichern
Function SaveMap:Int(ordner:String,name:String)
	If Confirm("Mapname: "+ordner+"/"+name+".map Speichern?")=True Then 
		Local file:TStream
		file = WriteFile(ordner+"/"+name+".map")
		WriteShort file,mapwidth
		WriteShort file,mapheight
		
		For Local x:Int = 0 To mapwidth-1
			For Local y:Int = 0 To mapheight-1
				WriteShort file,isomapL1[x][y]
				WriteShort file,isomapL2[x][y] 
				WriteShort file,autotilemap[x][y]
			Next
		Next
		
		For Local x:Int = 0 To mapwidth*3-1
			For Local y:Int = 0 To mapheight*3-1
				WriteShort file,pathmap[x][y]
			Next
		Next
		
		CloseFile(file)
	EndIf 
	Return True 
End Function 
Function ClearAutoMap()
	For Local x:Int = 0 To mapwidth-1
		For Local y:Int = 0 To mapheight-1
			autotilemap[x][y]=0
		Next
	Next
End Function
Function DrawBox(a,b,c,d)
  DrawLine a,b,a+c-1,b
  DrawLine a+c-1,b,a+c-1,b+d-1
  DrawLine a+c-1,b+d-1,a,b+d-1
  DrawLine a,b+d-1,a,b
End Function
Function RectsOverlap:Int(x0, y0, w0, h0, x2, y2, w2, h2)
    If x0 > (x2 + w2) Or (x0 + w0) < x2 Then Return False
    If y0 > (y2 + h2) Or (y0 + h0) < y2 Then Return False
    Return True
End Function