package main import ( "math" rl "github.com/gen2brain/raylib-go/raylib" ) type Tile struct { X, Y int Height float32 Walkable bool } const ( MapWidth = 100 MapHeight = 100 TileSize = 32 ) // Initialize the map with some height data func InitMap() [][]Tile { mapGrid := make([][]Tile, MapWidth) for x := 0; x < MapWidth; x++ { mapGrid[x] = make([]Tile, MapHeight) for y := 0; y < MapHeight; y++ { mapGrid[x][y] = Tile{ X: x, Y: y, Height: float32((x + y) % 10), // Example height Walkable: true, // Set to false for obstacles } } } return mapGrid } type Player struct { X, Y int // Current tile position PosX, PosY float32 // Actual position for smooth movement TargetPath []Tile Speed float32 } func DrawMap(mapGrid [][]Tile) { for x := 0; x < MapWidth; x++ { for y := 0; y < MapHeight; y++ { tile := mapGrid[x][y] // Simple top-down view: color based on height color := rl.Color{R: uint8(tile.Height * 25), G: 100, B: 100, A: 255} rl.DrawRectangle(int32(tile.X*TileSize), int32(tile.Y*TileSize-int(tile.Height)), TileSize, TileSize, color) } } } func DrawPlayer(player Player, mapGrid [][]Tile) { // Get current tile height currentTile := mapGrid[player.X][player.Y] // Draw player at PosX, PosY adjusted by height rl.DrawCircle(int32(player.PosX), int32(player.PosY-currentTile.Height), 10, rl.Red) } func GetTileAtMouse(mapGrid [][]Tile) (Tile, bool) { if !rl.IsMouseButtonPressed(rl.MouseLeftButton) { return Tile{}, false } mouseX := rl.GetMouseX() mouseY := rl.GetMouseY() tileX := mouseX / TileSize tileY := (mouseY + int32(mapGrid[tileX][0].Height)) / TileSize if tileX >= 0 && tileX < MapWidth && tileY >= 0 && tileY < MapHeight { return mapGrid[tileX][tileY], true } return Tile{}, false } // pathfinding type Node struct { Tile Tile Parent *Node G, H, F float32 } func FindPath(mapGrid [][]Tile, start, end Tile) []Tile { openList := []*Node{} closedList := make(map[[2]int]bool) startNode := &Node{Tile: start, G: 0, H: heuristic(start, end)} startNode.F = startNode.G + startNode.H openList = append(openList, startNode) for len(openList) > 0 { // Find node with lowest F current := openList[0] currentIndex := 0 for i, node := range openList { if node.F < current.F { current = node currentIndex = i } } // Move current to closed list openList = append(openList[:currentIndex], openList[currentIndex+1:]...) closedList[[2]int{current.Tile.X, current.Tile.Y}] = true // Check if reached the end if current.Tile.X == end.X && current.Tile.Y == end.Y { path := []Tile{} node := current for node != nil { path = append([]Tile{node.Tile}, path...) node = node.Parent } return path } // Generate neighbors neighbors := GetNeighbors(mapGrid, current.Tile) for _, neighbor := range neighbors { if !neighbor.Walkable || closedList[[2]int{neighbor.X, neighbor.Y}] { continue } tentativeG := current.G + distance(current.Tile, neighbor) inOpen := false var existingNode *Node for _, node := range openList { if node.Tile.X == neighbor.X && node.Tile.Y == neighbor.Y { existingNode = node inOpen = true break } } if !inOpen || tentativeG < existingNode.G { newNode := &Node{ Tile: neighbor, Parent: current, G: tentativeG, H: heuristic(neighbor, end), } newNode.F = newNode.G + newNode.H if !inOpen { openList = append(openList, newNode) } } } } // No path found return nil } func heuristic(a, b Tile) float32 { return float32(abs(a.X-b.X) + abs(a.Y-b.Y)) } func distance(a, b Tile) float32 { _ = a _ = b return 1.0 //uniform cost for now } func GetNeighbors(mapGrid [][]Tile, tile Tile) []Tile { directions := [][2]int{ {1, 0}, {-1, 0}, {0, 1}, {0, -1}, {1, 1}, {-1, -1}, {1, -1}, {-1, 1}, } neighbors := []Tile{} for _, dir := range directions { nx, ny := tile.X+dir[0], tile.Y+dir[1] if nx >= 0 && nx < MapWidth && ny >= 0 && ny < MapHeight { neighbors = append(neighbors, mapGrid[nx][ny]) } } return neighbors } func abs(x int) int { if x < 0 { return -x } return x } // end pathfinding func UpdatePlayer(player *Player, deltaTime float32, mapGrid [][]Tile) { if len(player.TargetPath) > 0 { targetTile := player.TargetPath[0] targetX := float32(targetTile.X * TileSize) targetY := float32(targetTile.Y*TileSize - int(targetTile.Height)) // Calculate direction dirX := targetX - player.PosX dirY := targetY - player.PosY distance := float32(math.Sqrt(float64(dirX*dirX + dirY*dirY))) if distance < player.Speed*deltaTime { // Snap to target tile player.PosX = targetX player.PosY = targetY player.X = targetTile.X player.Y = targetTile.Y // Remove the reached tile from the path player.TargetPath = player.TargetPath[1:] } else { // Move towards target player.PosX += (dirX / distance) * player.Speed * deltaTime player.PosY += (dirY / distance) * player.Speed * deltaTime } } } func HandleInput(player *Player, mapGrid [][]Tile) { clickedTile, clicked := GetTileAtMouse(mapGrid) if clicked { path := FindPath(mapGrid, mapGrid[player.X][player.Y], clickedTile) if path != nil { // Exclude the first tile (current position) if len(path) > 1 { player.TargetPath = path[1:] } } } } func main() { rl.InitWindow(800, 600, "RuneScape-like Game") defer rl.CloseWindow() mapGrid := InitMap() player := Player{ X: 10, Y: 10, PosX: float32(10 * TileSize), PosY: float32(10*TileSize - int(mapGrid[10][10].Height)), Speed: 100.0, // pixels per second } rl.SetTargetFPS(60) for !rl.WindowShouldClose() { deltaTime := rl.GetFrameTime() // Handle input HandleInput(&player, mapGrid) // Update player position UpdatePlayer(&player, deltaTime, mapGrid) // Draw everything rl.BeginDrawing() rl.ClearBackground(rl.RayWhite) DrawMap(mapGrid) DrawPlayer(player, mapGrid) rl.EndDrawing() } }