add tag [random_placement]

and use it to implement SCATTER_IMAGE,SCATTER_EMBELLISHMENTS and
SCATTER_UNITS
This commit is contained in:
gfgtdf 2015-12-07 21:49:09 +01:00
parent 9aa6722622
commit dc786dc181
3 changed files with 112 additions and 126 deletions

View file

@ -31,42 +31,21 @@
#
# This call will scatter 20 copies of a pine-tree graphic over grassland:
#! {SCATTER_IMAGE (terrain=Gg) 20 scenery/pine1.png}
[store_locations]
{FILTER}
variable=random_placement_locations
[/store_locations]
{VARIABLE REPEAT_i 0}
[while]
[variable]
name=REPEAT_i
less_than={NUMBER}
[/variable]
[variable]
name=random_placement_locations.length
greater_than=0
[/variable]
[do]
[set_variable]
name=random_subscript
rand="0..$($random_placement_locations.length - 1)"
[/set_variable]
[random_placement]
num_items={NUMBER}
variable=random_placement_location
allow_less=yes
[filter]
{FILTER}
[/filter]
[command]
[item]
image={IMAGE}
x,y=$random_placement_locations[$random_subscript].x,$random_placement_locations[$random_subscript].y
x,y=$random_placement_location.x,$random_placement_location.y
[/item]
{CLEAR_VARIABLE random_placement_locations[$random_subscript]}
{VARIABLE_OP REPEAT_i add 1}
[/do]
[/while]
{CLEAR_VARIABLE REPEAT_i}
{CLEAR_VARIABLE random_subscript}
{CLEAR_VARIABLE random_placement_locations}
[/command]
[/random_placement]
{CLEAR_VARIABLE random_placement_location}
#enddef
#define SCATTER_EMBELLISHMENTS TERRAINLIST EMBELLISHMENT_NAME PERCENTAGE
@ -75,39 +54,20 @@
#
# For example, this will add flowers to 5% of all grassland:
#! {SCATTER_EMBELLISHMENTS G* ^Efm 5}
[store_locations]
terrain={TERRAINLIST}
variable=terrain_variation_locations
[/store_locations]
{VARIABLE terrain_variations_to_place {PERCENTAGE}}
# TODO: change back to 100.0 once it no longer causes a crash
{VARIABLE_OP terrain_variations_to_place divide 100.0}
{VARIABLE_OP terrain_variations_to_place multiply $terrain_variation_locations.length}
{VARIABLE_OP terrain_variations_to_place round ceil}
[while]
[variable]
name=terrain_variations_to_place
greater_than=0
[/variable]
[do]
{VARIABLE_OP terrain_variation_i rand "0..$($terrain_variation_locations.length - 1)"}
[random_placement]
num_items="size * ({PERCENTAGE} / 100)"
variable=random_placement_location
allow_less=yes
[filter]
terrain={TERRAINLIST}
[/filter]
[command]
[terrain]
x,y=$terrain_variation_locations[$terrain_variation_i].x,$terrain_variation_locations[$terrain_variation_i].y
x,y=$random_placement_location.x,$random_placement_location.y
terrain={EMBELLISHMENT_NAME}
layer=overlay
[/terrain]
{VARIABLE_OP terrain_variations_to_place sub 1}
{CLEAR_VARIABLE terrain_variation_locations[$terrain_variation_i]}
[/do]
[/while]
{CLEAR_VARIABLE terrain_variation_locations,terrain_variations_to_place,terrain_variation_i}
[/command]
[/random_placement]
{CLEAR_VARIABLE random_placement_location}
#enddef

View file

