Merge pull request #557 from gfgtdf/lua_placement

add tag [random_placement]
This commit is contained in:
gfgtdf 2015-12-07 21:53:15 +01:00
commit 856839e93f
8 changed files with 238 additions and 398 deletions

View file

@ -169,56 +169,38 @@
boolean_not_equals=yes
[/variable]
[/filter_condition]
{VARIABLE_OP number_of_guards rand "0..2"}
{VARIABLE guard_i 1}
[while]
[variable]
name=guard_i
less_than_equal_to=$number_of_guards
[/variable]
[do]
[store_locations]
[filter_adjacent_location]
x,y=$x1,$y1
[/filter_adjacent_location]
[not]
[filter]
[/filter]
[/not]
variable=possible_guard_locations
[/store_locations]
{IF_VAR possible_guard_locations.length greater_than 0 (
[then]
{VARIABLE_OP random_location_index rand "0..$($possible_guard_locations.length - 1)"}
[move_unit_fake]
side=2
type=Goblin Spearman
x=$x1,$possible_guard_locations[$random_location_index].x
y=$y1,$possible_guard_locations[$random_location_index].y
[/move_unit_fake]
[unit]
id=guard_$x1|_$y1|_$guard_i
name= _ "Villager"
type=Goblin Spearman
side=2
x,y=$possible_guard_locations[$random_location_index].x,$possible_guard_locations[$random_location_index].y
random_traits=yes
[/unit]
[/then]
)}
{VARIABLE_OP guard_i add 1}
[/do]
[/while]
[random_placement]
num_items="$number_of_guards"
allow_less=yes
variable=guard_location
[filter]
[filter_adjacent_location]
x,y=$x1,$y1
[/filter_adjacent_location]
[not]
[filter]
[/filter]
[/not]
[/filter]
[command]
[move_unit_fake]
side=2
type=Goblin Spearman
x=$x1,$guard_location.x
y=$y1,$guard_location.y
[/move_unit_fake]
[unit]
id=guard_$x1|_$y1|_$guard_location.n
name= _ "Villager"
type=Goblin Spearman
side=2
x,y=$guard_location.x,$guard_location.y
random_traits=yes
[/unit]
[/command]
[/random_placement]
{VARIABLE village_$x1|_$y1|_cleared yes}
# Trigger an easter egg sometime after a third of villages have been taken
@ -268,7 +250,7 @@
[/then]
[/if]
{CLEAR_VARIABLE number_of_guards,guard_i,possible_guard_locations,random_location_index}
{CLEAR_VARIABLE number_of_guards,guard_i,guard_location,random_location_index}
[/event]
[event]

View file

@ -220,74 +220,29 @@
# clump all the ambushers into a single corner of the map.
#define UNDEAD_AMBUSH_AREA SIDE X_SPAN Y_SPAN NUMBER
[store_locations]
x={X_SPAN}
y={Y_SPAN}
terrain=Ss
[random_placement]
variable=ambush_location
num_items={NUMBER}
[filter]
x={X_SPAN}
y={Y_SPAN}
terrain=Ss
[filter_adjacent_location]
[not]
terrain=Ss
[/not]
[not]
[filter]
[/filter]
[/not]
[/filter_adjacent_location]
variable=possible_ambush_locations
[/store_locations]
{VARIABLE ambusher 0}
[while]
[variable]
name=ambusher
less_than={NUMBER}
[/variable]
[do]
{RANDOM 1..$possible_ambush_locations.length}
{VARIABLE_OP random sub 1}
# Check if the random location picked already contains an ambusher
# and if it does, we loop again to pick another one until an empty
# one is found.
[if]
[have_unit]
x,y=$possible_ambush_locations[$random].x,$possible_ambush_locations[$random].y
[/have_unit]
[else]
# random_ambusher_type_i is just a counter variable with
# which we point to a given element of the
# random_ambusher_type_table array. Here we make it loop
# over the array continuously.
[if]
[variable]
name=random_ambusher_type_i
less_than=4
[/variable]
[then]
{VARIABLE_OP random_ambusher_type_i add 1}
[/then]
[else]
{VARIABLE random_ambusher_type_i 0}
[/else]
[/if]
# And here we place an ambusher of a type specified by the
# random_ambusher_type_table and random_ambusher_type_i
# variables, thus always having the same ratio of different
# types.
[filter_adjacent_location]
[not]
terrain=Ss
[/not]
[not]
[filter]
[/filter]
[/not]
[/filter_adjacent_location]
[/filter]
[command]
{VARIABLE random_ambusher_type_i ($($random_ambusher_type_i + 1) % $random_ambusher_type_table.length)}
[unit]
x,y=$possible_ambush_locations[$random].x,$possible_ambush_locations[$random].y
x,y=$ambush_location.x,$ambush_location.y
type=$random_ambusher_type_table[$random_ambusher_type_i].type
side={SIDE}
role=ambusher
@ -299,7 +254,7 @@
silent=yes
[filter]
x,y=$possible_ambush_locations[$random].x,$possible_ambush_locations[$random].y
x,y=$ambush_location.x,$ambush_location.y
[/filter]
[effect]
@ -321,12 +276,8 @@
[/abilities]
[/effect]
[/object]
{VARIABLE_OP ambusher add 1}
[/else]
[/if]
[/do]
[/while]
[/command]
[/random_placement]
#enddef
# Only the number of ambushers differs by difficulty here.

