Skip to content

Commit

Permalink
Update everything to use the new wesnoth.map module
Browse files Browse the repository at this point in the history
- get_terrain and set_terrain replaced with direct indexing operations
- get_map_size mostly replaced with either the iterator or an on_board call.
  Only a few cases really needed to know the size of the map for some other purpose.
- shroud and fog operations, village owner, time areas, and location filters
- get_terrain_info replaced with terrain_types table
- Map generation functions create_map and create_filter
  • Loading branch information
CelticMinstrel committed Feb 28, 2021
1 parent 6558c79 commit 9d3bf19
Show file tree
Hide file tree
Showing 76 changed files with 343 additions and 391 deletions.
76 changes: 33 additions & 43 deletions data/ai/lua/ai_helper.lua
Expand Up @@ -69,11 +69,8 @@ end

function ai_helper.clear_labels()
-- Clear all labels on a map
local width, height = wesnoth.get_map_size()
for x = 1,width do
for y = 1,height do
wesnoth.label { x = x, y = y, text = "" }
end
for x, y in wesnoth.current.map:iter(true) do
wesnoth.label { x = x, y = y, text = "" }
end
end

Expand Down Expand Up @@ -667,8 +664,7 @@ function ai_helper.get_named_loc_xy(param_core, cfg, required_for)
if (param_core ~= '') then param_x, param_y = param_core .. '_x', param_core .. '_y' end
local x, y = cfg[param_x], cfg[param_y]
if x and y then
local width, height = wesnoth.get_map_size()
if (x < 1) or (x > width) or (y < 1) or (y > height) then
if not wesnoth.current.map:on_board(x, y) then
wml.error("Location is not on map: " .. param_x .. ',' .. param_y .. ' = ' .. x .. ',' .. y)
end

Expand Down Expand Up @@ -729,7 +725,7 @@ function ai_helper.get_multi_named_locs_xy(param_core, cfg, required_for)
end

function ai_helper.get_locations_no_borders(location_filter)
-- Returns the same locations array as wesnoth.get_locations(location_filter),
-- Returns the same locations array as wesnoth.map.find(location_filter),
-- but excluding hexes on the map border.
--
-- This is faster than alternative methods, at least with the current
Expand All @@ -738,7 +734,7 @@ function ai_helper.get_locations_no_borders(location_filter)

local old_include_borders = location_filter.include_borders
location_filter.include_borders = false
local locs = wesnoth.get_locations(location_filter)
local locs = wesnoth.map.find(location_filter)
location_filter.include_borders = old_include_borders
return locs
end
Expand All @@ -752,14 +748,14 @@ function ai_helper.get_closest_location(hex, location_filter, unit)

-- Find the maximum distance from 'hex' that's possible on the map
local max_distance = 0
local width, height = wesnoth.get_map_size()
local to_top_left = M.distance_between(hex[1], hex[2], 0, 0)
local map = wesnoth.current.map
local to_top_left = M.distance_between(hex, 0, 0)
if (to_top_left > max_distance) then max_distance = to_top_left end
local to_top_right = M.distance_between(hex[1], hex[2], width+1, 0)
local to_top_right = M.distance_between(hex, map.width-1, 0)
if (to_top_right > max_distance) then max_distance = to_top_right end
local to_bottom_left = M.distance_between(hex[1], hex[2], 0, height+1)
local to_bottom_left = M.distance_between(hex, 0, map.height-1)
if (to_bottom_left > max_distance) then max_distance = to_bottom_left end
local to_bottom_right = M.distance_between(hex[1], hex[2], width+1, height+1)
local to_bottom_right = M.distance_between(hex, map.width-1, map.height-1)
if (to_bottom_right > max_distance) then max_distance = to_bottom_right end

-- If the hex is supposed to be passable for a unit, it cannot be on the map border
Expand All @@ -782,11 +778,11 @@ function ai_helper.get_closest_location(hex, location_filter, unit)
}
end

local locs = wesnoth.get_locations(loc_filter)
local locs = wesnoth.map.find(loc_filter)

if unit then
for _,loc in ipairs(locs) do
local movecost = unit:movement(wesnoth.get_terrain(loc[1], loc[2]))
local movecost = unit:movement(wesnoth.current.map[loc])
if (movecost <= unit.max_moves) then return loc end
end
else
Expand All @@ -812,7 +808,7 @@ function ai_helper.get_passable_locations(location_filter, unit)
if unit then
local locs = {}
for _,loc in ipairs(all_locs) do
local movecost = unit:movement(wesnoth.get_terrain(loc[1], loc[2]))
local movecost = unit:movement(wesnoth.current.map[loc])
if (movecost <= unit.max_moves) then table.insert(locs, loc) end
end
return locs
Expand All @@ -828,7 +824,7 @@ function ai_helper.get_healing_locations(location_filter)

local locs = {}
for _,loc in ipairs(all_locs) do
if wesnoth.get_terrain_info(wesnoth.get_terrain(loc[1],loc[2])).healing > 0 then
if wesnoth.terrain_types[wesnoth.current.map[loc]].healing > 0 then
table.insert(locs, loc)
end
end
Expand All @@ -847,20 +843,17 @@ function ai_helper.distance_map(units, map)
map:iter(function(x, y, data)
local dist = 0
for _,unit in ipairs(units) do
dist = dist + M.distance_between(unit.x, unit.y, x, y)
dist = dist + M.distance_between(unit, x, y)
end
DM:insert(x, y, dist)
end)
else
local width, height = wesnoth.get_map_size()
for x = 1,width do
for y = 1,height do
local dist = 0
for _,unit in ipairs(units) do
dist = dist + M.distance_between(unit.x, unit.y, x, y)
end
DM:insert(x, y, dist)
for x, y in wesnoth.current.map:iter() do
local dist = 0
for _,unit in ipairs(units) do
dist = dist + M.distance_between(unit, x, y)
end
DM:insert(x, y, dist)
end
end

Expand All @@ -877,20 +870,17 @@ function ai_helper.inverse_distance_map(units, map)
map:iter(function(x, y, data)
local dist = 0
for _,unit in ipairs(units) do
dist = dist + 1. / (M.distance_between(unit.x, unit.y, x, y) + 1)
dist = dist + 1. / (M.distance_between(unit, x, y) + 1)
end
IDM:insert(x, y, dist)
end)
else
local width, height = wesnoth.get_map_size()
for x = 1,width do
for y = 1,height do
local dist = 0
for _,unit in ipairs(units) do
dist = dist + 1. / (M.distance_between(unit.x, unit.y, x, y) + 1)
end
IDM:insert(x, y, dist)
for x, y in wesnoth.current.map:iter() do
local dist = 0
for _,unit in ipairs(units) do
dist = dist + 1. / (M.distance_between(unit, x, y) + 1)
end
IDM:insert(x, y, dist)
end
end

Expand Down Expand Up @@ -998,8 +988,8 @@ function ai_helper.xyoff(x, y, ori, hex)
end

function ai_helper.split_location_list_to_strings(list)
-- Convert a list of locations @list as returned by wesnoth.get_locations into a pair of strings
-- suitable for passing in as x,y coordinate lists to wesnoth.get_locations.
-- Convert a list of locations @list as returned by wesnoth.map.find into a pair of strings
-- suitable for passing in as x,y coordinate lists to wesnoth.map.find.
-- Could alternatively convert to a WML table and use the find_in argument, but this is simpler.
local locsx, locsy = {}, {}
for i,loc in ipairs(list) do
Expand All @@ -1022,7 +1012,7 @@ function ai_helper.get_avoid_map(ai, avoid_tag, use_ai_aspect, default_avoid_tag
-- @use_ai_aspect == false or the default AI aspect is not set.

if avoid_tag then
return LS.of_pairs(wesnoth.get_locations(avoid_tag))
return LS.of_pairs(wesnoth.map.find(avoid_tag))
end

if use_ai_aspect then
Expand Down Expand Up @@ -1054,7 +1044,7 @@ function ai_helper.get_avoid_map(ai, avoid_tag, use_ai_aspect, default_avoid_tag

-- If we got here, that means neither @avoid_tag nor the default AI [avoid] aspect were used
if default_avoid_tag then
return LS.of_pairs(wesnoth.get_locations(default_avoid_tag))
return LS.of_pairs(wesnoth.map.find(default_avoid_tag))
else
return LS.create()
end
Expand Down Expand Up @@ -1526,12 +1516,12 @@ function ai_helper.next_hop(unit, x, y, cfg)
unit:to_map(old_x, old_y)
unit_in_way:to_map()

local terrain = wesnoth.get_terrain(next_hop_ideal[1], next_hop_ideal[2])
local terrain = wesnoth.current.map[next_hop_ideal]
local move_cost_endpoint = unit:movement_on(terrain)
local inverse_reach_map = LS.create()
for _,r in pairs(inverse_reach) do
-- We want the moves left for moving into the opposite direction in which the reach map was calculated
local terrain = wesnoth.get_terrain(r[1], r[2])
local terrain = wesnoth.current.map[r]
local move_cost = unit:movement_on(terrain)
local inverse_cost = r[3] + move_cost - move_cost_endpoint
inverse_reach_map:insert(r[1], r[2], inverse_cost)
Expand Down Expand Up @@ -1754,7 +1744,7 @@ function ai_helper.custom_cost_with_avoid(x, y, prev_cost, unit, avoid_map, ally
end

local max_moves = unit.max_moves
local terrain = wesnoth.get_terrain(x, y)
local terrain = wesnoth.current.map[{x, y}]
local move_cost = unit:movement_on(terrain)

if (move_cost > max_moves) then
Expand Down
20 changes: 11 additions & 9 deletions data/ai/lua/battle_calcs.lua
Expand Up @@ -677,8 +677,8 @@ function battle_calcs.battle_outcome(attacker, defender, cfg, cache)
if (def_max_hits > att_strikes) then def_max_hits = att_strikes end

-- Probability of landing a hit
local att_hit_prob = 1 - defender:defense_on(wesnoth.get_terrain(defender.x, defender.y)) / 100.
local def_hit_prob = 1 - attacker:defense_on(wesnoth.get_terrain(dst[1], dst[2])) / 100.
local att_hit_prob = 1 - defender:defense_on(wesnoth.current.map[defender]) / 100.
local def_hit_prob = 1 - attacker:defense_on(wesnoth.current.map[dst]) / 100.

-- Magical: attack and defense, and under all circumstances
if att_attack.magical then att_hit_prob = 0.7 end
Expand Down Expand Up @@ -808,13 +808,15 @@ function battle_calcs.attack_rating(attacker, defender, dst, cfg, cache)
if (att_stats.slowed ~= 0) then
damage = damage + 6 * (att_stats.slowed - att_stats.hp_chance[0])
end

local map = wesnoth.current.map

-- If attack is from a healing location, count that as slightly more than the healing amount
damage = damage - 1.25 * wesnoth.get_terrain_info(wesnoth.get_terrain(dst[1], dst[2])).healing
damage = damage - 1.25 * wesnoth.terrain_types[map[dst]].healing

-- Equivalently, if attack is adjacent to an unoccupied healing location, that's bad
for xa,ya in H.adjacent_tiles(dst[1], dst[2]) do
local healing = wesnoth.get_terrain_info(wesnoth.get_terrain(xa, ya)).healing
local healing = wesnoth.terrain_types[map[{xa, ya}]].healing
if (healing > 0) and (not wesnoth.units.get(xa, ya)) then
damage = damage + 1.25 * healing
end
Expand Down Expand Up @@ -865,7 +867,7 @@ function battle_calcs.attack_rating(attacker, defender, dst, cfg, cache)
end

-- If defender is on a healing location, count that as slightly more than the healing amount
damage = damage - 1.25 * wesnoth.get_terrain_info(wesnoth.get_terrain(defender.x, defender.y)).healing
damage = damage - 1.25 * wesnoth.terrain_types[map[defender]].healing

if (damage < 0) then damage = 0. end

Expand Down Expand Up @@ -908,7 +910,7 @@ function battle_calcs.attack_rating(attacker, defender, dst, cfg, cache)
-- If defender is on a village, add a bonus rating (we want to get rid of those preferentially)
-- So yes, this is positive, even though it's a plus for the defender
-- Note: defenders on healing locations also got a negative damage rating above (these don't exactly cancel each other though)
if wesnoth.get_terrain_info(wesnoth.get_terrain(defender.x, defender.y)).village then
if wesnoth.terrain_types[map[defender]].village then
defender_value = defender_value * (1. + 10. / attacker.max_hitpoints)
end

Expand All @@ -924,7 +926,7 @@ function battle_calcs.attack_rating(attacker, defender, dst, cfg, cache)
-- We don't need a bonus for good terrain for the attacker, as that is covered in the damage calculation
-- However, we add a small bonus for good terrain defense of the _defender_ on the _attack_ hex
-- This is in order to take good terrain away from defender on next move, all else being equal
local defender_defense = - (100 - defender:defense_on(wesnoth.get_terrain(dst[1], dst[2]))) / 100.
local defender_defense = - (100 - defender:defense_on(map[dst])) / 100.
defender_value = defender_value + defender_defense * defense_weight

-- Get a very small bonus for hexes in between defender and AI leader
Expand Down Expand Up @@ -1316,7 +1318,7 @@ function battle_calcs.best_defense_map(units, cfg)
if max_moves then unit.moves = old_moves end

for _,loc in ipairs(reach) do
local defense = unit:defense_on(wesnoth.get_terrain(loc[1], loc[2]))
local defense = unit:defense_on(wesnoth.current.map[loc])

if (defense > (defense_map:get(loc[1], loc[2]) or - math.huge)) then
defense_map:insert(loc[1], loc[2], defense)
Expand Down Expand Up @@ -1524,7 +1526,7 @@ function battle_calcs.get_attack_combos_subset(units, enemy, cfg)
-- Store information about it in 'loc' and add this to 'locs'
-- Want coordinates (dst) and terrain defense (for sorting)
loc.dst = xa * 1000 + ya
loc.hit_prob = 100 - unit:defense_on(wesnoth.get_terrain(xa, ya))
loc.hit_prob = 100 - unit:defense_on(wesnoth.current.map[{xa, ya}])
table.insert(locs, loc)

-- Also mark this hex as usable
Expand Down
22 changes: 12 additions & 10 deletions data/ai/lua/ca_castle_switch.lua
Expand Up @@ -33,7 +33,7 @@ end
local function other_units_on_keep(leader)
-- if we're on a keep, wait until there are no movable non-leader units on the castle before moving off
local leader_score = high_score
if wesnoth.get_terrain_info(wesnoth.get_terrain(leader.x, leader.y)).keep then
if wesnoth.terrain_types[wesnoth.current.map[leader]].keep then
local castle = AH.get_locations_no_borders {
{ "and", {
x = leader.x, y = leader.y, radius = 200,
Expand Down Expand Up @@ -172,13 +172,14 @@ function ca_castle_switch:evaluation(cfg, data, filter_own, recruiting_leader)

-- If we're on a keep,
-- don't move to another keep unless it's much better when uncaptured villages are present
if best_score > 0 and wesnoth.get_terrain_info(wesnoth.get_terrain(leader.x, leader.y)).keep then
local close_unowned_village = (wesnoth.get_villages {
{ "and", {
x = leader.x,
y = leader.y,
radius = leader.max_moves
}},
if best_score > 0 and wesnoth.terrain_types[wesnoth.current.map[leader]].keep then
local close_unowned_village = (wesnoth.map.find {
wml.tag['and']{
x = leader.x,
y = leader.y,
radius = leader.max_moves
},
gives_income = true,
owner_side = 0
})[1]
if close_unowned_village then
Expand All @@ -199,8 +200,9 @@ function ca_castle_switch:evaluation(cfg, data, filter_own, recruiting_leader)

if next_hop and ((next_hop[1] ~= leader.x) or (next_hop[2] ~= leader.y)) then
-- See if there is a nearby village that can be captured without delaying progress
local close_villages = wesnoth.get_villages( {
{ "and", { x = next_hop[1], y = next_hop[2], radius = leader.max_moves }},
local close_villages = wesnoth.map.find( {
wml.tag["and"]{ x = next_hop[1], y = next_hop[2], radius = leader.max_moves },
gives_income = true,
owner_side = 0 })
local cheapest_unit_cost = AH.get_cheapest_recruit_cost(leader)
for i,loc in ipairs(close_villages) do
Expand Down
4 changes: 2 additions & 2 deletions data/ai/lua/ca_grab_villages.lua
Expand Up @@ -28,7 +28,7 @@ function ca_grab_villages:evaluation(cfg, data, filter_own)

local avoid_map = LS.of_pairs(ai.aspects.avoid)

local all_villages, villages = wesnoth.get_villages(), {}
local all_villages, villages = wesnoth.map.find{gives_income = true}, {}
for _,village in ipairs(all_villages) do
if (not avoid_map:get(village[1], village[2])) then
table.insert(villages, village)
Expand Down Expand Up @@ -69,7 +69,7 @@ function ca_grab_villages:evaluation(cfg, data, filter_own)
end

-- Unowned and enemy-owned villages get a large bonus
local owner = wesnoth.get_village_owner(v[1], v[2])
local owner = wesnoth.map.get_owner(v)
if (not owner) then
village_rating = village_rating + 10000
else
Expand Down
3 changes: 2 additions & 1 deletion data/ai/lua/ca_recruit_rushers.lua
Expand Up @@ -20,7 +20,8 @@ if ca_castle_switch then
params.leader_takes_village = (function(leader)
local castle_switch_score = ca_castle_switch:evaluation({}, dummy_engine.data, nil, leader)
if castle_switch_score > 0 then
local take_village = #(wesnoth.get_villages {
local take_village = #(wesnoth.map.find {
gives_income = true,
x = dummy_engine.data.CS_leader_target[1],
y = dummy_engine.data.CS_leader_target[2]
}) > 0
Expand Down
4 changes: 2 additions & 2 deletions data/ai/lua/ca_spread_poison.lua
Expand Up @@ -54,8 +54,8 @@ function ca_spread_poison:evaluation(cfg, data, filter_own)
local cant_poison = defender.status.poisoned or defender.status.unpoisonable

-- For now, we also simply don't poison units on healing locations (unless standard combat CA does it)
local defender_terrain = wesnoth.get_terrain(defender.x, defender.y)
local healing = wesnoth.get_terrain_info(defender_terrain).healing
local defender_terrain = wesnoth.current.map[defender]
local healing = wesnoth.terrain_types[defender_terrain].healing

-- Also, poisoning units that would level up through the attack or could level on their turn as a result is very bad
local about_to_level = defender.max_experience - defender.experience <= (attacker.level * 2 * wesnoth.game_config.combat_experience)
Expand Down
6 changes: 3 additions & 3 deletions data/ai/lua/ca_village_hunt.lua
Expand Up @@ -16,7 +16,7 @@ function ca_village_hunt:evaluation(cfg, data, filter_own)

local avoid_map = LS.of_pairs(ai.aspects.avoid)

local all_villages, villages = wesnoth.get_villages(), {}
local all_villages, villages = wesnoth.map.find{gives_income = true}, {}
for _,village in ipairs(all_villages) do
if (not avoid_map:get(village[1], village[2])) then
table.insert(villages, village)
Expand All @@ -30,7 +30,7 @@ function ca_village_hunt:evaluation(cfg, data, filter_own)

local n_my_villages, n_allied_villages = 0, 0
for _,village in ipairs(villages) do
local owner = wesnoth.get_village_owner(village[1], village[2]) or -1
local owner = wesnoth.map.get_owner(village) or -1
if (owner == wesnoth.current.side) then
n_my_villages = n_my_villages + 1
end
Expand Down Expand Up @@ -66,7 +66,7 @@ function ca_village_hunt:evaluation(cfg, data, filter_own)
for _,unit in ipairs(units) do
local best_cost = AH.no_path
for i,v in ipairs(villages) do
if not wesnoth.match_location(v[1], v[2], { {"filter_owner", { {"ally_of", { side = wesnoth.current.side }} }} }) then
if not wesnoth.map.matches(v, { {"filter_owner", { {"ally_of", { side = wesnoth.current.side }} }} }) then
local path, cost = AH.find_path_with_avoid(unit, v[1], v[2], avoid_map)
if (cost < best_cost) then
local dst = AH.next_hop(unit, nil, nil, { path = path, avoid_map = avoid_map })
Expand Down

0 comments on commit 9d3bf19

Please sign in to comment.