Added patch by Brilliand for bug #2851

(Ghast and Necrophage double feeding)

  1. Added event ids to prevent duplicates

  2. Added event deletion via ids.
This commit is contained in:
Thonsew 2011-08-19 10:54:44 +00:00
parent d8a87d0335
commit aead1784f1
4 changed files with 105 additions and 59 deletions

View file

@ -99,6 +99,8 @@ Version 1.9.8+svn:
* Increased the swamp defense from 10% to 20%
* Decreased the hill defense from 60% to 50%
* WML engine:
* Added [event]id= support (to protect against duplicates)
* Added [event]delete=true support (to remove events that have an id set)
* Implemented sub= and divide= for abilities and weapon specials
(patch #2857)
* new attribute replay_save= in [end_level]. Defaults to yes, allows

View file

@ -893,6 +893,9 @@
[entry]
name = "Ben Anderman (crimson_penguin)"
comment = "unit list"
[/entry]
[entry]
name = "Brilliand"
[/entry]
[entry]
name = "Burkay Özdemir (Velory)"

View file

@ -527,18 +527,9 @@ Enemy units cannot see this unit while it is in deep water, except if they have
This unit gains 1 hitpoint added to its maximum whenever it kills a living unit."
[/dummy] # a hack to please wmlxgettext (using a bug in wmlxgettext!): dummy tag start: [abilities]
[/abilities]
# NOTE: the purpose of the unit.variables.killed_by_feeding variable is to
# prevent multiple instances of feeding stacking with each other: the first
# event will not trigger for units marked as already having been killed by
# feeding on this turn. The turn number is included in the variable value in
# order to decrease the chances of feeding not working on a unit as a result of
# a scenario event storing it upon death and bringing it back later, in which
# case the variable could still be intact. However, this doesn't work in every
# imaginable situation and therefore a better fix is still needed eventually.
[event]
name=last breath
id=ability_feeding_die
name=die
first_time_only=no
[filter]
@ -548,14 +539,6 @@ This unit gains 1 hitpoint added to its maximum whenever it kills a living unit.
not_living="yes"
[/status]
[/filter_wml]
[or]
[filter_wml]
[variables]
killed_by_feeding="on_turn_$turn_number"
[/variables]
[/filter_wml]
[/or]
[/not]
[/filter]
@ -563,13 +546,6 @@ This unit gains 1 hitpoint added to its maximum whenever it kills a living unit.
ability=feeding
[/filter_second]
{VARIABLE unit.variables.killed_by_feeding "on_turn_$turn_number"}
[unstore_unit]
variable=unit
find_vacant=no
[/unstore_unit]
[unstore_unit]
variable=second_unit
{COLOR_HEAL}
@ -592,25 +568,6 @@ This unit gains 1 hitpoint added to its maximum whenever it kills a living unit.
[/effect]
[/object]
[/event]
[event]
name=die
first_time_only=no
[filter]
[filter_wml]
[variables]
killed_by_feeding="on_turn_$turn_number"
[/variables]
[/filter_wml]
[/filter]
{CLEAR_VARIABLE unit.variables.killed_by_feeding}
[unstore_unit]
variable=unit
find_vacant=no
[/unstore_unit]
[/event]
[+abilities] # a hack to please wmlxgettext (using a bug in wmlxgettext!): dummy tag end: [/abilities]
#enddef
# wmllint: unbalanced-off

View file

@ -123,7 +123,6 @@ namespace {
bool manager_running = false;
int floating_label = 0;
std::vector< game_events::event_handler > new_handlers;
typedef std::pair< std::string, config* > wmi_command_change;
std::vector< wmi_command_change > wmi_command_changes;
@ -570,7 +569,92 @@ static map_location cfg_to_loc(const vconfig& cfg,int defaultx = 0, int defaulty
namespace {
std::vector<game_events::event_handler> event_handlers;
class t_event_handlers : public std::vector<game_events::event_handler> {
private:
std::vector<game_events::event_handler> insert_buffer;
std::set<std::string> remove_buffer;
bool buffering;
public:
/**
* Adds an event handler. An event with a nonempty ID will not
* be added if an event with that ID already exists. This method
* respects this class's buffering functionality.
*/
void add_event_handler(game_events::event_handler const & new_handler) {
if(buffering) {
insert_buffer.push_back(new_handler);
} else {
const config & cfg = new_handler.get_config();
std::string id = cfg["id"];
if(id != "") {
for(std::vector<game_events::event_handler>::iterator i = begin(); i < end(); i++) {
const config & temp_config = (*i).get_config();
if(id == temp_config["id"])
return;
}
}
this->push_back(new_handler);
}
}
/**
* Removes an event handler, identified by its ID. Events with
* empty IDs cannot be removed. This method respects this class's
* buffering functionality.
*/
void remove_event_handler(std::string const & id) {
if(id == "")
return;
if(buffering)
remove_buffer.insert(id);
std::vector<game_events::event_handler> &temp = buffering ? insert_buffer : *this;
std::vector<game_events::event_handler>::iterator i = temp.begin();
while(i < temp.end()) {
const config & temp_config = (*i).get_config();
std::string event_id = temp_config["id"];
if(event_id != "" && event_id == id)
i = temp.erase(i);
else
i++;
}
}
/**
* Starts buffering. While buffering, any calls to add_event_handler
* and remove_event_handler will not take effect until commit_buffer
* is called. This function is idempotent - starting a buffer
* when already buffering will not start a second buffer.
*/
void start_buffer() {
buffering = true;
}
/**
* Stops buffering and commits all changes.
*/
void commit_buffer() {
if(!buffering)
return;
buffering = false;
// Commit any event removals
for(std::set<std::string>::iterator i = remove_buffer.begin(); i != remove_buffer.end(); i++)
remove_event_handler(*i);
remove_buffer.clear();
// Commit any spawned events-within-events
for(std::vector<game_events::event_handler>::iterator i = insert_buffer.begin(); i != insert_buffer.end(); i++)
add_event_handler(*i);
insert_buffer.clear();
}
};
t_event_handlers event_handlers;
} // end anonymous namespace (4)
@ -2693,10 +2777,12 @@ WML_HANDLER_FUNCTION(disallow_end_turn, /*event_info*/, /*cfg*/)
// Adding new events
WML_HANDLER_FUNCTION(event, /*event_info*/, cfg)
{
if (!cfg["delayed_variable_substitution"].to_bool(true)) {
new_handlers.push_back(game_events::event_handler(cfg.get_parsed_config()));
if (cfg["remove"].to_bool(false)) {
event_handlers.remove_event_handler(cfg["id"]);
} else if (!cfg["delayed_variable_substitution"].to_bool(true)) {
event_handlers.add_event_handler(game_events::event_handler(cfg.get_parsed_config()));
} else {
new_handlers.push_back(game_events::event_handler(cfg.get_config()));
event_handlers.add_event_handler(game_events::event_handler(cfg.get_config()));
}
}
@ -2767,10 +2853,7 @@ WML_HANDLER_FUNCTION(clear_global_variable,/**/,pcfg)
static void commit_new_handlers() {
// Commit any spawned events-within-events
while(new_handlers.size() > 0) {
event_handlers.push_back(new_handlers.back());
new_handlers.pop_back();
}
event_handlers.commit_buffer();
}
static void commit_wmi_commands() {
// Commit WML Menu Item command changes
@ -2797,7 +2880,7 @@ static void commit_wmi_commands() {
}
} else if(!is_empty_command) {
LOG_NG << "setting command for " << mref->name << " to:\n" << *wcc.second;
event_handlers.push_back(game_events::event_handler(mref->command, true));
event_handlers.add_event_handler(game_events::event_handler(mref->command, true));
}
delete wcc.second;
@ -3068,7 +3151,7 @@ namespace game_events {
{
assert(!manager_running);
foreach (const config &ev, cfg.child_range("event")) {
event_handlers.push_back(game_events::event_handler(ev));
event_handlers.add_event_handler(game_events::event_handler(ev));
}
foreach (const std::string &id, utils::split(cfg["unit_wml_ids"])) {
unit_wml_ids.insert(id);
@ -3092,7 +3175,7 @@ namespace game_events {
typedef std::pair<std::string, wml_menu_item *> item;
foreach (const item &itor, resources::state_of_game->wml_menu_items) {
if (!itor.second->command.empty()) {
event_handlers.push_back(game_events::event_handler(itor.second->command, true));
event_handlers.add_event_handler(game_events::event_handler(itor.second->command, true));
}
++wmi_count;
}
@ -3176,8 +3259,7 @@ namespace game_events {
if(std::find(unit_wml_ids.begin(),unit_wml_ids.end(),id) == unit_wml_ids.end()) {
unit_wml_ids.insert(id);
foreach (const config &new_ev, cfgs) {
std::vector<game_events::event_handler> &temp = (pump_manager::count()) ? new_handlers : event_handlers;
temp.push_back(game_events::event_handler(new_ev));
event_handlers.add_event_handler(game_events::event_handler(new_ev));
}
}
}
@ -3208,6 +3290,8 @@ namespace game_events {
return false;
}
event_handlers.start_buffer();
bool result = false;
while(events_queue.empty() == false) {
game_events::queued_event ev = events_queue.front();