storyline event and log can now take a format string

This commit is contained in:
Crypto City 2022-06-10 07:17:18 +00:00
parent a1eebef592
commit 981a6098e8
4 changed files with 108 additions and 9 deletions

View File

@ -241,7 +241,10 @@ static void print_action_string(const Action &a, std::string &source, const std:
break;
case action_storyline_event:
CHECK_AND_ASSERT_THROW_MES(!a.str.empty(), "Expected a string for action_storyline_event");
source += I + "storyline event \"" + escape(a.str) + "\"\n";
source += I + "storyline event \"" + escape(a.str) + "\"";
for (const auto &op: a.ops)
source += " " + get_operand_string(op, op_unsigned);
source += "\n";
break;
case action_if:
{
@ -284,10 +287,9 @@ static void print_action_string(const Action &a, std::string &source, const std:
source += I + "start script " + get_operand_string(a.ops[0], op_unsigned) + "\n";
break;
case action_log:
CHECK_AND_ASSERT_THROW_MES(a.ops.size() <= 1, "Expected one operand at most for action_log, got " << a.ops.size());
source += I + "log \"" + escape(a.str) + "\"";
if (a.ops.size() == 1)
source += " " + get_operand_string(a.ops[0], op_unsigned);
for (const auto &op: a.ops)
source += " " + get_operand_string(op, op_unsigned);
source += "\n";
break;
}
@ -1257,6 +1259,47 @@ static uint32_t get_random_number(const crypto::hash &seed, uint64_t &salt)
return (SWAP32LE((uint32_t&)hash.data));
}
static std::string process_format_string(BlockchainStateProxy &proxy, uint32_t account, uint32_t owner, uint32_t city, const crypto::hash &seed, uint64_t &salt, const std::string &src, const std::vector<Operand> &ops)
{
size_t opidx = 0;
std::string str;
str.reserve(src.size() * 2 + 64);
for (size_t i = 0; i < src.size(); ++i)
{
if (src[i] != '%' || !strchr("uGT", src[i + 1]))
{
str.push_back(src[i]);
continue;
}
if (opidx >= ops.size())
printf("Format string '%s' does not have enough operands\n", src.c_str());
const auto value_signed = opidx >= ops.size() || ops[opidx].type == op_none ? std::make_pair<uint64_t, bool>(0, false) : get_attribute(proxy, ops[opidx], account, owner, city, seed, salt);
const uint64_t value = value_signed.first;
switch (src[i + 1])
{
case 'u':
str += std::to_string(value);
break;
case 'G':
str += cryptonote::print_money(value);
break;
case 'T':
str += cc::print_temperature(value);
break;
default:
printf("This should not be reached\n");
break;
}
++i;
++opidx;
}
return str;
}
static bool execute(cryptonote::cc_command_t cmd, BlockchainStateProxy &proxy, game_events_t &events, const Script &script, uint32_t account, uint32_t owner, uint32_t city, const Action &action, const crypto::hash &seed, uint64_t &salt)
{
if (action.type == action_none)
@ -1398,7 +1441,7 @@ static bool execute(cryptonote::cc_command_t cmd, BlockchainStateProxy &proxy, g
proxy.set_local_variable(account, action.str, value);
break;
case action_storyline_event:
events.add(cmd, account) << "Storyline event: " << action.str;
events.add(cmd, account) << "Storyline event: " << process_format_string(proxy, account, owner, city, seed, salt, action.str, action.ops);
break;
case action_if:
{
@ -1417,7 +1460,7 @@ static bool execute(cryptonote::cc_command_t cmd, BlockchainStateProxy &proxy, g
proxy.start_background_script(value);
break;
case action_log:
MINFO("Script log: " << action.str << (action.ops.empty() ? std::string() : std::to_string(value)));
MINFO("Script log: " << process_format_string(proxy, account, owner, city, seed, salt, action.str, action.ops));
break;
}
return true;

View File

@ -435,6 +435,8 @@ void set_partial_state_action_set_weight(script_partial_state_t *state);
void set_partial_state_action_pick(script_partial_state_t *state);
void set_partial_state_action_if(script_partial_state_t *state);
void set_partial_state_action_else(script_partial_state_t *state);
void set_partial_state_action_storyline_event(script_partial_state_t *state, const char *s);
void set_partial_state_action_log(script_partial_state_t *state, const char *s);
void set_partial_state_action_percent(script_partial_state_t *state);
void set_partial_state_state_init_actions(script_partial_state_t *state);
void set_partial_state_state_text(script_partial_state_t *state, const char *str);

View File

@ -497,6 +497,57 @@ void set_partial_state_action_else(script_partial_state_t *state)
state->expressions.pop_back();
}
static size_t get_format_string_expected_operands(const char *s)
{
size_t found = 0;
for (const char *ptr = s; *ptr; ++ptr)
{
if (*ptr == '%' && strchr("uGT", ptr[1]))
++found;
}
return found;
}
void set_partial_state_action_storyline_event(script_partial_state_t *state, const char *str)
{
MDEBUG("soryline_event");
const size_t expect = get_format_string_expected_operands(str);
if (expect != state->operands.size())
{
tfscript_error("Storyline event action format string expects " + std::to_string(expect) + " operands but has " + std::to_string(state->operands.size()));
return;
}
WeightedAction a;
a.action.type = action_storyline_event;
a.action.str = str;
a.action.ops = std::move(state->operands);
state->operands.clear();
state->actions.push_back(std::move(a));
}
void set_partial_state_action_log(script_partial_state_t *state, const char *str)
{
MDEBUG("log");
const size_t expect = get_format_string_expected_operands(str);
if (expect != state->operands.size())
{
tfscript_error("log action format string expects " + std::to_string(expect) + " operands but has " + std::to_string(state->operands.size()));
return;
}
WeightedAction a;
a.action.type = action_log;
a.action.str = str;
a.action.ops = std::move(state->operands);
state->operands.clear();
state->actions.push_back(std::move(a));
}
void set_partial_state_action_percent(script_partial_state_t *state)
{
MDEBUG("percent");

View File

@ -166,7 +166,7 @@ pick_actions: pick_actions pick_action { set_partial_state_action_inc_action_gro
pick_action: WEIGHT operand action { set_partial_state_action_set_weight(state); }
;
action_storyline_event: STORYLINE EVENT STRING { set_partial_state_action(state, action_storyline_event, $3, 0); }
action_storyline_event: STORYLINE EVENT STRING operands { set_partial_state_action_storyline_event(state, $3); }
;
action_if: IF { set_partial_state_action_add_action_group_count(state); } '(' expression ')' '{' if_actions '}' { set_partial_state_action_if(state); } optional_else
@ -183,13 +183,16 @@ optional_else: ELSE { set_partial_state_action_add_action_group_count(state); }
action_start_background_script: START BACKGROUND SCRIPT operand { set_partial_state_action(state, action_start_background_script, NULL, 1); }
;
action_log: LOG STRING { set_partial_state_action(state, action_log, $2, 0); }
action_log: LOG STRING operand { set_partial_state_action(state, action_log, $2, 1); }
action_log: LOG STRING operands { set_partial_state_action_log(state, $2); }
;
action_none: NONE { set_partial_state_action(state, action_none, NULL, 0); }
;
operands:operands operand {}
| {}
;
reserves: RESERVES '{' reserves_contents '}'
;