// Returns an array of all points that can be reached by walking from a // specific location that end in a certain general area. Assumes that a 1x1 // unit is doing the walking. // Format: // points = AllPathablePoints(src, dst, min, max) // // Inputs: // src - table[x,y] - Where the path starts. // dst - table[x,y] - Another point near where the path should go. // min - integer - Minimum distance from dst that the path should end at. // max - integer - Maximum distance from dst that the path should end at. // // Outputs: // points - array[table[x,y]] func AllPathablePointsFunc(a *Ai) lua.GoFunction { return func(L *lua.State) int { if !game.LuaCheckParamsOk(L, "AllPathablePoints", game.LuaPoint, game.LuaPoint, game.LuaInteger, game.LuaInteger) { return 0 } min := L.ToInteger(-2) max := L.ToInteger(-1) x1, y1 := game.LuaToPoint(L, -4) x2, y2 := game.LuaToPoint(L, -3) a.ent.Game().DetermineLos(x2, y2, max, grid) var dst []int for x := x2 - max; x <= x2+max; x++ { for y := y2 - max; y <= y2+max; y++ { if x > x2-min && x < x2+min && y > y2-min && y < y2+min { continue } if x < 0 || y < 0 || x >= len(grid) || y >= len(grid[0]) { continue } if !grid[x][y] { continue } dst = append(dst, a.ent.Game().ToVertex(x, y)) } } vis := 0 for i := range grid { for j := range grid[i] { if grid[i][j] { vis++ } } } base.Log().Printf("Visible: %d", vis) graph := a.ent.Game().Graph(a.ent.Side(), true, nil) src := []int{a.ent.Game().ToVertex(x1, y1)} reachable := algorithm.ReachableDestinations(graph, src, dst) L.NewTable() base.Log().Printf("%d/%d reachable from (%d, %d) -> (%d, %d)", len(reachable), len(dst), x1, y1, x2, y2) for i, v := range reachable { _, x, y := a.ent.Game().FromVertex(v) L.PushInteger(i + 1) game.LuaPushPoint(L, x, y) L.SetTable(-3) } return 1 } }
// Performs a move action to the closest one of any of the specifed inputs // points. The movement can be restricted to not spend more than a certain // amount of ap. // Format: // success, p = DoMove(dsts, max_ap) // // Input: // dsts - array[table[x,y]] - Array of all points that are acceptable // destinations. // max_ap - integer - Maxmium ap to spend while doing this move, if the // required ap exceeds this the entity will still move // as far as possible towards a destination. // // Output: // success = bool - True iff the move made it to a position in dsts. // p - table[x,y] - New position of this entity, or nil if the move failed. func DoMoveFunc(a *Ai) lua.GoFunction { return func(L *lua.State) int { if !game.LuaCheckParamsOk(L, "DoMove", game.LuaArray, game.LuaInteger) { return 0 } me := a.ent max_ap := L.ToInteger(-1) L.Pop(1) cur_ap := me.Stats.ApCur() if max_ap > cur_ap { max_ap = cur_ap } n := int(L.ObjLen(-1)) dsts := make([]int, n)[0:0] for i := 1; i <= n; i++ { L.PushInteger(i) L.GetTable(-2) x, y := game.LuaToPoint(L, -1) dsts = append(dsts, me.Game().ToVertex(x, y)) L.Pop(1) } var move *actions.Move var ok bool for i := range me.Actions { move, ok = me.Actions[i].(*actions.Move) if ok { break } } if !ok { // TODO: what to do here? This poor guy didn't have a move action :( L.PushNil() L.PushNil() return 2 } exec := move.AiMoveToPos(me, dsts, max_ap) if exec != nil { a.execs <- exec <-a.pause // TODO: Need to get a resolution x, y := me.Pos() v := me.Game().ToVertex(x, y) complete := false for i := range dsts { if v == dsts[i] { complete = true break } } L.PushBoolean(complete) game.LuaPushPoint(L, x, y) base.Log().Printf("Finished move") } else { base.Log().Printf("Didn't bother moving") L.PushBoolean(true) L.PushNil() } return 2 } }
// Performs an aoe attack against centered at the specified position. // Format: // res = DoAoeAttack(attack, pos) // // Inputs: // attack - string - Name of the attack to use. // pos - table[x,y] - Position to center the aoe around. // // Outputs: // res - boolean - true if the action performed, nil otherwise. func DoAoeAttackFunc(a *Ai) lua.GoFunction { return func(L *lua.State) int { if !game.LuaCheckParamsOk(L, "DoAoeAttack", game.LuaString, game.LuaPoint) { return 0 } me := a.ent name := L.ToString(-2) action := getActionByName(me, name) if action == nil { game.LuaDoError(L, fmt.Sprintf("Entity '%s' (id=%d) has no action named '%s'.", me.Name, me.Id, name)) return 0 } attack, ok := action.(*actions.AoeAttack) if !ok { game.LuaDoError(L, fmt.Sprintf("Action '%s' is not an aoe attack.", name)) return 0 } tx, ty := game.LuaToPoint(L, -1) exec := attack.AiAttackPosition(me, tx, ty) if exec != nil { a.execs <- exec <-a.pause L.PushBoolean(true) } else { L.PushNil() } return 1 } }
// Computes the ranged distance between two points. // Format: // dist = RangedDistBetweenPositions(p1, p2) // // Input: // p1 - table[x,y] // p2 - table[x,y] // // Output: // dist - integer - The ranged distance between the two positions. func RangedDistBetweenPositionsFunc(a *Ai) lua.GoFunction { return func(L *lua.State) int { if !game.LuaCheckParamsOk(L, "RangedDistBetweenPositions", game.LuaPoint, game.LuaPoint) { return 0 } x1, y1 := game.LuaToPoint(L, -2) x2, y2 := game.LuaToPoint(L, -1) dx := x2 - x1 if dx < 0 { dx = -dx } dy := y2 - y1 if dy < 0 { dy = -dy } if dx > dy { L.PushInteger(dx) } else { L.PushInteger(dy) } return 1 } }