Commit 89d5145b authored by Jason Rhinelander's avatar Jason Rhinelander

Add `Policy' class to handle policy operations

Manually playing with bits in various places is dirty: this adds a new
Policy class that abstracts all the bit values, while providing various
nicer operations and methods related to a policy value.

This also includes a eris::serialize::serializer<Policy> implementation
(which is just a simple wrapper around a serializer<uint32_t>) so that
a Policy object can be directly serialized.
parent 42629e3c
......@@ -5,6 +5,7 @@
#include "creativity/state/Storage.hpp"
#include "creativity/Reader.hpp"
#include "creativity/BookMarket.hpp"
#include "creativity/Policy.hpp"
#include <eris/random/rng.hpp>
#include <boost/random/uniform_int_distribution.hpp>
#include <boost/random/uniform_real_distribution.hpp>
......@@ -60,6 +61,8 @@ void Creativity::checkParameters() {
PROHIBIT(initial.keep_price, < 0);
#undef PROHIBIT
if (parameters.policy.unknown()) throw std::domain_error("Invalid Creativity setting: parameters.policy " + std::to_string((uint32_t) parameters.policy) + " is invalid");
// Reader optimization depends on the fine being non-decreasing in the number of books (so that
// each additional pirated book increases the fine by at least as much as the previous book).
// This simplifies the optimization problem greatly: readers can just increase pirated books
......@@ -74,7 +77,7 @@ void Creativity::checkParameters() {
// (Actually, we really care about probability times the fine, and so we could, in theory, allow
// a little bit of concavity depending on the detection parameters, but that would be a
// difficult calculation involving the CDF of a normal).
if (parameters.policy & POLICY_CATCH_PIRATES) {
if (parameters.policy.catchPirates()) {
if (parameters.policy_catch_fine[2] < 0)
throw std::domain_error("Invalid Creativity setting: parameters.policy_catch_fine must be convex (i.e. element [2] must be >= 0)");
if (parameters.policy_catch_fine[1] + parameters.policy_catch_fine[2] < 0)
......@@ -149,21 +152,18 @@ void Creativity::run() {
// If the next period is the first when the policy response becomes active, initialize it as
// needed.
if (sim->t() + 1 == parameters.policy_begins) {
uint32_t policies = parameters.policy;
auto pub_track_mask = POLICY_PUBLIC_SHARING | POLICY_PUBLIC_SHARING_VOTING;
if (policies & pub_track_mask) {
policies &= ~pub_track_mask;
// Create the PublicTracker to provide and compensate authors:
Policy policies(parameters.policy);
if (policies.unknown()) throw std::runtime_error("Creativity simulation has unknown/invalid `policy` value");
// Create the PublicTracker to provide and compensate authors (if under either public
// sharing policy):
if (policies.publicSharing() || policies.publicVoting())
sim->spawn<PublicTracker>(*this);
}
if (policies & POLICY_CATCH_PIRATES) {
policies &= ~POLICY_CATCH_PIRATES;
// Spawn the agent that detects and handles piracy:
// Spawn the agent that detects and handles piracy fines (if under the catch pirates policy):
if (policies.catchPirates())
sim->spawn<CopyrightPolice>(*this);
}
if (policies)
throw std::runtime_error("Creativity simulation has unknown/invalid `policy` value");
}
sim->run();
......@@ -277,17 +277,17 @@ bool Creativity::policyActive() const {
bool Creativity::publicSharingActive() const {
if (!setup_sim_) throw std::logic_error("Cannot call publicSharingActive() on a non-live or unconfigured simulation");
return parameters.policy & POLICY_PUBLIC_SHARING and policyActive();
return parameters.policy.publicSharing() && policyActive();
}
bool Creativity::publicVotingActive() const {
if (!setup_sim_) throw std::logic_error("Cannot call publicVotingActive() on a non-live or unconfigured simulation");
return parameters.policy & POLICY_PUBLIC_SHARING_VOTING and policyActive();
return parameters.policy.publicVoting() && policyActive();
}
bool Creativity::catchPiratesActive() const {
if (!setup_sim_) throw std::logic_error("Cannot call catchPiratesActive() on a non-live or unconfigured simulation");
return parameters.policy & POLICY_CATCH_PIRATES and policyActive();
return parameters.policy.catchPirates() && policyActive();
}
double Creativity::policyTaxes() const {
......
......@@ -14,19 +14,6 @@ namespace creativity { class Book; }
/// Primary namespace for all Creativity library code.
namespace creativity {
/** The bit for the public-sharing policy in `settings.policy`.
* \internal
*/
constexpr uint32_t POLICY_PUBLIC_SHARING = 1 << 0;
/** The bit for the catch-pirates policy in `settings.policy`.
* \internal
*/
constexpr uint32_t POLICY_CATCH_PIRATES = 1 << 1;
/** The bit for the public-sharing-with-voting policy in `settings.policy`.
* \internal
*/
constexpr uint32_t POLICY_PUBLIC_SHARING_VOTING = 1 << 2;
/** Central class for a creativity simulation; this class handles setting up the simulation
* according to configured parameters and running the simulation.
*
......
#pragma once
#include "creativity/Policy.hpp"
#include <eris/types.hpp>
#include <cstdint>
#include <array>
......@@ -110,26 +111,26 @@ struct CreativitySettings {
*/
eris::eris_time_t piracy_begins = 101;
/** Bitfield specifying the enabled policy response(s) used to address piracy. Currently
* supported bit values are:
/** The policy response(s) used to address piracy. Supported policies (which may be combined)
* are:
*
* - 0x1: policy_public_sharing: public tax for public provisioning and sharing; when the policy
* response begins, a PublicTracker agent is created that distributes copies of books to
* anyone at marginal cost. Authors are compensated in proportion to the number of downloads
* of their works from a per-reader lump sum tax.
* - public sharing: public tax for public provisioning and sharing; when the policy response
* begins, a PublicTracker agent is created that distributes copies of books to anyone at
* marginal cost. Authors are compensated in proportion to the number of downloads of their
* works from a per-reader lump sum tax.
*
* - 0x2: policy_catch: public tax for catching people downloading illegal copies. A lump sum
* tax is collected and goes towards the policing effort, with a larger tax resulting in a
* higher probability of detection.
* - catch pirates: public tax for catching people downloading illegal copies. A lump sum tax
* is collected and goes towards the policing effort, with a larger tax resulting in a higher
* probability of detection.
*
* - 0x4: policy_public_sharing_voting: public tax for public provisioning and sharing with
* reader votes. This is like policy_public_sharing, but instead of awarding the collected
* prize in proportion to downloads, readers cast votes for their favourite works in a period
* and the tax is redistributed proportionally to votes received.
* - public voting: public tax for public provisioning and sharing with reader votes. This is
* like public sharing, but instead of awarding the collected prize in proportion to
* downloads, readers cast votes for their favourite works in a period and the tax is
* redistributed proportionally to votes received.
*
* Other values are reserved for future use.
*/
uint32_t policy = 0;
Policy policy = Policy::PublicSharing();
/** The period in which the policy response to piracy begins. Which response is used depends on
* the `policy` parameter.
......
#include "creativity/Policy.hpp"
#include <sstream>
namespace creativity {
Policy::Policy(const std::string &p) : Policy() {
std::istringstream iss(p);
std::string policy;
while (std::getline(iss, policy, ',')) {
if (policy == "public" || policy == "public-sharing")
*this += PublicSharing();
else if (policy == "vote" || policy == "voting" || policy == "public-voting")
*this += PublicVoting();
else if (policy == "catch" || policy == "catch-pirates")
*this += CatchPirates();
else if (policy == "none" || policy == "")
/* ignore */;
else
throw std::runtime_error("Invalid/unknown Policy string: " + p);
}
}
Policy& Policy::operator+=(const Policy &add) {
if (add.publicSharing()) code_ |= POLICY_PUBLIC_SHARING;
if (add.publicVoting()) code_ |= POLICY_PUBLIC_VOTING;
if (add.catchPirates()) code_ |= POLICY_CATCH_PIRATES;
return *this;
}
Policy& Policy::operator-=(const Policy &remove) {
if (remove.publicSharing()) code_ &= ~POLICY_PUBLIC_SHARING;
if (remove.publicVoting()) code_ &= ~POLICY_PUBLIC_VOTING;
if (remove.catchPirates()) code_ &= ~POLICY_CATCH_PIRATES;
return *this;
}
}
#pragma once
#include <cstdint>
#include <string>
#include <eris/serialize/serializer.hpp>
namespace creativity {
/** Class for interpreting and/or constructing encoded policy values. The encoded value is stored
* in the Creativity `settings.policy` parameter. That value should be generated and interpreted
* only through the abstractions provided by this class.
*/
class Policy final {
public:
/// Default construction results in an all-disabled policy.
constexpr Policy() : Policy(0) {};
/** Constructs a Policy wrapper from an encoded policy value. This constructor intentionally
* allows implicit conversion from a uint32_t previously obtained from a Policy object.
*/
constexpr Policy(uint32_t policy_code) : code_{policy_code} {}
/** Constructs a Policy wrapper from booleans for each policy type.
*
* \param public_sharing if true, constructs a policy with public sharing enabled
* \param public_voting if true, constructs a policy with public sharing with voting
* enabled
* \param catch_pirates if true, constructs a policy with catching pirates enabled
*/
constexpr Policy(
bool public_sharing,
bool public_voting,
bool catch_pirates) : code_{
public_sharing * POLICY_PUBLIC_SHARING +
public_voting * POLICY_PUBLIC_VOTING +
catch_pirates * POLICY_CATCH_PIRATES}
{}
/** Constructs a Policy wrapper from a comma-separated list of policies. The following values
* are permitted:
*
* - "public-sharing" (or "public")
* - "public-voting" (or "voting" or "vote")
* - "catch-pirates" (or "catch")
* - "none" (or "") -- this value is silently ignored
*
* Any other value will result in a runtime_error exception being thrown.
*/
explicit Policy(const std::string &p);
/// Returns a constexpr Policy value for public sharing
constexpr static Policy PublicSharing() { return POLICY_PUBLIC_SHARING; }
/// Returns a constexpr Policy value for public voting
constexpr static Policy PublicVoting() { return POLICY_PUBLIC_VOTING; }
/// Returns a constexpr Policy value for catch pirates
constexpr static Policy CatchPirates() { return POLICY_CATCH_PIRATES; }
/// Returns a constexpr Policy value with all (known) policies enabled
constexpr static Policy All() { return Policy(true, true, true); }
/// Returns true if this policy has basic public sharing enabled
constexpr bool publicSharing() const { return code_ & POLICY_PUBLIC_SHARING; }
/// Returns true if this policy has public sharing with voting enabled
constexpr bool publicVoting() const { return code_ & POLICY_PUBLIC_VOTING; }
/// Returns true if this policy has catching pirates enabled
constexpr bool catchPirates() const { return code_ & POLICY_CATCH_PIRATES; }
/** Returns true if the policy has some unknown policy enabled (that is, some policy code that
* isn't constructible from the known policies).
*/
constexpr bool unknown() const { return code_ & ~(POLICY_PUBLIC_SHARING | POLICY_PUBLIC_VOTING | POLICY_CATCH_PIRATES); }
/** Returns true if all (known) policies are enabled. */
constexpr bool all() const { return publicSharing() && publicVoting() && catchPirates(); }
/** Implicit conversion to bool: returns true if any known policy is enabled *and* there are no
* unknown policy bits set in the policy code.
*/
constexpr operator bool() const { return (publicSharing() || publicVoting() || catchPirates()) && !unknown(); }
/** Implicit conversion to uint32_t policy code. */
constexpr operator uint32_t() const { return code_; }
/** Returns true if the two policy objects represent the same policy choice. */
constexpr bool operator==(const Policy &other) const { return code_ == other.code_; }
/** Negation of ==. */
constexpr bool operator!=(const Policy &other) const { return code_ != other.code_; }
/** Modifies this Policy value by enabling any policies enabled in the given Policy. Currently
* enabled policies remain enabled, even if not enabled in the target.
*
* Unknown policies in the target are ignored.
*/
Policy& operator+=(const Policy &add);
/** Modifies this Policy value by disabling any policies that are enabled in the given Policy.
* Unknown policies are ignored.
*/
Policy& operator-=(const Policy &remove);
/** Returns a new Policy object with all policies enabled that are enabled in either argument.
*/
constexpr Policy operator+(const Policy &plus) const {
return Policy(
publicSharing() || plus.publicSharing(),
publicVoting() || plus.publicVoting(),
catchPirates() || plus.catchPirates()
);
}
private:
uint32_t code_;
friend class eris::serialize::serializer<Policy>;
friend class eris::serialize::serializer<const Policy>;
// The bits for the known policies:
constexpr static uint32_t
POLICY_PUBLIC_SHARING = 1 << 0,
POLICY_CATCH_PIRATES = 1 << 1,
POLICY_PUBLIC_VOTING = 1 << 2;
};
}
namespace eris { namespace serialize {
/// Specialization of eris::serializer for a Policy reference
template <> class serializer<creativity::Policy> : public serializer<uint32_t> {
public:
/// Constructs a serializer that serializes the policy's internal uint32_t policy code
explicit serializer(creativity::Policy &p) : serializer<uint32_t>(p.code_) {}
};
/// Specialization of eris::serializer for a const Policy reference
template <> class serializer<const creativity::Policy> : public serializer<const uint32_t> {
public:
/// Constructs a serializer that serializes the policy's internal uint32_t policy code
explicit serializer(const creativity::Policy &p) : serializer<const uint32_t>(p.code_) {}
};
}}
......@@ -11,8 +11,8 @@ namespace creativity {
using namespace eris;
PublicTracker::PublicTracker(const Creativity &creativity) : creativity_{std::move(creativity)} {
bool dl = creativity_.parameters.policy & POLICY_PUBLIC_SHARING;
bool vote = creativity_.parameters.policy & POLICY_PUBLIC_SHARING_VOTING;
bool dl = creativity_.parameters.policy.publicSharing();
bool vote = creativity_.parameters.policy.publicVoting();
if (!dl && !vote)
throw std::logic_error("PublicTracker created for a simulation without public sharing or public voting!");
......
......@@ -28,6 +28,7 @@ void Results::addOptions() {
("all,a", value(analysis.all), "Implies --summary --write-vs-nowrite --average-effects --marginal-effects, but not --write-vs-nowrite-corr. If none of the above are given, this is the default.")
// ("none,n", value(analysis.none), "Show none of the above analysis. May not be combined with any of this above. This flag is intended for use with the --dump-* options.")
("short-run,s", value(analysis.shortrun), "Include short-run piracy/public analysis in the results. The data file must have short-run analysis (i.e. it must not have been created with --skip-short-run). Short-run analysis is skipped by default.")
("policy,g", value(policy_str_), "Only show analysis for simulations with this policy (or set of policies). Values are the same as creativity-cli's --policy argument, except for the special (default) value 'any', which disables policy filtering.")
;
options_.add(analysis_desc);
......@@ -78,6 +79,19 @@ void Results::postParse(boost::program_options::variables_map&) {
analysis.all or // --all explicitly given
not (analysis.summary or analysis.write_or_not or analysis.average or analysis.marginal)) // Nothing given: --all is default
analysis.summary = analysis.write_or_not = analysis.average = analysis.marginal = true;
if (policy_str_ == "any") {
analysis.policy_filter = false;
}
else {
analysis.policy_filter = true;
try {
analysis.policy = Policy(policy_str_);
}
catch (std::runtime_error &e) {
throw po::invalid_option_value("--policy " + policy_str_);
}
}
}
std::string Results::usage() const {
......
#pragma once
#include "creativity/cmdargs/CmdArgs.hpp"
#include "creativity/data/tabulate.hpp"
#include "creativity/Policy.hpp"
#include <eris/types.hpp>
#include <limits>
#include <string>
......@@ -51,6 +52,13 @@ class Results : public CmdArgs {
/// If true, do short-run analysis (will raise error if it doesn't exist in the data file)
bool shortrun = false;
/// If true, only show simulations with policy equal to `policy`
bool policy_filter = false;
/** The policy implied by the given --policy value. If none is given, this will be a
* no-policy option, and policy_str will be set to "any".
*/
Policy policy;
} analysis;
/// Condensed table output (currently only for average effects model)
......@@ -91,6 +99,9 @@ class Results : public CmdArgs {
/// Overridden to make sure only one of --text/--html/--latex are given.
virtual void postParse(boost::program_options::variables_map &vars) override;
/// The --policy argument (which is parsed into analysis.policy and .policy_filter)
std::string policy_str_ = "any";
};
}}
......@@ -78,7 +78,7 @@ void Simulator::addOptions() {
options_.add(piracy);
policy.add_options()
("policy,g", value(policies), "A list of policies to enable in the policy phase of the simulation. This option may be specified multiple times, or may be specified with a comma-separated list of policies. If the option is omitted, defaults to `public-sharing`; specify 'none' to disable all policies. Currently accepted policy names: 'public-sharing' - enables public sharing with redistribution (can be shorted to 'public'); 'public-voting' - like public-sharing, but readers cast votes for public works to determine payoffs (can be shorted to 'vote'); 'catch-pirates' - enables detection and fines (can be shorted to 'catch')")
("policy,g", value(this->policy), "A list of policies to enable in the policy phase of the simulation. May be specified as a comma-separated list of policies to enable simultaneously. If the option is omitted, defaults to `public-sharing`; specify 'none' or '' to disable all policies. Currently accepted policy names: 'public-sharing' - enables public sharing with redistribution (can be shorted to 'public'); 'public-voting' - like public-sharing, but readers cast votes for public works to determine payoffs (can be shorted to 'vote'); 'catch-pirates' - enables detection and fines (can be shorted to 'catch')")
("policy-begins,G", value(s_.policy_begins), "When a policy response is enabled, this specifies the period in which it begins. Has no effect if no policy response is enabled")
("prior-scale-policy,S", min<1>(s_.prior_scale_policy), "The same as --prior-scale, but applied in the first policy response period")
;
......@@ -139,25 +139,14 @@ void Simulator::postParse(boost::program_options::variables_map &vars) {
// Default, but only if the argument isn't specified (so that `--policy=` by itself disables the
// policy)
if (vars.count("policy") == 0) {
policies.push_back("public-sharing");
policy = "public-sharing";
}
for (const auto &p : policies) {
std::istringstream iss;
iss.str(p);
std::string policy;
while (std::getline(iss, policy, ',')) {
if (policy == "public" or policy == "public-sharing")
s_.policy |= POLICY_PUBLIC_SHARING;
else if (policy == "catch" or policy == "catch-pirates")
s_.policy |= POLICY_CATCH_PIRATES;
else if (policy == "vote" or policy == "voting" or policy == "public-voting")
s_.policy |= POLICY_PUBLIC_SHARING_VOTING;
else if (policy == "none" or policy == "")
/* ignore */;
else
throw po::invalid_option_value("--policy " + policy);
}
try {
s_.policy = Policy(policy);
}
catch (std::runtime_error &e) {
throw po::invalid_option_value("--policy " + policy);
}
}
......
......@@ -37,8 +37,8 @@ class Simulator : public CmdArgs {
*/
unsigned int periods = 300;
/// Stores the --policy= arguments given
std::vector<std::string> policies;
/// Stores the --policy= argument given
std::string policy;
/** The output file for simulation results; default is "creativity-SEED.crstate" for the
* CLI, unset for the GUI.
......
......@@ -453,7 +453,7 @@ std::vector<initial_datum> initial_data_fields() {
ADD_SETTING(piracy_begins);
ADD_SETTING(piracy_link_proportion);
ADD_SETTING(policy);
initial_data.emplace_back("policy", [](const CreativitySettings &cs) -> uint32_t { return cs.policy; });
ADD_SETTING(policy_begins);
ADD_SETTING(policy_public_sharing_tax);
ADD_SETTING(policy_catch_tax);
......
......@@ -9,6 +9,7 @@
#include "creativity/state/Storage.hpp"
#include "creativity/Creativity.hpp"
#include "creativity/config.hpp"
#include "creativity/Policy.hpp"
#include <eris/random/rng.hpp>
#include <boost/filesystem/path.hpp>
#include <cairomm/matrix.h>
......@@ -952,13 +953,13 @@ void GUI::thr_update_parameters() {
#undef SET_SB_ARRAY
widget<Gtk::CheckButton>("set_policy_public_sharing")->set_active(
creativity_.parameters.policy & POLICY_PUBLIC_SHARING);
creativity_.parameters.policy.publicSharing());
widget<Gtk::CheckButton>("set_policy_catch")->set_active(
creativity_.parameters.policy & POLICY_CATCH_PIRATES);
creativity_.parameters.policy.catchPirates());
widget<Gtk::CheckButton>("set_policy_public_voting")->set_active(
creativity_.parameters.policy & POLICY_PUBLIC_SHARING_VOTING);
creativity_.parameters.policy.publicVoting());
if (creativity_.parameters.policy & ~(POLICY_PUBLIC_SHARING | POLICY_CATCH_PIRATES | POLICY_PUBLIC_SHARING_VOTING))
if (creativity_.parameters.policy.unknown())
throw std::logic_error("Internal error: simulation policy value set to an unknown/invalid value");
}
......@@ -1260,12 +1261,13 @@ void GUI::initializeSim() {
#undef COPY_SB_ARRAY
#undef COPY_SB_INIT_D
set.policy = 0;
set.policy = Policy();
if (widget<Gtk::CheckButton>("set_policy_public_sharing")->get_active())
set.policy |= POLICY_PUBLIC_SHARING;
set.policy += Policy::PublicSharing();
if (widget<Gtk::CheckButton>("set_policy_public_voting")->get_active())
set.policy += Policy::PublicVoting();
if (widget<Gtk::CheckButton>("set_policy_catch")->get_active())
set.policy |= POLICY_CATCH_PIRATES;
set.policy += Policy::CatchPirates();
// Other non-simulation options need to be queued via a configure event:
Parameter p;
......
......@@ -53,9 +53,9 @@ class FileStorage : public StorageBackend, public eris::serialize::Serialization
/** Constructs and returns a FileStorage object that uses the given file for reading and
* (optionally) writing state data. The file is read or created immediately. All arguments
* (except the first) are forwarded to Serialize::open().
* (except the first) are forwarded to eris::Serializer::open().
*
* \sa Serialize::open()
* \sa eris::Serializer::open()
*
* \throws various exceptions if the file does not exist, cannot be read, is empty, or
* contains invalid data.
......
......@@ -69,9 +69,10 @@ int main(int argc, char *argv[]) {
const auto &policies = creativity.parameters.policy;
std::cout << std::setw(30) << "policy";
bool comma = false;
if (policies & POLICY_PUBLIC_SHARING) { if (comma) std::cout << ", "; comma = true; std::cout << "public sharing"; }
if (policies & POLICY_CATCH_PIRATES) { if (comma) std::cout << ", "; comma = true; std::cout << "catch and fine pirates"; }
if (policies & ~(POLICY_PUBLIC_SHARING | POLICY_CATCH_PIRATES)) { if (comma) std::cout << ", "; comma = true; std::cout << "(unknown policy)"; }
if (policies.publicSharing()) { if (comma) std::cout << ", "; comma = true; std::cout << "public sharing"; }
if (policies.publicVoting()) { if (comma) std::cout << ", "; comma = true; std::cout << "public voting"; }
if (policies.catchPirates()) { if (comma) std::cout << ", "; comma = true; std::cout << "catch pirates"; }
if (policies.unknown()) { if (comma) std::cout << ", "; comma = true; std::cout << "(unknown policy)"; }
if (!policies) std::cout << "no policy response";
std::cout << "\n";
PRINT_SETTING(policy_begins);
......@@ -131,10 +132,10 @@ int main(int argc, char *argv[]) {
ADD_SAME(piracy_link_proportion);
std::list<std::string> policies;
bool policy_public = false, policy_catch = false;
if (creativity.parameters.policy & POLICY_PUBLIC_SHARING) { policy_public = true; policies.push_back("public-sharing"); }
if (creativity.parameters.policy & POLICY_CATCH_PIRATES) { policy_catch = true; policies.push_back("catch-pirates"); }
if (creativity.parameters.policy & ~(POLICY_CATCH_PIRATES | POLICY_PUBLIC_SHARING)) policies.push_back("unknown-policy");
if (creativity.parameters.policy.publicSharing()) policies.push_back("public-sharing");
if (creativity.parameters.policy.publicVoting()) policies.push_back("public-voting");
if (creativity.parameters.policy.catchPirates()) policies.push_back("catch-pirates");
if (creativity.parameters.policy.unknown()) policies.push_back("unknown-policy");
if (!policies.empty()) {
std::cout << " --policy ";
bool first = true;
......@@ -147,9 +148,15 @@ int main(int argc, char *argv[]) {
ADD_SAME(policy_begins);
}
if (creativity.parameters.policy.publicSharing())
ADD("--public-sharing-tax", policy_public_sharing_tax);
if (policy_public) ADD("--public-sharing-tax", policy_public_sharing_tax);
if (policy_catch) {
if (creativity.parameters.policy.publicVoting()) {
ADD("--public-voting-tax", policy_public_voting_tax);
ADD("--public-voting-votes", policy_public_voting_votes);
}
if (creativity.parameters.policy.catchPirates()) {
ADD("--catch-tax", policy_catch_tax);
ADD("--catch-fine-any", policy_catch_fine[0]);
ADD("--catch-fine-const", policy_catch_fine[1]);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment