Properly port [kill] to Lua
This commit is contained in:
parent
f9b4b5681f
commit
339587ee2b
4 changed files with 67 additions and 162 deletions
|
@ -44,6 +44,7 @@ Version 1.13.7+dev:
|
|||
* Empty tags are no longer written to the configs of [unit]s and [side]s.
|
||||
* New [change_theme] tag to change the theme mid-scenario
|
||||
* New [zoom] tag allows changing the zoom level from an event
|
||||
* [kill]animate=yes now plays victory animations if applicable
|
||||
|
||||
Version 1.13.7:
|
||||
* AI:
|
||||
|
|
|
@ -950,8 +950,73 @@ function wml_actions.inspect(cfg)
|
|||
wesnoth.gamestate_inspector(cfg)
|
||||
end
|
||||
|
||||
local kill_recursion_preventer = wesnoth.require("lua/location_set.lua").create()
|
||||
function wml_actions.kill(cfg)
|
||||
wesnoth.kill(cfg)
|
||||
local number_killed = 0
|
||||
local secondary_unit = helper.get_child(cfg, "secondary_unit")
|
||||
local killer_loc = {0, 0}
|
||||
if secondary_unit then
|
||||
secondary_unit = wesnoth.get_units(secondary_unit)[1]
|
||||
if cfg.fire_event then
|
||||
if secondary_unit then
|
||||
killer_loc = {secondary_unit.loc}
|
||||
else
|
||||
wesnoth.log("warn", "failed to match [secondary_unit] in [kill] with a single on-board unit")
|
||||
end
|
||||
end
|
||||
end
|
||||
local dead_men_walking = wesnoth.get_units(cfg)
|
||||
for i,unit in ipairs(dead_men_walking) do
|
||||
local death_loc = {x = tonumber(unit.x) or 0, y = tonumber(unit.y) or 0}
|
||||
if not secondary_unit then killer_loc = death_loc end
|
||||
local can_fire = false
|
||||
|
||||
local recursion = (kill_recursion_preventer:get(death_loc.x, death_loc.y) or 0) + 1
|
||||
if cfg.fire_event then
|
||||
kill_recursion_preventer:insert(death_loc.x, death_loc.y, recursion)
|
||||
can_fire = true
|
||||
if death_loc.x == wesnoth.current.event.x1 and death_loc.y == wesnoth.current.event.y1 then
|
||||
if wesnoth.current.event.name == "die" or wesnoth.current.event.name == "last breath" then
|
||||
if recursion >= 10 then
|
||||
can_fire = false;
|
||||
wesnoth.log("error", "tried to fire 'die' or 'last breath' event on unit from the unit's 'die' or 'last breath' event with first_time_only=no!")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if can_fire then
|
||||
wesnoth.fire_event("last breath", death_loc, killer_loc)
|
||||
end
|
||||
if cfg.animate then
|
||||
wesnoth.scroll_to_tile(death_loc)
|
||||
local anim = wesnoth.create_animator()
|
||||
-- Possible TODO: Add weapon selection? (That's kinda a pain right now; see animate_unit defn)
|
||||
anim:add(unit, "death", "kill")
|
||||
if secondary_unit then
|
||||
anim:add(secondary_unit, "victory", "kill")
|
||||
end
|
||||
anim:run()
|
||||
end
|
||||
wml_actions.redraw{}
|
||||
|
||||
if can_fire then
|
||||
wesnoth.fire_event("die", death_loc, killer_loc)
|
||||
end
|
||||
if cfg.fire_event then
|
||||
if recursion <= 1 then
|
||||
kill_recursion_preventer:remove(death_loc.x, death_loc.y)
|
||||
else
|
||||
kill_recursion_preventer:insert(death_loc.x, death_loc.y, recursion)
|
||||
end
|
||||
end
|
||||
-- Test that it's valid (and still on the map) first, in case the event erased (or extracted) it.
|
||||
if unit.valid == "map" then unit:erase() end
|
||||
|
||||
number_killed = number_killed + 1
|
||||
end
|
||||
|
||||
-- TODO: Do I need to check recall lists or was that covered by the above loop?
|
||||
return number_killed
|
||||
end
|
||||
|
||||
function wml_actions.label( cfg )
|
||||
|
|
|
@ -3231,165 +3231,6 @@ int game_lua_kernel::intf_delay(lua_State *L)
|
|||
return 0;
|
||||
}
|
||||
|
||||
namespace { // Types
|
||||
|
||||
class recursion_preventer {
|
||||
typedef std::map<map_location, int> counter;
|
||||
static counter counter_;
|
||||
static const int max_recursion = 10;
|
||||
|
||||
map_location loc_;
|
||||
bool too_many_recursions_;
|
||||
|
||||
public:
|
||||
recursion_preventer(map_location& loc) :
|
||||
loc_(loc),
|
||||
too_many_recursions_(false)
|
||||
{
|
||||
counter::iterator inserted = counter_.emplace(loc_, 0).first;
|
||||
++inserted->second;
|
||||
too_many_recursions_ = inserted->second >= max_recursion;
|
||||
}
|
||||
~recursion_preventer()
|
||||
{
|
||||
counter::iterator itor = counter_.find(loc_);
|
||||
if (--itor->second == 0)
|
||||
{
|
||||
counter_.erase(itor);
|
||||
}
|
||||
}
|
||||
bool too_many_recursions() const
|
||||
{
|
||||
return too_many_recursions_;
|
||||
}
|
||||
};
|
||||
recursion_preventer::counter recursion_preventer::counter_;
|
||||
typedef std::unique_ptr<recursion_preventer> recursion_preventer_ptr;
|
||||
} // end anonymouse namespace (types)
|
||||
|
||||
int game_lua_kernel::intf_kill(lua_State *L)
|
||||
{
|
||||
vconfig cfg(luaW_checkvconfig(L, 1));
|
||||
|
||||
const game_events::queued_event &event_info = get_event_info();
|
||||
|
||||
size_t number_killed = 0;
|
||||
|
||||
bool secondary_unit = cfg.has_child("secondary_unit");
|
||||
game_events::entity_location killer_loc(map_location(0, 0));
|
||||
if(cfg["fire_event"].to_bool() && secondary_unit)
|
||||
{
|
||||
secondary_unit = false;
|
||||
const unit_filter ufilt(cfg.child("secondary_unit"), &game_state_);
|
||||
for(unit_map::const_unit_iterator unit = units().begin(); unit; ++unit) {
|
||||
if ( ufilt( *unit) )
|
||||
{
|
||||
killer_loc = game_events::entity_location(*unit);
|
||||
secondary_unit = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!secondary_unit) {
|
||||
WRN_LUA << "failed to match [secondary_unit] in [kill] with a single on-board unit" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
//Find all the dead units first, because firing events ruins unit_map iteration
|
||||
std::vector<unit *> dead_men_walking;
|
||||
const unit_filter ufilt(cfg, &game_state_);
|
||||
for (unit & u : units()){
|
||||
if ( ufilt(u) ) {
|
||||
dead_men_walking.push_back(&u);
|
||||
}
|
||||
}
|
||||
|
||||
for(unit * un : dead_men_walking) {
|
||||
map_location loc(un->get_location());
|
||||
bool fire_event = false;
|
||||
game_events::entity_location death_loc(*un);
|
||||
if(!secondary_unit) {
|
||||
killer_loc = game_events::entity_location(*un);
|
||||
}
|
||||
|
||||
if (cfg["fire_event"].to_bool())
|
||||
{
|
||||
// Prevent infinite recursion of 'die' events
|
||||
fire_event = true;
|
||||
recursion_preventer_ptr recursion_prevent;
|
||||
|
||||
if (event_info.loc1 == death_loc && (event_info.name == "die" || event_info.name == "last breath"))
|
||||
{
|
||||
recursion_prevent.reset(new recursion_preventer(death_loc));
|
||||
|
||||
if(recursion_prevent->too_many_recursions())
|
||||
{
|
||||
fire_event = false;
|
||||
|
||||
ERR_LUA << "tried to fire 'die' or 'last breath' event on primary_unit inside its own 'die' or 'last breath' event with 'first_time_only' set to false!" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fire_event) {
|
||||
play_controller_.pump().fire("last_breath", death_loc, killer_loc);
|
||||
}
|
||||
|
||||
// Visual consequences of the kill.
|
||||
if (game_display_) {
|
||||
if (cfg["animate"].to_bool()) {
|
||||
game_display_->scroll_to_tile(loc);
|
||||
if (unit_map::iterator iun = units().find(loc)) {
|
||||
unit_display::unit_die(loc, *iun);
|
||||
}
|
||||
} else {
|
||||
// Make sure the unit gets (fully) cleaned off the screen.
|
||||
game_display_->invalidate(loc);
|
||||
if (unit_map::iterator iun = units().find(loc)) {
|
||||
iun->anim_comp().invalidate(*game_display_);
|
||||
}
|
||||
}
|
||||
game_display_->redraw_minimap();
|
||||
}
|
||||
|
||||
if (fire_event) {
|
||||
play_controller_.pump().fire("die", death_loc, killer_loc);
|
||||
unit_map::iterator iun = units().find(death_loc);
|
||||
if ( death_loc.matches_unit(iun) ) {
|
||||
units().erase(iun);
|
||||
}
|
||||
}
|
||||
else units().erase(loc);
|
||||
|
||||
++number_killed;
|
||||
}
|
||||
|
||||
// If the filter doesn't contain positional information,
|
||||
// then it may match units on all recall lists.
|
||||
const config::attribute_value cfg_x = cfg["x"];
|
||||
const config::attribute_value cfg_y = cfg["y"];
|
||||
if((cfg_x.empty() || cfg_x == "recall")
|
||||
&& (cfg_y.empty() || cfg_y == "recall"))
|
||||
{
|
||||
//remove the unit from the corresponding team's recall list
|
||||
for(std::vector<team>::iterator pi = teams().begin();
|
||||
pi!=teams().end(); ++pi)
|
||||
{
|
||||
for(std::vector<unit_ptr>::iterator j = pi->recall_list().begin(); j != pi->recall_list().end();) { //TODO: This block is really messy, cleanup somehow...
|
||||
scoped_recall_unit auto_store("this_unit", pi->save_id(), j - pi->recall_list().begin());
|
||||
if (ufilt( *(*j), map_location() )) {
|
||||
j = pi->recall_list().erase(j);
|
||||
++number_killed;
|
||||
} else {
|
||||
++j;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lua_pushinteger(L, number_killed);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int game_lua_kernel::intf_label(lua_State *L)
|
||||
{
|
||||
if (game_display_) {
|
||||
|
@ -4163,7 +4004,6 @@ game_lua_kernel::game_lua_kernel(game_state & gs, play_controller & pc, reports
|
|||
{ "get_displayed_unit", &dispatch<&game_lua_kernel::intf_get_displayed_unit > },
|
||||
{ "highlight_hex", &dispatch<&game_lua_kernel::intf_highlight_hex > },
|
||||
{ "is_enemy", &dispatch<&game_lua_kernel::intf_is_enemy > },
|
||||
{ "kill", &dispatch<&game_lua_kernel::intf_kill > },
|
||||
{ "label", &dispatch<&game_lua_kernel::intf_label > },
|
||||
{ "lock_view", &dispatch<&game_lua_kernel::intf_lock_view > },
|
||||
{ "log_replay", &dispatch<&game_lua_kernel::intf_log_replay > },
|
||||
|
|
|
@ -149,7 +149,6 @@ 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_kill(lua_State *L);
|
||||
int intf_label(lua_State *L);
|
||||
int intf_redraw(lua_State *L);
|
||||
int intf_replace_schedule(lua_State *l);
|
||||
|
|
Loading…
Add table
Reference in a new issue