stk-code_catmod/src/karts/xml_characteristic.cpp

604 lines
21 KiB
C++

//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2006-2015 SuperTuxKart-Team
//
// 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 3
// 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; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "karts/xml_characteristic.hpp"
#include "utils/interpolation_array.hpp"
#include "utils/log.hpp"
#include "utils/string_utils.hpp"
#include "io/xml_node.hpp"
XmlCharacteristic::XmlCharacteristic(const XMLNode *node) :
m_values(CHARACTERISTIC_COUNT)
{
if (node)
load(node);
} // XmlCharacteristic constructor
// ----------------------------------------------------------------------------
/** process will execute the operation that is specified in the saved string.
* The format of the operations is specified in kart_characteristics.xml.
*/
void XmlCharacteristic::process(CharacteristicType type, Value value,
bool *is_set) const
{
if (m_values[type].empty())
// That value was not changed in this configuration
return;
switch (getType(type))
{
case TYPE_FLOAT:
processFloat(m_values[type], value.f, is_set);
break;
case TYPE_BOOL:
processBool(m_values[type], value.b, is_set);
break;
case TYPE_FLOAT_VECTOR:
{
const std::vector<std::string> processors =
StringUtils::split(m_values[type], ' ');
// If the array should be completely replaced
// That has to happen when the size is not the same or it is not yet set
bool shouldReplace = false;
if (*is_set)
{
if (processors.size() != value.fv->size())
shouldReplace = true;
else
{
std::vector<float>::iterator fit = value.fv->begin();
for (const std::string &processor : processors)
{
processFloat(processor, &*fit, is_set);
if (!*is_set)
{
Log::error("XmlCharacteristic::process", "Can't process %s",
processor.c_str());
value.fv->clear();
break;
}
fit++;
}
}
}
else
shouldReplace = true;
if (shouldReplace)
{
value.fv->resize(processors.size());
std::vector<float>::iterator fit = value.fv->begin();
for (const std::string &processor : processors)
{
*is_set = false;
processFloat(processor, &*fit, is_set);
if (!*is_set)
{
Log::error("XmlCharacteristic::process", "Can't process %s",
getName(type).c_str());
value.fv->clear();
break;
}
fit++;
}
}
break;
}
case TYPE_INTERPOLATION_ARRAY:
{
const std::vector<std::string> processors =
StringUtils::split(m_values[type], ' ');
// If the interpolation array should be completely replaced
// That has to happen when the format is not the same
bool shouldReplace = false;
if (*is_set)
{
if (processors.size() != value.fv->size())
shouldReplace = true;
else
{
for (const std::string &processor : processors)
{
std::vector<std::string> pair = StringUtils::split(processor, ':');
if (pair.size() != 2)
Log::error("XmlCharacteristic::process",
"Can't process %s: Wrong format", getName(type).c_str());
else
{
float x;
if (!StringUtils::fromString(pair[0], x))
Log::error("XmlCharacteristic::process",
"Can't process %s: Not a float", getName(type).c_str());
else
{
// Search the index of this x value
bool found = false;
for (unsigned int i = 0; i < value.ia->size(); i++)
{
if (value.ia->getX(i) == x)
{
float val;
processFloat(pair[1], &val, is_set);
value.ia->setY(i, val);
found = true;
break;
}
}
if (!found)
{
// The searched value was not found so we have
// a different format
shouldReplace = true;
break;
}
}
}
}
}
}
else
// It's not yet set, so we will the current content
shouldReplace = true;
if (shouldReplace)
{
value.ia->clear();
// Replace all values
for (const std::string &processor : processors)
{
std::vector<std::string> pair = StringUtils::split(processor,':');
if (pair.size() != 2)
Log::error("XmlCharacteristic::process",
"Can't process %s: Wrong format", getName(type).c_str());
else
{
float x;
if (!StringUtils::fromString(pair[0], x))
Log::error("XmlCharacteristic::process",
"Can't process %s: Not a float", getName(type).c_str());
else
{
float val;
*is_set = false;
processFloat(pair[1], &val, is_set);
if (!*is_set)
{
Log::error("XmlCharacteristic::process", "Can't process %s",
getName(type).c_str());
value.ia->clear();
break;
}
value.ia->push_back(x, val);
}
}
}
}
break;
}
default:
Log::fatal("XmlCharacteristic::process", "Unknown type for %s",
getName(type).c_str());
}
} // process
// ----------------------------------------------------------------------------
/** Executes an operation on a float value. */
void XmlCharacteristic::processFloat(const std::string &processor, float *value,
bool *is_set)
{
// Split the string by operators
static const std::string operators = "*/+-";
std::vector<std::string> parts;
std::vector<std::string> operations;
std::size_t pos = 0;
std::size_t pos2;
while ((pos2 = processor.find_first_of(operators, pos)) != std::string::npos)
{
parts.push_back(processor.substr(pos, pos2));
operations.push_back(processor.substr(pos2, pos2 + 1));
pos = pos2 + 1;
}
parts.push_back(processor.substr(pos));
// Compute the result
float x = *value;
std::size_t index = 0;
// If nothing preceeds the first operator, insert x
if (parts[index].empty())
{
if (!*is_set)
{
Log::error("XmlCharacteristic::processFloat", "x is unknown");
return;
}
// - is a special case: We don't take e.g. "-5" as relative, it
// describes a negative number
else if (operations[index] == "-")
*value = 0;
else
*value = x;
}
else
{
float val;
if (!StringUtils::fromString(parts[index], val))
{
Log::fatal("XmlCharacteristic::processFloat",
"Can't parse %s: Not a float", parts[index].c_str());
return;
}
*value = val;
}
index++;
for (; index < parts.size(); index++)
{
float val;
if (parts[index] == "x" || parts[index] == "X")
val = x;
else if (!StringUtils::fromString(parts[index], val))
{
Log::fatal("XmlCharacteristic::processFloat",
"Can't parse %s: Not a float", parts[index].c_str());
return;
}
if (operations[index - 1] == "*")
*value *= val;
else if (operations[index - 1] == "/")
*value /= val;
else if (operations[index - 1] == "+")
*value += val;
else if (operations[index - 1] == "-")
*value -= val;
else
Log::fatal("XmlCharacteristic::processFloat",
"Unknown operator (%s)", operations[index - 1].c_str());
}
*is_set = true;
} // processFloat
// ----------------------------------------------------------------------------
/** Executes an operation on a bool value. */
void XmlCharacteristic::processBool(const std::string &processor, bool *value,
bool *is_set)
{
if (processor == "true")
{
*value = true;
*is_set = true;
}
else if (processor == "false")
{
*value = false;
*is_set = true;
}
else
Log::error("XmlCharacteristic::processBool", "Can't parse %s: Not a bool",
processor.c_str());
} // processBool
// ----------------------------------------------------------------------------
/** Loads all commands from a given xml file.
* Non-existing tags will be omitted.
*/
void XmlCharacteristic::load(const XMLNode *node)
{
// Script-generated content generated by tools/create_kart_properties.py getXml
// Please don't change the following tag. It will be automatically detected
// by the script and replace the contained content.
// To update the code, use tools/update_characteristics.py
/* <characteristics-start getXml> */
if (const XMLNode *sub_node = node->getNode("suspension"))
{
sub_node->get("stiffness",
&m_values[SUSPENSION_STIFFNESS]);
sub_node->get("rest",
&m_values[SUSPENSION_REST]);
sub_node->get("travel",
&m_values[SUSPENSION_TRAVEL]);
sub_node->get("exp-spring-response",
&m_values[SUSPENSION_EXP_SPRING_RESPONSE]);
sub_node->get("max-force",
&m_values[SUSPENSION_MAX_FORCE]);
}
if (const XMLNode *sub_node = node->getNode("stability"))
{
sub_node->get("roll-influence",
&m_values[STABILITY_ROLL_INFLUENCE]);
sub_node->get("chassis-linear-damping",
&m_values[STABILITY_CHASSIS_LINEAR_DAMPING]);
sub_node->get("chassis-angular-damping",
&m_values[STABILITY_CHASSIS_ANGULAR_DAMPING]);
sub_node->get("downward-impulse-factor",
&m_values[STABILITY_DOWNWARD_IMPULSE_FACTOR]);
sub_node->get("track-connection-accel",
&m_values[STABILITY_TRACK_CONNECTION_ACCEL]);
sub_node->get("smooth-flying-impulse",
&m_values[STABILITY_SMOOTH_FLYING_IMPULSE]);
}
if (const XMLNode *sub_node = node->getNode("turn"))
{
sub_node->get("radius",
&m_values[TURN_RADIUS]);
sub_node->get("time-reset-steer",
&m_values[TURN_TIME_RESET_STEER]);
sub_node->get("time-full-steer",
&m_values[TURN_TIME_FULL_STEER]);
}
if (const XMLNode *sub_node = node->getNode("engine"))
{
sub_node->get("power",
&m_values[ENGINE_POWER]);
sub_node->get("max-speed",
&m_values[ENGINE_MAX_SPEED]);
sub_node->get("brake-factor",
&m_values[ENGINE_BRAKE_FACTOR]);
sub_node->get("brake-time-increase",
&m_values[ENGINE_BRAKE_TIME_INCREASE]);
sub_node->get("max-speed-reverse-ratio",
&m_values[ENGINE_MAX_SPEED_REVERSE_RATIO]);
}
if (const XMLNode *sub_node = node->getNode("gear"))
{
sub_node->get("switch-ratio",
&m_values[GEAR_SWITCH_RATIO]);
sub_node->get("power-increase",
&m_values[GEAR_POWER_INCREASE]);
}
if (const XMLNode *sub_node = node->getNode("mass"))
{
sub_node->get("value",
&m_values[MASS]);
}
if (const XMLNode *sub_node = node->getNode("wheels"))
{
sub_node->get("damping-relaxation",
&m_values[WHEELS_DAMPING_RELAXATION]);
sub_node->get("damping-compression",
&m_values[WHEELS_DAMPING_COMPRESSION]);
}
if (const XMLNode *sub_node = node->getNode("camera"))
{
sub_node->get("distance",
&m_values[CAMERA_DISTANCE]);
sub_node->get("forward-up-angle",
&m_values[CAMERA_FORWARD_UP_ANGLE]);
sub_node->get("backward-up-angle",
&m_values[CAMERA_BACKWARD_UP_ANGLE]);
}
if (const XMLNode *sub_node = node->getNode("jump"))
{
sub_node->get("animation-time",
&m_values[JUMP_ANIMATION_TIME]);
}
if (const XMLNode *sub_node = node->getNode("lean"))
{
sub_node->get("max",
&m_values[LEAN_MAX]);
sub_node->get("speed",
&m_values[LEAN_SPEED]);
}
if (const XMLNode *sub_node = node->getNode("anvil"))
{
sub_node->get("duration",
&m_values[ANVIL_DURATION]);
sub_node->get("weight",
&m_values[ANVIL_WEIGHT]);
sub_node->get("speed-factor",
&m_values[ANVIL_SPEED_FACTOR]);
}
if (const XMLNode *sub_node = node->getNode("parachute"))
{
sub_node->get("friction",
&m_values[PARACHUTE_FRICTION]);
sub_node->get("duration",
&m_values[PARACHUTE_DURATION]);
sub_node->get("duration-other",
&m_values[PARACHUTE_DURATION_OTHER]);
sub_node->get("lbound-fraction",
&m_values[PARACHUTE_LBOUND_FRACTION]);
sub_node->get("ubound-fraction",
&m_values[PARACHUTE_UBOUND_FRACTION]);
sub_node->get("max-speed",
&m_values[PARACHUTE_MAX_SPEED]);
}
if (const XMLNode *sub_node = node->getNode("bubblegum"))
{
sub_node->get("duration",
&m_values[BUBBLEGUM_DURATION]);
sub_node->get("speed-fraction",
&m_values[BUBBLEGUM_SPEED_FRACTION]);
sub_node->get("torque",
&m_values[BUBBLEGUM_TORQUE]);
sub_node->get("fade-in-time",
&m_values[BUBBLEGUM_FADE_IN_TIME]);
sub_node->get("shield-duration",
&m_values[BUBBLEGUM_SHIELD_DURATION]);
}
if (const XMLNode *sub_node = node->getNode("zipper"))
{
sub_node->get("duration",
&m_values[ZIPPER_DURATION]);
sub_node->get("force",
&m_values[ZIPPER_FORCE]);
sub_node->get("speed-gain",
&m_values[ZIPPER_SPEED_GAIN]);
sub_node->get("max-speed-increase",
&m_values[ZIPPER_MAX_SPEED_INCREASE]);
sub_node->get("fade-out-time",
&m_values[ZIPPER_FADE_OUT_TIME]);
}
if (const XMLNode *sub_node = node->getNode("swatter"))
{
sub_node->get("duration",
&m_values[SWATTER_DURATION]);
sub_node->get("distance",
&m_values[SWATTER_DISTANCE]);
sub_node->get("squash-duration",
&m_values[SWATTER_SQUASH_DURATION]);
sub_node->get("squash-slowdown",
&m_values[SWATTER_SQUASH_SLOWDOWN]);
}
if (const XMLNode *sub_node = node->getNode("plunger"))
{
sub_node->get("band-max-length",
&m_values[PLUNGER_BAND_MAX_LENGTH]);
sub_node->get("band-force",
&m_values[PLUNGER_BAND_FORCE]);
sub_node->get("band-duration",
&m_values[PLUNGER_BAND_DURATION]);
sub_node->get("band-speed-increase",
&m_values[PLUNGER_BAND_SPEED_INCREASE]);
sub_node->get("band-fade-out-time",
&m_values[PLUNGER_BAND_FADE_OUT_TIME]);
sub_node->get("in-face-time",
&m_values[PLUNGER_IN_FACE_TIME]);
}
if (const XMLNode *sub_node = node->getNode("startup"))
{
sub_node->get("time",
&m_values[STARTUP_TIME]);
sub_node->get("boost",
&m_values[STARTUP_BOOST]);
}
if (const XMLNode *sub_node = node->getNode("rescue"))
{
sub_node->get("duration",
&m_values[RESCUE_DURATION]);
sub_node->get("vert-offset",
&m_values[RESCUE_VERT_OFFSET]);
sub_node->get("height",
&m_values[RESCUE_HEIGHT]);
}
if (const XMLNode *sub_node = node->getNode("explosion"))
{
sub_node->get("duration",
&m_values[EXPLOSION_DURATION]);
sub_node->get("radius",
&m_values[EXPLOSION_RADIUS]);
sub_node->get("invulnerability-time",
&m_values[EXPLOSION_INVULNERABILITY_TIME]);
}
if (const XMLNode *sub_node = node->getNode("nitro"))
{
sub_node->get("duration",
&m_values[NITRO_DURATION]);
sub_node->get("engine-force",
&m_values[NITRO_ENGINE_FORCE]);
sub_node->get("consumption",
&m_values[NITRO_CONSUMPTION]);
sub_node->get("small-container",
&m_values[NITRO_SMALL_CONTAINER]);
sub_node->get("big-container",
&m_values[NITRO_BIG_CONTAINER]);
sub_node->get("max-speed-increase",
&m_values[NITRO_MAX_SPEED_INCREASE]);
sub_node->get("fade-out-time",
&m_values[NITRO_FADE_OUT_TIME]);
sub_node->get("max",
&m_values[NITRO_MAX]);
}
if (const XMLNode *sub_node = node->getNode("slipstream"))
{
sub_node->get("duration",
&m_values[SLIPSTREAM_DURATION]);
sub_node->get("length",
&m_values[SLIPSTREAM_LENGTH]);
sub_node->get("width",
&m_values[SLIPSTREAM_WIDTH]);
sub_node->get("collect-time",
&m_values[SLIPSTREAM_COLLECT_TIME]);
sub_node->get("use-time",
&m_values[SLIPSTREAM_USE_TIME]);
sub_node->get("add-power",
&m_values[SLIPSTREAM_ADD_POWER]);
sub_node->get("min-speed",
&m_values[SLIPSTREAM_MIN_SPEED]);
sub_node->get("max-speed-increase",
&m_values[SLIPSTREAM_MAX_SPEED_INCREASE]);
sub_node->get("fade-out-time",
&m_values[SLIPSTREAM_FADE_OUT_TIME]);
}
if (const XMLNode *sub_node = node->getNode("skid"))
{
sub_node->get("increase",
&m_values[SKID_INCREASE]);
sub_node->get("decrease",
&m_values[SKID_DECREASE]);
sub_node->get("max",
&m_values[SKID_MAX]);
sub_node->get("time-till-max",
&m_values[SKID_TIME_TILL_MAX]);
sub_node->get("visual",
&m_values[SKID_VISUAL]);
sub_node->get("visual-time",
&m_values[SKID_VISUAL_TIME]);
sub_node->get("revert-visual-time",
&m_values[SKID_REVERT_VISUAL_TIME]);
sub_node->get("min-speed",
&m_values[SKID_MIN_SPEED]);
sub_node->get("time-till-bonus",
&m_values[SKID_TIME_TILL_BONUS]);
sub_node->get("bonus-speed",
&m_values[SKID_BONUS_SPEED]);
sub_node->get("bonus-time",
&m_values[SKID_BONUS_TIME]);
sub_node->get("bonus-force",
&m_values[SKID_BONUS_FORCE]);
sub_node->get("physical-jump-time",
&m_values[SKID_PHYSICAL_JUMP_TIME]);
sub_node->get("graphical-jump-time",
&m_values[SKID_GRAPHICAL_JUMP_TIME]);
sub_node->get("post-skid-rotate-factor",
&m_values[SKID_POST_SKID_ROTATE_FACTOR]);
sub_node->get("reduce-turn-min",
&m_values[SKID_REDUCE_TURN_MIN]);
sub_node->get("reduce-turn-max",
&m_values[SKID_REDUCE_TURN_MAX]);
sub_node->get("enabled",
&m_values[SKID_ENABLED]);
}
/* <characteristics-end getXml> */
} // load