split game_events::manager code into separate files

This commit is contained in:
Chris Beck 2014-12-14 05:53:29 -05:00
parent 60e82d2f70
commit aff3cb8da5
14 changed files with 613 additions and 448 deletions

View file

@ -828,6 +828,8 @@ set(wesnoth-main_SRC
game_events/conditional_wml.cpp game_events/conditional_wml.cpp
game_events/entity_location.cpp game_events/entity_location.cpp
game_events/handlers.cpp game_events/handlers.cpp
game_events/manager.cpp
game_events/manager_impl.cpp
game_events/menu_item.cpp game_events/menu_item.cpp
game_events/pump.cpp game_events/pump.cpp
game_events/wmi_container.cpp game_events/wmi_container.cpp

View file

@ -300,6 +300,8 @@ wesnoth_sources = Split("""
game_events/conditional_wml.cpp game_events/conditional_wml.cpp
game_events/entity_location.cpp game_events/entity_location.cpp
game_events/handlers.cpp game_events/handlers.cpp
game_events/manager.cpp
game_events/manager_impl.cpp
game_events/menu_item.cpp game_events/menu_item.cpp
game_events/pump.cpp game_events/pump.cpp
game_events/wmi_container.cpp game_events/wmi_container.cpp

View file

@ -21,7 +21,7 @@
#include "global.hpp" #include "global.hpp"
#include "action_wml.hpp" #include "action_wml.hpp"
#include "conditional_wml.hpp" #include "conditional_wml.hpp"
#include "handlers.hpp" #include "manager.hpp"
#include "pump.hpp" #include "pump.hpp"
#include "actions/attack.hpp" #include "actions/attack.hpp"

View file

@ -20,6 +20,8 @@
#include "../global.hpp" #include "../global.hpp"
#include "handlers.hpp" #include "handlers.hpp"
#include "manager.hpp"
#include "manager_impl.hpp"
#include "menu_item.hpp" #include "menu_item.hpp"
#include "pump.hpp" #include "pump.hpp"
@ -50,333 +52,6 @@ static lg::log_domain log_event_handler("event_handler");
// This file is in the game_events namespace. // This file is in the game_events namespace.
namespace game_events { namespace game_events {
//t_event_handlers is essentially the implementation details of the manager
class manager::t_event_handlers {
typedef boost::unordered_map<std::string, handler_list> map_t;
typedef boost::unordered_map<std::string, boost::weak_ptr<event_handler> > id_map_t;
public:
typedef handler_vec::iterator iterator;
typedef handler_vec::const_iterator const_iterator;
private:
handler_vec active_; /// Active event handlers. Will not have elements removed unless the t_event_handlers is clear()ed.
map_t by_name_; /// Active event handlers with fixed event names, organized by event name.
handler_list dynamic_; /// Active event handlers with variables in their event names.
id_map_t id_map_; /// Allows quick locating of handlers by id.
void log_handlers();
/// Utility to standardize the event names used in by_name_.
static std::string standardize_name(const std::string & name);
public:
typedef handler_vec::size_type size_type;
t_event_handlers()
: active_()
, by_name_()
, dynamic_()
, id_map_()
{}
/// Read-only access to the handlers with varying event names.
const handler_list & get() const { return dynamic_; }
/// Read-only access to the handlers with fixed event names, by event name.
const handler_list & get(const std::string & name) const;
/// Adds an event handler.
void add_event_handler(const config & cfg, manager & man, bool is_menu_item=false);
/// Removes an event handler, identified by its ID.
void remove_event_handler(std::string const & id);
iterator begin() { return active_.begin(); }
const_iterator begin() const { return active_.begin(); }
iterator end() { return active_.end(); }
const_iterator end() const { return active_.end(); }
/// The number of active event handlers.
size_type size() const { return active_.size(); }
/// Access to active event handlers by index.
handler_ptr & operator[](size_type index) { return active_[index]; }
};//t_event_handlers
void manager::t_event_handlers::log_handlers()
{
if(lg::debug.dont_log("event_handler")) return;
std::stringstream ss;
BOOST_FOREACH( const handler_ptr & h, active_ ) {
if ( !h )
continue;
const config& cfg = h->get_config();
ss << "name=" << cfg["name"] << ", with id=" << cfg["id"] << "; ";
}
DBG_EH << "active handlers are now " << ss.str() << "\n";
}
/**
* Utility to standardize the event names used in by_name_.
* This means stripping leading and trailing spaces, and converting internal
* spaces to underscores.
*/
std::string manager::t_event_handlers::standardize_name(const std::string & name)
{
std::string retval;
size_t name_index = 0;
size_t name_size = name.size();
// Trim trailing spaces off the name.
while ( name_size > 0 && name[name_size-1] == ' ' )
--name_size ;
// Trim leading spaces off the name.
while ( name_index < name_size && name[name_index] == ' ' )
++name_index;
// Copy the rest, converting any remaining spaces to underscores.
retval.reserve(name_size - name_index);
while ( name_index < name_size ) {
char c = name[name_index++];
retval.push_back(c == ' ' ? '_' : c);
}
return retval;
}
/**
* Read-only access to the handlers with fixed event names, by event name.
*/
const handler_list & manager::t_event_handlers::get(const std::string & name) const
{
// Empty list for the "not found" case.
static const handler_list empty_list;
// Look for the name in the name map.
map_t::const_iterator find_it = by_name_.find(standardize_name(name));
return find_it == by_name_.end() ? empty_list : find_it->second;
}
/**
* Adds an event handler.
* An event with a nonempty ID will not be added if an event with that
* ID already exists.
*/
void manager::t_event_handlers::add_event_handler(const config & cfg, manager & man, bool is_menu_item)
{
const std::string name = cfg["name"];
std::string id = cfg["id"];
if(!id.empty()) {
// Ignore this handler if there is already one with this ID.
id_map_t::iterator find_it = id_map_.find(id);
if ( find_it != id_map_.end() && !find_it->second.expired() ) {
DBG_EH << "ignoring event handler for name='" << name
<< "' with id '" << id << "'\n";
return;
}
}
// Create a new handler.
DBG_EH << "inserting event handler for name=" << name <<
" with id=" << id << "\n";
handler_ptr new_handler(new event_handler(cfg, is_menu_item, active_.size(), man));
active_.push_back(new_handler);
// File by name.
if ( utils::might_contain_variables(name) )
dynamic_.push_back(new_handler);
else {
std::vector<std::string> name_list = utils::split(name);
BOOST_FOREACH( const std::string & single_name, name_list )
by_name_[standardize_name(single_name)].push_back(new_handler);
}
// File by ID.
if ( !id.empty() )
id_map_[id] = new_handler;
log_handlers();
}
/**
* Removes an event handler, identified by its ID.
* Events with empty IDs cannot be removed.
*/
void manager::t_event_handlers::remove_event_handler(std::string const & id)
{
if ( id.empty() )
return;
DBG_EH << "removing event handler with id " << id << "\n";
// Find the existing handler with this ID.
id_map_t::iterator find_it = id_map_.find(id);
if ( find_it != id_map_.end() ) {
handler_ptr handler = find_it->second.lock();
// Remove handler.
if ( handler )
handler->disable();
id_map_.erase(find_it); // Do this even if the lock failed.
// The index by name will self-adjust later. No need to adjust it now.
}
log_handlers();
}
/** Create an event handler. */
void manager::add_event_handler(const config & handler, bool is_menu_item)
{
event_handlers_->add_event_handler(handler, *this, is_menu_item);
}
/**
* Checks if an item has been used.
* (An empty id will never be considered used.)
*/
bool manager::item_used(const std::string & id)
{
return !id.empty() && used_items_.count(id) > 0;
}
/** Records if an item has been used. */
void manager::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);
}
/** Removes an event handler. */
void manager::remove_event_handler(const std::string & id)
{
event_handlers_->remove_event_handler(id);
}
/* ** manager ** */
manager::manager(const config& cfg)
: event_handlers_(new t_event_handlers())
, unit_wml_ids_()
, used_items_()
{
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);
}
// Guard against a memory leak (now) / memory corruption (when this is deleted).
// This is why creating multiple manager objects is prohibited.
assert(resources::lua_kernel != NULL);
wml_action::map::const_iterator action_end = wml_action::end();
wml_action::map::const_iterator action_cur = wml_action::begin();
for ( ; action_cur != action_end; ++action_cur ) {
resources::lua_kernel->set_wml_action(action_cur->first, action_cur->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);
}
}
// Create the event handlers for menu items.
resources::gamedata->get_wml_menu_items().init_handlers();
}
manager::~manager() {
clear_events();
}
/* ** manager::iteration ** */
/**
* Event-specific constructor.
* This iteration will go through all event handlers matching the given name
* (including those defined via menu items).
* An empty @a event_name will automatically match nothing.
*/
manager::iteration::iteration(const std::string & event_name, manager & man) :
main_list_(man.event_handlers_->get(event_name)),
var_list_(man.event_handlers_->get()),
event_name_(event_name),
end_(man.event_handlers_->size()),
current_is_known_(false),
main_is_current_(false),
main_it_(main_list_.begin()),
var_it_(event_name.empty() ? var_list_.end() : var_list_.begin())
{
}
/**
* Increment
* Incrementing guarantees that the next dereference will differ from the
* previous derference (unless the iteration is exhausted). However, multiple
* increments between dereferences are allowed to have the same effect as a
* single increment.
*/
manager::iteration & manager::iteration::operator++()
{
if ( !current_is_known_ )
// Either *this has never been dereferenced, or we already incremented
// since the last dereference. We are allowed to ignore this increment.
return *this;
// Guarantee a different element next dereference.
if ( main_is_current_ )
++main_it_;
else
++var_it_; // (We'll check for a name match when we dereference.)
// We no longer know which list is current.
current_is_known_ = false;
// Done.
return *this;
}
/**
* Dereference
* Will return a null pointer when the end of the iteration is reached.
*/
handler_ptr manager::iteration::operator*()
{
// Get the candidate for the current element from the main list.
handler_ptr main_ptr = *main_it_;
handler_vec::size_type main_index = ptr_index(main_ptr);
// Get the candidate for the current element from the var list.
handler_ptr var_ptr = *var_it_;
// (Loop while var_ptr would be chosen over main_ptr, but the name does not match.)
while ( var_ptr && var_ptr->index() < main_index &&
!var_ptr->matches_name(event_name_) )
var_ptr = *++var_it_;
handler_vec::size_type var_index = ptr_index(var_ptr);
// Which list? (Index ties go to the main list.)
current_is_known_ = main_index < end_ || var_index < end_;
main_is_current_ = main_index <= var_index;
if ( !current_is_known_ )
return handler_ptr(); // End of list; return a null pointer.
else
return main_is_current_ ? main_ptr : var_ptr;
}
/* ** handler_list::iterator ** */ /* ** handler_list::iterator ** */
/** /**
@ -520,35 +195,5 @@ bool event_handler::matches_name(const std::string &name) const
return false; return false;
} }
void manager::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 " << std::endl;
continue;
}
add_event_handler(new_ev);
}
}
void manager::write_events(config& cfg)
{
BOOST_FOREACH(const handler_ptr &eh, *event_handlers_) {
if ( !eh || eh->is_menu_item() ) {
continue;
}
cfg.add_child("event", eh->get_config());
}
cfg["used_items"] = utils::join(used_items_);
cfg["unit_wml_ids"] = utils::join(unit_wml_ids_);
}
} // end namespace game_events } // end namespace game_events

View file

@ -142,92 +142,6 @@ namespace game_events
/// The actual list. /// The actual list.
list_t data_; list_t data_;
}; };
/// 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.
/// If a second manager object is created before destroying the previous
/// one, the game will crash with an assertion failure.
class manager : boost::noncopyable {
public:
/// This class is similar to an input iterator through event handlers,
/// except each instance knows its own end (determined when constructed).
/// Subsequent dereferences are not guaranteed to return the same element,
/// so it is important to assign a dereference to a variable if you want
/// to use it more than once. On the other hand, a dereference will not
/// return a null pointer until the end of the iteration is reached (and
/// this is how to detect the end of the iteration).
///
/// For simplicity, this class is neither assignable nor equality
/// comparable nor default constructable, and there is no postincrement.
/// Typedefs are also skipped.
class iteration
{
public:
/// Event-specific constructor.
explicit iteration(const std::string & event_name, manager &);
// Increment:
iteration & operator++();
// Dereference:
handler_ptr operator*();
private: // functions
/// Gets the index from a pointer, capped at end_.
handler_vec::size_type ptr_index(const handler_ptr & ptr) const
{ return !bool(ptr) ? end_ : std::min(ptr->index(), end_); }
private: // data
/// The fixed-name event handlers for this iteration.
const handler_list & main_list_;
/// The varying-name event handlers for this iteration.
const handler_list & var_list_;
/// The event name for this iteration.
const std::string event_name_;
/// The end of this iteration. We intentionally exclude handlers
/// added after *this is constructed.
const handler_vec::size_type end_;
/// Set to true upon dereferencing.
bool current_is_known_;
/// true if the most recent dereference was taken from main_list_.
bool main_is_current_;
/// The current (or next) element from main_list_.
handler_list::iterator main_it_;
/// The current (or next) element from var_list_.
handler_list::iterator var_it_;
};
class t_event_handlers;
boost::scoped_ptr<t_event_handlers> event_handlers_;
std::set<std::string> unit_wml_ids_;
std::set<std::string> used_items_;
public:
/// Note that references will be maintained,
/// and must remain valid for the life of the object.
explicit manager(const config& scenario_cfg);
~manager();
/// Create an event handler.
void add_event_handler(const config & handler, bool is_menu_item=false);
/// 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);
/// Removes an event handler.
void remove_event_handler(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 #endif // GAME_EVENTS_HANDLERS_H_INCLUDED

230
src/game_events/manager.cpp Normal file
View file

@ -0,0 +1,230 @@
/*
Copyright (C) 2003 - 2014 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.
*/
#include "global.hpp"
#include "game_events/manager.hpp"
#include "handlers.hpp"
#include "manager_impl.hpp"
#include "menu_item.hpp"
#include "pump.hpp"
#include "formula_string_utils.hpp"
#include "game_data.hpp"
#include "log.hpp"
#include "reports.hpp"
#include "resources.hpp"
#include "scripting/game_lua_kernel.hpp"
#include "serialization/string_utils.hpp"
#include "soundsource.hpp"
#include "util.hpp"
#include <boost/foreach.hpp>
#include <boost/unordered_map.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)
namespace game_events {
/** Create an event handler. */
void manager::add_event_handler(const config & handler, bool is_menu_item)
{
event_handlers_->add_event_handler(handler, *this, is_menu_item);
}
/**
* Checks if an item has been used.
* (An empty id will never be considered used.)
*/
bool manager::item_used(const std::string & id)
{
return !id.empty() && used_items_.count(id) > 0;
}
/** Records if an item has been used. */
void manager::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);
}
/** Removes an event handler. */
void manager::remove_event_handler(const std::string & id)
{
event_handlers_->remove_event_handler(id);
}
/* ** manager ** */
manager::manager(const config& cfg)
: event_handlers_(new t_event_handlers())
, unit_wml_ids_()
, used_items_()
{
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);
}
// Guard against a memory leak (now) / memory corruption (when this is deleted).
// This is why creating multiple manager objects is prohibited.
assert(resources::lua_kernel != NULL);
wml_action::map::const_iterator action_end = wml_action::end();
wml_action::map::const_iterator action_cur = wml_action::begin();
for ( ; action_cur != action_end; ++action_cur ) {
resources::lua_kernel->set_wml_action(action_cur->first, action_cur->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);
}
}
// Create the event handlers for menu items.
resources::gamedata->get_wml_menu_items().init_handlers();
}
manager::~manager() {
clear_events();
}
/* ** manager::iteration ** */
/**
* Event-specific constructor.
* This iteration will go through all event handlers matching the given name
* (including those defined via menu items).
* An empty @a event_name will automatically match nothing.
*/
manager::iteration::iteration(const std::string & event_name, manager & man) :
main_list_(man.event_handlers_->get(event_name)),
var_list_(man.event_handlers_->get()),
event_name_(event_name),
end_(man.event_handlers_->size()),
current_is_known_(false),
main_is_current_(false),
main_it_(main_list_.begin()),
var_it_(event_name.empty() ? var_list_.end() : var_list_.begin())
{
}
/**
* Increment
* Incrementing guarantees that the next dereference will differ from the
* previous derference (unless the iteration is exhausted). However, multiple
* increments between dereferences are allowed to have the same effect as a
* single increment.
*/
manager::iteration & manager::iteration::operator++()
{
if ( !current_is_known_ )
// Either *this has never been dereferenced, or we already incremented
// since the last dereference. We are allowed to ignore this increment.
return *this;
// Guarantee a different element next dereference.
if ( main_is_current_ )
++main_it_;
else
++var_it_; // (We'll check for a name match when we dereference.)
// We no longer know which list is current.
current_is_known_ = false;
// Done.
return *this;
}
/**
* Dereference
* Will return a null pointer when the end of the iteration is reached.
*/
handler_ptr manager::iteration::operator*()
{
// Get the candidate for the current element from the main list.
handler_ptr main_ptr = *main_it_;
handler_vec::size_type main_index = ptr_index(main_ptr);
// Get the candidate for the current element from the var list.
handler_ptr var_ptr = *var_it_;
// (Loop while var_ptr would be chosen over main_ptr, but the name does not match.)
while ( var_ptr && var_ptr->index() < main_index &&
!var_ptr->matches_name(event_name_) )
var_ptr = *++var_it_;
handler_vec::size_type var_index = ptr_index(var_ptr);
// Which list? (Index ties go to the main list.)
current_is_known_ = main_index < end_ || var_index < end_;
main_is_current_ = main_index <= var_index;
if ( !current_is_known_ )
return handler_ptr(); // End of list; return a null pointer.
else
return main_is_current_ ? main_ptr : var_ptr;
}
void manager::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 " << std::endl;
continue;
}
add_event_handler(new_ev);
}
}
void manager::write_events(config& cfg)
{
BOOST_FOREACH(const handler_ptr &eh, *event_handlers_) {
if ( !eh || eh->is_menu_item() ) {
continue;
}
cfg.add_child("event", eh->get_config());
}
cfg["used_items"] = utils::join(used_items_);
cfg["unit_wml_ids"] = utils::join(unit_wml_ids_);
}
} //end namespace game_events

116
src/game_events/manager.hpp Normal file
View file

@ -0,0 +1,116 @@
/*
Copyright (C) 2003 - 2014 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_MANAGER_HPP
#define GAME_EVENTS_MANAGER_HPP
#include "game_events/handlers.hpp"
#include <boost/noncopyable.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/shared_ptr.hpp>
#include <set>
#include <string>
class game_lua_kernel;
namespace game_events {
class t_event_handlers;
/// 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.
/// If a second manager object is created before destroying the previous
/// one, the game will crash with an assertion failure.
class manager : boost::noncopyable {
public:
/// This class is similar to an input iterator through event handlers,
/// except each instance knows its own end (determined when constructed).
/// Subsequent dereferences are not guaranteed to return the same element,
/// so it is important to assign a dereference to a variable if you want
/// to use it more than once. On the other hand, a dereference will not
/// return a null pointer until the end of the iteration is reached (and
/// this is how to detect the end of the iteration).
///
/// For simplicity, this class is neither assignable nor equality
/// comparable nor default constructable, and there is no postincrement.
/// Typedefs are also skipped.
class iteration
{
public:
/// Event-specific constructor.
explicit iteration(const std::string & event_name, manager &);
// Increment:
iteration & operator++();
// Dereference:
handler_ptr operator*();
private: // functions
/// Gets the index from a pointer, capped at end_.
handler_vec::size_type ptr_index(const handler_ptr & ptr) const
{ return !bool(ptr) ? end_ : std::min(ptr->index(), end_); }
private: // data
/// The fixed-name event handlers for this iteration.
const handler_list & main_list_;
/// The varying-name event handlers for this iteration.
const handler_list & var_list_;
/// The event name for this iteration.
const std::string event_name_;
/// The end of this iteration. We intentionally exclude handlers
/// added after *this is constructed.
const handler_vec::size_type end_;
/// Set to true upon dereferencing.
bool current_is_known_;
/// true if the most recent dereference was taken from main_list_.
bool main_is_current_;
/// The current (or next) element from main_list_.
handler_list::iterator main_it_;
/// The current (or next) element from var_list_.
handler_list::iterator var_it_;
};
boost::scoped_ptr<t_event_handlers> event_handlers_;
std::set<std::string> unit_wml_ids_;
std::set<std::string> used_items_;
public:
/// Note that references will be maintained,
/// and must remain valid for the life of the object.
explicit manager(const config& scenario_cfg);
~manager();
/// Create an event handler.
void add_event_handler(const config & handler, bool is_menu_item=false);
/// 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);
/// Removes an event handler.
void remove_event_handler(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

View file

@ -0,0 +1,170 @@
/*
Copyright (C) 2003 - 2014 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.
*/
#include "game_events/manager_impl.hpp"
#include "game_events/handlers.hpp"
#include "game_events/manager.hpp"
#include "game_events/menu_item.hpp"
#include "game_events/pump.hpp"
#include "formula_string_utils.hpp"
#include "game_data.hpp"
#include "log.hpp"
#include "reports.hpp"
#include "resources.hpp"
#include "scripting/game_lua_kernel.hpp"
#include "serialization/string_utils.hpp"
#include "soundsource.hpp"
#include "util.hpp"
#include <boost/foreach.hpp>
#include <boost/unordered_map.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)
namespace game_events {
void t_event_handlers::log_handlers()
{
if(lg::debug.dont_log("event_handler")) return;
std::stringstream ss;
BOOST_FOREACH( const handler_ptr & h, active_ ) {
if ( !h )
continue;
const config& cfg = h->get_config();
ss << "name=" << cfg["name"] << ", with id=" << cfg["id"] << "; ";
}
DBG_EH << "active handlers are now " << ss.str() << "\n";
}
/**
* Utility to standardize the event names used in by_name_.
* This means stripping leading and trailing spaces, and converting internal
* spaces to underscores.
*/
std::string t_event_handlers::standardize_name(const std::string & name)
{
std::string retval;
size_t name_index = 0;
size_t name_size = name.size();
// Trim trailing spaces off the name.
while ( name_size > 0 && name[name_size-1] == ' ' )
--name_size ;
// Trim leading spaces off the name.
while ( name_index < name_size && name[name_index] == ' ' )
++name_index;
// Copy the rest, converting any remaining spaces to underscores.
retval.reserve(name_size - name_index);
while ( name_index < name_size ) {
char c = name[name_index++];
retval.push_back(c == ' ' ? '_' : c);
}
return retval;
}
/**
* Read-only access to the handlers with fixed event names, by event name.
*/
const handler_list & t_event_handlers::get(const std::string & name) const
{
// Empty list for the "not found" case.
static const handler_list empty_list;
// Look for the name in the name map.
map_t::const_iterator find_it = by_name_.find(standardize_name(name));
return find_it == by_name_.end() ? empty_list : find_it->second;
}
/**
* Adds an event handler.
* An event with a nonempty ID will not be added if an event with that
* ID already exists.
*/
void t_event_handlers::add_event_handler(const config & cfg, manager & man, bool is_menu_item)
{
const std::string name = cfg["name"];
std::string id = cfg["id"];
if(!id.empty()) {
// Ignore this handler if there is already one with this ID.
id_map_t::iterator find_it = id_map_.find(id);
if ( find_it != id_map_.end() && !find_it->second.expired() ) {
DBG_EH << "ignoring event handler for name='" << name
<< "' with id '" << id << "'\n";
return;
}
}
// Create a new handler.
DBG_EH << "inserting event handler for name=" << name <<
" with id=" << id << "\n";
handler_ptr new_handler(new event_handler(cfg, is_menu_item, active_.size(), man));
active_.push_back(new_handler);
// File by name.
if ( utils::might_contain_variables(name) )
dynamic_.push_back(new_handler);
else {
std::vector<std::string> name_list = utils::split(name);
BOOST_FOREACH( const std::string & single_name, name_list )
by_name_[standardize_name(single_name)].push_back(new_handler);
}
// File by ID.
if ( !id.empty() )
id_map_[id] = new_handler;
log_handlers();
}
/**
* Removes an event handler, identified by its ID.
* Events with empty IDs cannot be removed.
*/
void t_event_handlers::remove_event_handler(std::string const & id)
{
if ( id.empty() )
return;
DBG_EH << "removing event handler with id " << id << "\n";
// Find the existing handler with this ID.
id_map_t::iterator find_it = id_map_.find(id);
if ( find_it != id_map_.end() ) {
handler_ptr handler = find_it->second.lock();
// Remove handler.
if ( handler )
handler->disable();
id_map_.erase(find_it); // Do this even if the lock failed.
// The index by name will self-adjust later. No need to adjust it now.
}
log_handlers();
}
} // end namespace game_events

View file

@ -0,0 +1,82 @@
/*
Copyright (C) 2003 - 2014 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_MANAGER_IMPL_HPP
#define GAME_EVENTS_MANAGER_IMPL_HPP
#include "game_events/handlers.hpp"
#include <boost/noncopyable.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/unordered_map.hpp>
#include <boost/weak_ptr.hpp>
namespace game_events {
//t_event_handlers is essentially the implementation details of the manager
class t_event_handlers {
typedef boost::unordered_map<std::string, handler_list> map_t;
typedef boost::unordered_map<std::string, boost::weak_ptr<event_handler> > id_map_t;
public:
typedef handler_vec::iterator iterator;
typedef handler_vec::const_iterator const_iterator;
private:
handler_vec active_; /// Active event handlers. Will not have elements removed unless the t_event_handlers is clear()ed.
map_t by_name_; /// Active event handlers with fixed event names, organized by event name.
handler_list dynamic_; /// Active event handlers with variables in their event names.
id_map_t id_map_; /// Allows quick locating of handlers by id.
void log_handlers();
/// Utility to standardize the event names used in by_name_.
static std::string standardize_name(const std::string & name);
public:
typedef handler_vec::size_type size_type;
t_event_handlers()
: active_()
, by_name_()
, dynamic_()
, id_map_()
{}
/// Read-only access to the handlers with varying event names.
const handler_list & get() const { return dynamic_; }
/// Read-only access to the handlers with fixed event names, by event name.
const handler_list & get(const std::string & name) const;
/// Adds an event handler.
void add_event_handler(const config & cfg, manager & man, bool is_menu_item=false);
/// Removes an event handler, identified by its ID.
void remove_event_handler(std::string const & id);
iterator begin() { return active_.begin(); }
const_iterator begin() const { return active_.begin(); }
iterator end() { return active_.end(); }
const_iterator end() const { return active_.end(); }
/// The number of active event handlers.
size_type size() const { return active_.size(); }
/// Access to active event handlers by index.
handler_ptr & operator[](size_type index) { return active_[index]; }
};//t_event_handlers
} //end namespace game_events
#endif

View file

@ -17,10 +17,13 @@
* Definitions for a class that implements WML-defined (right-click) menu items. * Definitions for a class that implements WML-defined (right-click) menu items.
*/ */
#include "../global.hpp"
#include "menu_item.hpp" #include "menu_item.hpp"
#include "../global.hpp"
#include "conditional_wml.hpp" #include "conditional_wml.hpp"
#include "handlers.hpp" #include "handlers.hpp"
#include "manager.hpp"
#include "pump.hpp" #include "pump.hpp"
#include "../actions/undo.hpp" #include "../actions/undo.hpp"

View file

@ -22,6 +22,7 @@
#include "pump.hpp" #include "pump.hpp"
#include "conditional_wml.hpp" #include "conditional_wml.hpp"
#include "handlers.hpp" #include "handlers.hpp"
#include "manager.hpp"
#include "../display_chat_manager.hpp" #include "../display_chat_manager.hpp"
#include "../game_config.hpp" #include "../game_config.hpp"

View file

@ -29,7 +29,7 @@
#include "dialogs.hpp" #include "dialogs.hpp"
#include "display_chat_manager.hpp" #include "display_chat_manager.hpp"
#include "formula_string_utils.hpp" #include "formula_string_utils.hpp"
#include "game_events/handlers.hpp" #include "game_events/manager.hpp"
#include "game_events/menu_item.hpp" #include "game_events/menu_item.hpp"
#include "game_events/pump.hpp" #include "game_events/pump.hpp"
#include "game_preferences.hpp" #include "game_preferences.hpp"

View file

@ -22,7 +22,7 @@
#include "display_chat_manager.hpp" #include "display_chat_manager.hpp"
#include "game_end_exceptions.hpp" #include "game_end_exceptions.hpp"
#include "game_errors.hpp" //needed to be thrown #include "game_errors.hpp" //needed to be thrown
#include "game_events/handlers.hpp" #include "game_events/manager.hpp"
#include "game_events/pump.hpp" #include "game_events/pump.hpp"
#include "gettext.hpp" #include "gettext.hpp"
#include "log.hpp" #include "log.hpp"

View file

@ -26,7 +26,7 @@
#include "game_data.hpp" #include "game_data.hpp"
#include "game_config.hpp" // for add_color_info, etc #include "game_config.hpp" // for add_color_info, etc
#include "game_errors.hpp" // for game_error #include "game_errors.hpp" // for game_error
#include "game_events/handlers.hpp" // for add_events #include "game_events/manager.hpp" // for add_events
#include "game_preferences.hpp" // for encountered_units #include "game_preferences.hpp" // for encountered_units
#include "gettext.hpp" // for N_ #include "gettext.hpp" // for N_
#include "log.hpp" // for LOG_STREAM, logger, etc #include "log.hpp" // for LOG_STREAM, logger, etc