Merge pull request #4580 from wesnoth/lua_gamemap

Refactor the game map to permit exposing it to Lua
This commit is contained in:
Celtic Minstrel 2021-03-04 14:00:43 -05:00
commit 3a7eef0310
94 changed files with 1460 additions and 1276 deletions

View file

@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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)
@ -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

View file

@ -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
@ -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
@ -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
@ -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
@ -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
@ -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)
@ -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

View file

@ -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,
@ -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
@ -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

View file

@ -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)
@ -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

View file

@ -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

View file

@ -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)

View file

@ -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)
@ -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
@ -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 })

View file

@ -343,7 +343,7 @@ return {
{ "and", params.filter_own }
}[1]
if (not leader) or (not wesnoth.get_terrain_info(wesnoth.get_terrain(leader.x, leader.y)).keep) then
if (not leader) or (not wesnoth.terrain_types[wesnoth.current.map[leader]).keep] then
return 0
end
@ -694,8 +694,8 @@ return {
end
end
if not enemy_location then
local width, height = wesnoth.get_map_size()
enemy_location = { x = width + 1 - reference_hex[1], y = height + 1 - reference_hex[2] }
local map = wesnoth.current.map
enemy_location = { x = map.playable_width + 1 - reference_hex[1], y = map.playable_height + 1 - reference_hex[2] }
end
distance_to_enemy = wesnoth.map.distance_between(reference_hex[1], reference_hex[2], enemy_location.x, enemy_location.y)
end
@ -906,15 +906,15 @@ return {
end
end
local all_villages = wesnoth.get_villages()
local all_villages = wesnoth.map.find{gives_income = true}
local villages = {}
for _,v in ipairs(all_villages) do
local owner = wesnoth.get_village_owner(v[1], v[2])
local owner = wesnoth.map.get_owner(v)
if ((not owner) or wesnoth.sides.is_enemy(owner, wesnoth.current.side))
and (not exclude_map:get(v[1], v[2]))
and (not exclude_map:get(v))
then
for _,loc in ipairs(data.castle.locs) do
local dist = M.distance_between(v[1], v[2], loc[1], loc[2])
local dist = M.distance_between(v, loc)
if (dist <= fastest_unit_speed) then
table.insert(villages, v)
break
@ -966,7 +966,6 @@ return {
end
end
local width,height,border = wesnoth.get_map_size()
if (not recruit_data.unit_distances) then recruit_data.unit_distances = {} end
for i,v in ipairs(villages) do
local close_castle_hexes = {}
@ -987,7 +986,7 @@ return {
local viable_village = false
local village_best_hex, village_shortest_distance = {}, AH.no_path
for j,c in ipairs(close_castle_hexes) do
if c[1] > 0 and c[2] > 0 and c[1] <= width and c[2] <= height then
if wesnoth.current.map:on_board(c) then
local distance = 0
for x,unit in ipairs(test_units) do
local key = unit.type .. '_' .. v[1] .. '-' .. v[2] .. '_' .. c[1] .. '-' .. c[2]

View file

@ -27,7 +27,7 @@ function retreat_functions.min_hp(unit)
if unit.canrecruit then retreat_factor = retreat_factor * 1.5 end
-- Higher retreat willingness on bad terrain
local retreat_factor = retreat_factor * (100 - unit:defense_on(wesnoth.get_terrain(unit.x, unit.y))) / 50
local retreat_factor = retreat_factor * (100 - unit:defense_on(wesnoth.current.map[unit])) / 50
local min_hp = retreat_factor * unit.max_hitpoints
@ -160,7 +160,7 @@ function retreat_functions.get_retreat_injured_units(healees, regen_amounts, avo
local location_subset = {}
for j,loc in ipairs(possible_locations) do
if (not avoid_map) or (not avoid_map:get(loc[1], loc[2])) then
local heal_amount = wesnoth.get_terrain_info(wesnoth.get_terrain(loc[1], loc[2])).healing or 0
local heal_amount = wesnoth.terrain_types[wesnoth.current.map[loc]].healing or 0
if heal_amount == true then
-- handle deprecated syntax
-- TODO: remove this when removed from game
@ -244,7 +244,7 @@ function retreat_functions.get_retreat_injured_units(healees, regen_amounts, avo
rating = rating + enemy_rating
-- Penalty based on terrain defense for unit
rating = rating - (100 - u:defense_on(wesnoth.get_terrain(loc[1], loc[2])))/10
rating = rating - (100 - u:defense_on(wesnoth.current.map[loc]))/10
-- Penalty if a unit has to move out of the way
-- (based on hp of moving unit)

View file

@ -24,7 +24,7 @@ local function custom_cost(x, y, unit, enemy_rating_map, prefer_map)
-- non-preferred hexes rather than a bonus for preferred hexes as the cost function
-- must return values >=1 for the a* search to work.
local terrain = wesnoth.get_terrain(x, y)
local terrain = wesnoth.current.map[{x, y}]
local move_cost = unit:movement(terrain)
move_cost = move_cost + (enemy_rating_map:get(x, y) or 0)
@ -95,7 +95,7 @@ function ca_assassin_move:execution(cfg)
-- Penalties for damage by enemies
local enemy_rating_map = LS.create()
enemy_damage_map:iter(function(x, y, enemy_damage)
local hit_chance = (100 - unit:defense_on(wesnoth.get_terrain(x, y))) / 100.
local hit_chance = (100 - unit:defense_on(wesnoth.current.map[{x, y}])) / 100.
local rating = hit_chance * enemy_damage
rating = rating / unit.max_hitpoints
@ -131,7 +131,7 @@ function ca_assassin_move:execution(cfg)
local prefer_slf = wml.get_child(cfg, "prefer")
local prefer_map -- want this to be nil, not empty LS if [prefer] tag not given
if prefer_slf then
local preferred_hexes = wesnoth.get_locations(prefer_slf)
local preferred_hexes = wesnoth.map.find(prefer_slf)
prefer_map = LS.create()
for _,hex in ipairs(preferred_hexes) do
prefer_map:insert(hex[1], hex[2], true)

View file

@ -50,11 +50,9 @@ function ca_big_animals:execution(cfg)
local reach_map = AH.get_reachable_unocc(unit)
local wander_terrain = wml.get_child(cfg, "filter_location_wander") or {}
reach_map:iter( function(x, y, v)
-- Remove tiles that do not comform to the wander terrain filter
if (not wesnoth.match_location(x, y, wander_terrain)) then
reach_map:remove(x, y)
end
-- Remove tiles that do not comform to the wander terrain filter
reach_map = reach_map:filter(function(x, y, v)
return wesnoth.map.matches(x, y, wander_terrain)
end)
-- Now find the one of these hexes that is closest to the goal

View file

@ -21,41 +21,38 @@ local function bottleneck_is_my_territory(map, enemy_map)
local dummy_unit = unit:clone()
local territory_map = LS.create()
local width, height = wesnoth.get_map_size()
for x = 1,width do
for y = 1,height do
-- The hex might have been covered already previously
if (not territory_map:get(x,y)) then
dummy_unit.x, dummy_unit.y = x, y
for x, y in wesnoth.current.map:iter() do
-- The hex might have been covered already previously
if (not territory_map:get(x,y)) then
dummy_unit.x, dummy_unit.y = x, y
-- Find lowest movement cost to own front-line hexes
local min_cost, best_path = math.huge
map:iter(function(xm, ym, v)
local path, cost = AH.find_path_with_shroud(dummy_unit, xm, ym, { ignore_units = true })
if (cost < min_cost) then
min_cost, best_path = cost, path
end
end)
-- Find lowest movement cost to own front-line hexes
local min_cost, best_path = math.huge
map:iter(function(xm, ym, v)
local path, cost = AH.find_path_with_shroud(dummy_unit, xm, ym, { ignore_units = true })
if (cost < min_cost) then
min_cost, best_path = cost, path
end
end)
-- And the same to the enemy front line
local min_cost_enemy, best_path_enemy = math.huge
enemy_map:iter(function(xm, ym, v)
local path, cost = AH.find_path_with_shroud(dummy_unit, xm, ym, { ignore_units = true })
if (cost < min_cost_enemy) then
min_cost_enemy, best_path_enemy = cost, path
end
end)
-- And the same to the enemy front line
local min_cost_enemy, best_path_enemy = math.huge
enemy_map:iter(function(xm, ym, v)
local path, cost = AH.find_path_with_shroud(dummy_unit, xm, ym, { ignore_units = true })
if (cost < min_cost_enemy) then
min_cost_enemy, best_path_enemy = cost, path
end
end)
-- We can set the flags for the hexes along the entire path
-- for efficiency reasons (this is pretty slow, esp. on large maps)
if (min_cost < min_cost_enemy) then
for _,step in ipairs(best_path) do
territory_map:insert(step[1], step[2], true)
end
else -- We do need to use 0's in this case though, false won't work
for _,step in ipairs(best_path_enemy) do
territory_map:insert(step[1], step[2], 0)
end
-- We can set the flags for the hexes along the entire path
-- for efficiency reasons (this is pretty slow, esp. on large maps)
if (min_cost < min_cost_enemy) then
for _,step in ipairs(best_path) do
territory_map:insert(step[1], step[2], true)
end
else -- We do need to use 0's in this case though, false won't work
for _,step in ipairs(best_path_enemy) do
territory_map:insert(step[1], step[2], 0)
end
end
end

View file

@ -47,7 +47,7 @@ function ca_fast_attack_utils.gamedata_setup()
local gamedata = {}
local village_map = {}
for _,village in ipairs(wesnoth.get_villages()) do
for _,village in ipairs(wesnoth.map.find{gives_income = true}) do
if (not village_map[village[1]]) then village_map[village[1]] = {} end
village_map[village[1]][village[2]] = true
end
@ -56,7 +56,7 @@ function ca_fast_attack_utils.gamedata_setup()
local healing_map = {}
for _,loc in ipairs(AH.get_healing_locations {}) do
if (not healing_map[loc[1]]) then healing_map[loc[1]] = {} end
healing_map[loc[1]][loc[2]] = wesnoth.get_terrain_info(wesnoth.get_terrain(loc[1], loc[2])).healing
healing_map[loc[1]][loc[2]] = wesnoth.terrain_types[wesnoth.current.map[loc]].healing
end
gamedata.healing_map = healing_map
@ -157,7 +157,7 @@ function ca_fast_attack_utils.get_unit_defense(unit_copy, x, y, defense_maps)
if (not defense_maps[unit_copy.id][x]) then defense_maps[unit_copy.id][x] = {} end
if (not defense_maps[unit_copy.id][x][y]) then
local defense = unit_copy:defense_on(wesnoth.get_terrain(x, y)) / 100.
local defense = unit_copy:defense_on(wesnoth.current.map[{x, y}]) / 100.
defense_maps[unit_copy.id][x][y] = { defense = defense }
end

View file

@ -30,15 +30,15 @@ function ca_fast_move:execution(cfg)
-- Villages get added first, so that (hopefully, scouts and faster units will go for them first)
local village_value = ai.aspects.village_value
if leader and (village_value > 0) then
local villages = wesnoth.get_villages()
local villages = wesnoth.map.find{gives_income = true}
-- Eliminate villages in avoid_map and those owned by an allied side
-- Also remove unowned villages if the AI has no leader
for i = #villages,1,-1 do
if avoid_map:get(villages[i][1], villages[i][2]) then
if avoid_map:get(villages[i]) then
table.remove(villages, i)
else
local owner = wesnoth.get_village_owner(villages[i][1], villages[i][2])
local owner = wesnoth.map.get_owner(villages[i])
if owner and (not wesnoth.sides.is_enemy(owner, wesnoth.current.side)) then
table.remove(villages, i)
elseif (not leader) and (not owner) then

View file

@ -21,10 +21,9 @@ function ca_forest_animals_new_rabbit:execution(cfg)
-- Eliminate all holes that have an enemy within 'rabbit_enemy_distance' hexes
-- We also add a random number to the ones we keep, for selection of the holes later
local width, height = wesnoth.get_map_size()
local holes = {}
for _,item in ipairs(all_items) do
if (item.x > 0) and (item.x <= width) and (item.y > 0) and (item.y <= height) then
if wesnoth.current.map:on_board(item) then
local enemies = AH.get_attackable_enemies {
{ "filter_location", { x = item.x, y = item.y, radius = rabbit_enemy_distance } }
}

View file

@ -7,7 +7,7 @@ local MAISD = wesnoth.require "ai/micro_ais/micro_ai_self_data.lua"
local M = wesnoth.map
local function custom_cost(x, y, unit, avoid_map, enemy_map, enemy_attack_map, multiplier)
local terrain = wesnoth.get_terrain(x, y)
local terrain = wesnoth.current.map[{x, y}]
local move_cost = unit:movement(terrain)
if avoid_map and avoid_map:get(x, y) then

View file

@ -41,7 +41,7 @@ function ca_healer_move:evaluation(cfg, data)
-- Also, they cannot be on a healing location or regenerate
if (healee.moves == 0) then
if (not healee:matches { ability = "regenerates" }) then
local healing = wesnoth.get_terrain_info(wesnoth.get_terrain(healee.x, healee.y)).healing
local healing = wesnoth.terrain_types[wesnoth.current.map[healee]].healing
if (healing == 0) then
local is_healee = true
for _,healer in ipairs(healers_noMP) do
@ -100,8 +100,8 @@ function ca_healer_move:evaluation(cfg, data)
rating = rating - enemies_in_reach * 1000
-- All else being more or less equal, prefer villages and strong terrain
local terrain = wesnoth.get_terrain(x, y)
local is_village = wesnoth.get_terrain_info(terrain).village
local terrain = wesnoth.current.map[{x, y}]
local is_village = wesnoth.terrain_types[terrain].village
if is_village then rating = rating + 2 end
local defense = healer:defense_on(terrain)

View file

@ -22,7 +22,7 @@ end
function ca_herding_dog_move:execution(cfg)
-- We simply move the first dog first, order does not matter
local dog = get_dog(cfg)
local herding_perimeter = LS.of_pairs(wesnoth.get_locations(wml.get_child(cfg, "filter_location")))
local herding_perimeter = LS.of_pairs(wesnoth.map.find(wml.get_child(cfg, "filter_location")))
-- Find average distance of herding_perimeter from center
local herd_loc = AH.get_named_loc_xy('herd', cfg)

View file

@ -7,7 +7,7 @@ return function(cfg)
-- First, find all contiguous hexes around center hex that are inside herding_perimeter
local location_filter = wml.get_child(cfg, "filter_location")
local herd_loc = AH.get_named_loc_xy('herd', cfg)
local herding_area = LS.of_pairs(wesnoth.get_locations {
local herding_area = LS.of_pairs(wesnoth.map.find {
x = herd_loc[1],
y = herd_loc[2],
radius = 999,
@ -17,7 +17,7 @@ return function(cfg)
-- Then, also exclude hexes next to herding_perimeter; some of the functions work better like that
herding_area:iter( function(x, y, v)
for xa, ya in H.adjacent_tiles(x, y) do
if (wesnoth.match_location(xa, ya, location_filter) ) then
if (wesnoth.map.matches(xa, ya, location_filter) ) then
herding_area:remove(x, y)
end
end

View file

@ -63,7 +63,7 @@ function ca_herding_herd_sheep:execution(cfg)
-- And the closer dog goes first (so that it might be able to chase another sheep afterward)
rating = rating - M.distance_between(x, y, dog.x, dog.y) / 100.
-- Finally, prefer to stay on path, if possible
if (wesnoth.match_location(x, y, wml.get_child(cfg, "filter_location")) ) then rating = rating + 0.001 end
if (wesnoth.map.matches(x, y, wml.get_child(cfg, "filter_location")) ) then rating = rating + 0.001 end
reach_map:insert(x, y, rating)

View file

@ -27,7 +27,7 @@ function ca_lurkers:execution(cfg)
local reach = LS.of_pairs(wesnoth.find_reach(lurker.x, lurker.y))
local lurk_area = wml.get_child(cfg, "filter_location")
local reachable_attack_terrain =
LS.of_pairs(wesnoth.get_locations {
LS.of_pairs(wesnoth.map.find {
{ "and", { x = lurker.x, y = lurker.y, radius = lurker.moves } },
{ "and", lurk_area }
})
@ -46,7 +46,7 @@ function ca_lurkers:execution(cfg)
for _,target in ipairs(targets) do
-- Get reachable attack terrain next to target unit
local reachable_attack_terrrain_adj_target = LS.of_pairs(
wesnoth.get_locations { x = target.x, y = target.y, radius = 1 }
wesnoth.map.find { x = target.x, y = target.y, radius = 1 }
)
reachable_attack_terrrain_adj_target:inter(reachable_attack_terrain)
@ -63,7 +63,7 @@ function ca_lurkers:execution(cfg)
-- If we got here, unit did not attack: go to random wander terrain hex
if (lurker.moves > 0) and (not cfg.stationary) then
local reachable_wander_terrain =
LS.of_pairs( wesnoth.get_locations {
LS.of_pairs( wesnoth.map.find {
{ "and", { x = lurker.x, y = lurker.y, radius = lurker.moves } },
{ "and", wml.get_child(cfg, "filter_location_wander") or lurk_area }
})

View file

@ -47,7 +47,7 @@ function ca_protect_unit_move:execution(cfg, data)
local terrain_defense_map = LS.create()
reach_map:iter(function(x, y, data)
terrain_defense_map:insert(x, y, unit:defense_on(wesnoth.get_terrain(x, y)))
terrain_defense_map:insert(x, y, unit:defense_on(wesnoth.current.map[{x, y}]))
end)
local goal_distance_map = LS.create()

View file

@ -11,13 +11,12 @@ function ca_recruit_random:evaluation(cfg)
-- Check if leader is on keep
local leader = wesnoth.units.find_on_map { side = wesnoth.current.side, canrecruit = 'yes' }[1]
if (not leader) or (not wesnoth.get_terrain_info(wesnoth.get_terrain(leader.x, leader.y)).keep) then
if (not leader) or (not wesnoth.terrain_types[wesnoth.current.map[leader]).keep] then
return 0
end
-- Find all connected castle hexes
local castle_map = LS.of_pairs({ { leader.x, leader.y } })
local width, height, border = wesnoth.get_map_size()
local new_castle_hex_found = true
while new_castle_hex_found do
@ -26,11 +25,8 @@ function ca_recruit_random:evaluation(cfg)
castle_map:iter(function(x, y)
for xa,ya in H.adjacent_tiles(x, y) do
if (not castle_map:get(xa, ya))
and (xa >= 1) and (xa <= width)
and (ya >= 1) and (ya <= height)
then
local is_castle = wesnoth.get_terrain_info(wesnoth.get_terrain(xa, ya)).castle
if (not castle_map:get(xa, ya)) and wesnoth.current.map:on_board(xa, ya) then
local is_castle = wesnoth.terrain_types[wesnoth.current.map[{xa, ya}]].castle
if is_castle then
table.insert(new_hexes, { xa, ya })

View file

@ -61,7 +61,7 @@ function ca_stationed_guardian:execution(cfg)
if (not AH.is_visible_unit(wesnoth.current.side, unit_in_way))
or (unit_in_way == guardian)
then
local defense = guardian:defense_on(wesnoth.get_terrain(xa, ya))
local defense = guardian:defense_on(wesnoth.current.map[{xa, ya}])
local nh = AH.next_hop(guardian, xa, ya)
if nh then
if (nh[1] == xa) and (nh[2] == ya) and (defense > best_defense) then

View file

@ -65,16 +65,16 @@ function ca_wolves_move:execution(cfg)
table.sort(wolves, function(a, b)
return M.distance_between(a.x, a.y, target.x, target.y) > M.distance_between(b.x, b.y, target.x, target.y)
end)
-- First wolf moves toward target, but tries to stay away from map edges
local width, height = wesnoth.get_map_size()
local wolf1 = AH.find_best_move(wolves[1], function(x, y)
local dist_1t = M.distance_between(x, y, target.x, target.y)
local rating = - dist_1t
local map = wesnoth.current.map
if (x <= 5) then rating = rating - (6 - x) / 1.4 end
if (y <= 5) then rating = rating - (6 - y) / 1.4 end
if (width - x <= 5) then rating = rating - (6 - (width - x)) / 1.4 end
if (height - y <= 5) then rating = rating - (6 - (height - y)) / 1.4 end
if (map.playable_width - x <= 5) then rating = rating - (6 - (map.playable_width - x)) / 1.4 end
if (map.playable_height - y <= 5) then rating = rating - (6 - (map.playable_height - y)) / 1.4 end
-- Hexes that avoid_type units can reach get a massive penalty
if avoid_enemies_map:get(x, y) then rating = rating - 1000 end

View file

@ -46,7 +46,7 @@ function ca_zone_guardian:execution(cfg)
if (not AH.is_visible_unit(wesnoth.current.side, unit_in_way))
or (unit_in_way == guardian)
then
local defense = guardian:defense_on(wesnoth.get_terrain(xa, ya))
local defense = guardian:defense_on(wesnoth.current.map[{xa, ya}])
local nh = AH.next_hop(guardian, xa, ya)
if nh then
if (nh[1] == xa) and (nh[2] == ya) and (defense > best_defense) then

View file

@ -160,7 +160,7 @@
-- table needs to be VERY complete. Or, rename this to morph_terrain and
-- have the conversion table come in as an argument, too.
local locations = wesnoth.get_locations{terrain = 'Gd,Gd^Es,Gd^Em,Gd^Fmw,Gd^Fdw,Hh,Hh^Fdw,Hh^Fmw,Mm,Mm^Xm'}
local locations = wesnoth.map.find{terrain = 'Gd,Gd^Es,Gd^Em,Gd^Fmw,Gd^Fdw,Hh,Hh^Fdw,Hh^Fmw,Mm,Mm^Xm'}
local coverage = 0.25
-- Constant 25% random coverage.
@ -186,8 +186,8 @@ local loopCounter
for loopCounter = 1, snowNeeded do
local locationsIndex = wesnoth.random(#locations)
local coordinate = locations[locationsIndex]
local terrainCode = wesnoth.get_terrain(coordinate[1], coordinate[2])
wesnoth.set_terrain(coordinate[1], coordinate[2], (convert[terrainCode] or terrainCode))
local terrainCode = wesnoth.current.map[coordinate]
wesnoth.current.map[coordinate] = (convert[terrainCode] or terrainCode)
table.remove(locations, locationsIndex)
end
>>

View file

@ -18,8 +18,6 @@ function ca_ogres_flee:execution()
formula = 'movement_left = 0'
}
local width, height = wesnoth.get_map_size()
-- Need the enemy map and enemy attack map if avoid_enemies is set
local enemies = wesnoth.units.find_on_map { { "filter_side", { {"enemy_of", {side = wesnoth.current.side} } } } }
local enemy_attack_map = BC.get_attack_map(enemies)
@ -33,11 +31,12 @@ function ca_ogres_flee:execution()
if (not unit_in_way) or (unit_in_way == u) then
-- First rating is distance from a map edge
local map = wesnoth.current.map
local dist_left = r[1] - 1
local dist_right = width - r[1]
local dist_right = map.playable_width - r[1]
local dist_top_left = M.distance_between(r[1], r[2], 4, 1)
local dist_top_right = M.distance_between(r[1], r[2], 40, 1)
local dist_bottom = height - r[2]
local dist_bottom = map.playable_height - r[2]
local dist = math.min(dist_left, dist_right, dist_top_left, dist_top_right, dist_bottom)
local rating = - dist

View file

@ -17,7 +17,7 @@ function wml_actions.spread_bandit_villages(cfg)
vars.boss_found = false
vars.bandit_types = types
local villages = wesnoth.get_villages(cfg)
local villages = wesnoth.map.find{gives_income = true, wml.tag['and'](cfg)}
-- Shouldn't happen in the scenario, but a failsafe is always nice.
if count > #villages then count = #villages end
@ -45,7 +45,7 @@ local function bandits_found(x,y)
local radius = 1
local locs
repeat
locs = wesnoth.get_locations({T["not"] { T.filter {} } , T["and"] { x = x, y = y, radius = radius } })
locs = wesnoth.map.find({T["not"] { T.filter {} } , T["and"] { x = x, y = y, radius = radius } })
radius = radius + 1
until locs[1]
@ -62,7 +62,7 @@ local function bandits_found(x,y)
if rand3 <= boss_chance or #bandit_villages < 3 then
vars.boss_found = true
local loc = wesnoth.get_locations({T["not"] { T.filter {} } , T["and"] { x = x, y = y, radius = 2 } })[1]
local loc = wesnoth.map.find({T["not"] { T.filter {} } , T["and"] { x = x, y = y, radius = 2 } })[1]
wesnoth.fire_event("boss_found", x, y, loc[1], loc[2])
end
end

View file

@ -164,7 +164,7 @@
-- table needs to be VERY complete. Or, rename this to morph_terrain and
-- have the conversion table come in as an argument, too.
local locations = wesnoth.get_locations{terrain = 'Ce,Ch,Chr,Co,Gd,Gd^Vc,Gd^Vh,Gd^Vo,Gll^Fp,Hhd,Hhd^Fp,Hhd^Vo,Ke,Khr,Ko,Md,Mm,Rd,Re,Re^Vo,Rr,Ww'}
local locations = wesnoth.map.find{terrain = 'Ce,Ch,Chr,Co,Gd,Gd^Vc,Gd^Vh,Gd^Vo,Gll^Fp,Hhd,Hhd^Fp,Hhd^Vo,Ke,Khr,Ko,Md,Mm,Rd,Re,Re^Vo,Rr,Ww'}
local coverage = wml.variables.snowCoverage
wml.variables.snowCoverage = nil
@ -206,8 +206,8 @@ local loopCounter
for loopCounter = 1, snowNeeded do
local locationsIndex = wesnoth.random(#locations)
local coordinate = locations[locationsIndex]
local terrainCode = wesnoth.get_terrain(coordinate[1], coordinate[2])
wesnoth.set_terrain(coordinate[1], coordinate[2], (convert[terrainCode] or terrainCode))
local terrainCode = wesnoth.current.map[coordinate]
wesnoth.current.map[coordinate] = (convert[terrainCode] or terrainCode)
table.remove(locations, locationsIndex)
end
>>

View file

@ -12,7 +12,7 @@ function wesnoth.wml_actions.find_respawn_point(cfg)
end
repeat
respawn_point = wesnoth.get_locations({
respawn_point = wesnoth.map.find({
include_borders = false,
T["and"] {
T.filter {

View file

@ -47,7 +47,7 @@ function ca_transport:execution()
-- First see if a transport is within landing distance
local landing_site_map = LS.of_pairs(
wesnoth.get_locations {
wesnoth.map.find {
terrain = 'W*',
{ "filter_adjacent_location", { terrain = '!, W*' } }
}
@ -74,10 +74,10 @@ function ca_transport:execution()
if (rating >= -0.05) then
for x,y in H.adjacent_tiles(r[1], r[2]) do
if (not unit_map:get(x, y)) then
if wesnoth.match_location(x, y, { terrain = "!, W*" }) then
if wesnoth.map.matches(x, y, { terrain = "!, W*" }) then
rating = rating + 1
table.insert(adj_tiles, { x, y, 1. } )
elseif wesnoth.match_location(x, y,
elseif wesnoth.map.matches(x, y,
{
terrain = "W*",
{ "filter_adjacent_location", { terrain = "!, W*" } }
@ -122,7 +122,7 @@ function ca_transport:execution()
-- If we got here, no landing site was found. Do a deep-water move instead
local deep_water_map = LS.of_pairs(
wesnoth.get_locations {
wesnoth.map.find {
terrain = 'Wo',
{ "not", { { "filter_adjacent_location", { terrain = '!, Wo' } } } }
}

View file

@ -14,8 +14,8 @@ function wml_actions.spawn_units(cfg)
local done = 0
for i=1,count do
local locs = wesnoth.get_locations({T["not"] { T.filter {} } , T["and"] { x = x, y = y, radius = 1 } })
if #locs == 0 then locs = wesnoth.get_locations({T["not"] { T.filter {} } , T["and"] { x = x, y = y, radius = 2 } }) end
local locs = wesnoth.map.find({T["not"] { T.filter {} } , T["and"] { x = x, y = y, radius = 1 } })
if #locs == 0 then locs = wesnoth.map.find({T["not"] { T.filter {} } , T["and"] { x = x, y = y, radius = 2 } }) end
if #locs == 0 then break end
done = done + 1

View file

@ -397,7 +397,7 @@
local radius = 1
local locs
repeat
locs = wesnoth.get_locations({T["not"] { T.filter {} } , T["and"] { x = x, y = y, radius = radius } })
locs = wesnoth.map.find({T["not"] { T.filter {} } , T["and"] { x = x, y = y, radius = radius } })
radius = radius + 1
until locs[1]

View file

@ -45,7 +45,7 @@ function muff_toras_move:execution()
end
-- All else being equal, go with good terrain
local hit_chance = muff_toras:defense(wesnoth.get_terrain(x, y))
local hit_chance = muff_toras:defense(wesnoth.current.map[{x, y}])
rating = rating - hit_chance
-- Finally, we want to run away from enemies if there are no other factors.

View file

@ -2,7 +2,7 @@ local on_event = wesnoth.require("on_event")
-- players get recalled by free all heroes up to castle size
local function wc2_autorecall()
for side_num = 1, wml.variables.wc2_player_count do
local castle_tiles = wesnoth.get_locations {
local castle_tiles = wesnoth.map.find {
terrain = "C*",
wml.tag["and"] {
radius = 3,

View file

@ -125,7 +125,7 @@ on_event("recruit", function(ec)
if #to_recall == 0 then
return
end
local candidates = wesnoth.get_locations {
local candidates = wesnoth.map.find {
terrain = "K*,C*,*^C*,*^K*",
wml.tag["and"] {
wml.tag.filter {

View file

@ -31,7 +31,7 @@ local function wct_map_enemy_themed(race, pet, castle, village, chance)
return
end
--give themed castle
wesnoth.set_terrain(boss.loc, "K" .. castle, "base")
wesnoth.current.map[boss] = wesnoth.map.replace_base("K" .. castle)
wesnoth.wml_actions.terrain {
terrain="C" .. castle,
wml.tag["and"] {
@ -48,7 +48,7 @@ local function wct_map_enemy_themed(race, pet, castle, village, chance)
},
},
}
local elvish_castle = wesnoth.get_locations {
local elvish_castle = wesnoth.map.find {
terrain="Cv",
wml.tag.filter_adjacent_location {
terrain="Kv^*"
@ -57,7 +57,7 @@ local function wct_map_enemy_themed(race, pet, castle, village, chance)
-- extra tweak with trees to elvish castle
for i, tile in ipairs(elvish_castle) do
if wesnoth.random(10) <= 4 then
wesnoth.set_terrain(tile, "Cv^Fet")
wesnoth.current.map[tile] = "Cv^Fet"
end
end
-- adjacent themed villages

View file

@ -37,7 +37,7 @@ end
function bonus.place_item(x, y, image)
if image == "campfire" then
wesnoth.set_terrain(x, y, "*^Ecf", "overlay")
wesnoth.current.map[{x, y}] = "^Ecf"
image = nil
else
image = image or "scenery/lighthouse.png"
@ -134,7 +134,7 @@ end
function bonus.get_random_hero(x, y)
return wc2_utils.pick_random_filtered("wc2.random_heroes", wc2_era.generate_bonus_heroes, function(unittypeid)
for _, sf in ipairs(wc2_era.spawn_filters) do
if sf.types[unittypeid] and not wesnoth.match_location(x, y, sf.filter_location) then
if sf.types[unittypeid] and not wesnoth.map.matches(x, y, sf.filter_location) then
return false
end
end

View file

@ -17,9 +17,9 @@ local supply_images =
function wesnoth.wml_actions.wc2_map_supply_village(t)
local unit = wesnoth.units.get(t.x, t.y)
local loc = unit.loc
wesnoth.set_terrain(loc, "Kh^Vov", "overlay")
wesnoth.current.map[loc] = "^Vov"
wesnoth.set_village_owner(loc, unit.side, false)
wesnoth.map.set_owner(loc, unit.side, false)
local supply_image = ((wml.variables.wc2_supply_image_counter or 0) % #supply_images ) + 1
wml.variables.wc2_supply_image_counter = supply_image

View file

@ -246,12 +246,12 @@ function default_generate_map(data)
for i = 1, 20 do
local status, map = pcall(function()
cfg.seed = wesnoth.random(5000) + 7
return wesnoth.generate_default_map(w, h, cfg)
return wesnoth.map.generate(w, h, cfg)
end)
if status then
return map
end
end
cfg.seed = wesnoth.random(5000) + 7
return wesnoth.generate_default_map(w, h, cfg)
return wesnoth.map.generate(w, h, cfg)
end

View file

@ -65,7 +65,7 @@ function world_conquest_tek_map_decoration_1()
while #terrain_to_change > 0 and wesnoth.random(2) == 1 do
local i = wesnoth.random(#terrain_to_change)
map:set_terrain(terrain_to_change[i], "Wwf")
map[terrain_to_change[i]] = "Wwf"
terrain_to_change = wct_store_possible_encampment_ford()
end

View file

@ -28,7 +28,7 @@ function world_conquest_tek_map_decoration_2a()
local r = wesnoth.random(0, #terrain_to_change - 3)
for i = 1, r do
local loc = terrain_to_change[wesnoth.random(#terrain_to_change)]
map:set_terrain(loc, "Hh^Ftp")
map[loc] = "Hh^Ftp"
end
end
end
@ -41,7 +41,7 @@ function world_conquest_tek_map_decoration_2a()
local r = wesnoth.random(0, #terrain_to_change - 3)
for i = 1, r do
local loc = terrain_to_change[wesnoth.random(#terrain_to_change)]
map:set_terrain(loc, "Gs^Ftp")
map[loc] = "Gs^Ftp"
end
end
end
@ -154,7 +154,7 @@ function world_conquest_tek_map_decoration_2a()
local terrain_to_change = wct_store_possible_flowers("G*^Fet")
while #terrain_to_change > 0 and wesnoth.random(10) ~= 1 do
local loc = terrain_to_change[wesnoth.random(#terrain_to_change)]
map:set_terrain(loc, "Gg^Efm", "overlay")
map[loc] = "^Efm"
local terrain_to_change = wct_store_possible_flowers("G*^Fet")
end
-- extra coast

View file

@ -145,7 +145,7 @@ function world_conquest_tek_map_decoration_2c()
-- base amount in map surface
local r = helper.rand(tostring(total_tiles // 285) .. ".." .. tostring(total_tiles // 150))
for i = 1, math.min(r, #terrain_to_change) do
map:set_terrain(terrain_to_change[i], "Ai")
map[terrain_to_change[i]] = "Ai"
end
local icepack_candiates = map:get_locations(f.all(

View file

@ -16,7 +16,7 @@ end
function wct_provinces_castle(terrain_to_change, terrain)
if #terrain_to_change > 0 then
local loc = terrain_to_change[wesnoth.random(#terrain_to_change)]
map:set_terrain(loc, terrain)
map[loc] = terrain
end
end

View file

@ -58,7 +58,7 @@ function world_conquest_tek_map_repaint_2e()
local terrain_to_change = wct_store_empty_citadel()
while #terrain_to_change > 0 do
local loc = terrain_to_change[wesnoth.random(#terrain_to_change)]
map:set_terrain(loc, "Rr^Vhc")
map[loc] = "Rr^Vhc"
terrain_to_change = wct_store_empty_citadel()
end
-- improve roads quality
@ -137,7 +137,7 @@ function wct_map_yard(directions, counter_directions)
if #terrain_to_change > 0 then
local loc = terrain_to_change[wesnoth.random(#terrain_to_change)]
map:set_terrain(loc, "Gg^Eff")
map[loc] = "Gg^Eff"
set_terrain { "Gg^Eff",
f.adjacent( f.is_loc(loc), counter_directions, nil)
}
@ -203,7 +203,7 @@ function wct_map_decoration_3e_leantos()
))
for i, v in ipairs(terrain_to_change) do
if wesnoth.random(3) == 1 then
map:set_terrain(v, "Rrc")
map[v] = "Rrc"
table.insert(prestart_event, wml.tag.item {
x = v[1],

View file

@ -58,7 +58,7 @@ function world_conquest_tek_map_constructor_delta()
-- note: the reason why i didnt add support for lua functions yet is that i
-- might want lua filter objects to be serializable.
if is_in_octaegon(loc[1], loc[2]) then
map:set_terrain(loc, "Wwf")
map[loc] = "Wwf"
end
end
end

View file

@ -12,7 +12,7 @@ function world_conquest_tek_map_repaint_3f()
while #terrain_to_change > 0 do
-- the oriignal code also did not randomize this.
-- todo: but maybe we should? (use wesnoth.random(#terrain_to_change[) instead of 1 here)
map:set_terrain(terrain_to_change[1], "Mm")
map[terrain_to_change[1]] = "Mm"
terrain_to_change = wct_store_cave_passages_candidates()
end
set_terrain { "Gd",

View file

@ -152,7 +152,7 @@ function world_conquest_tek_map_decoration_4a()
local terrain_to_change = wct_store_possible_flowers("Rr^Vhc")
while #terrain_to_change > 0 and wesnoth.random(10) > 5 do
local loc = terrain_to_change[wesnoth.random(#terrain_to_change)]
map:set_terrain(loc, "Gg^Efm", "overlay")
map[loc] = "^Efm"
terrain_to_change = wct_store_possible_flowers("Rr^Vhc")
end
-- 1.12 new forest

View file

@ -281,7 +281,7 @@ function world_conquest_tek_map_repaint_4b()
local r = helper.rand(tostring(total_tiles // 600) .. ".." .. tostring(total_tiles // 300))
for mush_i = 1, math.min(r, #terrain_to_change) do
map:set_terrain(terrain_to_change[mush_i], "Hhd^Uf")
map[terrain_to_change[mush_i]] = "Hhd^Uf"
end
-- chances of few orcish castles
wct_possible_map4_castle("Co", 2)
@ -356,11 +356,11 @@ function world_conquest_tek_map_repaint_4b()
helper.shuffle(terrain_to_change)
for i = 1, #terrain_to_change // wesnoth.random(3, 4) do
map:set_terrain(terrain_to_change[i], "Ds^Esd")
map[terrain_to_change[i]] = "Ds^Esd"
end
helper.shuffle(terrain_to_change)
for i = 1, #terrain_to_change // 6 do
map:set_terrain(terrain_to_change[i], "Ds^Es")
map[terrain_to_change[i]] = "Ds^Es"
end
set_terrain { "Dd^Es",
f.terrain("Dd"),

View file

@ -264,11 +264,11 @@ function wct_map_4c_post_bunus_decoration()
for forge_i, v in ipairs(terrain_to_change) do
local r = wesnoth.random(6)
if r == 1 then
map:set_terrain(v, "Cud")
map[v] = "Cud"
elseif r == 2 then
map:set_terrain(v, "Kud")
map[v] = "Kud"
elseif r == 3 then
map:set_terrain(v, "Kv")
map[v] = "Kv"
end
end

View file

@ -271,7 +271,7 @@ local function world_conquest_tek_map_decoration_6a()
local terrain_to_change = wct_store_possible_dwarven_castle()
while #terrain_to_change > 0 and wesnoth.random(2) == 1 do
local loc = terrain_to_change[wesnoth.random(#terrain_to_change)]
map:set_terrain(loc, "Cud")
map[loc] = "Cud"
terrain_to_change = wct_store_possible_dwarven_castle()
end
-- decorative farmlands in base to log villages
@ -329,7 +329,7 @@ local function world_conquest_tek_map_decoration_6a()
), "ne,se,sw,nw", "1-6")
))
for i, loc in ipairs(terrain_to_change) do
map:set_terrain(loc, map:get_terrain(loc) .. "^Eff")
map[loc] = "^Eff"
end
end

View file

@ -44,7 +44,7 @@ function wct_maritime_bridges()
pb = functional.filter(pb, function(t) return #t.locs >0 end)
local sel = pb[wesnoth.random(#pb)]
local loc = sel.locs[wesnoth.random(#sel.locs)]
map:set_terrain(loc, "Ww^" .. sel.type)
map[loc] = "Ww^" .. sel.type
pb = get_possible_maritime_bridge()
end
end
@ -191,7 +191,7 @@ function world_conquest_tek_map_decoration_6b()
f.adjacent(f.terrain("Wog,Wwg"))
))
loc = locs[wesnoth.random(#locs)];
map:set_terrain(loc, "Iwr^Vl")
map[loc] = "Iwr^Vl"
end
set_terrain { "Wwg,Iwr,Wwg^Bw\\,Wwg^Bw\\,Wwg^Bw\\,Wwg^Bw\\",

View file

@ -5,7 +5,7 @@ local function wct_conect_factory_rails()
f.terrain("*^Br*"),
f.adjacent(f.terrain("*^Vhh"))
))
while #map:get_locations(wesnoth.create_filter(
while #map:get_locations(wesnoth.map.filter(
f.all(
f.terrain("*^Br*"),
f.adjacent(f.find_in("rails_conected")),
@ -55,7 +55,7 @@ local function wct_conect_factory_rails()
filter_extra = { rails_conected = rails_conected },
layer = "overlay",
}
rails_conected = map:get_locations(wesnoth.create_filter(
rails_conected = map:get_locations(wesnoth.map.filter(
f.all(
f.terrain("*^Br*"),
f.radius(1, f.find_in("rails_conected"))
@ -105,7 +105,7 @@ local function wct_dirty_deltas()
while #terrain_to_change > 0 do
local loc = 1 -- todo: maybe use terrain_to_change[wesnoth.random(#terrain_to_change)]
local ter = helper.rand("Gs,Hh^Uf,Cud,Gs^Uf,Gs,Hh,Ds^Edt,Ds,Hh^Fmf,Gs,Gs^Fmf")
map:set_terrain(loc, ter)
map[loc] = ter
terrain_to_change = wct_store_possible_dirty_delta()
end
end
@ -124,7 +124,7 @@ local function wct_ford_deltas()
while #terrain_to_change > 0 do
local loc = terrain_to_change[1]-- todo: maybe use errain_to_change[wesnoth.random(#terrain_to_change)]
local ter = helper.rand("Gg,Gg^Efm,Mm,Gg^Fet,Gg,Mm,Gg")
map:set_terrain(loc, ter)
map[loc] = ter
terrain_to_change = wct_store_possible_ford_delta()
end
end

View file

@ -135,12 +135,12 @@ local function world_conquest_tek_map_repaint_6d()
-- this is faster.
local r8_Re = map:get_tiles_radius(
map:get_locations(f.terrain("Re")),
wesnoth.create_filter(f.all()),
wesnoth.map.filter(f.all()),
8
)
local r6_Khs = map:get_tiles_radius(
map:get_locations(f.terrain("Khs")),
wesnoth.create_filter(f.all()),
wesnoth.map.filter(f.all()),
6
)
set_terrain { "Chs",

View file

@ -37,7 +37,7 @@ f = {
}
function get_locations(t)
local filter = wesnoth.create_filter(t.filter, t.filter_extra or {})
local filter = wesnoth.map.filter(t.filter, t.filter_extra or {})
return map:get_locations(filter, t.locs)
end
@ -46,7 +46,7 @@ function set_terrain_impl(data)
local nlocs_total = 0
for i = 1, #data do
if data[i].filter then
local f = wesnoth.create_filter(data[i].filter, data[i].known or {})
local f = wesnoth.map.filter(data[i].filter, data[i].known or {})
locs[i] = map:get_locations(f, data[i].locs)
else
locs[i] = data[i].locs
@ -68,7 +68,7 @@ function set_terrain_impl(data)
for j = 1, num_tiles do
local loc = locs[i][j]
if chance >= 1000 or chance >= wesnoth.random(1000) then
map:set_terrain(loc, helper.rand(terrains), layer)
map[loc] = wesnoth.map['replace_' .. layer](helper.rand(terrains))
nlocs_changed = nlocs_changed + 1
end
end

View file

@ -111,8 +111,8 @@ function wct_castle_expansion_side(side_num)
if keep_loc == nil then
return
end
local castle = map:get_tiles_radius({keep_loc}, wesnoth.create_filter(f.terrain("C*,K*")), 1)
local keep_area = map:get_tiles_radius({keep_loc}, wesnoth.create_filter(f.all()), 2)
local castle = map:get_tiles_radius({keep_loc}, wesnoth.map.filter(f.terrain("C*,K*")), 1)
local keep_area = map:get_tiles_radius({keep_loc}, wesnoth.map.filter(f.all()), 2)
local candidates = get_locations {
filter = f.all(
@ -148,7 +148,7 @@ function wct_castle_expansion_side(side_num)
end
helper.shuffle(candidates)
for i = 1, n_tiles_wanted do
map:set_terrain(candidates[i], "Ch")
map[candidates[i]] = "Ch"
end
end
@ -166,7 +166,7 @@ function get_oceanic()
f.y("1," .. tostring(map.height - 1))
)
local water_border_tiles = map:get_locations(f.all(f_is_border, f.terrain("Wo*")))
local filter_radius = wesnoth.create_filter(f.all(
local filter_radius = wesnoth.map.filter(f.all(
f.terrain("W*^V*,Wwr*,Ww,Wwg,Wwt,Wo*"),
--ignore rivers
f.adjacent(f.terrain("!,W*^*,S*^*,D*^*,Ai"), nil, "0-3")

View file

@ -1,6 +1,6 @@
function world_conquest_tek_map_noise_proxy(radius, fraction, terrain)
local terrain_to_change = map:get_locations(f.terrain(terrain))
local nop_filter = wesnoth.create_filter(f.all())
local nop_filter = wesnoth.map.filter(f.all())
helper.shuffle(terrain_to_change)
for terrain_i = 1, math.ceil(#terrain_to_change / fraction) do
local loc_a = terrain_to_change[terrain_i]
@ -13,9 +13,9 @@ function world_conquest_tek_map_noise_proxy(radius, fraction, terrain)
if #terrain_to_swap_b > 0 then
local loc_b = terrain_to_swap_b[wesnoth.random(#terrain_to_swap_b)]
local terrain_a, terrain_b = map:get_terrain(loc_a), map:get_terrain(loc_b)
map:set_terrain(loc_a, terrain_b)
map:set_terrain(loc_b, terrain_a)
local terrain_a, terrain_b = map[loc_a], map[loc_b]
map[loc_a] = terrain_b
map[loc_b] = terrain_a
end
end
end

View file

@ -76,7 +76,7 @@ function world_conquest_tek_map_dirt(mushrooms)
local terrain_to_change = wct_store_possible_muddy_swamps()
while #terrain_to_change > 0 do
for i, v in ipairs(terrain_to_change) do
map:set_terrain(v, "Sm")
map[v] = "Sm"
end
terrain_to_change = wct_store_possible_muddy_swamps()
end
@ -192,7 +192,7 @@ function wct_possible_map4_castle(terrain, value)
local terrain_to_change = wct_store_possible_map4_castle(value)
while #terrain_to_change > 0 and wesnoth.random(value + 1) == 1 do
local loc = terrain_to_change[wesnoth.random(#terrain_to_change)]
map:set_terrain(loc, terrain)
map[loc] = terrain
terrain_to_change = wct_store_possible_map4_castle(value)
end
end
@ -221,7 +221,7 @@ function wct_road_to_village(road, village)
local terrain_to_change = wct_store_possible_roads(village)
while #terrain_to_change > 0 do
local loc = terrain_to_change[wesnoth.random(#terrain_to_change)]
map:set_terrain(loc, road)
map[loc] = road
terrain_to_change = wct_store_possible_roads(village)
end
end
@ -234,7 +234,7 @@ function wct_iterate_roads_to(get_next, radius, terrain)
local locs = get_next(r)
while #locs > 0 do
local loc = locs[wesnoth.random(#locs)]
map:set_terrain(loc, terrain)
map[loc] = terrain
locs = get_next(r)
end
end
@ -249,7 +249,7 @@ end
function wct_iterate_roads_to_2(f_validpath, f_src, f_dest, terrain_road, radius)
local src_tiles = map:get_locations(f_src)
local dest_tiles = map:get_locations(f_dest)
local filter_path = wesnoth.create_filter(f_validpath)
local filter_path = wesnoth.map.filter(f_validpath)
local map = _G.map
local function filter_path_function(x, y)
@ -282,7 +282,7 @@ function wct_iterate_roads_to_2(f_validpath, f_src, f_dest, terrain_road, radius
if (dist_ad or 999) < dist then
next_locs[#next_locs + 1] = loc_ad
end
if dist_ad and map:get_terrain(loc_ad) == terrain_road then
if dist_ad and map[loc_ad] == terrain_road then
--we merged with another path.
goto path_found
end
@ -294,7 +294,7 @@ function wct_iterate_roads_to_2(f_validpath, f_src, f_dest, terrain_road, radius
::path_found::
wesnoth.log("debug", "generated path: " .. debug_wml(path))
for i, ploc in ipairs(path) do
map:set_terrain(ploc, terrain_road)
map[ploc] = terrain_road
end
end
end
@ -310,7 +310,7 @@ function wct_break_walls(wall, terrain)
local terrain_to_change = wct_store_broken_wall_candidates(wall)
while #terrain_to_change > 0 do
local loc = terrain_to_change[wesnoth.random(#terrain_to_change)]
map:set_terrain(loc, helper.rand(terrain))
map[loc] = helper.rand(terrain)
terrain_to_change = wct_store_broken_wall_candidates(wall)
end
end

View file

@ -676,7 +676,7 @@ local function river_to_lava_postfix(terrain_to_change)
}
wild_volcano_for_lava_zone(terrain_to_change)
local filter_adjacent_grassland = wesnoth.create_filter(f.all(
local filter_adjacent_grassland = wesnoth.map.filter(f.all(
f.terrain("G*^*"),
f.adjacent(f.find_in("terrain_to_change"))
), { terrain_to_change = terrain_to_change })

View file

@ -105,7 +105,7 @@ function get_f_wct_bonus_location_filter(map)
)
end
function wct_bonus_chose_scenery(loc, theme, filter_extra)
local terrain = map:get_terrain(loc)
local terrain = map[loc]
-- determine possible scenery values based on terrain
local scenery = "well_g,temple,tent2_g,tent1,village,monolith3,burial"
local terrain_to_scenery =
@ -162,7 +162,7 @@ function wct_bonus_chose_scenery(loc, theme, filter_extra)
::intial_list_screated::
local function matches_location(f)
local filter_object = wesnoth.create_filter(f, filter_extra)
local filter_object = wesnoth.map.filter(f, filter_extra)
return #map:get_locations(filter_object, {loc}) > 0
end
@ -401,7 +401,7 @@ function world_conquest_tek_bonus_points(theme)
local res = {}
local scenario_num = wesnoth.get_variable("wc2_scenario") or 1
oceanic = get_oceanic()
f_wct_bonus_location_filter = wesnoth.create_filter(get_f_wct_bonus_location_filter(map), { oceanic = oceanic })
f_wct_bonus_location_filter = wesnoth.map.filter(get_f_wct_bonus_location_filter(map), { oceanic = oceanic })
local possible_locs = map:get_locations(f_wct_bonus_location_filter)
function place_item(loc)
scenery = wct_bonus_chose_scenery(loc, theme, { oceanic = oceanic })

View file

@ -44,7 +44,7 @@ local function run_postgeneration(map_data, id, scenario_content, nplayers, nhum
nhumanplayers = nhumanplayer,
scenario = scenario_content,
}
_G.map = wesnoth.create_map(map_data)
_G.map = wesnoth.map.create(map_data)
_G.total_tiles = _G.map.width * _G.map.height
_G.prestart_event = scenario_content.event[1]
_G.print_time = function(msg)

View file

@ -18,36 +18,26 @@ local ice = {
--replaces terrain fo the wct custom terrain mod.
local function wct_map_custom_ruin_village(loc)
local function matches_terrain(filter)
filter.x = cx.x1
filter.y = cx.y1
return wesnoth.get_locations({ x = loc[1], y = loc[2], terrain = filter}) > 0
end
local map = wesnoth.current.map
loc = wesnoth.map.get(loc)
-- TODO: enable once https://github.com/wesnoth/wesnoth/issues/4894 is fixed.
if false then
if matches_terrain("*^Vh,*^Vha") then
wesnoth.set_terrain(loc, "*^Vhr", "overlay")
if loc:matches{terrain = "*^Vh,*^Vha"} then
map[loc] = "^Vhr"
end
if matches_terrain("*^Vhc,*^Vhca") then
wesnoth.set_terrain(loc, "*^Vhr", "overlay")
if loc:matches{terrain = "*^Vhc,*^Vhca"} then
map[loc] = "^Vhr"
end
end
end
on_event("die", function(cx)
local loc = { cx.x1, cx.y1 }
local function matches(filter)
filter.x = cx.x1
filter.y = cx.y1
return #wesnoth.get_locations(filter) > 0
end
local function matches_terrain(filter)
return #wesnoth.get_locations({ x = cx.x1, y = cx.y1, terrain = filter}) > 0
end
local map = wesnoth.current.map
local loc = wesnoth.map.get(cx.x1, cx.y1)
if wml.variables.wc2_config_enable_terrain_destruction == false then
return
end
if not matches_terrain("K*^*,C*^*,*^Fet,G*^F*,G*^Uf,A*,*^B*,Rrc,Iwr,*^Vhh,*^Vh*,*^Fda*") then
if not loc:matches{terrain = "K*^*,C*^*,*^Fet,G*^F*,G*^Uf,A*,*^B*,Rrc,Iwr,*^Vhh,*^Vh*,*^Fda*"} then
return
end
local function item(image)
@ -58,43 +48,43 @@ on_event("die", function(cx)
z_order = -10,
}
end
if matches_terrain("Kh,Kha,Kh^Vov,Kha^Vov") then
wesnoth.set_terrain(loc, "Khr", "base")
if loc:matches{terrain = "Kh,Kha,Kh^Vov,Kha^Vov"} then
map[loc] = "Khr^"
elseif matches_terrain("Ch,Cha") then
wesnoth.set_terrain(loc, "Chr^Es")
elseif loc:matches{terrain = "Ch,Cha"} then
map[loc] = "Chr^Es"
-- only without custom activated
elseif matches_terrain("Ch^Vh,Ch^Vhc") then
wesnoth.set_terrain(loc, "Chr", "base")
elseif loc:matches{terrain = "Ch^Vh,Ch^Vhc"} then
map[loc] = "Chr^"
elseif matches_terrain("Cd") then
wesnoth.set_terrain(loc, "Cdr^Es")
elseif loc:matches{terrain = "Cd"} then
map[loc] = "Cdr^Es"
elseif matches_terrain("Cd^Vd") then
wesnoth.set_terrain(loc, "Cdr", "base")
elseif loc:matches{terrain = "Cd^Vd"} then
map[loc] = "Cdr^"
elseif matches_terrain("Kd") then
wesnoth.set_terrain(loc, "Kdr^Es")
elseif loc:matches{terrain = "Kd"} then
map[loc] = "Kdr^Es"
elseif matches_terrain("Gg^Fmf,Gg^Fdf,Gg^Fp,Gg^Uf,Gs^Fmf,Gs^Fdf,Gs^Fp,Gs^Uf") then
wesnoth.set_terrain(loc, "Gll", "base")
elseif loc:matches{terrain = "Gg^Fmf,Gg^Fdf,Gg^Fp,Gg^Uf,Gs^Fmf,Gs^Fdf,Gs^Fp,Gs^Uf"} then
map[loc] = "Gll^"
elseif matches_terrain("Cv^Fds") then
wesnoth.set_terrain(loc, "Cv^Fdw")
elseif loc:matches{terrain = "Cv^Fds"} then
map[loc] "Cv^Fdw"
elseif matches_terrain("Rr^Fet,Cv^Fet") then
wesnoth.set_terrain(loc, "Rr^Fetd", "overlay")
elseif loc:matches{terrain = "Rr^Fet,Cv^Fet"} then
map[loc] = "^Fetd"
elseif matches_terrain("Aa") then
elseif loc:matches{terrain = "Aa"} then
item(snow[wesnoth.random(#snow)])
elseif matches_terrain("Ai") then
elseif loc:matches{terrain = "Ai"} then
item(ice[wesnoth.random(#ice)])
elseif matches_terrain("Ww^Bsb|,Ww^Bsb/,Ww^Bsb\\,Wwt^Bsb|,Wwt^Bsb/,Wwt^Bsb\\,Wwg^Bsb|,Wwg^Bsb/,Wwg^Bsb\\") then
wesnoth.set_terrain(loc, "Wwf^Edt")
elseif loc:matches{terrain = "Ww^Bsb|,Ww^Bsb/,Ww^Bsb\\,Wwt^Bsb|,Wwt^Bsb/,Wwt^Bsb\\,Wwg^Bsb|,Wwg^Bsb/,Wwg^Bsb\\"} then
map[loc] = "Wwf^Edt"
wesnoth.play_sound("water-blast.wav")
item("scenery/castle-ruins.png")
elseif matches_terrain("Rrc") then
elseif loc:matches{terrain = "Rrc"} then
if wesnoth.variables["bonus.theme"] == "paradise" then
wesnoth.wml_actions.remove_item {
x = cx.x1,
@ -102,33 +92,33 @@ on_event("die", function(cx)
image = "wc2_citadel_leanto"
}
item("scenery/trash.png")
wesnoth.set_terrain(loc, "Rrc^Edt")
map[loc] = "Rrc^Edt"
end
elseif matches_terrain("Iwr") then
elseif loc:matches{terrain = "Iwr"} then
wesnoth.wml_actions.remove_item {
x = cx.x1,
y = cx.y1,
image = "wc2_dock_ship"
}
item("scenery/trash.png")
wesnoth.set_terrain(loc, "Iwr^Edt")
elseif matches_terrain("*^Vh,**^Vhc,*^Vha,**^Vhca,*^Fda") then
map[loc] = "Iwr^Edt"
elseif loc:matches{terrain = "*^Vh,**^Vhc,*^Vha,**^Vhca,*^Fda"} then
wct_map_custom_ruin_village(loc)
if matches_terrain("Ch^V*") then
wesnoth.set_terrain(loc, "Chr", "base")
if loc:matches{terrain = "Ch^V*"} then
map[loc] = "Chr^"
end
-- TODO: enable once https://github.com/wesnoth/wesnoth/issues/4894 is fixed.
if false then
if matches_terrain("*^Fda") then
wesnoth.set_terrain(loc, "*^Fdw", "overlay")
if loc:matches{terrain = "*^Fda"} then
map[loc] = "^Fdw"
end
end
else
if matches_terrain("*^Vhh,*^Vhha") then
wesnoth.set_terrain(loc, "*^Vhhr", "overlay")
if loc:matches{terrain = "*^Vhh,*^Vhha"} then
map[loc] = "^Vhhr"
end
if matches_terrain("*^Bw|,*^Bw/,*^Bw\\") then
wesnoth.set_terrain(loc, wesnoth.get_terrain(loc) .. "r")
if loc:matches{terrain = "*^Bw|,*^Bw/,*^Bw\\"} then
map[loc] = map[loc] .. "r"
end
end
end)

269
data/lua/core/map.lua Normal file
View file

@ -0,0 +1,269 @@
--[========[Map module]========]
print("Loading map module...")
function wesnoth.map.split_terrain_code(code)
return table.unpack(code:split('^', {remove_empty = false}))
end
function wesnoth.map.read_location(...)
local x, y = ...
if y == nil then
if type(x.x) == 'number' and type(x.y) == 'number' then
x, y = x.x, x.y
elseif type(x[1]) == 'number' and type(x[2]) == 'number' then
x, y = table.unpack(x)
else
return nil, 0
end
return {x = x, y = y}, 1
elseif type(x) == 'number' or type(y) == 'number' then
return {x = x, y = y}, 2
end
return nil, 0
end
if wesnoth.kernel_type() ~= "Application Lua Kernel" then
-- possible terrain string inputs:
-- A A^ A^B ^ ^B
-- implied mode:
-- both base both overlay overlay
function wesnoth.map.replace_base(code)
local base, overlay = wesnoth.map.split_terrain_code(code)
if base == nil then -- ^ or ^B
-- There's no base to replace with, so do nothing
return ''
else
-- Use the specified base but ignore the overlay
return base .. '^'
end
end
function wesnoth.map.replace_overlay(code)
local base, overlay = wesnoth.map.split_terrain_code(code)
if overlay == nil or overlay == '' then -- A or A^
-- No overlay was specified, so we want to clear the overlay without touching the base
return '^'
else
-- An overlay was specified, so use that and ignore the base
return '^' .. overlay
end
end
function wesnoth.map.replace_both(code)
local base, overlay = wesnoth.map.split_terrain_code(code)
if base == '' then -- ^ or ^B
-- There's no way to find a base to replace with in this case.
-- Could use the existing base, but that's not really replacing both, is it?
error('replace_both: no base terrain specified')
elseif overlay == '' then -- A^
-- This would normally mean replace base while preserving overlay,
-- but we actually want to replace base and clear overlay.
return base
else
-- It's already going to replace both, so return unchanged
return code
end
end
end
if wesnoth.kernel_type() == "Game Lua Kernel" then
local hex_mt = {__metatable = 'terrain hex reference'}
function hex_mt.__index(self, key)
if key == 'fogged' then
return self:fogged_for(wesnoth.current.side)
elseif key == 'shrouded' then
return self:shrouded_for(wesnoth.current.side)
elseif key == 'team_label' then
local label = self:label_for(wesnoth.current.side)
if label then return label.text end
return nil
elseif key == 'global_label' then
local label = self:label_for(nil)
if label then return label.text end
return nil
elseif key == 'label' then
return self.team_label or self.global_label
elseif key == 'terrain' then
return wesnoth.current.map[self]
elseif key == 'base_terrain' then
return self.terrain:split('^')[1]
elseif key == 'overlay_terrain' then
return self.terrain:split('^', {remove_empty=false})[2]
elseif key == 'info' then
return wesnoth.get_terrain_info(wesnoth.current.map[self])
elseif key == 1 then
return self.x
elseif key == 2 then
return self.y
elseif #key > 0 and key[0] ~= '_' then
return hex_mt[key]
end
end
function hex_mt.__newindex(self, key, val)
if key == 'fogged' then
self:set_fogged(wesnoth.current.side, val)
elseif key == 'shrouded' then
self:set_shrouded(wesnoth.current.side, val)
elseif key == 'team_label' or key == 'global_label' or key == 'label' then
local cfg
if type(val) == 'string' or (type(val) == 'userdata' and getmetatable(val) == 'translatable string') then
cfg = {x = self.x, y = self.y, text = val}
else
cfg = wml.parsed(val)
cfg.x, cfg.y = self.x, self.y
end
if key == 'team_label' then
cfg.side = wesnoth.current.side
cfg.team_name = wesnoth.sides[wesnoth.current.side].team_name
elseif key == 'global_label' then
cfg.side = 0
cfg.team_name = nil
elseif cfg.side == nil and cfg.team_name == nil then
-- If side or team name explicitly specified, use that, otherwise use current side and no team
cfg.side = wesnoth.current.side
cfg.team_name = nil
end
wesnoth.map.add_label(cfg)
elseif key == 'terrain' then
wesnoth.current.map[self] = val
elseif key == 'base_terrain' then
wesnoth.current.map[self] = wesnoth.map.replace_base(val)
elseif key == 'overlay_terrain' then
wesnoth.current.map[self] = wesnoth.map.replace_overlay(val)
elseif key == 1 then
self.x = val
elseif key == 2 then
self.y = val
elseif key == 'info' then
error('hex.info is read-only', 1)
end
end
function hex_mt:fogged_for(side)
return wesnoth.map.is_fogged(side, self.x, self.y)
end
function hex_mt:shrouded_for(side)
return wesnoth.map.is_shrouded(side, self.x, self.y)
end
function hex_mt:set_fogged(side, val)
if val then
wesnoth.map.place_shroud(side, {val})
else
wesnoth.map.remove_shroud(side, {val})
end
end
function hex_mt:set_fogged(side, val)
if val then
wesnoth.map.place_fog(side, {val})
else
wesnoth.map.remove_fog(side, {val})
end
end
function hex_mt:label_for(who)
return wesnoth.map.get_label(self.x, self.y, who)
end
function hex_mt:matches(filter)
return wesnoth.map.matches(self.x, self.y, filter)
end
-- Backwards compatibility - length is always 2
hex_mt.__len = wesnoth.deprecate_api('#location', 'nil', 3, '1.17', function() return 2 end, 'Using the length of a location as a validity test is no longer supported. You should represent an invalid location by nil instead.')
function wesnoth.map.get(x, y)
if not x or not y then error('Missing coordinate') end
if type(x) ~= 'number' or type(y) ~= 'number' then
error('Coordinate must be a number')
end
return setmetatable({x = x, y = y}, hex_mt)
end
local find_locations = wesnoth.map.find
function wesnoth.map.find(cfg)
local hexes = find_locations(cfg)
for i = 1, #hexes do
hexes[i] = wesnoth.map.get(hexes[i][1], hexes[i][2])
end
return hexes
end
wesnoth.terrain_mask = wesnoth.deprecate_api('wesnoth.terrain_mask', 'wesnoth.current.map:terrain_mask', 1, nil, function(...)
wesnoth.current.map:terrain_mask(...)
end)
wesnoth.get_terrain = wesnoth.deprecate_api('wesnoth.get_terrain', 'wesnoth.current.map[loc]', 1, nil, function(x, y)
local loc = wesnoth.map.read_location(x, y)
if loc == nil then error('get_terrain: expected location') end
return wesnoth.current.map[loc]
end)
wesnoth.set_terrain = wesnoth.deprecate_api('wesnoth.set_terrain', 'wesnoth.current.map[loc]=', 1, nil, function(...)
local loc, n = wesnoth.map.read_location(...)
if n == 0 then error('set_terrain: expected location') end
local new_ter, mode, replace_if_failed = select(n + 1, ...)
if new_ter == '' or type(new_ter) ~= 'string' then error('set_terrain: expected terrain string') end
if replace_if_failed then
mode = mode or 'both'
new_ter = wesnoth.map.replace_if_failed(new_ter, mode, true)
elseif mode == 'both' or mode == 'base' or mode == 'overlay' then
new_ter = wesnoth.map['replace_' .. mode](new_ter)
else
error('set_terrain: invalid mode')
end
wesnoth.current.map[loc] = new_ter
end)
wesnoth.get_map_size = wesnoth.deprecate_api('wesnoth.get_map_size', 'wesnoth.current.map.playable_width,playable_height,border_size', 1, nil, function()
local m = wesnoth.current.map
return m.playable_width, m.playable_height, m.border_size
end)
wesnoth.special_locations = wesnoth.deprecate_api('wesnoth.special_locations', 'wesnoth.current.map:special_locations', 1, nil, setmetatable({}, {
__index = function(_, k) return wesnoth.current.map.special_locations[k] end,
__newindex = function(_, k, v) wesnoth.current.map.special_locations[k] = v end,
__len = function(_)
local n = 0
for k,v in pairs(wesnoth.current.map.special_locations) do
n = n + 1
end
return n
end,
__pairs = function(_) return pairs(wesnoth.current.map.special_locations) end,
}), 'Note: the length operator has been removed')
wesnoth.place_shroud = wesnoth.deprecate_api('wesnoth.place_shroud', 'wesnoth.map.place_shroud', 1, nil, wesnoth.map.place_shroud)
wesnoth.remove_shroud = wesnoth.deprecate_api('wesnoth.remove_shroud', 'wesnoth.map.remove_shroud', 1, nil, wesnoth.map.remove_shroud)
wesnoth.is_shrouded = wesnoth.deprecate_api('wesnoth.is_shrouded', 'wesnoth.map.is_shrouded', 1, nil, wesnoth.map.is_shrouded)
wesnoth.add_fog = wesnoth.deprecate_api('wesnoth.add_fog', 'wesnoth.map.place_fog', 1, nil, wesnoth.map.place_fog)
wesnoth.remove_fog = wesnoth.deprecate_api('wesnoth.remove_fog', 'wesnoth.map.remove_fog', 1, nil, wesnoth.map.remove_fog)
wesnoth.is_fogged = wesnoth.deprecate_api('wesnoth.is_fogged', 'wesnoth.map.is_fogged', 1, nil, wesnoth.map.is_fogged)
wesnoth.get_village_owner = wesnoth.deprecate_api('wesnoth.get_village_owner', 'wesnoth.map.get_owner', 1, nil, wesnoth.map.get_owner)
wesnoth.set_village_owner = wesnoth.deprecate_api('wesnoth.set_village_owner', 'wesnoth.map.set_owner', 1, nil, wesnoth.map.set_owner)
wesnoth.label = wesnoth.deprecate_api('wesnoth.label', 'wesnoth.map.add_label', 1, nil, wesnoth.map.add_label)
wesnoth.add_time_area = wesnoth.deprecate_api('wesnoth.add_time_area', 'wesnoth.map.place_area', 1, nil, wesnoth.map.place_area)
wesnoth.remove_time_area = wesnoth.deprecate_api('wesnoth.remove_time_area', 'wesnoth.map.remove_area', 1, nil, wesnoth.map.remove_area)
wesnoth.get_locations = wesnoth.deprecate_api('wesnoth.get_locations', 'wesnoth.map.find', 1, nil, wesnoth.map.find)
wesnoth.get_villages = wesnoth.deprecate_api('wesnoth.get_locations', 'wesnoth.map.find', 1, nil, function(cfg)
return wesnoth.map.find{gives_income = true, wml.tag["and"](cfg)}
end)
wesnoth.match_location = wesnoth.deprecate_api('wesnoth.match_location', 'wesnoth.map.matches', 1, nil, wesnoth.map.matches)
wesnoth.get_terrain_info = wesnoth.deprecate_api('wesnoth.get_terrain_info', 'wesnoth.terrain_types', 1, nil, function(t) return wesnoth.terrain_types[t] end)
end
if wesnoth.kernel_type() == "Mapgen Lua Kernel" then
-- More map module stuff
wesnoth.create_filter = wesnoth.deprecate_api('wesnoth.create_filter', 'wesnoth.map.filter', 1, nil, wesnoth.map.filter)
wesnoth.create_map = wesnoth.deprecate_api('wesnoth.create_map', 'wesnoth.map.create', 1, nil, wesnoth.map.create)
wesnoth.default_generate_height_map = wesnoth.deprecate_api('wesnoth.default_generate_height_map', 'wesnoth.map.generate_height_map', 1, nil, wesnoth.map.generate_height_map)
wesnoth.generate_default_map = wesnoth.deprecate_api('wesnoth.generate_default_map', 'wesnoth.map.generate', 1, nil, wesnoth.map.generate)
-- These were originally only on the map metatable, so the deprecated versions also need to be in the map module
wesnoth.map.get_locations = wesnoth.deprecate_api('map:get_locations', 'map:find', 1, nil, wesnoth.map.find)
wesnoth.map.get_tiles_radius = wesnoth.deprecate_api('map:get_tiles_radius', 'map:find_in_radius', 1, nil, function(map, locs, filter, radius)
return wesnoth.map.find_in_radius(map, locs, radius, filter)
end, 'The filter is now the last parameter, instead of the radius')
end
wesnoth.map.tiles_adjacent = wesnoth.deprecate_api('wesnoth.map.tiles_adjacent', 'wesnoth.map.are_hexes_adjacent', 1, nil, wesnoth.map.are_hexes_adjacent)
wesnoth.map.get_adjacent_tiles = wesnoth.deprecate_api('wesnoth.map.get_adjacent_tiles', 'wesnoth.map.get_adjacent_hexes', 1, nil, wesnoth.map.get_adjacent_hexes)

View file

@ -72,20 +72,13 @@ end
-- Not deprecated because, unlike wesnoth.map.get_adjacent_tiles,
-- this verifies that the locations are on the map.
function helper.adjacent_tiles(x, y, with_borders)
local x1,y1,x2,y2,b = 1,1,wesnoth.get_map_size()
if with_borders then
x1 = x1 - b
y1 = y1 - b
x2 = x2 + b
y2 = y2 + b
end
local adj = {wesnoth.map.get_adjacent_tiles(x, y)}
local i = 0
return function()
while i < #adj do
i = i + 1
local u, v = adj[i][1], adj[i][2]
if u >= x1 and u <= x2 and v >= y1 and v <= y2 then
if wesnoth.current.map:on_board(u, v, with_borders) then
return u, v
end
end

View file

@ -84,16 +84,20 @@ function methods:clear()
self.values = {}
end
function methods:get(x, y)
return self.values[index(x, y)]
function methods:get(...)
local loc = wesnoth.map.read_location(...)
return self.values[index(loc.x, loc.y)]
end
function methods:insert(x, y, v)
self.values[index(x, y)] = v or true
function methods:insert(...)
local loc, n = wesnoth.map.read_location(...)
local v = select(n + 1, ...)
self.values[index(loc.x, loc.y)] = v or true
end
function methods:remove(x, y)
self.values[index(x, y)] = nil
function methods:remove(...)
local loc = wesnoth.map.read_location(...)
self.values[index(loc.x, loc.y)] = nil
end
function methods:clone()
@ -286,11 +290,6 @@ function methods:random()
end
function location_set.create()
if wesnoth.get_map_size then
-- If called from the mapgen kernel, there's no map
local w,h,b = wesnoth.get_map_size()
assert(h + 2 * b < 9000)
end
return setmetatable({ values = {} }, locset_meta)
end

View file

@ -233,10 +233,10 @@ end
function wml_actions.store_map_dimensions(cfg)
local var = cfg.variable or "map_size"
local w, h, b = wesnoth.get_map_size()
wml.variables[var .. ".width"] = w
wml.variables[var .. ".height"] = h
wml.variables[var .. ".border_size"] = b
local map = wesnoth.current.map
wml.variables[var .. ".width"] = map.playable_width
wml.variables[var .. ".height"] = map.playable_height
wml.variables[var .. ".border_size"] = map.border_size
end
function wml_actions.unit_worth(cfg)
@ -305,7 +305,7 @@ function wml_actions.volume(cfg)
end
function wml_actions.scroll_to(cfg)
local loc = wesnoth.get_locations( cfg )[1]
local loc = wesnoth.map.find( cfg )[1]
if not loc then return end
if not utils.optional_side_filter(cfg) then return end
wesnoth.interface.scroll_to_hex(loc[1], loc[2], cfg.check_fogged, cfg.immediate)
@ -417,14 +417,14 @@ end
function wml_actions.store_locations(cfg)
-- the variable can be mentioned in a [find_in] subtag, so it
-- cannot be cleared before the locations are recovered
local locs = wesnoth.get_locations(cfg)
local locs = wesnoth.map.find(cfg)
local writer = utils.vwriter.init(cfg, "location")
for i, loc in ipairs(locs) do
local x, y = loc[1], loc[2]
local t = wesnoth.get_terrain(x, y)
local t = wesnoth.current.map[{x, y}]
local res = { x = x, y = y, terrain = t }
if wesnoth.get_terrain_info(t).village then
res.owner_side = wesnoth.get_village_owner(x, y) or 0
if wesnoth.terrain_types[t].village then
res.owner_side = wesnoth.map.get_owner(x, y) or 0
end
utils.vwriter.write(writer, res)
end
@ -475,7 +475,7 @@ function wml_actions.store_reachable_locations(cfg)
if location_filter then
reach = reach:filter(function(x, y)
return wesnoth.match_location(x, y, location_filter)
return wesnoth.map.matches(x, y, location_filter)
end)
end
reach:to_wml_var(variable)
@ -505,19 +505,26 @@ function wml_actions.capture_village(cfg)
side = wesnoth.sides.find(filter_side)[1]
if side then side = side.side end
end
local locs = wesnoth.get_locations(cfg)
local locs = wesnoth.map.find(cfg)
for i, loc in ipairs(locs) do
wesnoth.set_village_owner(loc[1], loc[2], side, fire_event)
wesnoth.map.set_owner(loc[1], loc[2], side, fire_event)
end
end
function wml_actions.terrain(cfg)
local terrain = cfg.terrain or wml.error("[terrain] missing required terrain= attribute")
local layer = cfg.layer or 'both'
if layer ~= 'both' and layer ~= 'overlay' and layer ~= 'base' then
wml.error('[terrain] invalid layer=')
end
cfg = wml.shallow_parsed(cfg)
cfg.terrain = nil
for i, loc in ipairs(wesnoth.get_locations(cfg)) do
wesnoth.set_terrain(loc[1], loc[2], terrain, cfg.layer, cfg.replace_if_failed)
for i, loc in ipairs(wesnoth.map.find(cfg)) do
local replacement = cfg.replace_if_failed
and wesnoth.map.replace_if_failed(terrain, layer)
or wesnoth.map['replace_' .. layer](terrain)
wesnoth.current.map[loc] = replacement
end
end
@ -529,7 +536,7 @@ function wml_actions.delay(cfg)
end
function wml_actions.floating_text(cfg)
local locs = wesnoth.get_locations(cfg)
local locs = wesnoth.map.find(cfg)
local text = cfg.text or wml.error("[floating_text] missing required text= attribute")
for i, loc in ipairs(locs) do
@ -654,10 +661,9 @@ function wml_actions.store_starting_location(cfg)
for _, side in ipairs(wesnoth.sides.find(cfg)) do
local loc = side.starting_location
if loc then
local terrain = wesnoth.get_terrain(loc[1], loc[2])
local result = { x = loc[1], y = loc[2], terrain = terrain }
if wesnoth.get_terrain_info(terrain).village then
result.owner_side = wesnoth.get_village_owner(loc[1], loc[2]) or 0
local result = { x = loc[1], y = loc[2], terrain = wesnoth.current.map[loc] }
if wesnoth.terrain_types[result.terrain].village then
result.owner_side = wesnoth.map.get_owner(loc) or 0
end
utils.vwriter.write(writer, result)
end
@ -665,14 +671,14 @@ function wml_actions.store_starting_location(cfg)
end
function wml_actions.store_villages( cfg )
local villages = wesnoth.get_villages( cfg )
local villages = wesnoth.map.find{gives_income = true, wml.tag['and'](cfg)}
local writer = utils.vwriter.init(cfg, "location")
for index, village in ipairs( villages ) do
utils.vwriter.write(writer, {
x = village[1],
y = village[2],
terrain = wesnoth.get_terrain( village[1], village[2] ),
owner_side = wesnoth.get_village_owner( village[1], village[2] ) or 0
terrain = wesnoth.current.map[village],
owner_side = wesnoth.map.get_owner(village) or 0
})
end
end
@ -714,17 +720,17 @@ end
function wml_actions.place_shroud(cfg)
local sides = utils.get_sides(cfg)
local tiles = wesnoth.get_locations(cfg)
local tiles = wesnoth.map.find(cfg)
for i,side in ipairs(sides) do
wesnoth.place_shroud(side.side, tiles)
wesnoth.map.place_shroud(side.side, tiles)
end
end
function wml_actions.remove_shroud(cfg)
local sides = utils.get_sides(cfg)
local tiles = wesnoth.get_locations(cfg)
local tiles = wesnoth.map.find(cfg)
for i,side in ipairs(sides) do
wesnoth.remove_shroud(side.side, tiles)
wesnoth.map.remove_shroud(side.side, tiles)
end
end
@ -732,7 +738,7 @@ function wml_actions.time_area(cfg)
if cfg.remove then
wml_actions.remove_time_area(cfg)
else
wesnoth.add_time_area(cfg)
wesnoth.map.place_area(cfg)
end
end
@ -740,7 +746,7 @@ function wml_actions.remove_time_area(cfg)
local id = cfg.id or wml.error("[remove_time_area] missing required id= key")
for _,w in ipairs(id:split()) do
wesnoth.remove_time_area(w)
wesnoth.map.remove_area(w)
end
end
@ -792,7 +798,7 @@ end
function wml_actions.label( cfg )
local new_cfg = wml.parsed( cfg )
for index, location in ipairs( wesnoth.get_locations( cfg ) ) do
for index, location in ipairs( wesnoth.map.find( cfg ) ) do
new_cfg.x, new_cfg.y = location[1], location[2]
wesnoth.label( new_cfg )
end
@ -823,14 +829,6 @@ function wml_actions.unsynced(cfg)
end)
end
local function on_board(x, y)
if type(x) ~= "number" or type(y) ~= "number" then
return false
end
local w, h = wesnoth.get_map_size()
return x >= 1 and y >= 1 and x <= w and y <= h
end
wml_actions.unstore_unit = function(cfg)
local variable = cfg.variable or wml.error("[unstore_unit] missing required 'variable' attribute")
local unit_cfg = wml.variables[variable] or wml.error("[unstore_unit]: variable '" .. variable .. "' doesn't exist")
@ -847,7 +845,7 @@ wml_actions.unstore_unit = function(cfg)
x,y = table.unpack(wesnoth.special_locations[cfg.location_id])
end
wesnoth.add_known_unit(unit.type)
if on_board(x, y) then
if wesnoth.current.map:on_board(x, y) then
if cfg.find_vacant then
x,y = wesnoth.find_vacant_tile(x, y, check_passability and unit)
end
@ -916,21 +914,21 @@ local function parse_fog_cfg(cfg)
local ssf = wml.get_child(cfg, "filter_side")
local sides = wesnoth.sides.find(ssf or {})
-- Location filter
local locs = wesnoth.get_locations(cfg)
local locs = wesnoth.map.find(cfg)
return locs, sides
end
function wml_actions.lift_fog(cfg)
local locs, sides = parse_fog_cfg(cfg)
for i = 1, #sides do
wesnoth.remove_fog(sides[i].side, locs, not cfg.multiturn)
wesnoth.map.remove_fog(sides[i].side, locs, not cfg.multiturn)
end
end
function wml_actions.reset_fog(cfg)
local locs, sides = parse_fog_cfg(cfg)
for i = 1, #sides do
wesnoth.add_fog(sides[i].side, locs, cfg.reset_view)
wesnoth.map.place_fog(sides[i].side, locs, cfg.reset_view)
end
end
@ -967,9 +965,9 @@ function wesnoth.wml_actions.store_unit_defense(cfg)
if terrain then
defense = unit:chance_to_be_hit(terrain)
elseif cfg.loc_x and cfg.loc_y then
defense = unit:chance_to_be_hit(wesnoth.get_terrain(cfg.loc_x, cfg.loc_y))
defense = unit:chance_to_be_hit(wesnoth.current.map[{cfg.loc_x, cfg.loc_y}])
else
defense = unit:chance_to_be_hit(wesnoth.get_terrain(unit.x, unit.y))
defense = unit:chance_to_be_hit(wesnoth.current.map[unit])
end
wml.variables[cfg.variable or "terrain_defense"] = defense
end
@ -982,9 +980,9 @@ function wesnoth.wml_actions.store_unit_defense_on(cfg)
if terrain then
defense = unit:defense_on(terrain)
elseif cfg.loc_x and cfg.loc_y then
defense = unit:defense_on(wesnoth.get_terrain(cfg.loc_x, cfg.loc_y))
defense = unit:defense_on(wesnoth.current.map[{cfg.loc_x, cfg.loc_y}])
else
defense = unit:defense_on(wesnoth.get_terrain(unit.x, unit.y))
defense = unit:defense_on(wesnoth.current.map[unit])
end
wml.variables[cfg.variable or "terrain_defense"] = defense
end
@ -1023,7 +1021,7 @@ function wml_actions.terrain_mask(cfg)
if cfg.mask_file then
mask = wesnoth.read_file(cfg.mask_file)
end
wesnoth.terrain_mask({x, y}, mask, {
wesnoth.current.map:terrain_mask({x, y}, mask, {
is_odd = is_odd,
rules = rules,
ignore_special_locations = cfg.ignore_special_locations,

View file

@ -18,7 +18,7 @@ local function add_animation(anim, cfg)
)
end
if unit and not wesnoth.is_fogged(wesnoth.current.side, unit.x, unit.y) then
if unit and not wesnoth.map.is_fogged(wesnoth.current.side, unit.x, unit.y) then
local primary = wml.get_child(cfg, "primary_attack")
local secondary = wml.get_child(cfg, "secondary_attack")
-- We don't have access to the secondary unit at this point.
@ -73,7 +73,7 @@ local function add_animation(anim, cfg)
local facing = wml.get_child(cfg, "facing")
if facing then
local facing_loc = wesnoth.get_locations(facing)[1]
local facing_loc = wesnoth.map.find(facing)[1]
if facing_loc then
local dir = wesnoth.map.get_relative_dir(unit.x, unit.y, facing_loc[1], facing_loc[2])
unit.facing = dir

View file

@ -39,23 +39,20 @@ function wesnoth.wml_actions.find_path(cfg)
end
-- only the first location with the lowest distance and lowest movement cost will match.
local locations = wesnoth.get_locations(filter_location)
local locations = wesnoth.map.find(filter_location)
local max_cost = nil
if not allow_multiple_turns then max_cost = unit.moves end --to avoid wrong calculation on already moved units
local current_distance, current_cost, current_steps = math.huge, math.huge, math.huge
local current_location = {}
local width,heigth = wesnoth.get_map_size() -- data for test below
local current_location
for index, location in ipairs(locations) do
-- we test if location passed to pathfinder is invalid (border);
-- if it is, do not use it, and continue the cycle
if location[1] == 0 or location[1] == ( width + 1 ) or location[2] == 0 or location[2] == ( heigth + 1 ) then
else
local distance = wesnoth.map.distance_between ( unit.x, unit.y, location[1], location[2] )
if not wesnoth.current.map:on_border(location) then
local distance = wesnoth.map.distance_between ( unit.x, unit.y, location )
-- if we pass an unreachable location then an empty path and high value cost will be returned
local path, cost = wesnoth.find_path( unit, location[1], location[2], {
local path, cost = wesnoth.find_path( unit, location, {
max_cost = max_cost,
ignore_units = ignore_units,
ignore_teleport = ignore_teleport,
@ -93,7 +90,7 @@ function wesnoth.wml_actions.find_path(cfg)
end
end
if #current_location == 0 then
if current_location == nil then
-- either no matching locations, or only inaccessible matching locations (maybe enemy units are there)
if #locations == 0 then
wesnoth.message("WML warning","[find_path]'s filter didn't match any location")
@ -102,7 +99,7 @@ function wesnoth.wml_actions.find_path(cfg)
else
local path, cost = wesnoth.find_path(
unit,
current_location[1], current_location[2],
current_location,
{
max_cost = max_cost,
ignore_units = ignore_units,
@ -139,8 +136,7 @@ function wesnoth.wml_actions.find_path(cfg)
for index, path_loc in ipairs(path) do
local sub_path, sub_cost = wesnoth.find_path(
unit,
path_loc[1],
path_loc[2],
path_loc,
{
max_cost = max_cost,
ignore_units = ignore_units,
@ -158,7 +154,7 @@ function wesnoth.wml_actions.find_path(cfg)
wml.variables[string.format( "%s.step[%d]", variable, index - 1 )] =
{ -- this structure takes less space in the inspection window
x = path_loc[1], y = path_loc[2],
terrain = wesnoth.get_terrain( path_loc[1], path_loc[2] ),
terrain = wesnoth.current.map[path_loc],
movement_cost = sub_cost,
required_turns = sub_turns
}

View file

@ -91,7 +91,7 @@ function wml_actions.harm_unit(cfg)
amount,
cfg.alignment or "neutral",
wesnoth.get_time_of_day( { unit_to_harm.x, unit_to_harm.y, true } ).lawful_bonus,
unit_to_harm:resistance( cfg.damage_type or "dummy" ),
100 - unit_to_harm:resistance_against( cfg.damage_type or "dummy" ),
resistance_multiplier
)

View file

@ -94,7 +94,7 @@ end
-- returns the 'name' of an item, this can be used as an id to remove the iten later.
function wml_actions.item(cfg)
local locs = wesnoth.get_locations(cfg)
local locs = wesnoth.map.find(cfg)
cfg = wml.parsed(cfg)
if not cfg.image and not cfg.halo then
wml.error "[item] missing required image= and halo= attributes."
@ -110,7 +110,7 @@ function wml_actions.item(cfg)
end
function wml_actions.remove_item(cfg)
local locs = wesnoth.get_locations(cfg)
local locs = wesnoth.map.find(cfg)
for i, loc in ipairs(locs) do
wesnoth.interface.remove_item(loc[1], loc[2], cfg.image)
end
@ -122,7 +122,7 @@ function wml_actions.store_items(cfg)
variable = tostring(variable or wml.error("invalid variable= in [store_items]"))
wml.variables[variable] = nil
local index = 0
for i, loc in ipairs(wesnoth.get_locations(cfg)) do
for i, loc in ipairs(wesnoth.map.find(cfg)) do
local items = scenario_items[loc]
if items then
for j, item in ipairs(items) do

View file

@ -61,16 +61,16 @@ function wesnoth.wml_actions.modify_side(cfg)
side.shroud = cfg.shroud
end
if cfg.reset_maps then
wesnoth.place_shroud(side.side, "all")
wesnoth.map.remove_shroud(side.side, "all")
end
if cfg.fog ~= nil then
side.fog = cfg.fog
end
if cfg.reset_view then
wesnoth.add_fog(side.side, {}, true)
wesnoth.map.place_fog(side.side, {}, true)
end
if cfg.shroud_data then
wesnoth.remove_shroud(side.side, cfg.shroud_data)
wesnoth.map.remove_shroud(side.side, cfg.shroud_data)
end
if cfg.share_vision then

View file

@ -11,7 +11,7 @@ wesnoth.wml_actions.random_placement = function(cfg)
local allow_less = cfg.allow_less == true
local variable_previous <close> = utils.scoped_var(variable)
local math_abs = math.abs
local locs = wesnoth.get_locations(filter)
local locs = wesnoth.map.find(filter)
if type(num_items) == "string" then
if num_items:match('^%s*%(.*%)%s*$') then
local params = {size = #locs}
@ -40,7 +40,7 @@ wesnoth.wml_actions.random_placement = function(cfg)
wml.variables[variable .. ".x"] = point[1]
wml.variables[variable .. ".y"] = point[2]
wml.variables[variable .. ".n"] = i
wml.variables[variable .. ".terrain"] = wesnoth.get_terrain(point[1], point[2])
wml.variables[variable .. ".terrain"] = wesnoth.current.map[point]
if distance < 0 then
-- optimisation: nothing to do for distance < 0
elseif distance == 0 then

View file

@ -15,7 +15,7 @@ local extra_templates = {
if type(cfg.x) == 'number' and type(cfg.y) == 'number' and cfg.terrain then
args.x = cfg.x
args.y = cfg.y
args.terrain = wesnoth.get_terrain(cfg.x, cfg.y)
args.terrain = wesnoth.current.map[{cfg.x, cfg.y}]
return 'Note: ($x,$y) has terrain $terrain.'
end
return ''

View file

@ -181,14 +181,11 @@ local function humans_can_recruit()
end
-- return true if any keeps exist
local function map_has_keeps()
local width,height,_ = wesnoth.get_map_size()
for x = 1, width do
for y = 1, height do
local terr = wesnoth.get_terrain(x, y)
local info = wesnoth.get_terrain_info(terr)
if info.keep then
return true
end
for x, y in wesnoth.current.map:iter() do
local terr = wesnoth.current.map[{x, y}]
local info = wesnoth.terrain_types[terr]
if info.keep then
return true
end
end
end

View file

@ -240,7 +240,7 @@ local function place_units(unittypes, x, y)
local dst_x, dst_y = wesnoth.find_vacant_tile(x, y, u)
u:to_map(dst_x, dst_y)
wesnoth.add_known_unit(v)
wesnoth.set_village_owner(dst_x, dst_y, 1)
wesnoth.map.set_owner(dst_x, dst_y, 1)
end
end
@ -291,7 +291,7 @@ on_event("new turn", function()
local unit_types = get_spawn_types(next_spawn.units, next_spawn.gold, random_spawns[next_spawn.pool_num])
local spawn_areas = {{"3-14", "15"}, {"1", "4-13"}, {"2-13", "1"}, {"1", "2-15"}}
local spawn_area = spawn_areas[wesnoth.random(#spawn_areas)]
local locations_in_area = wesnoth.get_locations { x = spawn_area[1], y = spawn_area[2], radius=1, include_borders=false }
local locations_in_area = wesnoth.map.find { x = spawn_area[1], y = spawn_area[2], radius=1, include_borders=false }
local chosen_location = locations_in_area[wesnoth.random(#locations_in_area)]
place_units(unit_types, chosen_location[1], chosen_location[2])
end)

View file

@ -1729,7 +1729,7 @@ My best advancement costs $next_cost gold and Im $experience|% there."
[lua]
code = <<
wesnoth.set_village_owner(20, 1, 1)
wesnoth.map.set_owner(20, 1, 1)
local u = wesnoth.units.find_on_map({ lua_function = "has_teleport" })[1]
local t, c = wesnoth.find_path(u, 23, 7)
for i,l in ipairs(t) do

View file

@ -241,7 +241,7 @@ std::unique_ptr<editor_action> editor_action_starting_position::perform(map_cont
{
std::unique_ptr<editor_action> undo;
const std::string* old_loc_id = mc.map().is_starting_position(loc_);
const std::string* old_loc_id = mc.map().is_special_location(loc_);
map_location old_loc = mc.map().special_location(loc_id_);
if(old_loc_id != nullptr) {
@ -271,7 +271,7 @@ std::unique_ptr<editor_action> editor_action_starting_position::perform(map_cont
void editor_action_starting_position::perform_without_undo(map_context& mc) const
{
const std::string* old_id = mc.map().is_starting_position(loc_);
const std::string* old_id = mc.map().is_special_location(loc_);
if(old_id != nullptr) {
mc.map().set_special_location(*old_id, map_location());
}

View file

@ -106,7 +106,7 @@ std::unique_ptr<editor_action> mouse_action::key_event(
|| event.key.keysym.sym == SDLK_DELETE) {
int res = event.key.keysym.sym - '0';
if (res > gamemap::MAX_PLAYERS || event.key.keysym.sym == SDLK_DELETE) res = 0;
const std::string* old_id = disp.map().is_starting_position(previous_move_hex_);
const std::string* old_id = disp.map().is_special_location(previous_move_hex_);
if (res == 0 && old_id != nullptr) {
a = std::make_unique<editor_action_starting_position>(map_location(), *old_id);
} else if (res > 0 && (old_id == nullptr || *old_id == std::to_string(res))) {
@ -402,7 +402,7 @@ std::unique_ptr<editor_action> mouse_action_starting_position::up_left(editor_di
if (!disp.map().on_board(hex)) {
return nullptr;
}
auto player_starting_at_hex = disp.map().is_starting_position(hex);
auto player_starting_at_hex = disp.map().is_special_location(hex);
if (has_ctrl_modifier()) {
if (player_starting_at_hex) {
@ -437,7 +437,7 @@ std::unique_ptr<editor_action> mouse_action_starting_position::click_left(editor
std::unique_ptr<editor_action> mouse_action_starting_position::up_right(editor_display& disp, int x, int y)
{
map_location hex = disp.hex_clicked_on(x, y);
auto player_starting_at_hex = disp.map().is_starting_position(hex);
auto player_starting_at_hex = disp.map().is_special_location(hex);
if (player_starting_at_hex != nullptr) {
return std::make_unique<editor_action_starting_position>(map_location(), *player_starting_at_hex);
} else {

View file

@ -84,20 +84,20 @@ editor_map::~editor_map()
void editor_map::sanity_check()
{
int errors = 0;
if (total_width() != tiles_.w) {
ERR_ED << "total_width is " << total_width() << " but tiles_.size() is " << tiles_.w << std::endl;
if (total_width() != tiles().w) {
ERR_ED << "total_width is " << total_width() << " but tiles().size() is " << tiles().w << std::endl;
++errors;
}
if (total_height() != tiles_.h) {
ERR_ED << "total_height is " << total_height() << " but tiles_[0].size() is " << tiles_.h << std::endl;
if (total_height() != tiles().h) {
ERR_ED << "total_height is " << total_height() << " but tiles()[0].size() is " << tiles().h << std::endl;
++errors;
}
if (w() + 2 * border_size() != total_width()) {
ERR_ED << "h is " << h_ << " and border_size is " << border_size() << " but total_width is " << total_width() << std::endl;
ERR_ED << "h is " << h() << " and border_size is " << border_size() << " but total_width is " << total_width() << std::endl;
++errors;
}
if (h() + 2 * border_size() != total_height()) {
ERR_ED << "w is " << w_ << " and border_size is " << border_size() << " but total_height is " << total_height() << std::endl;
ERR_ED << "w is " << w() << " and border_size is " << border_size() << " but total_height is " << total_height() << std::endl;
++errors;
}
for (const map_location& loc : selection_) {
@ -137,7 +137,7 @@ std::set<map_location> editor_map::set_starting_position_labels(display& disp)
std::string label;
for (const auto& pair : starting_positions_.left) {
for (const auto& pair : special_locations().left) {
bool is_number = std::find_if(pair.first.begin(), pair.first.end(), [](char c) { return !std::isdigit(c); }) == pair.first.end();
if (is_number) {
@ -246,8 +246,8 @@ void editor_map::resize(int width, int height, int x_offset, int y_offset,
// fix the starting positions
if(x_offset || y_offset) {
for (auto it = starting_positions_.left.begin(); it != starting_positions_.left.end(); ++it) {
starting_positions_.left.modify_data(it, [=](t_translation::coordinate & loc) { loc.add(-x_offset, -y_offset); });
for (auto it = special_locations().left.begin(); it != special_locations().left.end(); ++it) {
special_locations().left.modify_data(it, [=](t_translation::coordinate & loc) { loc.add(-x_offset, -y_offset); });
}
}
@ -298,130 +298,122 @@ bool editor_map::same_size_as(const gamemap& other) const
void editor_map::expand_right(int count, const t_translation::terrain_code & filler)
{
t_translation::ter_map tiles_new(tiles_.w + count, tiles_.h);
w_ += count;
for (int x = 0, x_end = tiles_.w; x != x_end; ++x) {
for (int y = 0, y_end = tiles_.h; y != y_end; ++y) {
tiles_new.get(x, y) = tiles_.get(x, y);
t_translation::ter_map tiles_new(tiles().w + count, tiles().h);
for (int x = 0, x_end = tiles().w; x != x_end; ++x) {
for (int y = 0, y_end = tiles().h; y != y_end; ++y) {
tiles_new.get(x, y) = tiles().get(x, y);
}
}
for (int x = tiles_.w, x_end = tiles_.w + count; x != x_end; ++x) {
for (int y = 0, y_end = tiles_.h; y != y_end; ++y) {
tiles_new.get(x, y) = filler == t_translation::NONE_TERRAIN ? tiles_.get(tiles_.w - 1, y) : filler;
for (int x = tiles().w, x_end = tiles().w + count; x != x_end; ++x) {
for (int y = 0, y_end = tiles().h; y != y_end; ++y) {
tiles_new.get(x, y) = filler == t_translation::NONE_TERRAIN ? tiles().get(tiles().w - 1, y) : filler;
}
}
tiles_ = std::move(tiles_new);
tiles() = std::move(tiles_new);
}
void editor_map::expand_left(int count, const t_translation::terrain_code & filler)
{
t_translation::ter_map tiles_new(tiles_.w + count, tiles_.h);
w_ += count;
for (int x = 0, x_end = tiles_.w; x != x_end; ++x) {
for (int y = 0, y_end = tiles_.h; y != y_end; ++y) {
tiles_new.get(x + count, y) = tiles_.get(x, y);
t_translation::ter_map tiles_new(tiles().w + count, tiles().h);
for (int x = 0, x_end = tiles().w; x != x_end; ++x) {
for (int y = 0, y_end = tiles().h; y != y_end; ++y) {
tiles_new.get(x + count, y) = tiles().get(x, y);
}
}
for (int x = 0, x_end = count; x != x_end; ++x) {
for (int y = 0, y_end = tiles_.h; y != y_end; ++y) {
tiles_new.get(x, y) = filler == t_translation::NONE_TERRAIN ? tiles_.get(0, y) : filler;
for (int y = 0, y_end = tiles().h; y != y_end; ++y) {
tiles_new.get(x, y) = filler == t_translation::NONE_TERRAIN ? tiles().get(0, y) : filler;
}
}
tiles_ = std::move(tiles_new);
tiles() = std::move(tiles_new);
}
void editor_map::expand_top(int count, const t_translation::terrain_code & filler)
{
t_translation::ter_map tiles_new(tiles_.w, tiles_.h + count);
h_ += count;
for (int x = 0, x_end = tiles_.w; x != x_end; ++x) {
for (int y = 0, y_end = tiles_.h; y != y_end; ++y) {
tiles_new.get(x, y + count) = tiles_.get(x, y);
t_translation::ter_map tiles_new(tiles().w, tiles().h + count);
for (int x = 0, x_end = tiles().w; x != x_end; ++x) {
for (int y = 0, y_end = tiles().h; y != y_end; ++y) {
tiles_new.get(x, y + count) = tiles().get(x, y);
}
}
for (int x = 0, x_end = tiles_.w; x != x_end; ++x) {
for (int x = 0, x_end = tiles().w; x != x_end; ++x) {
for (int y = 0, y_end = count; y != y_end; ++y) {
tiles_new.get(x, y) = filler == t_translation::NONE_TERRAIN ? tiles_.get(x, 0) : filler;
tiles_new.get(x, y) = filler == t_translation::NONE_TERRAIN ? tiles().get(x, 0) : filler;
}
}
tiles_ = std::move(tiles_new);
tiles() = std::move(tiles_new);
}
void editor_map::expand_bottom(int count, const t_translation::terrain_code & filler)
{
t_translation::ter_map tiles_new(tiles_.w, tiles_.h + count);
h_ += count;
for (int x = 0, x_end = tiles_.w; x != x_end; ++x) {
for (int y = 0, y_end = tiles_.h; y != y_end; ++y) {
tiles_new.get(x, y) = tiles_.get(x, y);
t_translation::ter_map tiles_new(tiles().w, tiles().h + count);
for (int x = 0, x_end = tiles().w; x != x_end; ++x) {
for (int y = 0, y_end = tiles().h; y != y_end; ++y) {
tiles_new.get(x, y) = tiles().get(x, y);
}
}
for (int x = 0, x_end = tiles_.w; x != x_end; ++x) {
for (int y = tiles_.h, y_end = tiles_.h + count; y != y_end; ++y) {
tiles_new.get(x, y) = filler == t_translation::NONE_TERRAIN ? tiles_.get(x, tiles_.h - 1) : filler;
for (int x = 0, x_end = tiles().w; x != x_end; ++x) {
for (int y = tiles().h, y_end = tiles().h + count; y != y_end; ++y) {
tiles_new.get(x, y) = filler == t_translation::NONE_TERRAIN ? tiles().get(x, tiles().h - 1) : filler;
}
}
tiles_ = std::move(tiles_new);
tiles() = std::move(tiles_new);
}
void editor_map::shrink_right(int count)
{
if(count < 0 || count > tiles_.w) {
if(count < 0 || count > tiles().w) {
throw editor_map_operation_exception();
}
t_translation::ter_map tiles_new(tiles_.w - count, tiles_.h);
t_translation::ter_map tiles_new(tiles().w - count, tiles().h);
for (int x = 0, x_end = tiles_new.w; x != x_end; ++x) {
for (int y = 0, y_end = tiles_new.h; y != y_end; ++y) {
tiles_new.get(x, y) = tiles_.get(x, y);
tiles_new.get(x, y) = tiles().get(x, y);
}
}
w_ -= count;
tiles_ = std::move(tiles_new);
tiles() = std::move(tiles_new);
}
void editor_map::shrink_left(int count)
{
if (count < 0 || count > tiles_.w) {
if (count < 0 || count > tiles().w) {
throw editor_map_operation_exception();
}
t_translation::ter_map tiles_new(tiles_.w - count, tiles_.h);
t_translation::ter_map tiles_new(tiles().w - count, tiles().h);
for (int x = 0, x_end = tiles_new.w; x != x_end; ++x) {
for (int y = 0, y_end = tiles_new.h; y != y_end; ++y) {
tiles_new.get(x, y) = tiles_.get(x + count, y);
tiles_new.get(x, y) = tiles().get(x + count, y);
}
}
w_ -= count;
tiles_ = std::move(tiles_new);
tiles() = std::move(tiles_new);
}
void editor_map::shrink_top(int count)
{
if (count < 0 || count > tiles_.h) {
if (count < 0 || count > tiles().h) {
throw editor_map_operation_exception();
}
t_translation::ter_map tiles_new(tiles_.w, tiles_.h - count);
t_translation::ter_map tiles_new(tiles().w, tiles().h - count);
for (int x = 0, x_end = tiles_new.w; x != x_end; ++x) {
for (int y = 0, y_end = tiles_new.h; y != y_end; ++y) {
tiles_new.get(x, y) = tiles_.get(x, y + count);
tiles_new.get(x, y) = tiles().get(x, y + count);
}
}
h_ -= count;
tiles_ = std::move(tiles_new);
tiles() = std::move(tiles_new);
}
void editor_map::shrink_bottom(int count)
{
if (count < 0 || count > tiles_.h) {
if (count < 0 || count > tiles().h) {
throw editor_map_operation_exception();
}
t_translation::ter_map tiles_new(tiles_.w, tiles_.h - count);
t_translation::ter_map tiles_new(tiles().w, tiles().h - count);
for (int x = 0, x_end = tiles_new.w; x != x_end; ++x) {
for (int y = 0, y_end = tiles_new.h; y != y_end; ++y) {
tiles_new.get(x, y) = tiles_.get(x, y);
tiles_new.get(x, y) = tiles().get(x, y);
}
}
h_ -= count;
tiles_ = std::move(tiles_new);
tiles() = std::move(tiles_new);
}

View file

@ -337,7 +337,11 @@ bool game_board::change_terrain(
} else if(mode_str == "overlay") {
mode = terrain_type_data::OVERLAY;
}
return change_terrain(loc, terrain, mode, replace_if_failed);
}
bool game_board::change_terrain(const map_location &loc, const t_translation::terrain_code &terrain, terrain_type_data::merge_mode& mode, bool replace_if_failed) {
/*
* When a hex changes from a village terrain to a non-village terrain, and
* a team owned that village it loses that village. When a hex changes from

View file

@ -16,6 +16,8 @@
#include "display_context.hpp"
#include "team.hpp"
#include "terrain/translation.hpp"
#include "terrain/type_data.hpp"
#include "units/map.hpp"
#include "units/id.hpp"
#include <optional>
@ -157,8 +159,8 @@ public:
bool try_add_unit_to_recall_list(const map_location& loc, const unit_ptr u);
std::optional<std::string> replace_map(const gamemap & r);
bool change_terrain(const map_location &loc, const std::string &t,
const std::string & mode, bool replace_if_failed); //used only by lua and debug commands
bool change_terrain(const map_location &loc, const std::string &t, const std::string & mode, bool replace_if_failed); //used only by lua and debug commands
bool change_terrain(const map_location &loc, const t_translation::terrain_code &t, terrain_type_data::merge_mode& mode, bool replace_if_failed); //used only by lua and debug commands
// Global accessor from unit.hpp

View file

@ -28,6 +28,7 @@
#include "serialization/string_utils.hpp"
#include "terrain/terrain.hpp"
#include "terrain/type_data.hpp"
#include "wml_exception.hpp"
#include <algorithm>
#include <sstream>
@ -101,12 +102,10 @@ void gamemap::write_terrain(const map_location &loc, config& cfg) const
cfg["terrain"] = t_translation::write_terrain_code(get_terrain(loc));
}
gamemap::gamemap(const std::string& data)
: tiles_(1, 1)
, tdata_()
, villages_()
, w_(-1)
, h_(-1)
gamemap::gamemap(const std::string& data):
gamemap_base(1, 1),
tdata_(),
villages_()
{
if(const auto* gcm = game_config_manager::get()) {
tdata_ = gcm->terrain_types();
@ -119,19 +118,24 @@ gamemap::gamemap(const std::string& data)
read(data);
}
gamemap::~gamemap()
gamemap_base::gamemap_base(int w, int h, terrain_code t)
: tiles_(w, h, t)
, starting_positions_()
{
}
gamemap_base::~gamemap_base()
{
}
void gamemap::read(const std::string& data, const bool allow_invalid)
{
tiles_ = t_translation::ter_map();
tiles() = t_translation::ter_map();
villages_.clear();
starting_positions_.clear();
special_locations().clear();
if(data.empty()) {
w_ = 0;
h_ = 0;
if(allow_invalid) return;
}
@ -140,7 +144,7 @@ void gamemap::read(const std::string& data, const bool allow_invalid)
const std::string& data_only = std::string(data, offset);
try {
tiles_ = t_translation::read_game_map(data_only, starting_positions_, t_translation::coordinate{ border_size(), border_size() });
tiles() = t_translation::read_game_map(data_only, special_locations(), t_translation::coordinate{ border_size(), border_size() });
} catch(const t_translation::error& e) {
// We re-throw the error but as map error.
@ -149,18 +153,13 @@ void gamemap::read(const std::string& data, const bool allow_invalid)
}
// Post processing on the map
w_ = total_width() - 2 * border_size();
h_ = total_height() - 2 * border_size();
//Disabled since there are callcases which pass along a valid map header but empty
//map data. Still, loading (and actually applying) an empty map causes problems later on.
//Other callcases which need to load a dummy map use completely empty data :(.
//VALIDATE((w_ >= 1 && h_ >= 1), "A map needs at least 1 tile, the map cannot be loaded.");
VALIDATE((total_width() >= 1 && total_height() >= 1), "A map needs at least 1 tile, the map cannot be loaded.");
for(int x = 0; x < total_width(); ++x) {
for(int y = 0; y < total_height(); ++y) {
// Is the terrain valid?
t_translation::terrain_code t = tiles_.get(x, y);
t_translation::terrain_code t = tiles().get(x, y);
if(tdata_->map().count(t) == 0) {
if(!tdata_->is_known(t)) {
std::stringstream ss;
@ -173,7 +172,7 @@ void gamemap::read(const std::string& data, const bool allow_invalid)
// Is it a village?
if(x >= border_size() && y >= border_size()
&& x < total_width()- border_size() && y < total_height()- border_size()
&& tdata_->is_village(tiles_.get(x, y))) {
&& tdata_->is_village(tiles().get(x, y))) {
villages_.push_back(map_location(x - border_size(), y - border_size()));
}
}
@ -209,40 +208,24 @@ int gamemap::read_header(const std::string& data)
std::string gamemap::write() const
{
return t_translation::write_game_map(tiles_, starting_positions_, t_translation::coordinate{ border_size(), border_size() }) + "\n";
return t_translation::write_game_map(tiles(), special_locations(), t_translation::coordinate{ border_size(), border_size() }) + "\n";
}
void gamemap::overlay(const gamemap& m, map_location loc, const std::vector<overlay_rule>& rules, bool m_is_odd, bool ignore_special_locations)
{
//the following line doesn't compile on all compiler without the 'this->'
overlay_impl(tiles_, starting_positions_, m.tiles_, m.starting_positions_, [this](auto&&... arg) { this->set_terrain(std::forward<decltype(arg)>(arg)...); }, loc, rules, m_is_odd, ignore_special_locations);
}
void gamemap::overlay_impl(
// const but changed via set_terrain
const t_translation::ter_map& m1,
starting_positions& m1_st,
const t_translation::ter_map& m2,
const starting_positions& m2_st,
std::function<void (const map_location&, const t_translation::terrain_code&, terrain_type_data::merge_mode, bool)> set_terrain,
map_location loc,
const std::vector<overlay_rule>& rules,
bool m_is_odd,
bool ignore_special_locations)
void gamemap_base::overlay(const gamemap_base& m, map_location loc, const std::vector<overlay_rule>& rules, bool m_is_odd, bool ignore_special_locations)
{
int xpos = loc.wml_x();
int ypos = loc.wml_y();
const int xstart = std::max<int>(0, -xpos);
const int xend = std::min<int>(m2.w, m1.w -xpos);
const int xend = std::min<int>(m.total_width(), total_width() - xpos);
const int xoffset = xpos;
const int ystart_even = std::max<int>(0, -ypos);
const int yend_even = std::min<int>(m2.h, m1.h - ypos);
const int yend_even = std::min<int>(m.total_height(), total_height() - ypos);
const int yoffset_even = ypos;
const int ystart_odd = std::max<int>(0, -ypos +(xpos & 1) -(m_is_odd ? 1 : 0));
const int yend_odd = std::min<int>(m2.h, m1.h - ypos +(xpos & 1) -(m_is_odd ? 1 : 0));
const int yend_odd = std::min<int>(m.total_height(), total_height() - ypos +(xpos & 1) -(m_is_odd ? 1 : 0));
const int yoffset_odd = ypos -(xpos & 1) + (m_is_odd ? 1 : 0);
for(int x1 = xstart; x1 != xend; ++x1) {
@ -257,8 +240,8 @@ void gamemap::overlay_impl(
const int x2 = x1 + xoffset;
const int y2 = y1 + yoffset;
const t_translation::terrain_code t = m2.get(x1,y1);
const t_translation::terrain_code current = m1.get(x2, y2);
const t_translation::terrain_code t = m.get_terrain({x1,y1, wml_loc()});
const t_translation::terrain_code current = get_terrain({x2, y2, wml_loc()});
if(t == t_translation::FOGGED || t == t_translation::VOID_TERRAIN) {
continue;
@ -288,7 +271,7 @@ void gamemap::overlay_impl(
}
if (!ignore_special_locations) {
for(auto& pair : m2_st.left) {
for(auto& pair : m.special_locations().left) {
int x = pair.second.wml_x();
int y = pair.second.wml_y();
@ -306,22 +289,22 @@ void gamemap::overlay_impl(
int y_new = y + ((x & 1 ) ? yoffset_odd : yoffset_even);
map_location pos_new = map_location(x_new, y_new, wml_loc());
m1_st.left.erase(pair.first);
m1_st.insert(starting_positions::value_type(pair.first, t_translation::coordinate(pos_new.x, pos_new.y)));
starting_positions_.left.erase(pair.first);
starting_positions_.insert(location_map::value_type(pair.first, t_translation::coordinate(pos_new.x, pos_new.y)));
}
}
}
t_translation::terrain_code gamemap::get_terrain(const map_location& loc) const
t_translation::terrain_code gamemap_base::get_terrain(const map_location& loc) const
{
if(on_board_with_border(loc)) {
return (*this)[loc];
return tiles_.get(loc.x + border_size(), loc.y + border_size());
}
return loc == map_location::null_location() ? t_translation::NONE_TERRAIN : t_translation::terrain_code();
}
map_location gamemap::special_location(const std::string& id) const
map_location gamemap_base::special_location(const std::string& id) const
{
auto it = starting_positions_.left.find(id);
if (it != starting_positions_.left.end()) {
@ -333,31 +316,46 @@ map_location gamemap::special_location(const std::string& id) const
}
}
map_location gamemap::starting_position(int n) const
map_location gamemap_base::starting_position(int n) const
{
return special_location(std::to_string(n));
}
int gamemap::num_valid_starting_positions() const
namespace {
bool is_number(const std::string& id) {
return std::find_if(id.begin(), id.end(), [](char c) { return !std::isdigit(c); }) == id.end();
}
}
int gamemap_base::num_valid_starting_positions() const
{
int res = 0;
for (auto pair : starting_positions_) {
const std::string& id = pair.left;
bool is_number = std::find_if(id.begin(), id.end(), [](char c) { return !std::isdigit(c); }) == id.end();
if (is_number) {
if (is_number(id)) {
res = std::max(res, std::stoi(id));
}
}
return res;
}
const std::string* gamemap::is_starting_position(const map_location& loc) const
int gamemap_base::is_starting_position(const map_location& loc) const
{
if(const std::string* locName = is_special_location(loc)) {
if(is_number(*locName)) {
return std::stoi(*locName);
}
}
return 0;
}
const std::string* gamemap_base::is_special_location(const map_location& loc) const
{
auto it = starting_positions_.right.find(loc);
return it == starting_positions_.right.end() ? nullptr : &it->second;
}
void gamemap::set_special_location(const std::string& id, const map_location& loc)
void gamemap_base::set_special_location(const std::string& id, const map_location& loc)
{
bool valid = loc.valid();
auto it_left = starting_positions_.left.find(id);
@ -374,21 +372,21 @@ void gamemap::set_special_location(const std::string& id, const map_location& lo
}
}
void gamemap::set_starting_position(int side, const map_location& loc)
void gamemap_base::set_starting_position(int side, const map_location& loc)
{
set_special_location(std::to_string(side), loc);
}
bool gamemap::on_board(const map_location& loc) const
bool gamemap_base::on_board(const map_location& loc) const
{
return loc.valid() && loc.x < w_ && loc.y < h_;
return loc.valid() && loc.x < w() && loc.y < h();
}
bool gamemap::on_board_with_border(const map_location& loc) const
bool gamemap_base::on_board_with_border(const map_location& loc) const
{
return !tiles_.data.empty() && // tiles_ is not empty when initialized.
loc.x >= -border_size() && loc.x < w_ + border_size() &&
loc.y >= -border_size() && loc.y < h_ + border_size();
loc.x >= -border_size() && loc.x < w() + border_size() &&
loc.y >= -border_size() && loc.y < h() + border_size();
}
void gamemap::set_terrain(const map_location& loc, const t_translation::terrain_code & terrain, const terrain_type_data::merge_mode mode, bool replace_if_failed) {
@ -418,7 +416,7 @@ void gamemap::set_terrain(const map_location& loc, const t_translation::terrain_
(*this)[loc] = new_terrain;
}
std::vector<map_location> gamemap::parse_location_range(const std::string &x, const std::string &y,
std::vector<map_location> gamemap_base::parse_location_range(const std::string &x, const std::string &y,
bool with_border) const
{
std::vector<map_location> res;
@ -463,3 +461,17 @@ std::vector<map_location> gamemap::parse_location_range(const std::string &x, co
}
return res;
}
std::string gamemap_base::to_string() const
{
return t_translation::write_game_map(tiles_, starting_positions_, { 1, 1 }) + "\n";
}
const std::vector<map_location> gamemap_base::starting_positions() const {
int n = num_valid_starting_positions();
std::vector<map_location> res;
for(int i = 1; i <= n; i++) {
res.push_back(starting_position(i));
}
return res;
}

View file

@ -23,6 +23,143 @@ class config;
//class terrain_type_data; Can't forward declare because of enum
// This could be moved to a separate file pair...
class gamemap_base
{
public:
using terrain_code = t_translation::terrain_code;
using terrain_map = t_translation::ter_map;
using location_map = t_translation::starting_positions;
virtual ~gamemap_base();
/** The default border style for a map. */
static const int default_border = 1;
/**
* Maximum number of players supported.
*
* Warning: when you increase this, you need to add
* more definitions to the team_colors.cfg file.
*/
static const int MAX_PLAYERS = 9;
std::string to_string() const;
/** Effective map width. */
int w() const { return total_width() - 2 * border_size(); }
/** Effective map height. */
int h() const { return total_height() - 2 * border_size(); }
/** Size of the map border. */
int border_size() const { return default_border; }
/** Real width of the map, including borders. */
int total_width() const { return tiles_.w; }
/** Real height of the map, including borders */
int total_height() const { return tiles_.h; }
/** Tell if the map is of 0 size. */
bool empty() const
{
return w() <= 0 || h() <= 0;
}
/**
* Tell if a location is on the map.
*/
bool on_board(const map_location& loc) const;
bool on_board_with_border(const map_location& loc) const;
/**
* Clobbers over the terrain at location 'loc', with the given terrain.
* Uses mode and replace_if_failed like merge_terrains().
*/
virtual void set_terrain(const map_location& loc, const terrain_code & terrain, const terrain_type_data::merge_mode mode = terrain_type_data::BOTH, bool replace_if_failed = false) = 0;
/**
* Looks up terrain at a particular location.
*
* Hexes off the map may be looked up, and their 'emulated' terrain will
* also be returned. This allows proper drawing of the edges of the map.
*/
terrain_code get_terrain(const map_location& loc) const;
location_map& special_locations() { return starting_positions_; }
const location_map& special_locations() const { return starting_positions_; }
const std::vector<map_location> starting_positions() const;
void set_special_location(const std::string& id, const map_location& loc);
map_location special_location(const std::string& id) const;
/** Manipulate starting positions of the different sides. */
void set_starting_position(int side, const map_location& loc);
map_location starting_position(int side) const;
/** Counts the number of sides that have valid starting positions on this map */
int num_valid_starting_positions() const;
/** returns the side number of the side starting at position loc, 0 if no such side exists. */
int is_starting_position(const map_location& loc) const;
/** returns the name of the special location at position loc, null if no such location exists. */
const std::string* is_special_location(const map_location& loc) const;
/** Parses ranges of locations into a vector of locations, using this map's dimensions as bounds. */
std::vector<map_location> parse_location_range(const std::string& xvals, const std::string &yvals, bool with_border = false) const;
struct overlay_rule
{
t_translation::ter_list old_;
t_translation::ter_list new_;
terrain_type_data::merge_mode mode_;
std::optional<t_translation::terrain_code> terrain_;
bool use_old_;
bool replace_if_failed_;
overlay_rule()
: old_()
, new_()
, mode_(terrain_type_data::BOTH)
, terrain_()
, use_old_(false)
, replace_if_failed_(false)
{
}
};
/** Overlays another map onto this one at the given position. */
void overlay(const gamemap_base& m, map_location loc, const std::vector<overlay_rule>& rules = std::vector<overlay_rule>(), bool is_odd = false, bool ignore_special_locations = false);
template<typename F>
void for_each_loc(const F& f) const
{
for(int x = 0; x < total_width(); ++x) {
for(int y = 0; y < total_height(); ++y) {
f(map_location{x, y , wml_loc()});
}
}
}
//Doesn't include border.
template<typename F>
void for_each_walkable_loc(const F& f) const
{
for(int x = 0; x < w(); ++x) {
for(int y = 0; y < h(); ++y) {
f(map_location{x, y});
}
}
}
protected:
gamemap_base() = default;
gamemap_base(int w, int h, terrain_code default_ter = terrain_code());
terrain_map& tiles() {return tiles_;}
const terrain_map& tiles() const {return tiles_;}
private:
terrain_map tiles_;
location_map starting_positions_;
};
/**
* Encapsulates the map of the game.
*
@ -30,7 +167,7 @@ class config;
* Each type of terrain is represented by a multiletter terrain code.
* @todo Update for new map-format.
*/
class gamemap
class gamemap : public gamemap_base
{
public:
@ -75,115 +212,26 @@ public:
*/
gamemap(const std::string& data); // throw(incorrect_map_format_error)
virtual ~gamemap();
void read(const std::string& data, const bool allow_invalid = true);
std::string write() const;
struct overlay_rule
{
t_translation::ter_list old_;
t_translation::ter_list new_;
terrain_type_data::merge_mode mode_;
std::optional<t_translation::terrain_code> terrain_;
bool use_old_;
bool replace_if_failed_;
overlay_rule()
: old_()
, new_()
, mode_(terrain_type_data::BOTH)
, terrain_()
, use_old_(false)
, replace_if_failed_(false)
{
}
};
/** Overlays another map onto this one at the given position. */
void overlay(const gamemap& m, map_location loc, const std::vector<overlay_rule>& rules = std::vector<overlay_rule>(), bool is_odd = false, bool ignore_special_locations = false);
static void overlay_impl(
// const but changed via set_terrain
const t_translation::ter_map& m1,
t_translation::starting_positions& m1_st,
const t_translation::ter_map& m2,
const t_translation::starting_positions& m2_st,
std::function<void (const map_location&, const t_translation::terrain_code&, terrain_type_data::merge_mode, bool)> set_terrain,
map_location loc,
const std::vector<overlay_rule>& rules,
bool is_odd,
bool ignore_special_locations);
/** Effective map width, in hexes. */
int w() const { return w_; }
/** Effective map height, in hexes. */
int h() const { return h_; }
/** Size of the map border. */
int border_size() const { return default_border; }
/** Real width of the map, including borders. */
int total_width() const { return tiles_.w; }
/** Real height of the map, including borders */
int total_height() const { return tiles_.h; }
const t_translation::terrain_code operator[](const map_location& loc) const
{
return tiles_.get(loc.x + border_size(), loc.y + border_size());
return tiles().get(loc.x + border_size(), loc.y + border_size());
}
private:
//private method, use set_terrain instead which also updates villages_.
t_translation::terrain_code& operator[](const map_location& loc)
{
return tiles_.get(loc.x + border_size(), loc.y + border_size());
return tiles().get(loc.x + border_size(), loc.y + border_size());
}
public:
/**
* Looks up terrain at a particular location.
*
* Hexes off the map may be looked up, and their 'emulated' terrain will
* also be returned. This allows proper drawing of the edges of the map.
*/
t_translation::terrain_code get_terrain(const map_location& loc) const;
void set_terrain(const map_location& loc, const terrain_code & terrain, const terrain_type_data::merge_mode mode = terrain_type_data::BOTH, bool replace_if_failed = false) override;
/** Writes the terrain at loc to cfg. */
void write_terrain(const map_location &loc, config& cfg) const;
/** Manipulate starting positions of the different sides. */
void set_starting_position(int side, const map_location& loc);
map_location starting_position(int side) const;
void set_special_location(const std::string& id, const map_location& loc);
map_location special_location(const std::string& id) const;
/** returns the side number of the side starting at position loc, 0 if no such side exists. */
const std::string* is_starting_position(const map_location& loc) const;
int num_valid_starting_positions() const;
/**
* Tell if a location is on the map.
*
* Should be called before indexing using [].
* @todo inline for performance? -- Ilor
*/
bool on_board(const map_location& loc) const;
bool on_board_with_border(const map_location& loc) const;
/** Tell if the map is of 0 size. */
bool empty() const
{
return w_ == 0 || h_ == 0;
}
/** Return a list of the locations of villages on the map. */
const std::vector<map_location>& villages() const { return villages_; }
@ -193,55 +241,6 @@ public:
/** Gets the list of terrains. */
const t_translation::ter_list& get_terrain_list() const;
/**
* Clobbers over the terrain at location 'loc', with the given terrain.
* Uses mode and replace_if_failed like merge_terrains().
*/
void set_terrain(const map_location& loc, const t_translation::terrain_code & terrain, const terrain_type_data::merge_mode mode=terrain_type_data::BOTH, bool replace_if_failed = false);
/**
* Maximum number of players supported.
*
* Warning: when you increase this, you need to add
* more definitions to the team_colors.cfg file.
*/
enum { MAX_PLAYERS = 9 };
/** The default border style for a map. */
static const int default_border = 1;
/** Parses ranges of locations into a vector of locations, using this map's dimensions as bounds. */
std::vector<map_location> parse_location_range(const std::string& xvals,
const std::string &yvals, bool with_border = false) const;
using starting_positions = t_translation::starting_positions;
const starting_positions& special_locations() const { return starting_positions_; }
template<typename F>
void for_each_loc(const F& f) const
{
for (int x = -border_size(); x < w() + border_size(); ++x) {
for (int y = -border_size(); y < h() + border_size(); ++y) {
f({ x, y });
}
}
}
//Doesn't include border.
template<typename F>
void for_each_walkable_loc(const F& f) const
{
for (int x = 0; x < w(); ++x) {
for (int y = 0; y < h(); ++y) {
f({ x, y });
}
}
}
protected:
t_translation::ter_map tiles_;
starting_positions starting_positions_;
private:
/**
@ -255,8 +254,4 @@ private:
protected:
std::vector<map_location> villages_;
/** Sizes of the map area. */
int w_;
int h_;
};

View file

@ -79,6 +79,7 @@
#include "scripting/lua_pathfind_cost_calculator.hpp"
#include "scripting/lua_race.hpp"
#include "scripting/lua_team.hpp"
#include "scripting/lua_terrainmap.hpp"
#include "scripting/lua_unit_type.hpp"
#include "scripting/push_check.hpp"
#include "synced_commands.hpp"
@ -211,83 +212,6 @@ std::vector<int> game_lua_kernel::get_sides_vector(const vconfig& cfg)
return filter.get_teams();
}
static int special_locations_len(lua_State *L)
{
lua_pushnumber(L, lua_kernel_base::get_lua_kernel<game_lua_kernel>(L).map().special_locations().size());
return 1;
}
static int special_locations_next(lua_State *L)
{
const t_translation::starting_positions::left_map& left = lua_kernel_base::get_lua_kernel<game_lua_kernel>(L).map().special_locations().left;
t_translation::starting_positions::left_const_iterator it;
if (lua_isnoneornil(L, 2)) {
it = left.begin();
}
else {
it = left.find(luaL_checkstring(L, 2));
if (it == left.end()) {
return 0;
}
++it;
}
if (it == left.end()) {
return 0;
}
lua_pushstring(L, it->first.c_str());
luaW_pushlocation(L, it->second);
return 2;
}
static int special_locations_pairs(lua_State *L)
{
lua_pushcfunction(L, &special_locations_next);
lua_pushvalue(L, -2);
lua_pushnil(L);
return 3;
}
static int special_locations_index(lua_State *L)
{
const t_translation::starting_positions::left_map& left = lua_kernel_base::get_lua_kernel<game_lua_kernel>(L).map().special_locations().left;
auto it = left.find(luaL_checkstring(L, 2));
if (it == left.end()) {
return 0;
}
else {
luaW_pushlocation(L, it->second);
return 1;
}
}
static int special_locations_newindex(lua_State *L)
{
lua_pushstring(L, "special locations cannot be modified using wesnoth.special_locations");
return lua_error(L);
}
static void push_locations_table(lua_State *L)
{
lua_newtable(L); // The functor table
lua_newtable(L); // The functor metatable
lua_pushstring(L, "__len");
lua_pushcfunction(L, &special_locations_len);
lua_rawset(L, -3);
lua_pushstring(L, "__index");
lua_pushcfunction(L, &special_locations_index);
lua_rawset(L, -3);
lua_pushstring(L, "__newindex");
lua_pushcfunction(L, &special_locations_newindex);
lua_rawset(L, -3);
lua_pushstring(L, "__pairs");
lua_pushcfunction(L, &special_locations_pairs);
lua_rawset(L, -3);
lua_setmetatable(L, -2); // Apply the metatable to the functor table
}
namespace {
/**
* Temporary entry to a queued_event stack
@ -939,158 +863,14 @@ int game_lua_kernel::intf_lock_view(lua_State *L)
return 0;
}
/**
* Gets a terrain code.
* - Arg 1: map location.
* - Ret 1: string.
*/
int game_lua_kernel::intf_get_terrain(lua_State *L)
{
map_location loc = luaW_checklocation(L, 1);
const t_translation::terrain_code& t = board().map().
get_terrain(loc);
lua_pushstring(L, t_translation::write_terrain_code(t).c_str());
return 1;
}
/**
* Sets a terrain code.
* - Arg 1: map location.
* - Arg 2: terrain code string.
* - Arg 3: layer: (overlay|base|both, default=both)
* - Arg 4: replace_if_failed, default = no
*/
int game_lua_kernel::intf_set_terrain(lua_State *L)
{
map_location loc = luaW_checklocation(L, 1);
std::string t_str(luaL_checkstring(L, 2));
std::string mode_str = "both";
bool replace_if_failed = false;
if (!lua_isnone(L, 3)) {
if (!lua_isnil(L, 3)) {
mode_str = luaL_checkstring(L, 3);
}
if(!lua_isnoneornil(L, 4)) {
replace_if_failed = luaW_toboolean(L, 4);
}
}
bool result = board().change_terrain(loc, t_str, mode_str, replace_if_failed);
if (game_display_) {
game_display_->needs_rebuild(result);
}
return 0;
}
/**
* Reaplces part of the map.
* - Arg 1: map location.
* - Arg 2: map data string.
* - Arg 3: table for optional named arguments
* - is_odd: boolen, if Arg2 has the odd mapo format (as if it was cut from a odd map location)
* - ignore_special_locations: boolean
* - rules: table of tables
*/
int game_lua_kernel::intf_terrain_mask(lua_State *L)
{
map_location loc = luaW_checklocation(L, 1);
std::string t_str(luaL_checkstring(L, 2));
bool is_odd = false;
bool ignore_special_locations = false;
std::vector<gamemap::overlay_rule> rules;
if(lua_istable(L, 3)) {
if(luaW_tableget(L, 3, "is_odd")) {
is_odd = luaW_toboolean(L, -1);
lua_pop(L, 1);
}
if(luaW_tableget(L, 3, "ignore_special_locations")) {
ignore_special_locations = luaW_toboolean(L, -1);
lua_pop(L, 1);
}
if(luaW_tableget(L, 3, "rules")) {
//todo: reduce code dublication by using read_rules_vector.
if(!lua_istable(L, -1)) {
return luaL_argerror(L, 3, "rules must be a table");
}
for (int i = 1, i_end = lua_rawlen(L, -1); i <= i_end; ++i)
{
lua_rawgeti(L, -1, i);
if(!lua_istable(L, -1)) {
return luaL_argerror(L, 3, "rules must be a table of tables");
}
rules.push_back(gamemap::overlay_rule());
auto& rule = rules.back();
if(luaW_tableget(L, -1, "old")) {
rule.old_ = t_translation::read_list(luaW_tostring(L, -1));
lua_pop(L, 1);
}
if(luaW_tableget(L, -1, "new")) {
rule.new_ = t_translation::read_list(luaW_tostring(L, -1));
lua_pop(L, 1);
}
if(luaW_tableget(L, -1, "layer")) {
auto str = luaW_tostring(L, -1);
rule.mode_ = str == "base" ? terrain_type_data::BASE : (str == "overlay" ? terrain_type_data::OVERLAY : terrain_type_data::BOTH);
lua_pop(L, 1);
}
if(luaW_tableget(L, -1, "terrain")) {
const t_translation::ter_list terrain = t_translation::read_list(luaW_tostring(L, -1));
if(!terrain.empty()) {
rule.terrain_ = terrain[0];
}
lua_pop(L, 1);
}
if(luaW_tableget(L, -1, "use_old")) {
rule.use_old_ = luaW_toboolean(L, -1);
lua_pop(L, 1);
}
if(luaW_tableget(L, -1, "replace_if_failed")) {
rule.replace_if_failed_ = luaW_toboolean(L, -1);
lua_pop(L, 1);
}
lua_pop(L, 1);
}
lua_pop(L, 1);
}
}
gamemap mask_map("");
mask_map.read(t_str, false);
board().map().overlay(mask_map, loc, rules, is_odd, ignore_special_locations);
for(team& t : board().teams()) {
t.fix_villages(board().map());
}
if (game_display_) {
game_display_->needs_rebuild(true);
}
return 0;
}
/**
* Gets details about a terrain.
* - Arg 1: terrain code string.
* - Ret 1: table.
*/
int game_lua_kernel::intf_get_terrain_info(lua_State *L)
int game_lua_kernel::impl_get_terrain_info(lua_State *L)
{
char const *m = luaL_checkstring(L, 1);
char const *m = luaL_checkstring(L, 2);
t_translation::terrain_code t = t_translation::read_terrain_code(m);
if (t == t_translation::NONE_TERRAIN) return 0;
const terrain_type& info = board().map().tdata()->get_terrain_info(t);
@ -1272,22 +1052,6 @@ int game_lua_kernel::intf_set_village_owner(lua_State *L)
return 0;
}
/**
* Returns the map size.
* - Ret 1: width.
* - Ret 2: height.
* - Ret 3: border size.
*/
int game_lua_kernel::intf_get_map_size(lua_State *L)
{
const gamemap &map = board().map();
lua_pushinteger(L, map.w());
lua_pushinteger(L, map.h());
lua_pushinteger(L, map.border_size());
return 3;
}
/**
* Returns the currently overed tile.
* - Ret 1: x.
@ -1468,6 +1232,10 @@ int game_lua_kernel::impl_current_get(lua_State *L)
return_int_attrib("turn", play_controller_.turn());
return_string_attrib("synced_state", synced_state());
return_bool_attrib("user_can_invoke_commands", !play_controller_.is_lingering() && play_controller_.gamestate().init_side_done() && !events::commands_disabled && gamedata().phase() == game_data::PLAY);
if(strcmp(m, "map") == 0) {
return intf_terrainmap_get(L);
}
if (strcmp(m, "event_context") == 0)
{
@ -3484,7 +3252,7 @@ int game_lua_kernel::intf_delay(lua_State *L)
return 0;
}
int game_lua_kernel::intf_label(lua_State *L)
int game_lua_kernel::intf_label(lua_State *L, bool add)
{
if (game_display_) {
vconfig cfg(luaW_checkvconfig(L, 1));
@ -3493,12 +3261,48 @@ int game_lua_kernel::intf_label(lua_State *L)
terrain_label label(screen.labels(), cfg.get_config());
screen.labels().set_label(label.location(), label.text(), label.creator(), label.team_name(), label.color(),
screen.labels().set_label(label.location(), add ? label.text() : "", label.creator(), label.team_name(), label.color(),
label.visible_in_fog(), label.visible_in_shroud(), label.immutable(), label.category(), label.tooltip());
}
return 0;
}
int game_lua_kernel::intf_get_label(lua_State* L)
{
if(game_display_) {
game_display &screen = *game_display_;
auto loc = luaW_checklocation(L, 1);
const terrain_label* label = nullptr;
switch(lua_type(L, 2)) {
// Missing 2nd argument - get global label
case LUA_TNONE: case LUA_TNIL:
label = screen.labels().get_label(loc, "");
break;
// Side number - get label belonging to that side's team
case LUA_TNUMBER:
if(size_t n = luaL_checkinteger(L, 2); n > 0 && n <= teams().size()) {
label = screen.labels().get_label(loc, teams().at(n - 1).team_name());
}
break;
// String - get label belonging to the team with that name
case LUA_TSTRING:
label = screen.labels().get_label(loc, luaL_checkstring(L, 2));
break;
// Side userdata - get label belonging to that side's team
case LUA_TUSERDATA:
label = screen.labels().get_label(loc, luaW_checkteam(L, 2).team_name());
break;
}
if(label) {
config cfg;
label->write(cfg);
luaW_pushconfig(L, cfg);
return 1;
}
}
return 0;
}
int game_lua_kernel::intf_redraw(lua_State *L)
{
if (game_display_) {
@ -4217,8 +4021,6 @@ game_lua_kernel::game_lua_kernel(game_state & gs, play_controller & pc, reports
{ "sound_volume", &intf_sound_volume },
{ "unsynced", &intf_do_unsynced },
{ "add_event_handler", &dispatch<&game_lua_kernel::intf_add_event > },
{ "add_fog", &dispatch2<&game_lua_kernel::intf_toggle_fog, false > },
{ "add_time_area", &dispatch<&game_lua_kernel::intf_add_time_area > },
{ "add_sound_source", &dispatch<&game_lua_kernel::intf_add_sound_source > },
{ "allow_end_turn", &dispatch<&game_lua_kernel::intf_allow_end_turn > },
{ "allow_undo", &dispatch<&game_lua_kernel::intf_allow_undo > },
@ -4234,48 +4036,31 @@ game_lua_kernel::game_lua_kernel(game_state & gs, play_controller & pc, reports
{ "fire_event_by_id", &dispatch2<&game_lua_kernel::intf_fire_event, true > },
{ "get_all_vars", &dispatch<&game_lua_kernel::intf_get_all_vars > },
{ "get_end_level_data", &dispatch<&game_lua_kernel::intf_get_end_level_data > },
{ "get_locations", &dispatch<&game_lua_kernel::intf_get_locations > },
{ "get_map_size", &dispatch<&game_lua_kernel::intf_get_map_size > },
{ "get_sound_source", &dispatch<&game_lua_kernel::intf_get_sound_source > },
{ "get_terrain", &dispatch<&game_lua_kernel::intf_get_terrain > },
{ "get_terrain_info", &dispatch<&game_lua_kernel::intf_get_terrain_info > },
{ "get_time_of_day", &dispatch<&game_lua_kernel::intf_get_time_of_day > },
{ "get_max_liminal_bonus", &dispatch<&game_lua_kernel::intf_get_max_liminal_bonus > },
{ "get_variable", &dispatch<&game_lua_kernel::intf_get_variable > },
{ "get_villages", &dispatch<&game_lua_kernel::intf_get_villages > },
{ "get_village_owner", &dispatch<&game_lua_kernel::intf_get_village_owner > },
{ "label", &dispatch<&game_lua_kernel::intf_label > },
{ "log_replay", &dispatch<&game_lua_kernel::intf_log_replay > },
{ "log", &dispatch<&game_lua_kernel::intf_log > },
{ "match_location", &dispatch<&game_lua_kernel::intf_match_location > },
{ "message", &dispatch<&game_lua_kernel::intf_message > },
{ "open_help", &dispatch<&game_lua_kernel::intf_open_help > },
{ "play_sound", &dispatch<&game_lua_kernel::intf_play_sound > },
{ "print", &dispatch<&game_lua_kernel::intf_print > },
{ "redraw", &dispatch<&game_lua_kernel::intf_redraw > },
{ "remove_event_handler", &dispatch<&game_lua_kernel::intf_remove_event > },
{ "remove_fog", &dispatch2<&game_lua_kernel::intf_toggle_fog, true > },
{ "remove_time_area", &dispatch<&game_lua_kernel::intf_remove_time_area > },
{ "remove_sound_source", &dispatch<&game_lua_kernel::intf_remove_sound_source > },
{ "replace_schedule", &dispatch<&game_lua_kernel::intf_replace_schedule > },
{ "select_hex", &dispatch<&game_lua_kernel::intf_select_hex > },
{ "set_time_of_day", &dispatch<&game_lua_kernel::intf_set_time_of_day > },
{ "is_fogged", &dispatch2<&game_lua_kernel::intf_get_fog_or_shroud, true > },
{ "is_shrouded", &dispatch2<&game_lua_kernel::intf_get_fog_or_shroud, false > },
{ "set_end_campaign_credits", &dispatch<&game_lua_kernel::intf_set_end_campaign_credits > },
{ "set_end_campaign_text", &dispatch<&game_lua_kernel::intf_set_end_campaign_text > },
{ "create_side", &dispatch<&game_lua_kernel::intf_create_side > },
{ "set_next_scenario", &dispatch<&game_lua_kernel::intf_set_next_scenario > },
{ "set_terrain", &dispatch<&game_lua_kernel::intf_set_terrain > },
{ "set_variable", &dispatch<&game_lua_kernel::intf_set_variable > },
{ "set_village_owner", &dispatch<&game_lua_kernel::intf_set_village_owner > },
{ "simulate_combat", &dispatch<&game_lua_kernel::intf_simulate_combat > },
{ "synchronize_choice", &intf_synchronize_choice },
{ "synchronize_choices", &intf_synchronize_choices },
{ "terrain_mask", &dispatch<&game_lua_kernel::intf_terrain_mask > },
{ "teleport", &dispatch<&game_lua_kernel::intf_teleport > },
{ "place_shroud", &dispatch2<&game_lua_kernel::intf_shroud_op, true > },
{ "remove_shroud", &dispatch2<&game_lua_kernel::intf_shroud_op, false > },
{ nullptr, nullptr }
};lua_getglobal(L, "wesnoth");
if (!lua_istable(L,-1)) {
@ -4317,6 +4102,22 @@ game_lua_kernel::game_lua_kernel(game_state & gs, play_controller & pc, reports
// Create the unit_types table
cmd_log_ << lua_unit_type::register_table(L);
// Create the unit_types table
cmd_log_ << lua_terrainmap::register_metatables(L);
// Create the unit_types table
cmd_log_ << "Adding terrain_types table...\n";
lua_getglobal(L, "wesnoth");
lua_newuserdatauv(L, 0, 0);
lua_createtable(L, 0, 2);
lua_pushcfunction(L, &dispatch<&game_lua_kernel::impl_get_terrain_info>);
lua_setfield(L, -2, "__index");
lua_pushstring(L, "terrain types");
lua_setfield(L, -2, "__metatable");
lua_setmetatable(L, -2);
lua_setfield(L, -2, "terrain_types");
lua_pop(L, 1);
// Create the ai elements table.
cmd_log_ << "Adding ai elements table...\n";
@ -4341,6 +4142,41 @@ game_lua_kernel::game_lua_kernel(game_state & gs, play_controller & pc, reports
lua_pushcfunction(L, &lua_common::intf_tovconfig);
lua_setfield(L, -2, "tovconfig");
lua_pop(L, 1);
// Add functions to the map module
luaW_getglobal(L, "wesnoth", "map");
static luaL_Reg const map_callbacks[] {
// Map methods
{"terrain_mask", &intf_terrain_mask},
{"on_board", &intf_on_board},
{"on_border", &intf_on_border},
{"iter", &intf_terrainmap_iter},
// Shroud operations
{"place_shroud", &dispatch2<&game_lua_kernel::intf_shroud_op, true>},
{"remove_shroud", &dispatch2<&game_lua_kernel::intf_shroud_op, false>},
{"is_shrouded", &dispatch2<&game_lua_kernel::intf_get_fog_or_shroud, false>},
// Fog operations
{"place_fog", &dispatch2<&game_lua_kernel::intf_toggle_fog, false>},
{"remove_fog", &dispatch2<&game_lua_kernel::intf_toggle_fog, true>},
{"is_fogged", &dispatch2<&game_lua_kernel::intf_get_fog_or_shroud, true>},
// Village operations
{"get_owner", &dispatch<&game_lua_kernel::intf_get_village_owner>},
{"set_owner", &dispatch<&game_lua_kernel::intf_set_village_owner>},
// Label operations
{"add_label", &dispatch2<&game_lua_kernel::intf_label, true>},
{"remove_label", &dispatch2<&game_lua_kernel::intf_label, false>},
{"get_label", &dispatch<&game_lua_kernel::intf_get_label>},
// Time area operations
{"place_area", &dispatch<&game_lua_kernel::intf_add_time_area>},
{"remove_area", &dispatch<&game_lua_kernel::intf_remove_time_area>},
// Filters
{"find", &dispatch<&game_lua_kernel::intf_get_locations>},
{"matches", &dispatch<&game_lua_kernel::intf_match_location>},
{"replace_if_failed", intf_replace_if_failed},
{ nullptr, nullptr }
};
luaL_setfuncs(L, map_callbacks, 0);
lua_pop(L, 1);
// Create the units module
cmd_log_ << "Adding units module...\n";
@ -4480,8 +4316,6 @@ game_lua_kernel::game_lua_kernel(game_state & gs, play_controller & pc, reports
lua_getglobal(L, "wesnoth");
lua_newtable(L);
lua_setfield(L, -2, "game_events");
push_locations_table(L);
lua_setfield(L, -2, "special_locations");
lua_pop(L, 1);
// Create the theme_items table.

View file

@ -92,10 +92,7 @@ class game_lua_kernel : public lua_kernel_base
int intf_unit_ability(lua_State *L);
int intf_view_locked(lua_State *L);
int intf_lock_view(lua_State *L);
int intf_get_terrain(lua_State *L);
int intf_set_terrain(lua_State *L);
int intf_terrain_mask(lua_State *L);
int intf_get_terrain_info(lua_State *L);
int impl_get_terrain_info(lua_State *L);
int intf_get_time_of_day(lua_State *L);
int intf_get_max_liminal_bonus(lua_State *L);
int intf_get_village_owner(lua_State *L);
@ -154,7 +151,8 @@ class game_lua_kernel : public lua_kernel_base
int intf_remove_event(lua_State *L);
int intf_color_adjust(lua_State *L);
int intf_delay(lua_State *L);
int intf_label(lua_State *L);
int intf_label(lua_State *L, bool add);
int intf_get_label(lua_State* L);
int intf_redraw(lua_State *L);
int intf_replace_schedule(lua_State *l);
int intf_set_time_of_day(lua_State *L);

View file

@ -544,8 +544,9 @@ lua_kernel_base::lua_kernel_base()
{ "vector_diff", &lua_map_location::intf_vector_diff },
{ "vector_negation", &lua_map_location::intf_vector_negation },
{ "rotate_right_around_center", &lua_map_location::intf_rotate_right_around_center },
{ "tiles_adjacent", &lua_map_location::intf_tiles_adjacent },
{ "get_adjacent_tiles", &lua_map_location::intf_get_adjacent_tiles },
{ "are_hexes_adjacent", &lua_map_location::intf_tiles_adjacent },
{ "get_adjacent_hexes", &lua_map_location::intf_get_adjacent_tiles },
{ "get_hexes_in_radius", &lua_map_location::intf_get_tiles_in_radius },
{ "distance_between", &lua_map_location::intf_distance_between },
{ "get_in_basis_N_NE", &lua_map_location::intf_get_in_basis_N_NE },
{ "get_relative_dir", &lua_map_location::intf_get_relative_dir },

View file

@ -14,8 +14,10 @@
#include "scripting/lua_map_location_ops.hpp"
#include "scripting/lua_common.hpp"
#include "scripting/push_check.hpp"
#include "map/location.hpp"
#include "pathutils.hpp"
#include <string>
#include <utility>
@ -87,7 +89,7 @@ int intf_vector_diff(lua_State* L)
{
map_location l1, l2;
if(!luaW_tolocation(L, 1, l1) || !luaW_tolocation(L, 2, l2)) {
lua_pushstring(L, "vector_sum: requires two locations");
lua_pushstring(L, "vector_diff: requires two locations");
return lua_error(L);
}
@ -137,7 +139,7 @@ int intf_tiles_adjacent(lua_State* L)
{
map_location l1, l2;
if(!luaW_tolocation(L, 1, l1) || !luaW_tolocation(L, 2, l2)) {
lua_pushstring(L, "vector_sum: requires two locations");
lua_pushstring(L, "tiles_adjacent: requires two locations");
return lua_error(L);
}
@ -164,6 +166,26 @@ int intf_get_adjacent_tiles(lua_State* L)
return 6;
}
/**
* Expose map_location get_tiles_in_radius
* - Arg 1: A location
* - Ret: The locations
*/
int intf_get_tiles_in_radius(lua_State* L)
{
map_location l1;
if(!luaW_tolocation(L, 1, l1)) {
return luaL_argerror(L, 1, "expected a location");
}
int radius = luaL_checkinteger(L, 2);
std::vector<map_location> locs;
get_tiles_in_radius(l1, radius, locs);
lua_push(L, locs);
return 1;
}
/**
* Expose map_location distance_between
* - Args 1, 2: Two locations

View file

@ -30,6 +30,7 @@ int intf_vector_negation(lua_State*);
int intf_rotate_right_around_center(lua_State*);
int intf_tiles_adjacent(lua_State*);
int intf_get_adjacent_tiles(lua_State*);
int intf_get_tiles_in_radius(lua_State*);
int intf_distance_between(lua_State*);
int intf_get_in_basis_N_NE(lua_State*);
int intf_get_relative_dir(lua_State*);

View file

@ -40,9 +40,9 @@ static lg::log_domain log_scripting_lua_mapgen("scripting/lua/mapgen");
#define ERR_LMG LOG_STREAM(err, log_scripting_lua_mapgen)
//general helper functions for parsing
struct inalid_lua_argument : public std::exception
struct invalid_lua_argument : public std::exception
{
explicit inalid_lua_argument(const std::string& msg) : errormessage_(msg) {}
explicit invalid_lua_argument(const std::string& msg) : errormessage_(msg) {}
const char* what() const noexcept { return errormessage_.c_str(); }
private:
@ -184,6 +184,11 @@ static int luaW_push_locationset(lua_State* L, const std::set<map_location>& loc
static std::set<map_location> luaW_to_locationset(lua_State* L, int index)
{
std::set<map_location> res;
map_location single;
if(luaW_tolocation(L, index, single)) {
res.insert(single);
return res;
}
lua_pushvalue(L, index);
size_t len = lua_rawlen(L, -1);
for(size_t i = 0; i != len; ++i) {
@ -199,7 +204,7 @@ class filter_impl
{
public:
filter_impl() {};
virtual bool matches(const mapgen_gamemap& m, map_location l) = 0;
virtual bool matches(const gamemap_base& m, map_location l) = 0;
virtual ~filter_impl() {};
};
@ -234,7 +239,7 @@ public:
LOG_LMG << "created and filter\n";
}
bool matches(const mapgen_gamemap& m, map_location l) override
bool matches(const gamemap_base& m, map_location l) override
{
LOG_MATCHES(and);
for(const auto& pfilter : list_) {
@ -255,7 +260,7 @@ public:
LOG_LMG << "created or filter\n";
}
bool matches(const mapgen_gamemap& m, map_location l) override
bool matches(const gamemap_base& m, map_location l) override
{
LOG_MATCHES(or);
for(const auto& pfilter : list_) {
@ -276,7 +281,7 @@ public:
LOG_LMG << "created nand filter\n";
}
bool matches(const mapgen_gamemap& m, map_location l) override
bool matches(const gamemap_base& m, map_location l) override
{
LOG_MATCHES(nand);
for(const auto& pfilter : list_) {
@ -297,7 +302,7 @@ public:
LOG_LMG << "created nor filter\n";
}
bool matches(const mapgen_gamemap& m, map_location l) override
bool matches(const gamemap_base& m, map_location l) override
{
LOG_MATCHES(nor);
for(const auto& pfilter : list_) {
@ -322,7 +327,7 @@ public:
lua_pop(L, 1);
}
bool matches(const mapgen_gamemap& m, map_location l) override
bool matches(const gamemap_base& m, map_location l) override
{
LOG_MATCHES(cached);
int cache_size = 2 * m.total_width() * m.total_height();
@ -357,7 +362,7 @@ public:
filter_ = parse_range(luaW_tostring(L, -1));
lua_pop(L, 1);
}
bool matches(const mapgen_gamemap&, map_location l) override
bool matches(const gamemap_base&, map_location l) override
{
LOG_MATCHES(x);
return l.x >= 0 && l.x < int(filter_.size()) && filter_[l.x];
@ -377,7 +382,7 @@ public:
lua_pop(L, 1);
}
bool matches(const mapgen_gamemap&, map_location l) override
bool matches(const gamemap_base&, map_location l) override
{
LOG_MATCHES(y);
return l.y >= 0 && l.y < int(filter_.size()) && filter_[l.y];
@ -394,10 +399,10 @@ public:
LOG_LMG << "creating onborder filter\n";
}
bool matches(const mapgen_gamemap& m, map_location l) override
bool matches(const gamemap_base& m, map_location l) override
{
LOG_MATCHES(onborder);
return !m.on_map_noborder(l);
return !m.on_board(l);
}
};
@ -414,10 +419,10 @@ public:
lua_pop(L, 1);
}
bool matches(const mapgen_gamemap& m, map_location l) override
bool matches(const gamemap_base& m, map_location l) override
{
LOG_MATCHES(terrain);
const t_translation::terrain_code letter = m[l];
const t_translation::terrain_code letter = m.get_terrain(l);
return t_translation::terrain_matches(letter, filter_);
}
@ -451,7 +456,7 @@ public:
lua_pop(L, 1);
}
bool matches(const mapgen_gamemap& m, map_location l) override
bool matches(const gamemap_base& m, map_location l) override
{
LOG_MATCHES(adjacent);
int count = 0;
@ -459,7 +464,7 @@ public:
offset_list_t& offsets = (l.wml_x() & 1) ? odd_offsets_ : even_offsets_;
for(const auto& offset : offsets) {
map_location ad = {l.x + offset.first, l.y + offset.second};
if(m.on_map(ad) && filter_->matches(m, ad)) {
if(m.on_board_with_border(ad) && filter_->matches(m, ad)) {
if(accepted_counts_.size() == 0) {
return true;
}
@ -496,7 +501,7 @@ public:
}
set_ = &insert_res.first->second;
}
bool matches(const mapgen_gamemap&, map_location l) override
bool matches(const gamemap_base&, map_location l) override
{
LOG_MATCHES(findin);
if(set_) {
@ -529,14 +534,14 @@ public:
lua_pop(L, 1);
}
bool matches(const mapgen_gamemap& m, map_location l) override
bool matches(const gamemap_base& m, map_location l) override
{
LOG_MATCHES(radius);
std::set<map_location> result;
get_tiles_radius({{ l }}, radius_, result,
[&](const map_location& l) {
return m.on_map(l);
return m.on_board_with_border(l);
},
[&](const map_location& l) {
return !filter_radius_ || filter_radius_->matches(m, l);
@ -573,7 +578,7 @@ public:
ERR_LMG << "formula error" << e.what() << "\n";
}
}
bool matches(const mapgen_gamemap&, map_location l) override
bool matches(const gamemap_base&, map_location l) override
{
LOG_MATCHES(formula);
try {
@ -589,7 +594,7 @@ public:
};
// todo: maybe invent a gerneral macro for this string_switch implementation.
enum filter_keys { F_AND, F_OR, F_NAND, F_NOR, F_X, F_Y, F_FIND_IN, F_ADJACENT, F_TERRAIN, F_RADUIS, F_FORMULA, F_CACHED };
enum filter_keys { F_AND, F_OR, F_NAND, F_NOR, F_X, F_Y, F_FIND_IN, F_ADJACENT, F_TERRAIN, F_RADIUS, F_FORMULA, F_CACHED };
//todoc++14: std::unordered_map doesn'tsupport herterogrnous lookup.
//todo consider renaming and -> all ,or ->any, nor -> none, nand -> notall
static const std::unordered_map<std::string, filter_keys> keys {
@ -604,14 +609,14 @@ static const std::unordered_map<std::string, filter_keys> keys {
{ "terrain", F_TERRAIN },
{ "cached", F_CACHED },
{ "formula", F_FORMULA },
{ "radius", F_RADUIS }
{ "radius", F_RADIUS }
};
std::unique_ptr<filter_impl> build_filter(lua_State* L, int res_index, knows_sets_t& ks)
{
LOG_LMG << "buildfilter: start\n";
if(!lua_istable(L, -1)) {
throw inalid_lua_argument("buildfilter: expected table");
throw invalid_lua_argument("buildfilter: expected table");
}
lua_rawgeti(L, -1, 1);
std::string s = std::string(luaW_tostring(L, -1));
@ -619,7 +624,7 @@ std::unique_ptr<filter_impl> build_filter(lua_State* L, int res_index, knows_set
auto it = keys.find(s);
if(it == keys.end()) {
//fixme use proper exception type.
throw inalid_lua_argument(std::string("buildfilter: invalid filter type ") + s);
throw invalid_lua_argument(std::string("buildfilter: invalid filter type ") + s);
}
auto key = it->second;
lua_pop(L, 1);
@ -643,7 +648,7 @@ std::unique_ptr<filter_impl> build_filter(lua_State* L, int res_index, knows_set
return std::make_unique<adjacent_filter>(L, res_index, ks);
case F_TERRAIN:
return std::make_unique<terrain_filter>(L, res_index, ks);
case F_RADUIS:
case F_RADIUS:
return std::make_unique<radius_filter>(L, res_index, ks);
case F_CACHED:
return std::make_unique<cached_filter>(L, res_index, ks);
@ -672,7 +677,7 @@ filter::filter(lua_State* L, int data_index, int res_index)
LOG_LMG << "finished creating filter object\n";
}
bool filter::matches(const mapgen_gamemap& m, map_location l)
bool filter::matches(const gamemap_base& m, map_location l)
{
log_scope("filter::matches");
return impl_->matches(m, l);
@ -685,7 +690,7 @@ filter::~filter()
}
static int intf_mg_get_locations_part2(lua_State* L, mapgen_gamemap& m, lua_mapgen::filter& f)
static int intf_mg_get_locations_part2(lua_State* L, gamemap_base& m, lua_mapgen::filter& f)
{
location_set res;
LOG_LMG << "map:get_locations vaidargs\n";
@ -717,7 +722,7 @@ int intf_mg_get_locations(lua_State* L)
{
//todo: create filter form table if needed
LOG_LMG << "map:get_locations\n";
mapgen_gamemap& m = luaW_checkterrainmap(L, 1);
gamemap_base& m = luaW_checkterrainmap(L, 1);
if(luaW_is_mgfilter(L, 2)) {
lua_mapgen::filter& f = luaW_check_mgfilter(L, 2);
return intf_mg_get_locations_part2(L, m, f);
@ -733,14 +738,14 @@ int intf_mg_get_locations(lua_State* L)
int intf_mg_get_tiles_radius(lua_State* L)
{
mapgen_gamemap& m = luaW_checkterrainmap(L, 1);
lua_mapgen::filter& f = luaW_check_mgfilter(L, 3);
gamemap_base& m = luaW_checkterrainmap(L, 1);
location_set s = luaW_to_locationset(L, 2);
int r = luaL_checkinteger(L, 3);
lua_mapgen::filter& f = luaW_check_mgfilter(L, 4);
location_set res;
int r = luaL_checkinteger(L, 4);
get_tiles_radius(std::move(s), r, res,
[&](const map_location& l) {
return m.on_map(l);
return m.on_board_with_border(l);
},
[&](const map_location& l) {
return f.matches(m, l);
@ -790,7 +795,7 @@ static lua_mapgen::filter* luaW_push_mgfilter(lua_State *L, T&&... params)
/**
* Create a filter.
*/
int intf_terainfilter_create(lua_State *L)
int intf_terrainfilter_create(lua_State *L)
{
try {
int res_index = 0;
@ -804,7 +809,7 @@ int intf_terainfilter_create(lua_State *L)
luaW_push_mgfilter(L, std::move(res));
return 1;
}
catch(const inalid_lua_argument& e) {
catch(const invalid_lua_argument& e) {
return luaL_argerror(L, 1, e.what());
}
}
@ -816,7 +821,7 @@ int intf_terainfilter_create(lua_State *L)
* - Arg 2: string containing the name of the property.
* - Ret 1: something containing the attribute.
*/
static int impl_terainfilter_get(lua_State *L)
static int impl_terrainfilter_get(lua_State *L)
{
lua_mapgen::filter& f = luaW_check_mgfilter(L, 1);
UNUSED(f);
@ -829,7 +834,7 @@ static int impl_terainfilter_get(lua_State *L)
* - Arg 2: string containing the name of the property.
* - Arg 3: something containing the attribute.
*/
static int impl_terainfilter_set(lua_State *L)
static int impl_terrainfilter_set(lua_State *L)
{
lua_mapgen::filter& f = luaW_check_mgfilter(L, 1);
UNUSED(f);
@ -852,7 +857,7 @@ static int intf_clearcache(lua_State *L)
/**
* Destroys a map object before it is collected (__gc metamethod).
*/
static int impl_terainfilter_collect(lua_State *L)
static int impl_terrainfilter_collect(lua_State *L)
{
lua_mapgen::filter& f = luaW_check_mgfilter(L, 1);
f.~filter();
@ -868,15 +873,15 @@ namespace lua_terrainfilter {
cmd_out << "Adding terrainmamap metatable...\n";
luaL_newmetatable(L, terrinfilterKey);
lua_pushcfunction(L, impl_terainfilter_collect);
lua_pushcfunction(L, impl_terrainfilter_collect);
lua_setfield(L, -2, "__gc");
lua_pushcfunction(L, impl_terainfilter_get);
lua_pushcfunction(L, impl_terrainfilter_get);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, impl_terainfilter_set);
lua_pushcfunction(L, impl_terrainfilter_set);
lua_setfield(L, -2, "__newindex");
lua_pushstring(L, "terrain_filter");
lua_setfield(L, -2, "__metatable");
// terainmap methods
// terrainmap methods
lua_pushcfunction(L, intf_clearcache);
lua_setfield(L, -2, "clear_cache");

View file

@ -18,6 +18,7 @@
#include <memory>
#include <map>
#include <set>
#include "map/map.hpp"
#include "scripting/lua_common.hpp"
struct lua_State;
@ -45,7 +46,7 @@ namespace lua_mapgen
~filter();
bool matches(const mapgen_gamemap& m, map_location l);
bool matches(const gamemap_base& m, map_location l);
//todo: add a clear cache function.
private:
std::map<std::string, std::set<map_location>> known_sets_;
@ -63,7 +64,7 @@ lua_mapgen::filter& luaW_check_mgfilter(lua_State *L, int index);
void lua_mgfilter_setmetatable(lua_State *L);
int intf_terainfilter_create(lua_State *L);
int intf_terrainfilter_create(lua_State *L);
int intf_mg_get_locations(lua_State* L);
int intf_mg_get_tiles_radius(lua_State* L);

View file

@ -22,6 +22,9 @@
#include "scripting/lua_common.hpp"
#include "scripting/push_check.hpp"
#include "scripting/game_lua_kernel.hpp"
#include "resources.hpp"
#include "game_board.hpp"
#include "play_controller.hpp"
#include "lua/lauxlib.h"
#include "lua/lua.h"
@ -30,77 +33,26 @@ static lg::log_domain log_scripting_lua("scripting/lua");
#define LOG_LUA LOG_STREAM(info, log_scripting_lua)
#define ERR_LUA LOG_STREAM(err, log_scripting_lua)
static const char terrinmapKey[] = "terrainmap";
static const char maplocationKey[] = "special_locations";
static const char terrainmapKey[] = "terrain map";
static const char maplocationKey[] = "special locations";
static const char mapReplaceIfFailedKey[] = "replace_if_failed terrain code";
namespace replace_if_failed_idx {
enum {CODE = 1, MODE = 2};
}
using std::string_view;
//////// SPECIAL LOCATION ////////
bool luaW_isslocs(lua_State* L, int index)
{
return luaL_testudata(L, index, maplocationKey) != nullptr;
}
mapgen_gamemap* luaW_toslocs(lua_State *L, int index)
{
if(!lua_istable(L, index)) {
return nullptr;
}
lua_rawgeti(L, index, 1);
mapgen_gamemap* m = luaW_toterrainmap(L, -1);
lua_pop(L, 1);
return m;
}
mapgen_gamemap& luaW_check_slocs(lua_State *L, int index)
{
if(mapgen_gamemap* m = luaW_toslocs(L, index)) {
return *m;
}
luaW_type_error(L, index, "terrainmap");
throw "luaW_type_error didn't thow.";
}
void lua_slocs_setmetatable(lua_State *L)
{
luaL_setmetatable(L, maplocationKey);
}
/**
* @a index the index of the map object.
*/
void luaW_pushslocs(lua_State *L, int index)
{
lua_pushvalue(L, index);
//stack: map
lua_createtable(L, 1, 0);
//stack: map, slocs
lua_pushvalue(L, -2);
//stack: map, slocs, map
lua_rawseti(L, -2, 1);
//stack: map, slocs
luaL_setmetatable(L, maplocationKey);
//stack: map, slocs
lua_remove(L, -2);
//stack: slocs
}
int impl_slocs_get(lua_State* L)
{
//todo: calling map.special_locations[1] will return the underlying map
// object instead of the first starting position, because the lua
// special locations is actually a table with the map object at
// index 1. The probably easiest way to fix this inconsitency is
// to just disallow all integerindicies here.
mapgen_gamemap& m = luaW_check_slocs(L, 1);
gamemap_base& m = luaW_checkterrainmap(L, 1);
string_view id = luaL_checkstring(L, 2);
auto res = m.special_location(std::string(id));
if(res.wml_x() >= 0) {
if(res.valid()) {
luaW_pushlocation(L, res);
}
else {
} else {
//functions with variable return numbers have been causing problem in the past
lua_pushnil(L);
}
@ -109,7 +61,7 @@ int impl_slocs_get(lua_State* L)
int impl_slocs_set(lua_State* L)
{
mapgen_gamemap& m = luaW_check_slocs(L, 1);
gamemap_base& m = luaW_checkterrainmap(L, 1);
string_view id = luaL_checkstring(L, 2);
map_location loc = luaW_checklocation(L, 3);
@ -117,116 +69,140 @@ int impl_slocs_set(lua_State* L)
return 0;
}
int impl_slocs_next(lua_State *L)
{
gamemap_base& m = luaW_checkterrainmap(L, lua_upvalueindex(1));
const t_translation::starting_positions::left_map& left = m.special_locations().left;
t_translation::starting_positions::left_const_iterator it;
if (lua_isnoneornil(L, 2)) {
it = left.begin();
}
else {
it = left.find(luaL_checkstring(L, 2));
if (it == left.end()) {
return 0;
}
++it;
}
if (it == left.end()) {
return 0;
}
lua_pushstring(L, it->first.c_str());
luaW_pushlocation(L, it->second);
return 2;
}
int impl_slocs_iter(lua_State *L)
{
lua_settop(L, 1);
lua_pushvalue(L, 1);
lua_pushcclosure(L, &impl_slocs_next, 1);
lua_pushvalue(L, 1);
lua_pushnil(L);
return 3;
}
//////// MAP ////////
mapgen_gamemap::mapgen_gamemap(std::string_view s)
: tiles_()
, starting_positions_()
{
if(s.empty()) {
return;
}
//throws t_translation::error
//todo: make read_game_map take a string_view
tiles_ = t_translation::read_game_map(s, starting_positions_, t_translation::coordinate{ 1, 1 });
tiles() = t_translation::read_game_map(s, special_locations(), t_translation::coordinate{ 1, 1 });
}
mapgen_gamemap::mapgen_gamemap(int w, int h, terrain_code t)
: tiles_(w, h, t)
, starting_positions_()
: gamemap_base(w, h, t)
{
}
std::string mapgen_gamemap::to_string() const
// This can produce invalid combinations in rare case
// where an overlay doesn't have an independent terrain definition,
// or if you set an overlay with no base and merge mode other than OVERLAY.
void simplemerge(t_translation::terrain_code old_t, t_translation::terrain_code& new_t, const terrain_type_data::merge_mode mode)
{
return t_translation::write_game_map(tiles_, starting_positions_, { 1, 1 }) + "\n";
switch(mode) {
case terrain_type_data::OVERLAY:
new_t = t_translation::terrain_code(old_t.base, new_t.overlay);
break;
case terrain_type_data::BASE:
new_t = t_translation::terrain_code(new_t.base, old_t.overlay);
break;
case terrain_type_data::BOTH:
new_t = t_translation::terrain_code(new_t.base, new_t.overlay);
break;
}
}
void mapgen_gamemap::set_terrain(const map_location& loc, const terrain_code & terrain, const terrain_type_data::merge_mode mode)
void mapgen_gamemap::set_terrain(const map_location& loc, const terrain_code & terrain, const terrain_type_data::merge_mode mode, bool)
{
terrain_code& t = (*this)[loc];
terrain_code old = t;
t = terrain;
terrain_code old = get_terrain(loc);
terrain_code t = terrain;
simplemerge(old, t, mode);
tiles().get(loc.x + border_size(), loc.y + border_size()) = t;
}
void mapgen_gamemap::simplemerge(terrain_code old_t, terrain_code& new_t, const terrain_type_data::merge_mode mode)
{
if(mode == terrain_type_data::OVERLAY) {
new_t = t_translation::terrain_code(old_t.base, new_t.overlay);
}
if(mode == terrain_type_data::BASE) {
new_t = t_translation::terrain_code(new_t.base, old_t.overlay);
}
}
struct lua_map_ref {
virtual gamemap_base& get_map() = 0;
virtual ~lua_map_ref() {}
};
void mapgen_gamemap::set_special_location(const std::string& id, const map_location& loc)
{
bool valid = loc.valid();
auto it_left = starting_positions_.left.find(id);
if (it_left != starting_positions_.left.end()) {
if (valid) {
starting_positions_.left.replace_data(it_left, loc);
}
else {
starting_positions_.left.erase(it_left);
}
// Mapgen map reference, owned by Lua
struct lua_map_ref_gen : public lua_map_ref {
mapgen_gamemap map;
template<typename... T>
lua_map_ref_gen(T&&... params) : map(std::forward<T>(params)...) {}
gamemap_base& get_map() override {
return map;
}
else {
starting_positions_.left.insert(it_left, std::pair(id, loc));
}
}
};
map_location mapgen_gamemap::special_location(const std::string& id) const
{
auto it = starting_positions_.left.find(id);
if (it != starting_positions_.left.end()) {
auto& coordinate = it->second;
return map_location(coordinate.x, coordinate.y);
// Main map reference, owned by the engine
struct lua_map_ref_main : public lua_map_ref {
gamemap& map;
lua_map_ref_main(gamemap& ref) : map(ref) {}
gamemap_base& get_map() override {
return map;
}
else {
return map_location();
};
// Non-owning map reference to either type (used for special location userdata)
struct lua_map_ref_locs : public lua_map_ref {
gamemap_base& map;
lua_map_ref_locs(gamemap_base& ref) : map(ref) {}
gamemap_base& get_map() override {
return map;
}
}
};
bool luaW_isterrainmap(lua_State* L, int index)
{
return luaL_testudata(L, index, terrinmapKey) != nullptr;
return luaL_testudata(L, index, terrainmapKey) != nullptr || luaL_testudata(L, index, maplocationKey) != nullptr;
}
mapgen_gamemap* luaW_toterrainmap(lua_State *L, int index)
gamemap_base* luaW_toterrainmap(lua_State *L, int index)
{
if(luaW_isterrainmap(L, index)) {
return static_cast<mapgen_gamemap*>(lua_touserdata(L, index));
return &static_cast<lua_map_ref*>(lua_touserdata(L, index))->get_map();
}
return nullptr;
}
mapgen_gamemap& luaW_checkterrainmap(lua_State *L, int index)
gamemap_base& luaW_checkterrainmap(lua_State *L, int index)
{
if(luaW_isterrainmap(L, index)) {
return *static_cast<mapgen_gamemap*>(lua_touserdata(L, index));
return static_cast<lua_map_ref*>(lua_touserdata(L, index))->get_map();
}
luaW_type_error(L, index, "terrainmap");
throw "luaW_type_error didn't throw";
}
void lua_terrainmap_setmetatable(lua_State *L)
{
luaL_setmetatable(L, terrinmapKey);
}
template<typename... T>
mapgen_gamemap* luaW_pushmap(lua_State *L, T&&... params)
{
mapgen_gamemap* res = new(L) mapgen_gamemap(std::forward<T>(params)...);
lua_terrainmap_setmetatable(L);
return res;
}
/**
* Create a map.
* - Arg 1: string descripbing the map data.
@ -235,53 +211,112 @@ mapgen_gamemap* luaW_pushmap(lua_State *L, T&&... params)
* - Arg 2: int, height
* - Arg 3: string, terrain
*/
int intf_terainmap_create(lua_State *L)
int intf_terrainmap_create(lua_State *L)
{
if(lua_isnumber(L, 1) && lua_isnumber(L, 2)) {
int w = lua_tonumber(L, 1);
int h = lua_tonumber(L, 2);
auto terrain = t_translation::read_terrain_code(luaL_checkstring(L, 3));
luaW_pushmap(L, w, h, terrain);
return 1;
}
else {
new(L) lua_map_ref_gen(w, h, terrain);
} else {
string_view data_str = luaL_checkstring(L, 1);
luaW_pushmap(L, data_str);
return 1;
new(L) lua_map_ref_gen(data_str);
}
luaL_setmetatable(L, terrainmapKey);
return 1;
}
int intf_terrainmap_get(lua_State* L)
{
new(L) lua_map_ref_main(const_cast<gamemap&>(resources::gameboard->map()));
luaL_setmetatable(L, terrainmapKey);
return 1;
}
/**
* Destroys a map object before it is collected (__gc metamethod).
*/
static int impl_terainmap_collect(lua_State *L)
static int impl_terrainmap_collect(lua_State *L)
{
mapgen_gamemap *u = static_cast<mapgen_gamemap*>(lua_touserdata(L, 1));
u->mapgen_gamemap::~mapgen_gamemap();
lua_map_ref* m = static_cast<lua_map_ref*>(lua_touserdata(L, 1));
m->lua_map_ref::~lua_map_ref();
return 0;
}
static void luaW_push_terrain(lua_State* L, gamemap_base& map, map_location loc)
{
auto t = map.get_terrain(loc);
lua_pushstring(L, t_translation::write_terrain_code(t).c_str());
}
static void impl_merge_terrain(lua_State* L, gamemap_base& map, map_location loc)
{
auto mode = terrain_type_data::BOTH;
bool replace_if_failed = false;
string_view t_str;
if(luaL_testudata(L, 3, mapReplaceIfFailedKey)) {
replace_if_failed = true;
lua_getiuservalue(L, 3, replace_if_failed_idx::CODE);
t_str = luaL_checkstring(L, -1);
lua_getiuservalue(L, 3, replace_if_failed_idx::MODE);
mode = terrain_type_data::merge_mode(luaL_checkinteger(L, -1));
} else {
t_str = luaL_checkstring(L, 3);
if(t_str.front() == '^') {
mode = terrain_type_data::OVERLAY;
} else if(t_str.back() == '^') {
mode = terrain_type_data::BASE;
}
}
auto ter = t_translation::read_terrain_code(t_str);
if(auto gm = dynamic_cast<gamemap*>(&map)) {
if(resources::gameboard) {
bool result = resources::gameboard->change_terrain(loc, ter, mode, replace_if_failed);
for(team& t : resources::gameboard->teams()) {
t.fix_villages(*gm);
}
if(resources::controller) {
resources::controller->get_display().needs_rebuild(result);
}
}
} else map.set_terrain(loc, ter, mode, replace_if_failed);
}
/**
* Gets some data on a map (__index metamethod).
* - Arg 1: full userdata containing the map.
* - Arg 2: string containing the name of the property.
* - Ret 1: something containing the attribute.
*/
static int impl_terainmap_get(lua_State *L)
static int impl_terrainmap_get(lua_State *L)
{
mapgen_gamemap& tm = luaW_checkterrainmap(L, 1);
gamemap_base& tm = luaW_checkterrainmap(L, 1);
map_location loc;
if(luaW_tolocation(L, 2, loc)) {
luaW_push_terrain(L, tm, loc);
return 1;
}
char const *m = luaL_checkstring(L, 2);
// Find the corresponding attribute.
return_int_attrib("width", tm.total_width());
return_int_attrib("height", tm.total_height());
return_int_attrib("playable_width", tm.w());
return_int_attrib("playable_height", tm.h());
return_int_attrib("border_size", tm.border_size());
return_string_attrib("data", tm.to_string());
if(strcmp(m, "special_locations") == 0) {
luaW_pushslocs(L, 1);
new(L) lua_map_ref_locs(tm);
luaL_setmetatable(L, maplocationKey);
return 1;
}
if(luaW_getmetafield(L, 1, m)) {
if(luaW_getglobal(L, "wesnoth", "map", m)) {
return 1;
}
return 0;
@ -293,58 +328,113 @@ static int impl_terainmap_get(lua_State *L)
* - Arg 2: string containing the name of the property.
* - Arg 3: something containing the attribute.
*/
static int impl_terainmap_set(lua_State *L)
static int impl_terrainmap_set(lua_State *L)
{
mapgen_gamemap& tm = luaW_checkterrainmap(L, 1);
UNUSED(tm);
gamemap_base& tm = luaW_checkterrainmap(L, 1);
map_location loc;
// The extra check that value (arg 3) isn't a number is because without it,
// map[4] = 5 would be interpreted as map[{4, 5}] = nil, due to the way
// luaW_tolocation modifies the stack if it finds a pair of numbers on it.
if(lua_type(L, 3) != LUA_TNUMBER && luaW_tolocation(L, 2, loc)) {
impl_merge_terrain(L, tm, loc);
return 0;
}
char const *m = luaL_checkstring(L, 2);
std::string err_msg = "unknown modifiable property of map: ";
err_msg += m;
return luaL_argerror(L, 2, err_msg.c_str());
}
/*
The map iterator, when called with false as its parameter
is roughly equivalent to the following Lua function:
function map_iter()
local map, x, y = wesnoth.current.map, 0, 1
return function()
if x == map.playable_width then
if y == map.playable_height then
return nil
else
x, y = 1, y + 1
end
else
x = x + 1
end
return x, y, map[{x, y}]
end
end
/**
* Sets a terrain code.
* - Arg 1: map location.
* - Arg 2: terrain code string.
* - Arg 3: layer: (overlay|base|both, default=both)
*/
static int intf_set_terrain(lua_State *L)
template<bool with_border>
static int impl_terrainmap_iter(lua_State* L)
{
mapgen_gamemap& tm = luaW_checkterrainmap(L, 1);
map_location loc = luaW_checklocation(L, 2);
string_view t_str = luaL_checkstring(L, 3);
auto terrain = t_translation::read_terrain_code(t_str);
auto mode = terrain_type_data::BOTH;
if(!lua_isnoneornil(L, 4)) {
string_view mode_str = luaL_checkstring(L, 4);
if(mode_str == "base") {
mode = terrain_type_data::BASE;
}
else if(mode_str == "overlay") {
mode = terrain_type_data::OVERLAY;
// Retrieve the upvalues stored with the function
gamemap_base& tm = luaW_checkterrainmap(L, lua_upvalueindex(1));
map_location prev_loc = luaW_checklocation(L, lua_upvalueindex(2));
int w = with_border ? tm.total_width() - 1 : tm.w();
int h = with_border ? tm.total_height() - 1 : tm.h();
int x, y;
// Given the previous location, determine the next one to be returned
if(prev_loc.wml_x() == w) {
if(prev_loc.wml_y() == h) {
lua_pushnil(L);
return 1;
} else {
x = with_border ? 0 : 1;
y = prev_loc.wml_y() + 1;
}
} else {
x = prev_loc.wml_x() + 1;
y = prev_loc.wml_y();
}
tm.set_terrain(loc, terrain, mode);
return 0;
// Assign the upvalue representing the previous location
map_location next_loc(x, y, wml_loc{});
luaW_pushlocation(L, next_loc);
lua_replace(L, lua_upvalueindex(2));
// Return the new location and its terrain code
lua_pushinteger(L, x);
lua_pushinteger(L, y);
luaW_push_terrain(L, tm, next_loc);
return 3;
}
/**
* Gets a terrain code.
* - Arg 1: map location.
* - Ret 1: string.
*/
static int intf_get_terrain(lua_State *L)
int intf_terrainmap_iter(lua_State* L)
{
mapgen_gamemap& tm = luaW_checkterrainmap(L, 1);
map_location loc = luaW_checklocation(L, 2);
luaW_checkterrainmap(L, 1);
bool with_border = lua_isboolean(L, 2) ? luaW_toboolean(L, 2) : false;
lua_settop(L, 1);
luaW_pushlocation(L, map_location(with_border ? -1 : 0, 1, wml_loc{}));
if(with_border) {
lua_pushcclosure(L, impl_terrainmap_iter<true>, 2);
} else {
lua_pushcclosure(L, impl_terrainmap_iter<false>, 2);
}
return 1;
}
auto t = tm[loc];
lua_pushstring(L, t_translation::write_terrain_code(t).c_str());
int intf_on_board(lua_State* L)
{
gamemap_base& tm = luaW_checkterrainmap(L, 1);
map_location loc = luaW_checklocation(L, 2);
bool with_border = luaL_opt(L, luaW_toboolean, 3, false);
lua_pushboolean(L, with_border ? tm.on_board_with_border(loc) : tm.on_board(loc));
return 1;
}
int intf_on_border(lua_State* L)
{
gamemap_base& tm = luaW_checkterrainmap(L, 1);
map_location loc = luaW_checklocation(L, 2);
lua_pushboolean(L, tm.on_board_with_border(loc) && !tm.on_board(loc));
return 1;
}
@ -363,12 +453,13 @@ static std::vector<gamemap::overlay_rule> read_rules_vector(lua_State *L, int in
rule.old_ = t_translation::read_list(luaW_tostring(L, -1));
lua_pop(L, 1);
}
if(luaW_tableget(L, -1, "new")) {
rule.new_ = t_translation::read_list(luaW_tostring(L, -1));
lua_pop(L, 1);
}
if(luaW_tableget(L, -1, "mode")) {
if(luaW_tableget(L, -1, "layer")) {
auto str = luaW_tostring(L, -1);
rule.mode_ = str == "base" ? terrain_type_data::BASE : (str == "overlay" ? terrain_type_data::OVERLAY : terrain_type_data::BOTH);
lua_pop(L, 1);
@ -397,27 +488,26 @@ static std::vector<gamemap::overlay_rule> read_rules_vector(lua_State *L, int in
return rules;
}
/**
* Reaplces part of the map.
* Replaces part of the map.
* - Arg 1: map location.
* - Arg 2: map data string.
* - Arg 3: table for optional named arguments
* - is_odd: boolen, if Arg2 has the odd mapo format (as if it was cut from a odd map location)
* - is_odd: boolean, if Arg2 has the odd map format (as if it was cut from a odd map location)
* - ignore_special_locations: boolean
* - rules: table of tables
*/
int mapgen_gamemap::intf_mg_terrain_mask(lua_State *L)
int intf_terrain_mask(lua_State *L)
{
mapgen_gamemap& tm1 = luaW_checkterrainmap(L, 1);
gamemap_base& map = luaW_checkterrainmap(L, 1);
map_location loc = luaW_checklocation(L, 2);
mapgen_gamemap& tm2 = luaW_checkterrainmap(L, 3);
bool is_odd = false;
bool ignore_special_locations = false;
std::vector<gamemap::overlay_rule> rules;
if(lua_istable(L, 4)) {
is_odd = luaW_table_get_def<bool>(L, 4, "is_odd", false);
ignore_special_locations = luaW_table_get_def<bool>(L, 4, "ignore_special_locations", false);
is_odd = luaW_table_get_def(L, 4, "is_odd", false);
ignore_special_locations = luaW_table_get_def(L, 4, "ignore_special_locations", false);
if(luaW_tableget(L, 4, "rules")) {
if(!lua_istable(L, -1)) {
@ -427,58 +517,105 @@ int mapgen_gamemap::intf_mg_terrain_mask(lua_State *L)
lua_pop(L, 1);
}
}
if(lua_isstring(L, 3)) {
const std::string t_str = luaL_checkstring(L, 3);
std::unique_ptr<gamemap_base> mask;
if(dynamic_cast<gamemap*>(&map)) {
auto mask_ptr = new gamemap("");
mask_ptr->read(t_str, false);
mask.reset(mask_ptr);
} else {
mask.reset(new mapgen_gamemap(t_str));
}
map.overlay(*mask, loc, rules, is_odd, ignore_special_locations);
} else {
gamemap_base& mask = luaW_checkterrainmap(L, 3);
map.overlay(mask, loc, rules, is_odd, ignore_special_locations);
}
if(resources::gameboard) {
if(auto gmap = dynamic_cast<gamemap*>(&map)) {
for(team& t : resources::gameboard->teams()) {
t.fix_villages(*gmap);
}
}
}
gamemap::overlay_impl(
tm1.tiles_,
tm1.starting_positions_,
tm2.tiles_,
tm2.starting_positions_,
[&](const map_location& loc, const t_translation::terrain_code& t, terrain_type_data::merge_mode mode, bool) { tm1.set_terrain(loc, t, mode); },
loc,
rules,
is_odd,
ignore_special_locations
);
if(resources::controller) {
resources::controller->get_display().needs_rebuild(true);
}
return 0;
}
int intf_replace_if_failed(lua_State* L)
{
auto mode = terrain_type_data::BOTH;
if(!lua_isnoneornil(L, 2)) {
string_view mode_str = luaL_checkstring(L, 2);
if(mode_str == "base") {
mode = terrain_type_data::BASE;
} else if(mode_str == "overlay") {
mode = terrain_type_data::OVERLAY;
} else if(mode_str != "both") {
return luaL_argerror(L, 2, "must be one of 'base', 'overlay', or 'both'");
}
}
lua_newuserdatauv(L, 0, 2);
lua_pushinteger(L, int(mode));
lua_setiuservalue(L, -2, replace_if_failed_idx::MODE);
lua_pushvalue(L, 1);
lua_setiuservalue(L, -2, replace_if_failed_idx::CODE);
luaL_setmetatable(L, mapReplaceIfFailedKey);
return 1;
}
static int impl_replace_if_failed_tostring(lua_State* L)
{
static const char* mode_strs[] = {"base", "overlay", "both"};
lua_getiuservalue(L, 1, replace_if_failed_idx::CODE);
string_view t_str = luaL_checkstring(L, -1);
lua_getiuservalue(L, 1, replace_if_failed_idx::MODE);
int mode = luaL_checkinteger(L, -1);
lua_pushfstring(L, "replace_if_failed('%s', '%s')", t_str.data(), mode_strs[mode]);
return 1;
}
namespace lua_terrainmap {
std::string register_metatables(lua_State* L)
{
std::ostringstream cmd_out;
cmd_out << "Adding terrainmamap metatable...\n";
cmd_out << "Adding terrain map metatable...\n";
luaL_newmetatable(L, terrinmapKey);
lua_pushcfunction(L, impl_terainmap_collect);
luaL_newmetatable(L, terrainmapKey);
lua_pushcfunction(L, impl_terrainmap_collect);
lua_setfield(L, -2, "__gc");
lua_pushcfunction(L, impl_terainmap_get);
lua_pushcfunction(L, impl_terrainmap_get);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, impl_terainmap_set);
lua_pushcfunction(L, impl_terrainmap_set);
lua_setfield(L, -2, "__newindex");
lua_pushstring(L, "terainmap");
lua_pushstring(L, terrainmapKey);
lua_setfield(L, -2, "__metatable");
luaL_newmetatable(L, mapReplaceIfFailedKey);
lua_pushcfunction(L, impl_replace_if_failed_tostring);
lua_setfield(L, -2, "__tostring");
lua_pushstring(L, mapReplaceIfFailedKey);
lua_setfield(L, -2, "__metatable");
// terainmap methods
lua_pushcfunction(L, intf_set_terrain);
lua_setfield(L, -2, "set_terrain");
lua_pushcfunction(L, intf_get_terrain);
lua_setfield(L, -2, "get_terrain");
lua_pushcfunction(L, intf_mg_get_locations);
lua_setfield(L, -2, "get_locations");
lua_pushcfunction(L, intf_mg_get_tiles_radius);
lua_setfield(L, -2, "get_tiles_radius");
lua_pushcfunction(L, &mapgen_gamemap::intf_mg_terrain_mask);
lua_setfield(L, -2, "terrain_mask");
cmd_out << "Adding terrainmamap2 metatable...\n";
cmd_out << "Adding special locations metatable...\n";
luaL_newmetatable(L, maplocationKey);
lua_pushcfunction(L, impl_slocs_get);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, impl_slocs_set);
lua_setfield(L, -2, "__newindex");
lua_pushstring(L, "special_locations");
lua_pushcfunction(L, impl_slocs_iter);
lua_setfield(L, -2, "__pairs");
lua_pushstring(L, maplocationKey);
lua_setfield(L, -2, "__metatable");
return cmd_out.str();

View file

@ -17,6 +17,7 @@
#include <string>
#include "scripting/lua_common.hpp"
#include "map/location.hpp"
#include "map/map.hpp"
#include "terrain/translation.hpp"
#include "terrain/type_data.hpp"
@ -24,60 +25,13 @@ struct lua_State;
class lua_unit;
struct map_location;
// this clas is similar to the orginal gamemap class but they have no 'is' rlation:
// mapgen_gamemap, unlike gamemap offers 'raw' access to the data
// gamemap, unlike mapgen_gamemap uses terain type data.
class mapgen_gamemap
{
// Unlike the original gamemap, this offers 'raw' access to the data.
// The original gamemap uses terrain type data.
class mapgen_gamemap : public gamemap_base {
public:
using terrain_code = t_translation::terrain_code;
using terrain_map = t_translation::ter_map;
using starting_positions = t_translation::starting_positions;
explicit mapgen_gamemap(std::string_view data);
mapgen_gamemap(int w, int h, terrain_code);
std::string to_string() const;
/** Effective map width. */
int w() const { return total_width() - 2; }
/** Effective map height. */
int h() const { return total_height() - 2; }
/** Real width of the map, including borders. */
int total_width() const { return tiles_.w; }
/** Real height of the map, including borders */
int total_height() const { return tiles_.h; }
bool on_map(const map_location& loc) const
{
return loc.wml_x() >= 0 && loc.wml_x() < total_width() && loc.wml_y() >= 0 && loc.wml_y() < total_height();
}
bool on_map_noborder(const map_location& loc) const
{
return loc.wml_x() > 0 && loc.wml_x() < total_width() - 1 && loc.wml_y() > 0 && loc.wml_y() < total_height() - 1;
}
terrain_code& operator[](const map_location& loc)
{
return tiles_.get(loc.wml_x(), loc.wml_y());
}
const terrain_code& operator[](const map_location& loc) const
{
return tiles_.get(loc.wml_x(), loc.wml_y());
}
void set_terrain(const map_location& loc, const terrain_code & terrain, const terrain_type_data::merge_mode mode);
static void simplemerge(terrain_code old, terrain_code& t, const terrain_type_data::merge_mode mode);
starting_positions& special_locations() { return starting_positions_; }
const starting_positions& special_locations() const { return starting_positions_; }
void set_special_location(const std::string& id, const map_location& loc);
map_location special_location(const std::string& id) const;
void set_terrain(const map_location& loc, const terrain_code & terrain, const terrain_type_data::merge_mode mode = terrain_type_data::BOTH, bool replace_if_failed = false) override;
template<typename F>
void for_each_loc(const F& f) const
@ -88,37 +42,25 @@ public:
}
}
}
static int intf_mg_terrain_mask(lua_State *L);
private:
t_translation::ter_map tiles_;
starting_positions starting_positions_;
};
bool luaW_isslocs(lua_State* L, int index);
mapgen_gamemap* luaW_toslocs(lua_State *L, int index);
mapgen_gamemap& luaW_check_slocs(lua_State *L, int index);
void lua_slocs_setmetatable(lua_State *L);
void luaW_pushslocs(lua_State *L, int index);
int impl_slocs_get(lua_State* L);
int impl_slocs_set(lua_State* L);
int intf_terrain_mask(lua_State *L);
bool luaW_isterrainmap(lua_State* L, int index);
mapgen_gamemap* luaW_toterrainmap(lua_State *L, int index);
gamemap_base* luaW_toterrainmap(lua_State *L, int index);
mapgen_gamemap& luaW_checkterrainmap(lua_State *L, int index);
gamemap_base& luaW_checkterrainmap(lua_State *L, int index);
void lua_terrainmap_setmetatable(lua_State *L);
mapgen_gamemap* luaW_pushmap(lua_State *L, mapgen_gamemap&& u);
int intf_terrainmap_create(lua_State *L);
int intf_terrainmap_get(lua_State *L);
int intf_terainmap_create(lua_State *L);
int intf_replace_if_failed(lua_State* L);
int intf_terrainmap_iter(lua_State* L);
int intf_on_board(lua_State* L);
int intf_on_border(lua_State* L);
namespace lua_terrainmap {
std::string register_metatables(lua_State *L);

View file

@ -222,10 +222,6 @@ mapgen_lua_kernel::mapgen_lua_kernel(const config* vars)
static luaL_Reg const callbacks[] {
{ "find_path", &intf_find_path },
{ "random", &intf_random },
{ "create_filter", &intf_terainfilter_create },
{ "create_map", &intf_terainmap_create },
{ "default_generate_height_map", &intf_default_generate_height_map },
{ "generate_default_map", &intf_default_generate },
{ "get_variable", &dispatch<&mapgen_lua_kernel::intf_get_variable> },
{ nullptr, nullptr }
};
@ -236,6 +232,23 @@ mapgen_lua_kernel::mapgen_lua_kernel(const config* vars)
lua_pop(L, 1);
assert(lua_gettop(L) == 0);
static luaL_Reg const map_callbacks[] {
// Map methods
{ "find", &intf_mg_get_locations },
{ "find_in_radius", &intf_mg_get_tiles_radius },
// Static functions
{ "filter", &intf_terrainfilter_create },
{ "create", &intf_terrainmap_create },
{ "generate_height_map", &intf_default_generate_height_map },
{ "generate", &intf_default_generate },
{ nullptr, nullptr }
};
luaW_getglobal(L, "wesnoth", "map");
assert(lua_istable(L,-1));
luaL_setfuncs(L, map_callbacks, 0);
lua_pop(L, 1);
assert(lua_gettop(L) == 0);
cmd_log_ << lua_terrainmap::register_metatables(L);
cmd_log_ << lua_terrainfilter::register_metatables(L);