Split game_events.cpp.

VC and XCode projects still need to be updated.
This commit is contained in:
JaMiT 2013-08-11 19:31:20 -05:00
parent 72920fe01c
commit c0435769bd
40 changed files with 3515 additions and 3149 deletions

View file

@ -228,8 +228,16 @@
<Unit filename="../../src/game_display.hpp" />
<Unit filename="../../src/game_end_exceptions.hpp" />
<Unit filename="../../src/game_errors.hpp" />
<Unit filename="../../src/game_events.cpp" />
<Unit filename="../../src/game_events.hpp" />
<Unit filename="../../src/game_events/action_wml.cpp" />
<Unit filename="../../src/game_events/action_wml.hpp" />
<Unit filename="../../src/game_events/conditional_wml.cpp" />
<Unit filename="../../src/game_events/conditional_wml.hpp" />
<Unit filename="../../src/game_events/entity_location.cpp" />
<Unit filename="../../src/game_events/entity_location.hpp" />
<Unit filename="../../src/game_events/handlers.cpp" />
<Unit filename="../../src/game_events/handlers.hpp" />
<Unit filename="../../src/game_events/pump.cpp" />
<Unit filename="../../src/game_events/pump.hpp" />
<Unit filename="../../src/game_preferences.cpp" />
<Unit filename="../../src/game_preferences.hpp" />
<Unit filename="../../src/game_preferences_display.cpp" />

View file

@ -315,8 +315,16 @@
<Unit filename="..\..\src\game_end_exceptions.hpp" />
<Unit filename="..\..\src\game_errors.cpp" />
<Unit filename="..\..\src\game_errors.hpp" />
<Unit filename="..\..\src\game_events.cpp" />
<Unit filename="..\..\src\game_events.hpp" />
<Unit filename="..\..\src\game_events\action_wml.cpp" />
<Unit filename="..\..\src\game_events\action_wml.hpp" />
<Unit filename="..\..\src\game_events\conditional_wml.cpp" />
<Unit filename="..\..\src\game_events\conditional_wml.hpp" />
<Unit filename="..\..\src\game_events\entity_location.cpp" />
<Unit filename="..\..\src\game_events\entity_location.hpp" />
<Unit filename="..\..\src\game_events\handlers.cpp" />
<Unit filename="..\..\src\game_events\handlers.hpp" />
<Unit filename="..\..\src\game_events\pump.cpp" />
<Unit filename="..\..\src\game_events\pump.hpp" />
<Unit filename="..\..\src\game_instance.cpp" />
<Unit filename="..\..\src\game_instance.hpp" />
<Unit filename="..\..\src\game_preferences.cpp" />

View file

@ -3390,6 +3390,18 @@
<File Name="../../src/ai/configuration.hpp"/>
<File Name="../../src/ai/testing.cpp"/>
</VirtualDirectory>
<VirtualDirectory Name="game_events">
<File Name="../../src/game_events/action_wml.cpp" />
<File Name="../../src/game_events/action_wml.hpp" />
<File Name="../../src/game_events/conditional_wml.cpp" />
<File Name="../../src/game_events/conditional_wml.hpp" />
<File Name="../../src/game_events/entity_location.cpp" />
<File Name="../../src/game_events/entity_location.hpp" />
<File Name="../../src/game_events/handlers.cpp" />
<File Name="../../src/game_events/handlers.hpp" />
<File Name="../../src/game_events/pump.cpp" />
<File Name="../../src/game_events/pump.hpp" />
</VirtualDirectory>
<VirtualDirectory Name="gui">
<VirtualDirectory Name="lib">
<VirtualDirectory Name="types">
@ -4227,7 +4239,6 @@
<File Name="../../src/playsingle_controller.hpp"/>
<File Name="../../src/color_range.cpp"/>
<File Name="../../src/log.cpp"/>
<File Name="../../src/game_events.hpp"/>
<File Name="../../src/joystick.hpp"/>
<File Name="../../src/font.cpp"/>
<File Name="../../src/marked-up_text.cpp"/>
@ -4380,7 +4391,6 @@
<File Name="../../src/color_range.hpp"/>
<File Name="../../src/config_cache.cpp"/>
<File Name="../../src/wml_separators.hpp"/>
<File Name="../../src/game_events.cpp"/>
<File Name="../../src/network.cpp"/>
<File Name="../../src/playmp_controller.cpp"/>
<File Name="../../src/Makefile"/>

View file