@ -462,15 +462,6 @@
#! {TRAIT_LOYAL}
#! [/modifications]
#! )}
[store_locations]
{FILTER}
# Exclude border hexes
include_borders=no
variable=possible_unit_locations
[/store_locations]
[set_variables]
name=unit_type_table
@ -482,65 +473,27 @@
[/set_variables]
{VARIABLE unit_type_table_i 0}
{VARIABLE units_to_place {NUMBER}}
[while]
[variable]
name=units_to_place
greater_than=0
[/variable]
[do]
[set_variable]
name=random_subscript
rand=1..$possible_unit_locations.length
[/set_variable]
{VARIABLE_OP random_subscript sub 1}
[random_placement]
num_items={NUMBER}
variable=random_placement_location
allow_less=yes
radius={PADDING_RADIUS}
[filter]
{FILTER}
include_borders=no
[/filter]
[command]
[unit]
type=$unit_type_table[$unit_type_table_i].type
x,y=$possible_unit_locations[$random_subscript].x,$possible_unit_locations[$random_subscript].y
x,y=$random_placement_location.x,$random_placement_location.y
{UNIT_WML}
[/unit]
{VARIABLE unit_type_table_i $(($unit_type_table_i + 1) % $unit_type_table.length)}
[/command]
[/random_placement]
[store_locations]
find_in=possible_unit_locations
[not]
x,y=$possible_unit_locations[$random_subscript].x,$possible_unit_locations[$random_subscript].y
radius={PADDING_RADIUS}
[/not]
variable=possible_unit_locations
[/store_locations]
[if]
[variable]
name=possible_unit_locations.length
less_than=1
[/variable]
[then]
{VARIABLE units_to_place 0}
[/then]
[/if]
{VARIABLE_OP unit_type_table_i add 1}
[if]
[variable]
name=unit_type_table_i
numerical_equals=$unit_type_table.length
[/variable]
[then]
{VARIABLE unit_type_table_i 0}
[/then]
[/if]
{VARIABLE_OP units_to_place sub 1}
[/do]
[/while]
{CLEAR_VARIABLE unit_type_table,unit_type_table_i,possible_unit_locations,random_subscript,units_to_place}
{CLEAR_VARIABLE unit_type_table,unit_type_table_i,random_placement_location}
#enddef
#define FORCE_CHANCE_TO_HIT FILTER SECOND_FILTER CTH_NUMBER EXTRA_CONDITIONS_WML

View file

@ -1643,3 +1643,76 @@ function wml_actions.unsynced(cfg)
wml_actions.command(cfg)
end)
end
wesnoth.wml_actions.random_placement = function(cfg)
local dist_le_radius = nil
local parsed = helper.shallow_parsed(cfg)
local filter = helper.get_child(parsed, "filter") or {}
local command = helper.get_child(parsed, "command") or helper.wml_error("[random_placement] missing required [command] subtag")
local radius = cfg.radius or 0
local num_items = cfg.num_items or helper.wml_error("[random_placement] missing required 'num_items' attribute")
local variable = cfg.variable or helper.wml_error("[random_placement] missing required 'variable' attribute")
local use_delay = cfg.use_delay == true
local allow_less = cfg.allow_less == true
if radius < 0 then
-- optimisation for radius = -1
dist_le_radius = function() return false end
elseif radius == 0 then
-- optimisation for radius = 0
dist_le_radius = function(x1,y1,x2,y2) return x1 == x2 and y1 == y2 end
else
-- optimisation: cloasure is faster than string lookups.
local math_abs = math.abs
-- same effect as helper.distance_between(x1,y1,x2,y2) <= radius but faster.
dist_le_radius = function(x1,y1,x2,y2)
local d_x = math_abs(x1-x2)
if d_x > radius then
return false
end
if d_x % 2 ~= 0 then
if x1 % 2 == 0 then
y2 = y2 - 0.5
else
y2 = y2 + 0.5
end
end
local d_y = math_abs(y1-y2)
return d_x + 2*d_y <= 2*radius
end
end
local locs = wesnoth.get_locations(filter)
if type(num_items) == "string" then
num_items = math.floor(loadstring("local size = " .. #locs .. "; return " .. num_items)())
print("num_items=" .. num_items .. ", #locs=" .. #locs)
end
local size = #locs
for i = 1, num_items do
if size == 0 then
if allow_less then
wesnoth.message("placed only " .. i .. " items")
return
else
helper.wml_error("[random_placement] failed to place items. only " .. i .. " items were placed")
end
end
local index = wesnoth.random(size)
local point = locs[index]
wesnoth.set_variable(variable .. ".x", point[1])
wesnoth.set_variable(variable .. ".y", point[2])
wesnoth.set_variable(variable .. ".n", i)
for j = size, 1, -1 do
if dist_le_radius(locs[j][1], locs[j][2], point[1], point[2]) then
-- optimisation: swapping elements and storing size in an extra variable is faster than table.remove(locs, j)
locs[j] = locs[size]
size = size - 1
end
end
wesnoth.wml_actions.command (command)
if use_delay and (i % 10 == 0) then
--prevent freezing.
wesnoth.delay(0);
end
end
end