split game_events::manager code into separate files
This commit is contained in:
parent
60e82d2f70
commit
aff3cb8da5
14 changed files with 613 additions and 448 deletions
|
@ -828,6 +828,8 @@ set(wesnoth-main_SRC
|
|||
game_events/conditional_wml.cpp
|
||||
game_events/entity_location.cpp
|
||||
game_events/handlers.cpp
|
||||
game_events/manager.cpp
|
||||
game_events/manager_impl.cpp
|
||||
game_events/menu_item.cpp
|
||||
game_events/pump.cpp
|
||||
game_events/wmi_container.cpp
|
||||
|
|
|
@ -300,6 +300,8 @@ wesnoth_sources = Split("""
|
|||
game_events/conditional_wml.cpp
|
||||
game_events/entity_location.cpp
|
||||
game_events/handlers.cpp
|
||||
game_events/manager.cpp
|
||||
game_events/manager_impl.cpp
|
||||
game_events/menu_item.cpp
|
||||
game_events/pump.cpp
|
||||
game_events/wmi_container.cpp
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
#include "global.hpp"
|
||||
#include "action_wml.hpp"
|
||||
#include "conditional_wml.hpp"
|
||||
#include "handlers.hpp"
|
||||
#include "manager.hpp"
|
||||
#include "pump.hpp"
|
||||
|
||||
#include "actions/attack.hpp"
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
|
||||
#include "../global.hpp"
|
||||
#include "handlers.hpp"
|
||||
#include "manager.hpp"
|
||||
#include "manager_impl.hpp"
|
||||
#include "menu_item.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.
|
||||
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 ** */
|
||||
|
||||
/**
|
||||
|
@ -520,35 +195,5 @@ bool event_handler::matches_name(const std::string &name) const
|
|||
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
|
||||
|
||||
|
|
|
@ -142,92 +142,6 @@ namespace game_events
|
|||
/// The actual list.
|
||||
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
|
||||
|
|
230
src/game_events/manager.cpp
Normal file
230
src/game_events/manager.cpp
Normal 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
116
src/game_events/manager.hpp
Normal 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
|
170
src/game_events/manager_impl.cpp
Normal file
170
src/game_events/manager_impl.cpp
Normal 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
|
82
src/game_events/manager_impl.hpp
Normal file
82
src/game_events/manager_impl.hpp
Normal 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
|
|
@ -17,10 +17,13 @@
|
|||
* Definitions for a class that implements WML-defined (right-click) menu items.
|
||||
*/
|
||||
|
||||
#include "../global.hpp"
|
||||
#include "menu_item.hpp"
|
||||
|
||||
#include "../global.hpp"
|
||||
|
||||
#include "conditional_wml.hpp"
|
||||
#include "handlers.hpp"
|
||||
#include "manager.hpp"
|
||||
#include "pump.hpp"
|
||||
|
||||
#include "../actions/undo.hpp"
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "pump.hpp"
|
||||
#include "conditional_wml.hpp"
|
||||
#include "handlers.hpp"
|
||||
#include "manager.hpp"
|
||||
|
||||
#include "../display_chat_manager.hpp"
|
||||
#include "../game_config.hpp"
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
#include "dialogs.hpp"
|
||||
#include "display_chat_manager.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/pump.hpp"
|
||||
#include "game_preferences.hpp"
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
#include "display_chat_manager.hpp"
|
||||
#include "game_end_exceptions.hpp"
|
||||
#include "game_errors.hpp" //needed to be thrown
|
||||
#include "game_events/handlers.hpp"
|
||||
#include "game_events/manager.hpp"
|
||||
#include "game_events/pump.hpp"
|
||||
#include "gettext.hpp"
|
||||
#include "log.hpp"
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
#include "game_data.hpp"
|
||||
#include "game_config.hpp" // for add_color_info, etc
|
||||
#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 "gettext.hpp" // for N_
|
||||
#include "log.hpp" // for LOG_STREAM, logger, etc
|
||||
|
|
Loading…
Add table
Reference in a new issue