View file

@ -284,8 +284,10 @@
#define ORC_AMBUSH_AREA SIDE X_SPAN Y_SPAN NUMBER
# Any hill that's completely surrounded by other hills, mountains or
# forest is eligible as an ambush location.
[store_locations]
[random_placement]
variable=ambush_location
num_items={NUMBER}
[filter]
x={X_SPAN}
y={Y_SPAN}
terrain=Hh
@ -297,37 +299,8 @@
[/not]
[/filter_adjacent_location]
[/not]
variable=possible_ambush_locations
[/store_locations]
{VARIABLE ambusher 0}
[while]
[variable]
name=ambusher
less_than={NUMBER}
[/variable]
[do]
{RANDOM 1..$possible_ambush_locations.length}
{VARIABLE_OP random sub 1}
# Check if the random location picked already contains an ambusher
# and if it does, we loop again to pick another one until an empty
# one is found.
[if]
[have_unit]
x,y=$possible_ambush_locations[$random].x,$possible_ambush_locations[$random].y
[/have_unit]
[else]
# random_ambusher_type_i is just a counter variable with
# which we point to a given element of the
# random_ambusher_type_table array. Here we make it loop
# over the array continuously.
[/filter]
[command]
[if]
[variable]
name=random_ambusher_type_i
@ -349,7 +322,7 @@
# types.
[unit]
x,y=$possible_ambush_locations[$random].x,$possible_ambush_locations[$random].y
x,y=$ambush_location.x,$ambush_location.y
type=$random_ambusher_type_table[$random_ambusher_type_i].type
side={SIDE}
facing=sw
@ -366,7 +339,7 @@
silent=yes
[filter]
x,y=$possible_ambush_locations[$random].x,$possible_ambush_locations[$random].y
x,y=$ambush_location.x,$ambush_location.y
[/filter]
[effect]
@ -388,12 +361,9 @@
[/abilities]
[/effect]
[/object]
{VARIABLE_OP ambusher add 1}
[/else]
[/if]
[/do]
[/while]
[/command]
[/random_placement]
#enddef
# Only the number of ambushers differs by difficulty here.

View file

