Storyscreen: split common WML parsing code into a helper class

This commit is contained in:
Charles Dang 2017-05-03 20:07:03 +11:00
parent 3ff2816fee
commit e97b992393
8 changed files with 200 additions and 198 deletions

View file

@ -1042,6 +1042,8 @@
<Unit filename="../../src/statistics.hpp" />
<Unit filename="../../src/storyscreen/controller.cpp" />
<Unit filename="../../src/storyscreen/controller.hpp" />
<Unit filename="../../src/storyscreen/parser.cpp" />
<Unit filename="../../src/storyscreen/parser.hpp" />
<Unit filename="../../src/storyscreen/part.cpp" />
<Unit filename="../../src/storyscreen/part.hpp" />
<Unit filename="../../src/synced_checkup.cpp" />

View file

@ -359,6 +359,7 @@ settings.cpp
side_filter.cpp
statistics.cpp
storyscreen/controller.cpp
storyscreen/parser.cpp
storyscreen/part.cpp
synced_checkup.cpp
synced_commands.cpp

View file

@ -21,11 +21,6 @@
#include "storyscreen/controller.hpp"
#include "storyscreen/part.hpp"
#include "game_data.hpp"
#include "game_events/conditional_wml.hpp"
#include "game_events/manager.hpp"
#include "game_events/pump.hpp"
#include "gettext.hpp"
#include "log.hpp"
#include "resources.hpp"
#include "variable.hpp"
@ -46,98 +41,16 @@ controller::controller(const vconfig& data, const std::string& scenario_name)
resolve_wml(data);
}
void controller::resolve_wml(const vconfig& cfg)
void controller::resolve_wml_helper(const std::string& key, const vconfig& node)
{
for(vconfig::all_children_iterator i = cfg.ordered_begin(); i != cfg.ordered_end(); ++i) {
// i->first and i->second are goddamn temporaries; do not make references
const std::string key = i->first;
const vconfig node = i->second;
if(key == "part" && !node.empty()) {
part_pointer_type const story_part(new part(node));
// Use scenario name as part title if the WML doesn't supply a custom one.
if((*story_part).show_title() && (*story_part).title().empty()) {
(*story_part).set_title(scenario_name_);
}
parts_.push_back(story_part);
if(key == "part" && !node.empty()) {
part_pointer_type const story_part(new part(node));
// Use scenario name as part title if the WML doesn't supply a custom one.
if((*story_part).show_title() && (*story_part).title().empty()) {
(*story_part).set_title(scenario_name_);
}
// [if]
else if(key == "if") {
// check if the [if] tag has a [then] child;
// if we try to execute a non-existing [then], we get a segfault
if(game_events::conditional_passed(node)) {
if(node.has_child("then")) {
resolve_wml(node.child("then"));
}
}
// condition not passed, check [elseif] and [else]
else {
// get all [elseif] children and set a flag
vconfig::child_list elseif_children = node.get_children("elseif");
bool elseif_flag = false;
// for each [elseif]: test if it has a [then] child
// if the condition matches, execute [then] and raise flag
for(vconfig::child_list::const_iterator elseif = elseif_children.begin();
elseif != elseif_children.end(); ++elseif) {
if(game_events::conditional_passed(*elseif)) {
if(elseif->has_child("then")) {
resolve_wml(elseif->child("then"));
}
elseif_flag = true;
break;
}
}
// if we have an [else] tag and no [elseif] was successful (flag not raised), execute it
if(node.has_child("else") && !elseif_flag) {
resolve_wml(node.child("else"));
}
}
}
// [switch]
else if(key == "switch") {
const std::string var_name = node["variable"];
const std::string var_actual_value = resources::gamedata->get_variable_const(var_name);
bool case_not_found = true;
for(vconfig::all_children_iterator j = node.ordered_begin(); j != node.ordered_end(); ++j) {
if(j->first != "case") {
continue;
}
// Enter all matching cases.
const std::string var_expected_value = (j->second)["value"];
if(var_actual_value == var_expected_value) {
case_not_found = false;
resolve_wml(j->second);
}
}
if(case_not_found) {
for(vconfig::all_children_iterator j = node.ordered_begin(); j != node.ordered_end(); ++j) {
if(j->first != "else") {
continue;
}
// Enter all elses.
resolve_wml(j->second);
}
}
}
// [deprecated_message]
else if(key == "deprecated_message") {
// Won't appear until the scenario start event finishes.
lg::wml_error() << node["message"] << '\n';
}
// [wml_message]
else if(key == "wml_message") {
// As with [deprecated_message],
// it won't appear until the scenario start event is complete.
resources::game_events->pump().put_wml_message(
node["logger"], node["message"], node["in_chat"].to_bool(false));
}
parts_.push_back(story_part);
}
}

View file

@ -21,18 +21,17 @@
#define STORYSCREEN_CONTROLLER_HPP_INCLUDED
#include "events.hpp"
#include "storyscreen/parser.hpp"
#include <memory>
#include <string>
class vconfig;
namespace storyscreen
{
class part;
class floating_image;
class controller
class controller : private story_parser
{
public:
typedef std::shared_ptr<part> part_pointer_type;
@ -50,8 +49,8 @@ public:
}
private:
// Executes WML flow instructions and inserts parts.
void resolve_wml(const vconfig& cfg);
/** Inherited from story_parser. */
virtual void resolve_wml_helper(const std::string& key, const vconfig& node) override;
std::string scenario_name_;

118
src/storyscreen/parser.cpp Normal file
View file

@ -0,0 +1,118 @@
/*
Copyright (C) 2009 - 2017 by Ignacio R. Morelle <shadowm2006@gmail.com>
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 "storyscreen/parser.hpp"
#include "game_data.hpp"
#include "game_events/conditional_wml.hpp"
#include "game_events/manager.hpp"
#include "game_events/pump.hpp"
#include "log.hpp"
#include "resources.hpp"
#include "variable.hpp"
namespace storyscreen
{
void story_parser::resolve_wml(const vconfig& cfg)
{
// Execution flow/branching/[image]
for(vconfig::all_children_iterator i = cfg.ordered_begin(); i != cfg.ordered_end(); ++i) {
// i->first and i->second are goddamn temporaries; do not make references
const std::string key = i->first;
const vconfig node = i->second;
// Execute any special actions derived classes provide.
resolve_wml_helper(key, node);
// [if]
if(key == "if") {
// check if the [if] tag has a [then] child;
// if we try to execute a non-existing [then], we get a segfault
if(game_events::conditional_passed(node)) {
if(node.has_child("then")) {
resolve_wml(node.child("then"));
}
}
// condition not passed, check [elseif] and [else]
else {
// get all [elseif] children and set a flag
vconfig::child_list elseif_children = node.get_children("elseif");
bool elseif_flag = false;
// for each [elseif]: test if it has a [then] child
// if the condition matches, execute [then] and raise flag
for(vconfig::child_list::const_iterator elseif = elseif_children.begin();
elseif != elseif_children.end(); ++elseif) {
if(game_events::conditional_passed(*elseif)) {
if(elseif->has_child("then")) {
resolve_wml(elseif->child("then"));
}
elseif_flag = true;
break;
}
}
// if we have an [else] tag and no [elseif] was successful (flag not raised), execute it
if(node.has_child("else") && !elseif_flag) {
resolve_wml(node.child("else"));
}
}
}
// [switch]
else if(key == "switch") {
const std::string var_name = node["variable"];
const std::string var_actual_value = resources::gamedata->get_variable_const(var_name);
bool case_not_found = true;
for(vconfig::all_children_iterator j = node.ordered_begin(); j != node.ordered_end(); ++j) {
if(j->first != "case") {
continue;
}
// Enter all matching cases.
const std::string var_expected_value = (j->second)["value"];
if(var_actual_value == var_expected_value) {
case_not_found = false;
resolve_wml(j->second);
}
}
if(case_not_found) {
for(vconfig::all_children_iterator j = node.ordered_begin(); j != node.ordered_end(); ++j) {
if(j->first != "else") {
continue;
}
// Enter all elses.
resolve_wml(j->second);
}
}
}
// [deprecated_message]
else if(key == "deprecated_message") {
// Won't appear until the scenario start event finishes.
lg::wml_error() << node["message"] << '\n';
}
// [wml_message]
else if(key == "wml_message") {
// As with [deprecated_message],
// it won't appear until the scenario start event is complete.
resources::game_events->pump().put_wml_message(
node["logger"], node["message"], node["in_chat"].to_bool(false));
}
}
}
} // namespace storyscreen

View file

@ -0,0 +1,48 @@
/*
Copyright (C) 2009 - 2017 by Ignacio R. Morelle <shadowm2006@gmail.com>
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 STORYSCREEN_PARSER_HPP_INCLUDED
#define STORYSCREEN_PARSER_HPP_INCLUDED
#include <string>
class vconfig;
namespace storyscreen
{
/**
* Small helper class to encapsulate the common logic for parsing storyscreen WML.
*/
class story_parser
{
public:
story_parser() = default;
story_parser(const story_parser&) = delete;
operator=(const story_parser&) = delete;
/** Takes care of initializing and branching properties. */
virtual void resolve_wml(const vconfig& cfg);
/**
* May be implemented by derived classes to perform additional actions
* When executing @ref resolve_wml.
*/
virtual void resolve_wml_helper(const std::string& key, const vconfig& node) = 0;
};
} // namespace storyscreen
#endif

View file

@ -20,13 +20,6 @@
#include "storyscreen/part.hpp"
#include "config.hpp"
#include "game_data.hpp"
#include "game_events/conditional_wml.hpp"
#include "game_events/manager.hpp"
#include "game_events/pump.hpp"
#include "log.hpp"
#include "resources.hpp"
#include "serialization/string_utils.hpp"
#include "variable.hpp"
namespace storyscreen
@ -246,96 +239,19 @@ void part::resolve_wml(const vconfig& cfg)
sound_ = cfg["sound"].str();
}
// Execution flow/branching/[image]
for(vconfig::all_children_iterator i = cfg.ordered_begin(); i != cfg.ordered_end(); ++i) {
// i->first and i->second are goddamn temporaries; do not make references
const std::string key = i->first;
const vconfig node = i->second;
// Inherited
story_parser::resolve_wml(cfg);
}
// [background_layer]
if(key == "background_layer") {
background_layers_.push_back(node.get_parsed_config());
}
// [image]
else if(key == "image") {
floating_images_.push_back(node.get_parsed_config());
}
// [if]
else if(key == "if") {
// check if the [if] tag has a [then] child;
// if we try to execute a non-existing [then], we get a segfault
if(game_events::conditional_passed(node)) {
if(node.has_child("then")) {
resolve_wml(node.child("then"));
}
}
// condition not passed, check [elseif] and [else]
else {
// get all [elseif] children and set a flag
vconfig::child_list elseif_children = node.get_children("elseif");
bool elseif_flag = false;
// for each [elseif]: test if it has a [then] child
// if the condition matches, execute [then] and raise flag
for(vconfig::child_list::const_iterator elseif = elseif_children.begin();
elseif != elseif_children.end(); ++elseif) {
if(game_events::conditional_passed(*elseif)) {
if(elseif->has_child("then")) {
resolve_wml(elseif->child("then"));
}
elseif_flag = true;
break;
}
}
// if we have an [else] tag and no [elseif] was successful (flag not raised), execute it
if(node.has_child("else") && !elseif_flag) {
resolve_wml(node.child("else"));
}
}
}
// [switch]
else if(key == "switch") {
const std::string var_name = node["variable"];
const std::string var_actual_value = resources::gamedata->get_variable_const(var_name);
bool case_not_found = true;
for(vconfig::all_children_iterator j = node.ordered_begin(); j != node.ordered_end(); ++j) {
if(j->first != "case") {
continue;
}
// Enter all matching cases.
const std::string var_expected_value = (j->second)["value"];
if(var_actual_value == var_expected_value) {
case_not_found = false;
resolve_wml(j->second);
}
}
if(case_not_found) {
for(vconfig::all_children_iterator j = node.ordered_begin(); j != node.ordered_end(); ++j) {
if(j->first != "else") {
continue;
}
// Enter all elses.
resolve_wml(j->second);
}
}
}
// [deprecated_message]
else if(key == "deprecated_message") {
// Won't appear until the scenario start event finishes.
lg::wml_error() << node["message"] << '\n';
}
// [wml_message]
else if(key == "wml_message") {
// As with [deprecated_message],
// it won't appear until the scenario start event is complete.
resources::game_events->pump().put_wml_message(
node["logger"], node["message"], node["in_chat"].to_bool(false));
}
void part::resolve_wml_helper(const std::string& key, const vconfig& node)
{
// [background_layer]
if(key == "background_layer") {
background_layers_.push_back(node.get_parsed_config());
}
// [image]
else if(key == "image") {
floating_images_.push_back(node.get_parsed_config());
}
}

View file

@ -20,6 +20,8 @@
#ifndef STORYSCREEN_PART_HPP_INCLUDED
#define STORYSCREEN_PART_HPP_INCLUDED
#include "storyscreen/parser.hpp"
#include <string>
#include <utility>
#include <vector>
@ -220,7 +222,7 @@ private:
/**
* Represents and contains information about a single storyscreen part.
*/
class part
class part : private story_parser
{
public:
/**
@ -333,8 +335,11 @@ public:
}
private:
/** Takes care of initializing and branching properties. */
void resolve_wml(const vconfig& cfg);
/** Inherited from story_parser. */
virtual void resolve_wml(const vconfig& cfg) override;
/** Inherited from story_parser. */
virtual void resolve_wml_helper(const std::string& key, const vconfig& node) override;
static BLOCK_LOCATION string_tblock_loc(const std::string& s);
static TEXT_ALIGNMENT string_title_align(const std::string& s);