@ -690,7 +690,11 @@ set(wesnoth-main_SRC
game_controller.cpp
game_display.cpp
game_errors.cpp
game_events.cpp
game_events/action_wml.cpp
game_events/conditional_wml.cpp
game_events/entity_location.cpp
game_events/handlers.cpp
game_events/pump.cpp
game_instance.cpp
game_preferences.cpp
game_preferences_display.cpp

View file

@ -266,7 +266,11 @@ wesnoth_sources = Split("""
game_controller.cpp
game_display.cpp
game_errors.cpp
game_events.cpp
game_events/action_wml.cpp
game_events/conditional_wml.cpp
game_events/entity_location.cpp
game_events/handlers.cpp
game_events/pump.cpp
game_instance.cpp
game_preferences.cpp
gamestatus.cpp

View file

@ -24,7 +24,7 @@
#include "../attack_prediction.hpp"
#include "../game_config.hpp"
#include "../game_display.hpp"
#include "../game_events.hpp"
#include "../game_events/pump.hpp"
#include "../game_preferences.hpp"
#include "../gamestatus.hpp"
#include "../gettext.hpp"

View file

@ -25,7 +25,7 @@
#include "../config.hpp"
#include "../game_display.hpp"
#include "../game_events.hpp"
#include "../game_events/pump.hpp"
#include "../game_preferences.hpp"
#include "../gamestatus.hpp"
#include "../gettext.hpp"

View file

@ -23,7 +23,7 @@
#include "vision.hpp"
#include "../game_display.hpp"
#include "../game_events.hpp"
#include "../game_events/pump.hpp"
#include "../game_preferences.hpp"
#include "../gettext.hpp"
#include "../hotkeys.hpp"

View file

@ -23,7 +23,6 @@
#include "move.hpp"
#include "../game_display.hpp"
#include "../game_events.hpp"
#include "../log.hpp"
#include "../play_controller.hpp"
#include "../replay.hpp"

View file

@ -23,7 +23,7 @@
#include "../config.hpp"
#include "../game_display.hpp"
#include "../game_events.hpp"
#include "../game_events/pump.hpp"
#include "../log.hpp"
#include "../map.hpp"
#include "../map_label.hpp"

View file

@ -33,8 +33,8 @@
#include "../formula_function.hpp"
#include "../formula_fwd.hpp"
#include "../game_display.hpp"
#include "../game_events.hpp"
#include "../log.hpp"
#include "../map.hpp"
#include "../mouse_handler_base.hpp"
#include "../resources.hpp"
#include "../tod_manager.hpp"

View file

@ -25,7 +25,7 @@
#include "../../array.hpp"
#include "../../dialogs.hpp"
#include "../../game_events.hpp"
#include "../../game_events/pump.hpp"
#include "../../gamestatus.hpp"
#include "../../log.hpp"
#include "../../mouse_handler_base.hpp"

View file

@ -24,7 +24,7 @@
#include "manager.hpp"
#include "formula/ai.hpp"
#include "registry.hpp"
#include "../game_events.hpp"
#include "../game_events/pump.hpp"
#include "../log.hpp"
#include "../serialization/string_utils.hpp"
#include "composite/component.hpp"

View file

@ -22,7 +22,6 @@
#include "actions/attack.hpp"
#include "actions/undo.hpp"
#include "dialogs.hpp"
#include "game_events.hpp"
#include "game_display.hpp"
#include "game_preferences.hpp"
#include "gui/dialogs/game_delete.hpp"

View file

@ -1,239 +0,0 @@
/*
Copyright (C) 2003 - 2013 by David White <dave@whitevine.net>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#ifndef GAME_EVENTS_H_INCLUDED
#define GAME_EVENTS_H_INCLUDED
#include "map.hpp"
#include "unit_map.hpp"
#include "variable.hpp"
/**
* @file
* Define the game's events mechanism.
*
* Events might be units moving or fighting, or when victory or defeat occurs.
* A scenario's configuration file will define actions to take when certain events occur.
* This module is responsible for making sure that when the events occur, the actions take place.
*
* Note that game events have nothing to do with SDL events,
* like mouse movement, keyboard events, etc.
* See events.hpp for how they are handled.
*/
class unit;
namespace game_events
{
class event_handler;
/**
* Changes a terrain location.
* Ensures that villages are properly lost and that new terrains are discovered.
*/
void change_terrain(const map_location &loc, const t_translation::t_terrain &t,
gamemap::tmerge_mode mode, bool replace_if_failed);
// The game event manager loads the scenario configuration object,
// and ensures that events are handled according to the
// scenario configuration for its lifetime.
//
// Thus, a manager object should be created when a scenario is played,
// and destroyed at the end of the scenario.
struct manager {
/// Note that references will be maintained,
/// and must remain valid for the life of the object.
manager(const config& scenario_cfg);
~manager();
// Allow iterating over the active handlers.
typedef std::vector<event_handler> t_active;
typedef t_active::iterator iterator;
static iterator begin();
static iterator end();
/// Starts buffering event handler creation.
static void start_buffering();
/// Ends buffering event handler creation.
static void stop_buffering();
/// Commits the event handlers that were buffered.
static void commit_buffer();
/// Returns true when a manager exists, so events can be processed.
static bool running() { return running_; }
private:
variable::manager variable_manager;
static bool running_;
};
struct entity_location : public map_location {
entity_location(const map_location &loc, size_t id = 0);
entity_location(const map_location &loc, size_t id,
const map_location &filter_loc);
explicit entity_location(const unit &);
entity_location(const unit &u, const map_location &filter_loc);
int filter_x() const { return filter_loc_.x; }
int filter_y() const { return filter_loc_.y; }
bool matches_unit(const unit_map::const_iterator & un_it) const;
bool matches_unit_filter(const unit_map::const_iterator & un_it,
const vconfig & filter) const;
static const entity_location null_entity;
private:
/// The underlying ID of the unit associated with this.
/// Set to 0 if there is no associated unit.
size_t id_;
/// This map_location allows a unit to be filtered as if it were
/// somewhere other than where it is. (Use the parent struct if
/// you want to locate the unit.)
map_location filter_loc_;
};
struct queued_event {
queued_event(const std::string& name, const entity_location& loc1,
const entity_location& loc2,
const config& data)
: name(name), loc1(loc1), loc2(loc2),data(data) {}
std::string name;
entity_location loc1;
entity_location loc2;
config data;
};
class event_handler
{
public:
event_handler(const config &cfg, bool is_menu_item = false);
bool matches_name(const std::string& name) const;
bool disabled() const { return disabled_; }
bool is_menu_item() const { return is_menu_item_; }
void handle_event(const queued_event& event_info);
const config &get_config() const { return cfg_; }
private:
bool first_time_only_;
bool disabled_;
bool is_menu_item_;
config cfg_;
};
/**
* Runs the action handler associated to the command sequence @a cfg.
*/
void handle_event_commands(const queued_event &event_info, const vconfig &cfg);
/**
* Runs the action handler associated to @a cmd with parameters @a cfg.
*/
void handle_event_command(const std::string &cmd,
const queued_event &event_info, const vconfig &cfg);
void write_events(config& cfg);
void add_events(
const config::const_child_itors &cfgs,
const std::string& type = std::string());
/** Used for [wml_message]. */
void handle_wml_log_message(const config& cfg);
/** Used for [deprecated_message]. */
void handle_deprecated_message(const config& cfg);
/**
* Function to fire an event.
*
* Events may have up to two arguments, both of which must be locations.
*/
bool fire(const std::string& event,
const entity_location& loc1=entity_location::null_entity,
const entity_location& loc2=entity_location::null_entity,
const config& data=config());
void raise(const std::string& event,
const entity_location& loc1=entity_location::null_entity,
const entity_location& loc2=entity_location::null_entity,
const config& data=config());
bool conditional_passed(const vconfig& cond, bool backwards_compat=true);
/**
* Handles newly-created handlers. Flushes WML messages and errors.
*/
void commit();
bool pump();
typedef void (*action_handler)(const queued_event &, const vconfig &);
/**
* This function can be used to detect when no WML/Lua has been executed.
*/
size_t wml_tracking();
// Declarations that will be more useful after the split:
typedef void (*wml_handler_function)(const queued_event &event_info,
const vconfig &cfg);
/// Create an event handler.
void add_event_handler(const config & event);
/// Add a pending menu item command change.
void add_wmi_change(const std::string & id, const config & new_command);
/// Removes an event handler.
void remove_event_handler(const std::string & id);
/// Removes a pending menu item command change.
void remove_wmi_change(const std::string & id);
/// Handles all the different types of actions that can be triggered by an event.
void commit_wmi_commands();
bool matches_special_filter(const config &cfg, const vconfig& filter);
/// Helper function which determines whether a wml_message text can
/// really be pushed into the wml_messages_stream, and does it.
void put_wml_message(const std::string& logger, const std::string& message);
/// Registers a standard action handler.
void register_action(const std::string & tag, wml_handler_function handler);
/// Checks if an item has been used.
bool item_used(const std::string & id);
/// Records if an item has been used.
void item_used(const std::string & id, bool used);
/// Returns whether or not we are skipping messages.
bool skip_messages();
/// Sets whether or not we are skipping messages.
void skip_messages(bool skip);
/// Returns whether or not the screen (map visuals) needs to be rebuilt.
bool screen_needs_rebuild();
/// Sets whether or not the screen (map visuals) needs to be rebuilt.
void screen_needs_rebuild(bool rebuild);
/// Returns whether or not we believe WML might have changed something.
bool context_mutated();
/// Sets whether or not we believe WML might have changed something.
void context_mutated(bool mutated);
/// Clears all events tha have been raised (and not pumped).
void clear_events();
} // end namespace game_events
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,57 @@
/*
Copyright (C) 2003 - 2013 by David White <dave@whitevine.net>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
/**
* @file
* Define actions for the game's events mechanism.
*
* Events might be units moving or fighting, or when victory or defeat occurs.
* A scenario's configuration file will define actions to take when certain events occur.
* This module is responsible for implementing some of those actions (i.e. Action WML).
* The actions not implemented in this module are implemented in Lua.
*/
#ifndef GAME_EVENTS_ACTION_WML_H_INCLUDED
#define GAME_EVENTS_ACTION_WML_H_INCLUDED
#include "../map.hpp"
class config;
struct map_location;
namespace t_translation {
struct t_terrain;
}
namespace game_events
{
// Most of the functionality in the source file is accessed via callbacks,
// registered with register_action().
/**
* Changes a terrain location.
* Ensures that villages are properly lost and that new terrains are discovered.
*/
void change_terrain(const map_location &loc, const t_translation::t_terrain &t,
gamemap::tmerge_mode mode, bool replace_if_failed);
/** Used for [deprecated_message]. */
void handle_deprecated_message(const config& cfg);
/** Used for [wml_message]. */
void handle_wml_log_message(const config& cfg);
}
#endif // GAME_EVENTS_ACTION_WML_H_INCLUDED

View file

@ -0,0 +1,236 @@
/*
Copyright (C) 2003 - 2013 by David White <dave@whitevine.net>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
/**
* @file
* Implementations of conditional action WML tags.
*/
#include "../global.hpp"
#include "conditional_wml.hpp"
#include "../config.hpp"
#include "../gamestatus.hpp"
#include "../log.hpp"
#include "../resources.hpp"
#include "../serialization/string_utils.hpp"
#include "../team.hpp"
#include "../terrain_filter.hpp"
#include "../unit.hpp"
#include "../unit_map.hpp"
#include "../unit_types.hpp"
#include "../util.hpp"
#include "../variable.hpp"
#include <boost/foreach.hpp>
static lg::log_domain log_engine("engine");
#define WRN_NG LOG_STREAM(warn, log_engine)
// This file is in the game_events namespace.
namespace game_events {
namespace { // Support functions
bool internal_conditional_passed(const vconfig& cond, bool& backwards_compat)
{
static std::vector<std::pair<int,int> > default_counts = utils::parse_ranges("1-99999");
// If the if statement requires we have a certain unit,
// then check for that.
const vconfig::child_list& have_unit = cond.get_children("have_unit");
backwards_compat = backwards_compat && have_unit.empty();
for(vconfig::child_list::const_iterator u = have_unit.begin(); u != have_unit.end(); ++u) {
if(resources::units == NULL)
return false;
std::vector<std::pair<int,int> > counts = (*u).has_attribute("count")
? utils::parse_ranges((*u)["count"]) : default_counts;
int match_count = 0;
BOOST_FOREACH(const unit &i, *resources::units)
{
if ( i.hitpoints() > 0 && i.matches_filter(*u) ) {
++match_count;
if(counts == default_counts) {
// by default a single match is enough, so avoid extra work
break;
}
}
}
if ((*u)["search_recall_list"].to_bool())
{
for(std::vector<team>::iterator team = resources::teams->begin();
team!=resources::teams->end(); ++team)
{
if(counts == default_counts && match_count) {
break;
}
const std::vector<unit>& avail_units = team->recall_list();
for(std::vector<unit>::const_iterator unit = avail_units.begin(); unit!=avail_units.end(); ++unit) {
if(counts == default_counts && match_count) {
break;
}
scoped_recall_unit auto_store("this_unit", team->save_id(), unit - avail_units.begin());
if ( unit->matches_filter(*u) ) {
++match_count;
}
}
}
}
if(!in_ranges(match_count, counts)) {
return false;
}
}
// If the if statement requires we have a certain location,
// then check for that.
const vconfig::child_list& have_location = cond.get_children("have_location");
backwards_compat = backwards_compat && have_location.empty();
for(vconfig::child_list::const_iterator v = have_location.begin(); v != have_location.end(); ++v) {
std::set<map_location> res;
terrain_filter(*v, *resources::units).get_locations(res);
std::vector<std::pair<int,int> > counts = (*v).has_attribute("count")
? utils::parse_ranges((*v)["count"]) : default_counts;
if(!in_ranges<int>(res.size(), counts)) {
return false;
}
}
// Check against each variable statement,
// to see if the variable matches the conditions or not.
const vconfig::child_list& variables = cond.get_children("variable");
backwards_compat = backwards_compat && variables.empty();
BOOST_FOREACH(const vconfig &values, variables)
{
const std::string name = values["name"];
config::attribute_value value = resources::gamedata->get_variable_const(name);
#define TEST_STR_ATTR(name, test) do { \
if (values.has_attribute(name)) { \
std::string attr_str = values[name].str(); \
std::string str_value = value.str(); \
if (!(test)) return false; \
} \
} while (0)
#define TEST_NUM_ATTR(name, test) do { \
if (values.has_attribute(name)) { \
double attr_num = values[name].to_double(); \
double num_value = value.to_double(); \
if (!(test)) return false; \
} \
} while (0)
#define TEST_BOL_ATTR(name, test) do { \
if (values.has_attribute(name)) { \
bool attr_bool = values[name].to_bool(); \
bool bool_value = value.to_bool(); \
if (!(test)) return false; \
} \
} while (0)
TEST_STR_ATTR("equals", str_value == attr_str);
TEST_STR_ATTR("not_equals", str_value != attr_str);
TEST_NUM_ATTR("numerical_equals", num_value == attr_num);
TEST_NUM_ATTR("numerical_not_equals", num_value != attr_num);
TEST_NUM_ATTR("greater_than", num_value > attr_num);
TEST_NUM_ATTR("less_than", num_value < attr_num);
TEST_NUM_ATTR("greater_than_equal_to", num_value >= attr_num);
TEST_NUM_ATTR("less_than_equal_to", num_value <= attr_num);
TEST_BOL_ATTR("boolean_equals", bool_value == attr_bool);
TEST_BOL_ATTR("boolean_not_equals", bool_value != attr_bool);
TEST_STR_ATTR("contains", str_value.find(attr_str) != std::string::npos);
#undef TEST_STR_ATTR
#undef TEST_NUM_ATTR
#undef TEST_BOL_ATTR
}
return true;
}
} // end anonymous namespace (support functions)
bool conditional_passed(const vconfig& cond, bool backwards_compat)
{
bool allow_backwards_compat = backwards_compat = backwards_compat &&
cond["backwards_compat"].to_bool(true);
bool matches = internal_conditional_passed(cond, allow_backwards_compat);
// Handle [and], [or], and [not] with in-order precedence
int or_count = 0;
vconfig::all_children_iterator cond_i = cond.ordered_begin();
vconfig::all_children_iterator cond_end = cond.ordered_end();
while(cond_i != cond_end)
{
const std::string& cond_name = cond_i.get_key();
const vconfig& cond_filter = cond_i.get_child();
// Handle [and]
if(cond_name == "and")
{
matches = matches && conditional_passed(cond_filter, backwards_compat);
backwards_compat = false;
}
// Handle [or]
else if(cond_name == "or")
{
matches = matches || conditional_passed(cond_filter, backwards_compat);
++or_count;
}
// Handle [not]
else if(cond_name == "not")
{
matches = matches && !conditional_passed(cond_filter, backwards_compat);
backwards_compat = false;
}
++cond_i;
}
// Check for deprecated [or] syntax
if(matches && or_count > 1 && allow_backwards_compat)
{
///@deprecated r18803 [or] syntax
lg::wml_error << "possible deprecated [or] syntax: now forcing re-interpretation\n";
/**
* @todo For now we will re-interpret it according to the old
* rules, but this should be later to prevent re-interpretation
* errors.
*/
const vconfig::child_list& orcfgs = cond.get_children("or");
for(unsigned int i=0; i < orcfgs.size(); ++i) {
if(conditional_passed(orcfgs[i])) {
return true;
}
}
return false;
}
return matches;
}
bool matches_special_filter(const config &cfg, const vconfig& filter)
{
if (!cfg) {
WRN_NG << "attempt to filter attack for an event with no attack data.\n";
// better to not execute the event (so the problem is more obvious)
return false;
}
const attack_type attack(cfg);
return attack.matches_filter(filter.get_parsed_config());
}
} // end namespace game_events

View file

@ -0,0 +1,35 @@
/*
Copyright (C) 2003 - 2013 by David White <dave@whitevine.net>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
/**
* @file
* Define conditionals for the game's events mechanism,
* a.k.a. conditional actions WML.
*/
#ifndef GAME_EVENTS_CONDITIONAL_WML_H_INCLUDED
#define GAME_EVENTS_CONDITIONAL_WML_H_INCLUDED
class config;
class vconfig;
namespace game_events
{
bool conditional_passed(const vconfig& cond, bool backwards_compat=true);
bool matches_special_filter(const config &cfg, const vconfig& filter);
}
#endif // GAME_EVENTS_CONDITIONAL_WML_H_INCLUDED

View file

@ -0,0 +1,106 @@
/*
Copyright (C) 2003 - 2013 by David White <dave@whitevine.net>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
/**
* @file
* The structure that tracks WML event locations.
*/
#include "global.hpp"
#include "entity_location.hpp"
#include "../unit.hpp"
#include "../variable.hpp"
// This file is in the game_events namespace.
namespace game_events {
const entity_location entity_location::null_entity(map_location::null_location);
/**
* Constructor for when an event has a location but not necessarily a unit.
* Can also be used if the event has a unit and the caller already has the
* unit's location and underlying ID.
*/
entity_location::entity_location(const map_location &loc, size_t id)
: map_location(loc), id_(id), filter_loc_(loc)
{}
/**
* Constructor for when an event has a unit that needs to be filtered as if
* it was in a different location.
*/
entity_location::entity_location(const map_location &loc, size_t id,
const map_location & filter_loc)
: map_location(loc), id_(id), filter_loc_(filter_loc)
{}
/**
* Convenience constructor for when an event has a unit, saving the caller
* the need to explicitly get the location and underlying ID.
*/
entity_location::entity_location(const unit &u)
: map_location(u.get_location())
, id_(u.underlying_id())
, filter_loc_(*this)
{}
/**
* Convenience constructor for when an event has a unit that needs to be
* filtered as if it was in a different location, and the caller does not
* want to explicitly get the unit's location and underlying ID.
*/
entity_location::entity_location(const unit &u, const map_location & filter_loc)
: map_location(u.get_location())
, id_(u.underlying_id())
, filter_loc_(filter_loc)
{}
/**
* Determines if @a un_it matches (using underlying ID) the unit that was
* supplied when this was constructed.
* If no unit was supplied, then all units (including non-existent units)
* match.
*/
bool entity_location::matches_unit(const unit_map::const_iterator & un_it) const
{
return id_ == 0 || ( un_it.valid() && id_ == un_it->underlying_id() );
}
/**
* Determines if @a un_it matches @a filter. If the filter is not empty,
* the unit is required to additionally match the unit that was supplied
* when this was constructed.
*/
bool entity_location::matches_unit_filter(const unit_map::const_iterator & un_it,
const vconfig & filter) const
{
if ( !un_it.valid() )
return false;
if ( filter.empty() )
// Skip the check for un_it matching *this.
return true;
// Filter the unit at the filter location (should be the unit's
// location if no special filter location was specified).
return un_it->matches_filter(filter, filter_loc_) &&
matches_unit(un_it);
}
} // end namespace game_events

View file

@ -0,0 +1,60 @@
/*
Copyright (C) 2003 - 2013 by David White <dave@whitevine.net>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
/**
* @file
* Define locations as used by the game's events mechanism.
*/
#ifndef GAME_EVENTS_ENTITY_LOCATION_H_INCLUDED
#define GAME_EVENTS_ENTITY_LOCATION_H_INCLUDED
#include "../map_location.hpp"
#include "../unit_map.hpp"
class unit;
class vconfig;
namespace game_events
{
struct entity_location : public map_location {
entity_location(const map_location &loc, size_t id = 0);
entity_location(const map_location &loc, size_t id,
const map_location &filter_loc);
explicit entity_location(const unit &);
entity_location(const unit &u, const map_location &filter_loc);
int filter_x() const { return filter_loc_.x; }
int filter_y() const { return filter_loc_.y; }
bool matches_unit(const unit_map::const_iterator & un_it) const;
bool matches_unit_filter(const unit_map::const_iterator & un_it,
const vconfig & filter) const;
static const entity_location null_entity;
private:
/// The underlying ID of the unit associated with this.
/// Set to 0 if there is no associated unit.
size_t id_;
/// This map_location allows a unit to be filtered as if it were
/// somewhere other than where it is. (Use the parent struct if
/// you want to locate the unit.)
map_location filter_loc_;
};
}
#endif // GAME_EVENTS_ENTITY_LOCATION_H_INCLUDED

View file

@ -0,0 +1,541 @@
/*
Copyright (C) 2003 - 2013 by David White <dave@whitevine.net>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
/**
* @file
* The structure that tracks WML event handlers.
* (Typically, handlers are defined by [event] tags.)
*/
#include "global.hpp"
#include "handlers.hpp"
#include "pump.hpp"
#include "../formula_string_utils.hpp"
#include "../gamestatus.hpp"
#include "../log.hpp"
#include "../reports.hpp"
#include "../resources.hpp"
#include "../scripting/lua.hpp"
#include "../serialization/string_utils.hpp"
#include "../soundsource.hpp"
#include <boost/foreach.hpp>
#include <iostream>
static lg::log_domain log_engine("engine");
#define DBG_NG LOG_STREAM(debug, log_engine)
#define LOG_NG LOG_STREAM(info, log_engine)
#define WRN_NG LOG_STREAM(warn, log_engine)
static lg::log_domain log_event_handler("event_handler");
#define DBG_EH LOG_STREAM(debug, log_event_handler)
// This file is in the game_events namespace.
namespace game_events {
namespace { // Types
typedef std::map<std::string, wml_handler_function> static_wml_action_map;
typedef std::pair< std::string, config* > wmi_command_change;
class t_event_handlers {
typedef manager::t_active t_active;
public:
typedef t_active::iterator iterator;
typedef t_active::const_iterator const_iterator;
private:
t_active active_; ///Active event handlers
t_active insert_buffer_; ///Event handlers added while pumping events
std::set<std::string> remove_buffer_; ///Event handlers removed while pumping events
bool buffering_;
void log_handler(std::stringstream& ss,
const std::vector<event_handler>& handlers,
const std::string& msg)
{
BOOST_FOREACH(const event_handler& h, handlers){
const config& cfg = h.get_config();
ss << "name=" << cfg["name"] << ", with id=" << cfg["id"] << "; ";
}
DBG_EH << msg << " handlers are now " << ss.str() << "\n";
ss.str(std::string());
}
void log_handlers()
{
if(lg::debug.dont_log("event_handler")) return;
std::stringstream ss;
log_handler(ss, active_, "active");
log_handler(ss, insert_buffer_, "insert buffered");
BOOST_FOREACH(const std::string& h, remove_buffer_){
ss << "id=" << h << "; ";
}
DBG_EH << "remove buffered handlers are now " << ss.str() << "\n";
}
public:
t_event_handlers()
: active_(), insert_buffer_(), remove_buffer_(), buffering_(false)
{}
/**
* 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(event_handler const & new_handler)
{
if(buffering_) {
DBG_EH << "buffering event handler for name=" << new_handler.get_config()["name"] <<
" with id " << new_handler.get_config()["id"] << "\n";
insert_buffer_.push_back(new_handler);
log_handlers();
}
else {
const config & cfg = new_handler.get_config();
std::string id = cfg["id"];
if(!id.empty()) {
BOOST_FOREACH( event_handler const & eh, active_ ) {
config const & temp_config(eh.get_config());
if(id == temp_config["id"]) {
DBG_EH << "ignoring event handler for name=" << cfg["name"] <<
" with id " << id << "\n";
return;
}
}
}
DBG_EH << "inserting event handler for name=" << cfg["name"] <<
" with id=" << id << "\n";
active_.push_back(new_handler);
log_handlers();
}
}
/**
* 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; }
DBG_EH << "removing event handler with id " << id << "\n";
if(buffering_) { remove_buffer_.insert(id); }
t_active &temp = buffering_ ? insert_buffer_ : active_;
t_active::iterator i = temp.begin();
while(i < temp.end()) {
config const & temp_config = (*i).get_config();
std::string event_id = temp_config["id"];
if(event_id != "" && event_id == id) {
i = temp.erase(i); }
else {
++i; }
}
log_handlers();
}
/**
* 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_buffering()
{
buffering_ = true;
DBG_EH << "starting buffering...\n";
}
void stop_buffering()
{
DBG_EH << "stopping buffering...\n";
buffering_ = false;
}
/**
* Commits all buffered events
*/
void commit_buffer()
{
DBG_EH << "committing buffered event handlers, buffering: " << buffering_ << "\n";
if(buffering_)
return;
// Commit any event removals
BOOST_FOREACH( std::string const & i, remove_buffer_ ){
remove_event_handler(i); }
remove_buffer_.clear();
// Commit any spawned events-within-events
BOOST_FOREACH( event_handler const & i, insert_buffer_ ){
add_event_handler(i); }
insert_buffer_.clear();
log_handlers();
}
void clear()
{
active_.clear();
insert_buffer_.clear();
remove_buffer_.clear();
buffering_ = false;
}
iterator begin() { return active_.begin(); }
const_iterator begin() const { return active_.begin(); }
iterator end() { return active_.end(); }
const_iterator end() const { return active_.end(); }
};
}
namespace { // Variables
t_event_handlers event_handlers;
/** Map of the default action handlers known of the engine. */
static_wml_action_map static_wml_actions;
std::set<std::string> unit_wml_ids;
std::set<std::string> used_items;
std::vector< wmi_command_change > wmi_command_changes;
} // end anonymous namespace (variables)
/** Create an event handler. */
void add_event_handler(const config & handler)
{
event_handlers.add_event_handler(event_handler(handler));
}
/** Add a pending menu item command change. */
void add_wmi_change(const std::string & id, const config & new_command)
{
wmi_command_changes.push_back(wmi_command_change(id, new config(new_command)));
}
/** Handles all the different types of actions that can be triggered by an event. */
void commit_wmi_commands()
{
// Commit WML Menu Item command changes
while(wmi_command_changes.size() > 0) {
wmi_command_change wcc = wmi_command_changes.front();
const bool is_empty_command = wcc.second->empty();
wml_menu_item*& mref = resources::gamedata->get_wml_menu_items().get_item(wcc.first);
const bool has_current_handler = !mref->command.empty();
config::attribute_value event_id = (*wcc.second)["id"];
if(event_id.empty()) {
event_id = mref->event_id;
if(!event_id.empty())
(*wcc.second)["id"] = event_id;
}
mref->command = *(wcc.second);
mref->command["name"] = mref->name;
mref->command["first_time_only"] = false;
if(has_current_handler) {
BOOST_FOREACH(event_handler& hand, event_handlers) {
if(hand.is_menu_item() && hand.matches_name(mref->name)) {
LOG_NG << "changing command for " << mref->name << " to:\n" << *wcc.second;
hand = event_handler(mref->command, true);
}
}
} else if(!is_empty_command) {
LOG_NG << "setting command for " << mref->name << " to:\n" << *wcc.second;
event_handlers.add_event_handler(event_handler(mref->command, true));
}
delete wcc.second;
wmi_command_changes.erase(wmi_command_changes.begin());
}
}
/**
* Checks if an item has been used.
* (An empty id will never be considered used.)
*/
bool item_used(const std::string & id)
{
return !id.empty() && used_items.count(id) > 0;
}
/** Records if an item has been used. */
void item_used(const std::string & id, bool used)
{
// Empty IDs are not tracked.
if ( id.empty() )
return;
if ( used )
used_items.insert(id);
else
used_items.erase(id);
}
/** Registers a standard action handler. */
void register_action(const std::string & tag, wml_handler_function handler)
{
static_wml_actions[tag] = handler;
}
/** Removes a pending menu item command change. */
void remove_wmi_change(const std::string & id)
{
std::vector<wmi_command_change>::iterator wcc = wmi_command_changes.begin();
while ( wcc != wmi_command_changes.end() ) {
if ( wcc->first != id ) {
++wcc;
continue;
}
delete wcc->second;
wcc->second = NULL;
wcc = wmi_command_changes.erase(wcc);
}
}
/** Removes an event handler. */
void remove_event_handler(const std::string & id)
{
event_handlers.remove_event_handler(id);
}
bool manager::running_ = false;
manager::manager(const config& cfg)
: variable_manager()
{
assert(!running_);
BOOST_FOREACH(const config &ev, cfg.child_range("event")) {
add_event_handler(ev);
}
BOOST_FOREACH(const std::string &id, utils::split(cfg["unit_wml_ids"])) {
unit_wml_ids.insert(id);
}
resources::lua_kernel = new LuaKernel(cfg);
running_ = true;
BOOST_FOREACH(static_wml_action_map::value_type &action, static_wml_actions) {
resources::lua_kernel->set_wml_action(action.first, action.second);
}
const std::string used = cfg["used_items"];
if(!used.empty()) {
const std::vector<std::string>& v = utils::split(used);
for(std::vector<std::string>::const_iterator i = v.begin(); i != v.end(); ++i) {
item_used(*i, true);
}
}
int wmi_count = 0;
typedef std::pair<std::string, wml_menu_item *> item;
BOOST_FOREACH(const item &itor, resources::gamedata->get_wml_menu_items().get_menu_items()) {
if (!itor.second->command.empty()) {
event_handlers.add_event_handler(event_handler(itor.second->command, true));
}
++wmi_count;
}
if(wmi_count > 0) {
LOG_NG << wmi_count << " WML menu items found, loaded." << std::endl;
}
}
manager::~manager() {
assert(running_);
running_ = false;
clear_events();
event_handlers.clear();
reports::reset_generators();
delete resources::lua_kernel;
resources::lua_kernel = NULL;
unit_wml_ids.clear();
used_items.clear();
}
/** Returns an iterator to the first event handler. */
manager::iterator manager::begin()
{
return event_handlers.begin();
}
/** Returns an iterator to one past the last event handler. */
manager::iterator manager::end()
{
return event_handlers.end();
}
/** Starts buffering event handler creation. */
void manager::start_buffering()
{
event_handlers.start_buffering();
}
/** Ends buffering event handler creation. */
void manager::stop_buffering()
{
event_handlers.stop_buffering();
}
/** Commits the event handlers that were buffered. */
void manager::commit_buffer()
{
event_handlers.commit_buffer();
}
event_handler::event_handler(const config &cfg, bool imi) :
first_time_only_(cfg["first_time_only"].to_bool(true)),
disabled_(false), is_menu_item_(imi), cfg_(cfg)
{}
void event_handler::handle_event(const queued_event& event_info)
{
if (first_time_only_)
{
disabled_ = true;
}
if (is_menu_item_) {
DBG_NG << cfg_["name"] << " will now invoke the following command(s):\n" << cfg_;
}
handle_event_commands(event_info, vconfig(cfg_));
}
bool event_handler::matches_name(const std::string &name) const
{
const std::string my_names =
utils::interpolate_variables_into_string(cfg_["name"], *(resources::gamedata));
std::string::const_iterator itor,
it_begin = my_names.begin(),
it_end = my_names.end(),
match_it = name.begin(),
match_begin = name.begin(),
match_end = name.end();
int skip_count = 0;
for(itor = it_begin; itor != it_end; ++itor) {
bool do_eat = false,
do_skip = false;
switch(*itor) {
case ',':
if(itor - it_begin - skip_count == match_it - match_begin && match_it == match_end) {
return true;
}
it_begin = itor + 1;
match_it = match_begin;
skip_count = 0;
continue;
case '\f':
case '\n':
case '\r':
case '\t':
case '\v':
do_skip = (match_it == match_begin || match_it == match_end);
break;
case ' ':
do_skip = (match_it == match_begin || match_it == match_end);
// fall through to case '_'
case '_':
do_eat = (match_it != match_end && (*match_it == ' ' || *match_it == '_'));
break;
default:
do_eat = (match_it != match_end && *match_it == *itor);
break;
}
if(do_eat) {
++match_it;
} else if(do_skip) {
++skip_count;
} else {
itor = std::find(itor, it_end, ',');
if(itor == it_end) {
return false;
}
it_begin = itor + 1;
match_it = match_begin;
skip_count = 0;
}
}
if(itor - it_begin - skip_count == match_it - match_begin && match_it == match_end) {
return true;
}
return false;
}
void add_events(const config::const_child_itors &cfgs, const std::string& type)
{
if(!type.empty()) {
if(std::find(unit_wml_ids.begin(),unit_wml_ids.end(),type) != unit_wml_ids.end()) return;
unit_wml_ids.insert(type);
}
BOOST_FOREACH(const config &new_ev, cfgs) {
if(type.empty() && new_ev["id"].empty())
{
WRN_NG << "attempt to add an [event] with empty id=, ignoring \n";
continue;
}
add_event_handler(new_ev);
}
}
void write_events(config& cfg)
{
assert(manager::running());
BOOST_FOREACH(const event_handler &eh, event_handlers) {
if ( eh.disabled() || eh.is_menu_item() ) {
continue;
}
cfg.add_child("event", eh.get_config());
}
std::stringstream used;
std::set<std::string>::const_iterator u;
for(u = used_items.begin(); u != used_items.end(); ++u) {
if(u != used_items.begin())
used << ",";
used << *u;
}
cfg["used_items"] = used.str();
std::stringstream ids;
for(u = unit_wml_ids.begin(); u != unit_wml_ids.end(); ++u) {
if(u != unit_wml_ids.begin())
ids << ",";
ids << *u;
}
cfg["unit_wml_ids"] = ids.str();
if (resources::soundsources)
resources::soundsources->write_sourcespecs(cfg);
resources::lua_kernel->save_game(cfg);
}
} // end namespace game_events

View file

@ -0,0 +1,120 @@
/*
Copyright (C) 2003 - 2013 by David White <dave@whitevine.net>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
/**
* @file
* Define the handlers for the game's events mechanism.
*
* Events might be units moving or fighting, or when victory or defeat occurs.
* A scenario's configuration file will define actions to take when certain events occur.
* This module is responsible for tracking these definitions.
*/
#ifndef GAME_EVENTS_HANDLERS_H_INCLUDED
#define GAME_EVENTS_HANDLERS_H_INCLUDED
#include "../config.hpp"
#include "../variable.hpp"
class vconfig;
namespace game_events
{
struct queued_event;
typedef void (*wml_handler_function)(const queued_event &event_info,
const vconfig &cfg);
typedef void (*action_handler)(const queued_event &, const vconfig &);
class event_handler
{
public:
event_handler(const config &cfg, bool is_menu_item = false);
bool matches_name(const std::string& name) const;
bool disabled() const { return disabled_; }
bool is_menu_item() const { return is_menu_item_; }
void handle_event(const queued_event& event_info);
const config &get_config() const { return cfg_; }
private:
bool first_time_only_;
bool disabled_;
bool is_menu_item_;
config cfg_;
};
// The game event manager loads the scenario configuration object,
// and ensures that events are handled according to the
// scenario configuration for its lifetime.
//
// Thus, a manager object should be created when a scenario is played,
// and destroyed at the end of the scenario.
struct manager {
/// Note that references will be maintained,
/// and must remain valid for the life of the object.
manager(const config& scenario_cfg);
~manager();
// Allow iterating over the active handlers.
typedef std::vector<event_handler> t_active;
typedef t_active::iterator iterator;
static iterator begin();
static iterator end();
/// Starts buffering event handler creation.
static void start_buffering();
/// Ends buffering event handler creation.
static void stop_buffering();
/// Commits the event handlers that were buffered.
static void commit_buffer();
/// Returns true when a manager exists, so events can be processed.
static bool running() { return running_; }
private:
variable::manager variable_manager;
static bool running_;
};
/// Create an event handler.
void add_event_handler(const config & event);
/// Add a pending menu item command change.
void add_wmi_change(const std::string & id, const config & new_command);
/// Handles all the different types of actions that can be triggered by an event.
void commit_wmi_commands();
/// Checks if an item has been used.
bool item_used(const std::string & id);
/// Records if an item has been used.
void item_used(const std::string & id, bool used);
/// Registers a standard action handler.
void register_action(const std::string & tag, wml_handler_function handler);
/// Removes an event handler.
void remove_event_handler(const std::string & id);
/// Removes a pending menu item command change.
void remove_wmi_change(const std::string & id);
void add_events(const config::const_child_itors &cfgs,
const std::string& type = std::string());
void write_events(config& cfg);
}
#endif // GAME_EVENTS_HANDLERS_H_INCLUDED

584
src/game_events/pump.cpp Normal file
View file

@ -0,0 +1,584 @@
/*
Copyright (C) 2003 - 2013 by David White <dave@whitevine.net>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
/**
* @file
* Handles the current state of WML-events. This includes raising and firing,
* as well as tracking the context for event firing.
*/
#include "../global.hpp"
#include "pump.hpp"
#include "conditional_wml.hpp"
#include "../game_config.hpp"
#include "../game_display.hpp"
#include "../gamestatus.hpp"
#include "../gettext.hpp"
#include "../log.hpp"
#include "../play_controller.hpp"
#include "../resources.hpp"
#include "../scripting/lua.hpp"
#include "../side_filter.hpp"
#include "../unit.hpp"
#include "../unit_map.hpp"
#include "../whiteboard/manager.hpp"
#include "../variable.hpp"
#include <boost/foreach.hpp>
#include <iomanip>
#include <iostream>
static lg::log_domain log_engine("engine");
#define DBG_NG LOG_STREAM(debug, log_engine)
#define LOG_NG LOG_STREAM(info, log_engine)
#define ERR_NG LOG_STREAM(err, log_engine)
static lg::log_domain log_wml("wml");
#define DBG_WML LOG_STREAM(debug, log_wml)
#define LOG_WML LOG_STREAM(info, log_wml)
#define WRN_WML LOG_STREAM(warn, log_wml)
#define ERR_WML LOG_STREAM(err, log_wml)
static lg::log_domain log_event_handler("event_handler");
#define DBG_EH LOG_STREAM(debug, log_event_handler)
// std::getline might be broken in Visual Studio so show a warning
#ifdef _MSC_VER
#if _MSC_VER < 1300
#ifndef GETLINE_PATCHED
#pragma message("warning: the std::getline implementation in your compiler might be broken.")
#pragma message(" http://support.microsoft.com/default.aspx?scid=kb;EN-US;q240015")
#endif
#endif
#endif
// This file is in the game_events namespace.
namespace game_events {
namespace { // Types
/**
* State when processing a flight of events or commands.
*/
struct event_context
{
bool mutated;
bool skip_messages;
event_context(bool s): mutated(true), skip_messages(s) {}
};
event_context default_context(false);
event_context *current_context = &default_context;
/**
* Context state with automatic lifetime handling.
*/
struct scoped_context
{
event_context *old_context;
event_context new_context;
scoped_context()
: old_context(current_context)
, new_context(old_context != &default_context && old_context->skip_messages)
{
current_context = &new_context;
}
~scoped_context()
{
old_context->mutated |= new_context.mutated;
current_context = old_context;
}
};
class pump_manager {
public:
pump_manager() :
x1_(resources::gamedata->get_variable("x1")),
x2_(resources::gamedata->get_variable("x2")),
y1_(resources::gamedata->get_variable("y1")),
y2_(resources::gamedata->get_variable("y2"))
{
++instance_count;
}
~pump_manager() {
resources::gamedata->get_variable("x1") = x1_;
resources::gamedata->get_variable("x2") = x2_;
resources::gamedata->get_variable("y1") = y1_;
resources::gamedata->get_variable("y2") = y2_;
--instance_count;
}
static unsigned count() {
return instance_count;
}
private:
static unsigned instance_count;
int x1_, x2_, y1_, y2_;
};
unsigned pump_manager::instance_count=0;
} // end anonymous namespace (types)
namespace { // Variables
std::deque<queued_event> events_queue;
bool does_screen_need_rebuild = false;
/// The value returned by wml_tracking();
size_t internal_wml_tracking = 0;
std::stringstream wml_messages_stream;
} // end anonymous namespace (variables)
namespace { // Support functions
inline bool events_init()
{
return resources::screen != 0;
}
/**
* Returns true iff the given event passes all its filters.
*/
bool filter_event(const event_handler& handler, const queued_event& ev)
{
const unit_map *units = resources::units;
unit_map::const_iterator unit1 = units->find(ev.loc1);
unit_map::const_iterator unit2 = units->find(ev.loc2);
vconfig filters(handler.get_config());
BOOST_FOREACH(const vconfig &condition, filters.get_children("filter_condition"))
{
if (!conditional_passed(condition)) {
return false;
}
}
BOOST_FOREACH(const vconfig &f, filters.get_children("filter_side"))
{
side_filter ssf(f);
if ( !ssf.match(resources::controller->current_side()) )
return false;
}
BOOST_FOREACH(const vconfig &f, filters.get_children("filter"))
{
if ( !ev.loc1.matches_unit_filter(unit1, f) ) {
return false;
}
}
vconfig::child_list special_filters = filters.get_children("filter_attack");
bool special_matches = special_filters.empty();
if ( !special_matches && unit1 != units->end() )
{
const bool matches_unit = ev.loc1.matches_unit(unit1);
const config & attack = ev.data.child("first");
BOOST_FOREACH(const vconfig &f, special_filters)
{
if ( f.empty() )
special_matches = true;
else if ( !matches_unit )
return false;
special_matches = special_matches ||
matches_special_filter(attack, f);
}
}
if(!special_matches) {
return false;
}
BOOST_FOREACH(const vconfig &f, filters.get_children("filter_second"))
{
if ( !ev.loc2.matches_unit_filter(unit2, f) ) {
return false;
}
}
special_filters = filters.get_children("filter_second_attack");
special_matches = special_filters.empty();
if ( !special_matches && unit2 != units->end() )
{
const bool matches_unit = ev.loc2.matches_unit(unit2);
const config & attack = ev.data.child("second");
BOOST_FOREACH(const vconfig &f, special_filters)
{
if ( f.empty() )
special_matches = true;
else if ( !matches_unit )
return false;
special_matches = special_matches ||
matches_special_filter(attack, f);
}
}
if(!special_matches) {
return false;
}
// All filters passed.
return true;
}
bool process_event(event_handler& handler, const queued_event& ev)
{
if(handler.disabled())
return false;
unit_map *units = resources::units;
scoped_xy_unit first_unit("unit", ev.loc1.x, ev.loc1.y, *units);
scoped_xy_unit second_unit("second_unit", ev.loc2.x, ev.loc2.y, *units);
scoped_weapon_info first_weapon("weapon", ev.data.child("first"));
scoped_weapon_info second_weapon("second_weapon", ev.data.child("second"));
if ( !filter_event(handler, ev) )
return false;
// The event hasn't been filtered out, so execute the handler.
++internal_wml_tracking;
scoped_context evc;
handler.handle_event(ev);
if(ev.name == "select") {
resources::gamedata->last_selected = ev.loc1;
}
if ( screen_needs_rebuild() ) {
screen_needs_rebuild(false);
game_display *screen = resources::screen;
screen->recalculate_minimap();
screen->invalidate_all();
screen->rebuild_all();
}
return context_mutated();
}
/**
* Helper function for show_wml_errors(), which gathers
* the messages from a stringstream.
*/
void fill_wml_messages_map(std::map<std::string, int>& msg_map, std::stringstream& source)
{
while(true) {
std::string msg;
std::getline(source, msg);
if(source.eof()) {
break;
}
if(msg == "") {
continue;
}
if(msg_map.find(msg) == msg_map.end()) {
msg_map[msg] = 1;
} else {
msg_map[msg]++;
}
}
// Make sure the eof flag is cleared otherwise no new messages are shown
source.clear();
}
/**
* Shows a summary of the errors encountered in WML so far,
* to avoid a lot of the same messages to be shown.
* Identical messages are shown once, with (between braces)
* the number of times that message was encountered.
* The order in which the messages are shown does not need
* to be the order in which these messages are encountered.
* Messages are always written to std::cerr.
*/
void show_wml_errors()
{
// Get all unique messages in messages,
// with the number of encounters for these messages
std::map<std::string, int> messages;
fill_wml_messages_map(messages, lg::wml_error);
// Show the messages collected
const std::string caption = "Invalid WML found";
for(std::map<std::string, int>::const_iterator itor = messages.begin();
itor != messages.end(); ++itor) {
std::stringstream msg;
msg << itor->first;
if(itor->second > 1) {
msg << " (" << itor->second << ")";
}
resources::screen->add_chat_message(time(NULL), caption, 0, msg.str(),
events::chat_handler::MESSAGE_PUBLIC, false);
std::cerr << caption << ": " << msg.str() << '\n';
}
}
void show_wml_messages()
{
// Get all unique messages in messages,
// with the number of encounters for these messages
std::map<std::string, int> messages;
fill_wml_messages_map(messages, wml_messages_stream);
// Show the messages collected
const std::string caption = "WML";
for(std::map<std::string, int>::const_iterator itor = messages.begin();
itor != messages.end(); ++itor) {
std::stringstream msg;
msg << itor->first;
if(itor->second > 1) {
msg << " (" << itor->second << ")";
}
resources::screen->add_chat_message(time(NULL), caption, 0, msg.str(),
events::chat_handler::MESSAGE_PUBLIC, false);
}
}
} // end anonymous namespace (support functions)
/** Returns whether or not we believe WML might have changed something. */
bool context_mutated()
{
return current_context->mutated;
}
/** Sets whether or not we believe WML might have changed something. */
void context_mutated(bool mutated)
{
current_context->mutated = mutated;
}
/** Returns whether or not the screen (map visuals) needs to be rebuilt. */
bool screen_needs_rebuild()
{
return does_screen_need_rebuild;
}
/** Sets whether or not the screen (map visuals) needs to be rebuilt. */
void screen_needs_rebuild(bool rebuild)
{
does_screen_need_rebuild = rebuild;
}
/** Returns whether or not we are skipping messages. */
bool skip_messages()
{
return current_context->skip_messages;
}
/** Sets whether or not we are skipping messages. */
void skip_messages(bool skip)
{
current_context->skip_messages = skip;
}
/**
* Helper function which determines whether a wml_message text can
* really be pushed into the wml_messages_stream, and does it.
*/
void put_wml_message(const std::string& logger, const std::string& message)
{
if (logger == "err" || logger == "error") {
ERR_WML << message << "\n";
wml_messages_stream << _("Error: ") << message << "\n";
} else if (logger == "warn" || logger == "wrn" || logger == "warning") {
WRN_WML << message << "\n";
wml_messages_stream << _("Warning: ") << message << "\n";
} else if ((logger == "debug" || logger == "dbg") && !lg::debug.dont_log(log_wml)) {
DBG_WML << message << "\n";
wml_messages_stream << _("Debug: ") << message << "\n";
} else if (!lg::info.dont_log(log_wml)) {
LOG_WML << message << "\n";
wml_messages_stream << _("Info: ") << message << "\n";
}
}
void handle_event_commands(const queued_event& event_info, const vconfig &cfg)
{
resources::lua_kernel->run_wml_action("command", cfg, event_info);
}
void handle_event_command(const std::string &cmd,
const queued_event &event_info, const vconfig &cfg)
{
log_scope2(log_engine, "handle_event_command");
LOG_NG << "handling command '" << cmd << "' from "
<< (cfg.is_volatile()?"volatile ":"") << "cfg 0x"
<< std::hex << std::setiosflags(std::ios::uppercase)
<< reinterpret_cast<uintptr_t>(&cfg.get_config()) << std::dec << "\n";
if (!resources::lua_kernel->run_wml_action(cmd, cfg, event_info))
{
ERR_NG << "Couldn't find function for wml tag: "<< cmd <<"\n";
}
DBG_NG << "done handling command...\n";
}
bool fire(const std::string& event,
const entity_location& loc1,
const entity_location& loc2,
const config& data)
{
assert(manager::running());
raise(event,loc1,loc2,data);
return pump();
}
void raise(const std::string& event,
const entity_location& loc1,
const entity_location& loc2,
const config& data)
{
assert(manager::running());
if(!events_init())
return;
DBG_EH << "raising event: " << event << "\n";
events_queue.push_back(queued_event(event, loc1, loc2, data));
}
bool pump()
{
//ensure the whiteboard doesn't attempt to build its future unit map
//for the duration of this method
wb::real_map real_unit_map;
assert(manager::running());
if(!events_init())
return false;
pump_manager pump_instance;
if(pump_manager::count() >= game_config::max_loop) {
ERR_NG << "game_events::pump() waiting to process new events because "
<< "recursion level would exceed maximum " << game_config::max_loop << '\n';
return false;
}
if(!lg::debug.dont_log("event_handler")) {
std::stringstream ss;
BOOST_FOREACH(const queued_event& ev, events_queue) {
ss << "name=" << ev.name << "; ";
}
DBG_EH << "processing queued events: " << ss.str() << "\n";
}
const size_t old_wml_track = internal_wml_tracking;
bool result = false;
while(events_queue.empty() == false) {
if(pump_manager::count() <= 1)
manager::start_buffering();
queued_event ev = events_queue.front();
events_queue.pop_front(); // pop now for exception safety
const std::string& event_name = ev.name;
// Clear the unit cache, since the best clearing time is hard to figure out
// due to status changes by WML. Every event will flush the cache.
unit::clear_status_caches();
if ( resources::lua_kernel->run_event(ev) )
++internal_wml_tracking;
bool init_event_vars = true;
manager::iterator end_handler = manager::end();
manager::iterator cur_handler = manager::begin();
for ( ; cur_handler != end_handler; ++cur_handler ) {
event_handler & handler = *cur_handler;
if(!handler.matches_name(event_name))
continue;
// Set the variables for the event
if (init_event_vars) {
resources::gamedata->get_variable("x1") = ev.loc1.filter_x() + 1;
resources::gamedata->get_variable("y1") = ev.loc1.filter_y() + 1;
resources::gamedata->get_variable("x2") = ev.loc2.filter_x() + 1;
resources::gamedata->get_variable("y2") = ev.loc2.filter_y() + 1;
init_event_vars = false;
}
DBG_EH << "processing event " << event_name << " with id="<<
handler.get_config()["id"] << "\n";
if(process_event(handler, ev))
{
result = true;
}
}
if(pump_manager::count() <= 1)
manager::stop_buffering();
// Only commit new handlers when finished iterating over event_handlers.
commit();
}
if ( old_wml_track != internal_wml_tracking )
// Notify the whiteboard of any event.
// This is used to track when moves, recruits, etc. happen.
resources::whiteboard->on_gamestate_change();
return result;
}
/** Clears all events tha have been raised (and not pumped). */
void clear_events()
{
events_queue.clear();
}
void commit()
{
DBG_EH << "committing new event handlers, number of pump_instances: " <<
pump_manager::count() << "\n";
manager::commit_buffer();
commit_wmi_commands();
// Dialogs can only be shown if the display is not locked
if (!resources::screen->video().update_locked()) {
show_wml_errors();
show_wml_messages();
}
}
/**
* This function can be used to detect when no WML/Lua has been executed.
*
* If two calls to this function return the same value, then one can
* assume that the usual game mechanics have been followed, and code does
* not have to account for all the things WML/Lua can do. If the return
* values are different, then something unusual might have happened between
* those calls.
*
* This is not intended as a precise metric. Rather, it is motivated by
* how large the number of fired WML events is, compared to the (typical)
* number of WML event handlers. It is intended for code that can benefit
* from caching some aspect of the game state and that cannot rely on
* [allow_undo] not being used when that state changes.
*/
size_t wml_tracking()
{
return internal_wml_tracking;
}
} // end namespace game_events

114
src/game_events/pump.hpp Normal file
View file

@ -0,0 +1,114 @@
/*
Copyright (C) 2003 - 2013 by David White <dave@whitevine.net>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
/**
* @file
* Define the game's event mechanism.
*
* Events might be units moving or fighting, or when victory or defeat occurs.
* A scenario's configuration file will define actions to take when certain events occur.
* This module is responsible for the processing of events.
*
* Note that game events have nothing to do with SDL events,
* like mouse movement, keyboard events, etc.
* See events.hpp for how they are handled.
*/
#ifndef GAME_EVENTS_PUMP_H_INCLUDED
#define GAME_EVENTS_PUMP_H_INCLUDED
#include "entity_location.hpp"
#include "../config.hpp"
class vconfig;
namespace game_events
{
struct queued_event {
queued_event(const std::string& name, const entity_location& loc1,
const entity_location& loc2, const config& data)
: name(name), loc1(loc1), loc2(loc2), data(data)
{}
std::string name;
entity_location loc1;
entity_location loc2;
config data;
};
/// Returns whether or not we believe WML might have changed something.
bool context_mutated();
/// Sets whether or not we believe WML might have changed something.
void context_mutated(bool mutated);
/// Returns whether or not the screen (map visuals) needs to be rebuilt.
bool screen_needs_rebuild();
/// Sets whether or not the screen (map visuals) needs to be rebuilt.
void screen_needs_rebuild(bool rebuild);
/// Returns whether or not we are skipping messages.
bool skip_messages();
/// Sets whether or not we are skipping messages.
void skip_messages(bool skip);
/// Helper function which determines whether a wml_message text can
/// really be pushed into the wml_messages_stream, and does it.
void put_wml_message(const std::string& logger, const std::string& message);
/**
* Runs the action handler associated to the command sequence @a cfg.
*/
void handle_event_commands(const queued_event &event_info, const vconfig &cfg);
/**
* Runs the action handler associated to @a cmd with parameters @a cfg.
*/
void handle_event_command(const std::string &cmd,
const queued_event &event_info, const vconfig &cfg);
/**
* Function to fire an event.
*
* Events may have up to two arguments, both of which must be locations.
*/
bool fire(const std::string& event,
const entity_location& loc1=entity_location::null_entity,
const entity_location& loc2=entity_location::null_entity,
const config& data=config());
void raise(const std::string& event,
const entity_location& loc1=entity_location::null_entity,
const entity_location& loc2=entity_location::null_entity,
const config& data=config());
bool pump();
/// Clears all events tha have been raised (and not pumped).
void clear_events();
/**
* Handles newly-created handlers. Flushes WML messages and errors.
*/
void commit();
/**
* This function can be used to detect when no WML/Lua has been executed.
*/
size_t wml_tracking();
}
#endif // GAME_EVENTS_PUMP_H_INCLUDED

View file

@ -24,6 +24,7 @@
#include "actions/create.hpp"
#include "filesystem.hpp"
#include "game_events/handlers.hpp"
#include "gettext.hpp"
#include "log.hpp"
#include "game_preferences.hpp"
@ -43,7 +44,6 @@
#include "whiteboard/side_actions.hpp"
#include "sound.hpp"
#include "soundsource.hpp"
#include "game_events.hpp"
#include "map_label.hpp"
#include <boost/bind.hpp>

View file

@ -31,7 +31,7 @@
#include "formatter.hpp"
#include "filechooser.hpp"
#include "game_end_exceptions.hpp"
#include "game_events.hpp"
#include "game_events/pump.hpp"
#include "game_preferences.hpp"
#include "gettext.hpp"
#include "gui/dialogs/chat_log.hpp"

View file

@ -22,7 +22,7 @@
#include "attack_prediction_display.hpp"
#include "dialogs.hpp"
#include "game_end_exceptions.hpp"
#include "game_events.hpp"
#include "game_events/pump.hpp"
#include "gettext.hpp"
#include "gui/dialogs/unit_attack.hpp"
#include "gui/widgets/settings.hpp"

View file

@ -24,7 +24,9 @@
#include "actions/undo.hpp"
#include "actions/vision.hpp"
#include "dialogs.hpp"
#include "game_events.hpp"
#include "game_events/conditional_wml.hpp"
#include "game_events/handlers.hpp"
#include "game_events/pump.hpp"
#include "gettext.hpp"
#include "halo.hpp"
#include "loadscreen.hpp"

View file

@ -1,4 +1,3 @@
/*
Copyright (C) 2006 - 2013 by Joerg Hinrichs <joerg.hinrichs@alice-dsl.de>
wesnoth playlevel Copyright (C) 2003 by David White <dave@whitevine.net>
@ -27,7 +26,7 @@
#include "ai/testing.hpp"
#include "dialogs.hpp"
#include "game_end_exceptions.hpp"
#include "game_events.hpp"
#include "game_events/pump.hpp"
#include "game_preferences.hpp"
#include "gettext.hpp"
#include "gui/dialogs/transient_message.hpp"

View file

@ -28,7 +28,7 @@
#include "dialogs.hpp"
#include "game_display.hpp"
#include "game_end_exceptions.hpp"
#include "game_events.hpp"
#include "game_events/pump.hpp"
#include "game_preferences.hpp"
#include "gamestatus.hpp"
#include "log.hpp"

View file

@ -17,7 +17,7 @@
#include "actions/vision.hpp"
#include "game_end_exceptions.hpp"
#include "game_events.hpp"
#include "game_events/handlers.hpp"
#include "gettext.hpp"
#include "log.hpp"
#include "map_label.hpp"

View file

@ -14,7 +14,7 @@
#include "sound.hpp"
#include "soundsource.hpp"
#include "game_events.hpp"
#include "game_events/handlers.hpp"
namespace savegame {

View file

@ -40,6 +40,9 @@
#include "attack_prediction.hpp"
#include "filesystem.hpp"
#include "game_display.hpp"
#include "game_events/action_wml.hpp"
#include "game_events/conditional_wml.hpp"
#include "game_events/pump.hpp"
#include "game_preferences.hpp"
#include "gamestatus.hpp"
#include "log.hpp"

View file

@ -15,8 +15,9 @@
#ifndef SCRIPTING_LUA_HPP
#define SCRIPTING_LUA_HPP
#include "game_events.hpp"
#include "game_events/handlers.hpp"
class unit;
struct lua_State;
namespace ai {
@ -25,6 +26,10 @@ class lua_ai_context;
class engine_lua;
} // of namespace ai
namespace game_events {
struct queued_event;
}
void extract_preload_scripts(config const &);
class LuaKernel

View file

@ -27,7 +27,8 @@
#include "variable.hpp"
#include "display.hpp"
#include "game_events.hpp"
#include "game_events/action_wml.hpp"
#include "game_events/conditional_wml.hpp"
#include "gamestatus.hpp"
#include "gettext.hpp"
#include "intro.hpp"

View file

@ -24,7 +24,6 @@
#include "storyscreen/controller.hpp"
#include "display.hpp"
#include "game_events.hpp"
#include "gettext.hpp"
#include "intro.hpp"
#include "language.hpp"

View file

@ -25,7 +25,8 @@
#include "config.hpp"
#include "gamestatus.hpp"
#include "game_events.hpp"
#include "game_events/action_wml.hpp"
#include "game_events/conditional_wml.hpp"
#include "image.hpp"
#include "serialization/string_utils.hpp"
#include "util.hpp"

View file

@ -20,8 +20,9 @@
#include "team.hpp"
#include "ai/manager.hpp"
#include "game_events.hpp"
#include "game_events/pump.hpp"
#include "gamestatus.hpp"
#include "map.hpp"
#include "resources.hpp"
#include "game_preferences.hpp"
#include "whiteboard/side_actions.hpp"

View file

@ -18,7 +18,6 @@
#include "unit_display.hpp"
#include "game_preferences.hpp"
#include "game_events.hpp"
#include "log.hpp"
#include "mouse_events.hpp"
#include "resources.hpp"