@ -1392,55 +1392,36 @@
speaker=Lich-Lord Jevyan
message= _ "Rise, rise from the ground!"
[/message]
[random_placement]
variable=wc_loc
allow_less=yes
num_items={ON_DIFFICULTY 3 6 9}
[filter]
terrain=S*
[store_locations]
terrain=S*
[and]
x,y=42,40
radius=14
[/and]
[not]
[filter]
[/filter]
[/not]
variable=possible_wc_locs
[/store_locations]
{VARIABLE wc_counter 0}
[while]
[variable]
name=possible_wc_locs.length
greater_than_equal_to=1
[/variable]
[variable]
name=wc_counter
less_than={ON_DIFFICULTY 3 6 9}
[/variable]
[do]
{RANDOM "0..$($possible_wc_locs.length - 1)"}
[and]
x,y=42,40
radius=14
[/and]
[not]
[filter]
[/filter]
[/not]
[/filter]
[command]
[sound]
name={SOUND_LIST:ZOMBIE_HIT}
[/sound]
{LOYAL_UNIT 2 (Walking Corpse) $possible_wc_locs[$random].x $possible_wc_locs[$random].y}
{LOYAL_UNIT 2 (Walking Corpse) $wc_loc.x $wc_loc.y}
[+unit]
animate=yes
[/unit]
[/command]
[/random_placement]
{CLEAR_VARIABLE possible_wc_locs[$random]}
{VARIABLE_OP wc_counter add 1}
[/do]
[/while]
{CLEAR_VARIABLE possible_wc_locs,wc_counter}
{CLEAR_VARIABLE wc_loc}
[/event]
# This event spawns skeletons to shallow water bordering deep water.
@ -1452,26 +1433,12 @@
speaker=Lich-Lord Jevyan
message= _ "Come in from the deep my loyal soldiers!"
[/message]
[store_locations]
terrain=Ww
[not]
[filter]
[/filter]
[/not]
[not]
[filter]
side=1
[/filter]
radius=1
[/not]
[filter_adjacent_location]
terrain=Wo
adjacent=nw,sw
[random_placement]
variable=skele_loc
allow_less=yes
num_items={ON_DIFFICULTY 3 4 5}
[filter]
terrain=Ww
[not]
[filter]
@ -1485,26 +1452,26 @@
radius=1
[/not]
[/filter_adjacent_location]
variable=possible_skele_locs
[/store_locations]
[filter_adjacent_location]
terrain=Wo
adjacent=nw,sw
{VARIABLE skele_counter 0}
[while]
[variable]
name=possible_skele_locs.length
greater_than_equal_to=1
[/variable]
[not]
[filter]
[/filter]
[/not]
[variable]
name=skele_counter
less_than={ON_DIFFICULTY 3 4 5}
[/variable]
[do]
{RANDOM "0..$($possible_skele_locs.length - 1)"}
[not]
[filter]
side=1
[/filter]
radius=1
[/not]
[/filter_adjacent_location]
[/filter]
[command]
[store_locations]
terrain=Wo
@ -1522,7 +1489,7 @@
[/not]
[filter_adjacent_location]
x,y=$possible_skele_locs[$random].x,$possible_skele_locs[$random].y
x,y=$skele_loc.x,$skele_loc.y
[/filter_adjacent_location]
variable=skele_from
@ -1550,8 +1517,8 @@
[/switch]
[scroll_to]
x=$possible_skele_locs[$random].x
y=$possible_skele_locs[$random].y
x=$skele_loc.x
y=$skele_loc.y
[/scroll_to]
[sound]
@ -1565,19 +1532,14 @@
[move_unit_fake]
side=2
type=$skele_type
x=$skele_from.x,$possible_skele_locs[$random].x
y=$skele_from.y,$possible_skele_locs[$random].y
x=$skele_from.x,$skele_loc.x
y=$skele_from.y,$skele_loc.y
[/move_unit_fake]
{LOYAL_UNIT 2 $skele_type $possible_skele_locs[$random].x $possible_skele_locs[$random].y}
{CLEAR_VARIABLE possible_skele_locs[$random]}
{VARIABLE_OP skele_counter add 1}
[/do]
[/while]
{CLEAR_VARIABLE possible_skele_locs,skele_counter,skele_from}
{LOYAL_UNIT 2 $skele_type $skele_loc.x $skele_loc.y}
[/command]
[/random_placement]
{CLEAR_VARIABLE skele_loc, skele_from, skele_type}
[/event]
# This triggers on turns 2,9,16,23,... and sets the next corpse spawn turn

View file

@ -613,8 +613,7 @@
[event]
name=create_minion
first_time_only=no
[store_locations]
{SCATTER_UNITS 1 (Crawling Horror) 0 (
x=6-14
y=7-14
terrain=Rr,Uu
@ -622,25 +621,11 @@
[not]
[filter]
[/filter]
[/not]
variable=possible_spawn_locations
[/store_locations]
[if]
[variable]
name=possible_spawn_locations.length
greater_than_equal_to=1
[/variable]
[then]
{VARIABLE_OP new_minion_loc_i rand "1..$possible_spawn_locations.length"}
{VARIABLE_OP new_minion_loc_i sub 1}
{UNIT 2 (Crawling Horror) $possible_spawn_locations[$new_minion_loc_i].x $possible_spawn_locations[$new_minion_loc_i].y (upkeep=free)}
[/then]
[/if]
{CLEAR_VARIABLE possible_spawn_locations}
[/not]
) (
side=2
upkeep=free
)}
[/event]
# Event 1: The Death of Eloh and the revealing of the true monster

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,80 @@ 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
local variable_previous = utils.start_var_scope(variable)
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
utils.end_var_scope(variable, variable_previous)
end