Skip to content

Commit

Permalink
Properly port [kill] to Lua
Browse files Browse the repository at this point in the history
  • Loading branch information
CelticMinstrel committed Apr 5, 2017
1 parent 36dbd7c commit 4c89e93
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 162 deletions.
67 changes: 66 additions & 1 deletion data/lua/wml-tags.lua
Expand Up @@ -947,8 +947,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 = {unit.loc}
if not secondary_unit then killer_loc = death_loc end
local can_fire = false

if cfg.fire_event then
local recursion = (kill_recursion_preventer:get(death_loc.x, death_loc.y) or 0) + 1
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
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
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(unit, "death", "kill")
--[[
if secondary_unit then
anim:add_unit(secondary_unit, "victory", "kill")
end
]]
anim:run()
end
wml_actions.redraw{}

if can_fire then
wesnoth.fire_event("die", death_loc, killer_loc)
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 )
Expand Down
160 changes: 0 additions & 160 deletions src/scripting/game_lua_kernel.cpp
Expand Up @@ -3175,165 +3175,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_.insert(std::make_pair(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_) {
Expand Down Expand Up @@ -4107,7 +3948,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", &dispatch<&game_lua_kernel::intf_log > },
Expand Down
1 change: 0 additions & 1 deletion src/scripting/game_lua_kernel.hpp
Expand Up @@ -150,7 +150,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);
Expand Down

0 comments on commit 4c89e93

Please sign in to comment.