931 lines
31 KiB
C++
931 lines
31 KiB
C++
#include "PlayerState.hh"
|
|
|
|
#include "Server.hh"
|
|
|
|
using namespace std;
|
|
|
|
namespace Episode3 {
|
|
|
|
Condition::Condition() {
|
|
this->clear();
|
|
}
|
|
|
|
bool Condition::operator==(const Condition& other) const {
|
|
return (this->type == other.type) &&
|
|
(this->remaining_turns == other.remaining_turns) &&
|
|
(this->a_arg_value == other.a_arg_value) &&
|
|
(this->dice_roll_value == other.dice_roll_value) &&
|
|
(this->flags == other.flags) &&
|
|
(this->card_definition_effect_index == other.card_definition_effect_index) &&
|
|
(this->card_ref == other.card_ref) &&
|
|
(this->value == other.value) &&
|
|
(this->condition_giver_card_ref == other.condition_giver_card_ref) &&
|
|
(this->random_percent == other.random_percent) &&
|
|
(this->value8 == other.value8) &&
|
|
(this->order == other.order) &&
|
|
(this->unknown_a8 == other.unknown_a8);
|
|
}
|
|
bool Condition::operator!=(const Condition& other) const {
|
|
return !this->operator==(other);
|
|
}
|
|
|
|
void Condition::clear() {
|
|
this->type = ConditionType::NONE;
|
|
this->remaining_turns = 0;
|
|
this->a_arg_value = 0;
|
|
this->dice_roll_value = 0;
|
|
this->flags = 0;
|
|
this->card_definition_effect_index = 0;
|
|
this->card_ref = 0xFFFF;
|
|
this->value = 0;
|
|
this->condition_giver_card_ref = 0xFFFF;
|
|
this->random_percent = 0;
|
|
this->value8 = 0;
|
|
this->order = 0;
|
|
this->unknown_a8 = 0;
|
|
}
|
|
|
|
void Condition::clear_FF() {
|
|
this->type = ConditionType::INVALID_FF;
|
|
this->remaining_turns = 0xFF;
|
|
this->a_arg_value = -1;
|
|
this->dice_roll_value = 0xFF;
|
|
this->flags = 0xFF;
|
|
this->card_definition_effect_index = 0xFF;
|
|
this->card_ref = 0xFFFF;
|
|
this->value = -1;
|
|
this->condition_giver_card_ref = 0xFFFF;
|
|
this->random_percent = 0xFF;
|
|
this->value8 = -1;
|
|
this->order = 0xFF;
|
|
this->unknown_a8 = 0xFF;
|
|
}
|
|
|
|
std::string Condition::str(shared_ptr<const Server> s) const {
|
|
auto card_ref_str = s->debug_str_for_card_ref(this->card_ref);
|
|
auto giver_ref_str = s->debug_str_for_card_ref(this->condition_giver_card_ref);
|
|
return std::format(
|
|
"Condition[type={}, turns={}, a_arg={}, dice={}, flags={:02X}, "
|
|
"def_eff_index={}, ref={}, value={}, giver_ref={} "
|
|
"percent={} value8={} order={} a8={}]",
|
|
phosg::name_for_enum(this->type),
|
|
this->remaining_turns,
|
|
this->a_arg_value,
|
|
this->dice_roll_value,
|
|
this->flags,
|
|
this->card_definition_effect_index,
|
|
card_ref_str,
|
|
this->value,
|
|
giver_ref_str,
|
|
this->random_percent,
|
|
this->value8,
|
|
this->order,
|
|
this->unknown_a8);
|
|
}
|
|
|
|
EffectResult::EffectResult() {
|
|
this->clear();
|
|
}
|
|
|
|
void EffectResult::clear() {
|
|
this->attacker_card_ref = 0xFFFF;
|
|
this->target_card_ref = 0xFFFF;
|
|
this->value = 0;
|
|
this->current_hp = 0;
|
|
this->ap = 0;
|
|
this->tp = 0;
|
|
this->flags = 0;
|
|
this->operation = 0;
|
|
this->condition_index = 0;
|
|
this->dice_roll_value = 0;
|
|
}
|
|
|
|
std::string EffectResult::str(shared_ptr<const Server> s) const {
|
|
string attacker_ref_str = s->debug_str_for_card_ref(this->attacker_card_ref);
|
|
string target_ref_str = s->debug_str_for_card_ref(this->target_card_ref);
|
|
return std::format(
|
|
"EffectResult[att_ref={}, target_ref={}, value={}, "
|
|
"cur_hp={}, ap={}, tp={}, flags={:02X}, op={}, "
|
|
"cond_index={}, dice={}]",
|
|
attacker_ref_str,
|
|
target_ref_str,
|
|
this->value,
|
|
this->current_hp,
|
|
this->ap,
|
|
this->tp,
|
|
this->flags,
|
|
this->operation,
|
|
this->condition_index,
|
|
this->dice_roll_value);
|
|
}
|
|
|
|
CardShortStatus::CardShortStatus() {
|
|
this->clear();
|
|
}
|
|
|
|
bool CardShortStatus::operator==(const CardShortStatus& other) const {
|
|
return (this->card_ref == other.card_ref) &&
|
|
(this->current_hp == other.current_hp) &&
|
|
(this->card_flags == other.card_flags) &&
|
|
(this->loc == other.loc) &&
|
|
(this->unused1 == other.unused1) &&
|
|
(this->max_hp == other.max_hp) &&
|
|
(this->unused2 == other.unused2);
|
|
}
|
|
bool CardShortStatus::operator!=(const CardShortStatus& other) const {
|
|
return !this->operator==(other);
|
|
}
|
|
|
|
std::string CardShortStatus::str(shared_ptr<const Server> s) const {
|
|
string loc_s = this->loc.str();
|
|
string ref_str = s->debug_str_for_card_ref(this->card_ref);
|
|
return std::format(
|
|
"CardShortStatus[ref={}, cur_hp={}, flags={:08X}, loc={}, "
|
|
"u1={:04X}, max_hp={}, u2={}]",
|
|
ref_str,
|
|
this->current_hp,
|
|
this->card_flags,
|
|
loc_s,
|
|
this->unused1,
|
|
this->max_hp,
|
|
this->unused2);
|
|
}
|
|
|
|
void CardShortStatus::clear() {
|
|
this->card_ref = 0xFFFF;
|
|
this->current_hp = 0;
|
|
this->card_flags = 0;
|
|
this->loc.clear();
|
|
this->unused1 = 0xFFFF;
|
|
this->max_hp = 0;
|
|
this->unused2 = 0;
|
|
}
|
|
|
|
void CardShortStatus::clear_FF() {
|
|
this->card_ref = 0xFFFF;
|
|
this->current_hp = 0xFFFF;
|
|
this->card_flags = 0xFFFFFFFF;
|
|
this->loc.clear_FF();
|
|
this->unused1 = 0xFFFF;
|
|
this->max_hp = -1;
|
|
this->unused2 = 0xFF;
|
|
}
|
|
|
|
ActionState::ActionState() {
|
|
this->clear();
|
|
}
|
|
|
|
void ActionState::clear() {
|
|
this->client_id = 0xFFFF;
|
|
this->unused = 0;
|
|
this->facing_direction = Direction::RIGHT;
|
|
this->attacker_card_ref = 0xFFFF;
|
|
this->defense_card_ref = 0xFFFF;
|
|
this->original_attacker_card_ref = 0xFFFF;
|
|
this->target_card_refs.clear(0xFFFF);
|
|
this->action_card_refs.clear(0xFFFF);
|
|
this->unused2 = 0xFFFF;
|
|
}
|
|
|
|
std::string ActionState::str(shared_ptr<const Server> s) const {
|
|
string attacker_ref_s = s->debug_str_for_card_ref(this->attacker_card_ref);
|
|
string defense_ref_s = s->debug_str_for_card_ref(this->defense_card_ref);
|
|
string original_attacker_ref_s = s->debug_str_for_card_ref(this->original_attacker_card_ref);
|
|
string target_refs_s = s->debug_str_for_card_refs(this->target_card_refs);
|
|
string action_refs_s = s->debug_str_for_card_refs(this->action_card_refs);
|
|
return std::format(
|
|
"ActionState[client={:X}, u={}, facing={}, attacker_ref={}, "
|
|
"def_ref={}, target_refs={}, action_refs={}, "
|
|
"orig_attacker_ref={}]",
|
|
this->client_id,
|
|
this->unused,
|
|
phosg::name_for_enum(this->facing_direction),
|
|
attacker_ref_s,
|
|
defense_ref_s,
|
|
target_refs_s,
|
|
action_refs_s,
|
|
original_attacker_ref_s);
|
|
}
|
|
|
|
ActionChain::ActionChain() {
|
|
this->clear();
|
|
}
|
|
|
|
bool ActionChain::operator==(const ActionChain& other) const {
|
|
return (this->effective_ap == other.effective_ap) &&
|
|
(this->effective_tp == other.effective_tp) &&
|
|
(this->ap_effect_bonus == other.ap_effect_bonus) &&
|
|
(this->damage == other.damage) &&
|
|
(this->acting_card_ref == other.acting_card_ref) &&
|
|
(this->unknown_card_ref_a3 == other.unknown_card_ref_a3) &&
|
|
(this->attack_action_card_refs == other.attack_action_card_refs) &&
|
|
(this->attack_action_card_ref_count == other.attack_action_card_ref_count) &&
|
|
(this->attack_medium == other.attack_medium) &&
|
|
(this->target_card_ref_count == other.target_card_ref_count) &&
|
|
(this->action_subphase == other.action_subphase) &&
|
|
(this->strike_count == other.strike_count) &&
|
|
(this->damage_multiplier == other.damage_multiplier) &&
|
|
(this->attack_number == other.attack_number) &&
|
|
(this->tp_effect_bonus == other.tp_effect_bonus) &&
|
|
(this->physical_attack_bonus_nte == other.physical_attack_bonus_nte) &&
|
|
(this->tech_attack_bonus_nte == other.tech_attack_bonus_nte) &&
|
|
(this->card_ap == other.card_ap) &&
|
|
(this->card_tp == other.card_tp) &&
|
|
(this->flags == other.flags) &&
|
|
(this->target_card_refs == other.target_card_refs);
|
|
}
|
|
bool ActionChain::operator!=(const ActionChain& other) const {
|
|
return !this->operator==(other);
|
|
}
|
|
|
|
std::string ActionChain::str(shared_ptr<const Server> s) const {
|
|
string acting_card_ref_s = s->debug_str_for_card_ref(this->acting_card_ref);
|
|
string unknown_card_ref_a3_s = s->debug_str_for_card_ref(this->unknown_card_ref_a3);
|
|
string attack_action_card_refs_s = s->debug_str_for_card_refs(this->attack_action_card_refs);
|
|
string target_card_refs_s = s->debug_str_for_card_refs(this->target_card_refs);
|
|
return std::format(
|
|
"ActionChain[eff_ap={}, eff_tp={}, ap_bonus={}, damage={}, "
|
|
"acting_ref={}, unknown_ref_a3={}, attack_action_refs={}, "
|
|
"attack_action_ref_count={}, medium={}, target_ref_count={}, "
|
|
"subphase={}, strikes={}, damage_mult={}, attack_num={}, "
|
|
"tp_bonus={}, phys_bonus_nte={}, tech_bonus_nte={}, card_ap={}, "
|
|
"card_tp={}, flags={:08X}, target_refs={}]",
|
|
this->effective_ap,
|
|
this->effective_tp,
|
|
this->ap_effect_bonus,
|
|
this->damage,
|
|
acting_card_ref_s,
|
|
unknown_card_ref_a3_s,
|
|
attack_action_card_refs_s,
|
|
this->attack_action_card_ref_count,
|
|
phosg::name_for_enum(this->attack_medium),
|
|
this->target_card_ref_count,
|
|
phosg::name_for_enum(this->action_subphase),
|
|
this->strike_count,
|
|
this->damage_multiplier,
|
|
this->attack_number,
|
|
this->tp_effect_bonus,
|
|
this->physical_attack_bonus_nte,
|
|
this->tech_attack_bonus_nte,
|
|
this->card_ap,
|
|
this->card_tp,
|
|
this->flags,
|
|
target_card_refs_s);
|
|
}
|
|
|
|
void ActionChain::clear() {
|
|
this->effective_ap = 0;
|
|
this->effective_tp = 0;
|
|
this->ap_effect_bonus = 0;
|
|
this->damage = 0;
|
|
this->acting_card_ref = 0xFFFF;
|
|
this->unknown_card_ref_a3 = 0xFFFF;
|
|
this->attack_action_card_ref_count = 0;
|
|
this->attack_medium = AttackMedium::UNKNOWN;
|
|
this->target_card_ref_count = 0;
|
|
this->action_subphase = ActionSubphase::INVALID_FF;
|
|
this->strike_count = 1;
|
|
this->damage_multiplier = 1;
|
|
this->attack_number = 0xFF;
|
|
this->tp_effect_bonus = 0;
|
|
this->physical_attack_bonus_nte = 0;
|
|
this->tech_attack_bonus_nte = 0;
|
|
this->card_ap = 0;
|
|
this->card_tp = 0;
|
|
this->flags = 0;
|
|
this->attack_action_card_refs.clear(0xFFFF);
|
|
this->target_card_refs.clear(0xFFFF);
|
|
}
|
|
|
|
void ActionChain::clear_FF() {
|
|
this->effective_ap = -1;
|
|
this->effective_tp = -1;
|
|
this->ap_effect_bonus = -1;
|
|
this->damage = -1;
|
|
this->acting_card_ref = 0xFFFF;
|
|
this->unknown_card_ref_a3 = 0xFFFF;
|
|
this->attack_action_card_refs.clear(0xFFFF);
|
|
this->attack_action_card_ref_count = 0xFF;
|
|
this->attack_medium = AttackMedium::INVALID_FF;
|
|
this->target_card_ref_count = 0xFF;
|
|
this->action_subphase = ActionSubphase::INVALID_FF;
|
|
this->strike_count = 0xFF;
|
|
this->damage_multiplier = -1;
|
|
this->attack_number = 0xFF;
|
|
this->tp_effect_bonus = -1;
|
|
this->physical_attack_bonus_nte = 0xFF;
|
|
this->tech_attack_bonus_nte = 0xFF;
|
|
this->card_ap = -1;
|
|
this->card_tp = -1;
|
|
this->flags = 0xFFFFFFFF;
|
|
this->target_card_refs.clear(0xFFFF);
|
|
}
|
|
|
|
ActionChainWithConds::ActionChainWithConds() {
|
|
this->clear();
|
|
}
|
|
|
|
bool ActionChainWithConds::operator==(const ActionChainWithConds& other) const {
|
|
return (this->chain == other.chain && this->conditions == other.conditions);
|
|
}
|
|
bool ActionChainWithConds::operator!=(const ActionChainWithConds& other) const {
|
|
return !this->operator==(other);
|
|
}
|
|
|
|
std::string ActionChainWithConds::str(shared_ptr<const Server> s) const {
|
|
string ret = "ActionChainWithConds[chain=";
|
|
ret += this->chain.str(s);
|
|
ret += ", conds=[";
|
|
for (size_t z = 0; z < this->conditions.size(); z++) {
|
|
if (this->conditions[z].type != ConditionType::NONE) {
|
|
if (ret.back() != '[') {
|
|
ret += ", ";
|
|
}
|
|
ret += std::format("{}:", z);
|
|
ret += this->conditions[z].str(s);
|
|
}
|
|
}
|
|
ret += "]]";
|
|
return ret;
|
|
}
|
|
|
|
void ActionChainWithConds::clear() {
|
|
this->chain.effective_ap = 0;
|
|
this->chain.effective_tp = 0;
|
|
this->chain.ap_effect_bonus = 0;
|
|
this->chain.damage = 0;
|
|
this->clear_inner();
|
|
}
|
|
|
|
void ActionChainWithConds::clear_FF() {
|
|
this->chain.clear_FF();
|
|
for (size_t z = 0; z < 9; z++) {
|
|
this->conditions[z].clear_FF();
|
|
}
|
|
}
|
|
|
|
void ActionChainWithConds::clear_inner() {
|
|
this->chain.unknown_card_ref_a3 = 0xFFFF;
|
|
this->chain.acting_card_ref = 0xFFFF;
|
|
this->chain.attack_medium = AttackMedium::INVALID_FF;
|
|
this->chain.flags = 0;
|
|
this->chain.action_subphase = ActionSubphase::INVALID_FF;
|
|
this->chain.attack_number = 0xFF;
|
|
this->reset();
|
|
this->clear_target_card_refs();
|
|
this->chain.attack_action_card_ref_count = 0;
|
|
this->chain.attack_action_card_refs.clear(0xFFFF);
|
|
}
|
|
|
|
void ActionChainWithConds::clear_target_card_refs() {
|
|
this->chain.target_card_ref_count = 0;
|
|
this->chain.target_card_refs.clear(0xFFFF);
|
|
}
|
|
|
|
void ActionChainWithConds::reset() {
|
|
this->chain.effective_ap = 0;
|
|
this->chain.effective_tp = 0;
|
|
this->chain.ap_effect_bonus = 0;
|
|
this->chain.tp_effect_bonus = 0;
|
|
this->chain.physical_attack_bonus_nte = 0;
|
|
this->chain.tech_attack_bonus_nte = 0;
|
|
this->chain.damage = 0;
|
|
this->chain.strike_count = 1;
|
|
this->chain.damage_multiplier = 1;
|
|
}
|
|
|
|
bool ActionChainWithConds::check_flag(uint32_t flags) const {
|
|
return (this->chain.flags & flags) != 0;
|
|
}
|
|
|
|
void ActionChainWithConds::clear_flags(uint32_t flags) {
|
|
this->chain.flags &= ~flags;
|
|
}
|
|
|
|
void ActionChainWithConds::set_flags(uint32_t flags) {
|
|
this->chain.flags |= flags;
|
|
}
|
|
|
|
void ActionChainWithConds::add_attack_action_card_ref(
|
|
uint16_t card_ref, shared_ptr<Server> server) {
|
|
if (card_ref != 0xFFFF) {
|
|
this->chain.attack_action_card_refs[this->chain.attack_action_card_ref_count++] = card_ref;
|
|
}
|
|
this->set_flags(8);
|
|
this->chain.action_subphase = server->get_current_action_subphase();
|
|
}
|
|
|
|
void ActionChainWithConds::add_target_card_ref(uint16_t card_ref) {
|
|
if (card_ref != 0xFFFF &&
|
|
this->chain.target_card_ref_count < this->chain.target_card_refs.size()) {
|
|
this->chain.target_card_refs[this->chain.target_card_ref_count++] = card_ref;
|
|
}
|
|
}
|
|
|
|
void ActionChainWithConds::compute_attack_medium(shared_ptr<Server> server) {
|
|
this->chain.attack_medium = AttackMedium::PHYSICAL;
|
|
for (size_t z = 0; z < this->chain.attack_action_card_ref_count; z++) {
|
|
uint16_t card_ref = this->chain.attack_action_card_refs[z];
|
|
if (card_ref == 0xFFFF) {
|
|
break;
|
|
}
|
|
auto ce = server->definition_for_card_ref(card_ref);
|
|
if (!ce) {
|
|
continue;
|
|
}
|
|
if (card_class_is_tech_like(ce->def.card_class(), server->options.is_nte())) {
|
|
this->chain.attack_medium = AttackMedium::TECH;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool ActionChainWithConds::get_condition_value(
|
|
ConditionType cond_type,
|
|
uint16_t card_ref,
|
|
uint8_t def_effect_index,
|
|
uint16_t value,
|
|
uint16_t* out_value) const {
|
|
bool any_found = false;
|
|
uint8_t max_order = 10;
|
|
for (size_t z = 0; z < 9; z++) {
|
|
auto& cond = this->conditions[z];
|
|
if (((cond_type == ConditionType::ANY) || (cond.type == cond_type)) &&
|
|
((def_effect_index == 0xFF) || (cond.card_definition_effect_index == def_effect_index)) &&
|
|
((card_ref == 0xFFFF) || (cond.card_ref == card_ref)) &&
|
|
((value == 0xFFFF) || (cond.value == value))) {
|
|
if (!any_found || (max_order < cond.order)) {
|
|
if (!out_value) {
|
|
return true;
|
|
}
|
|
*out_value = cond.value;
|
|
max_order = cond.order;
|
|
}
|
|
any_found = true;
|
|
}
|
|
}
|
|
return any_found;
|
|
}
|
|
|
|
void ActionChainWithConds::set_action_subphase_from_card(
|
|
shared_ptr<const Card> card) {
|
|
this->chain.action_subphase = card->server()->get_current_action_subphase();
|
|
}
|
|
|
|
bool ActionChainWithConds::can_apply_attack() const {
|
|
return this->check_flag(4) ? false : (this->chain.target_card_ref_count != 0);
|
|
}
|
|
|
|
uint8_t ActionChainWithConds::get_adjusted_move_ability_nte(uint8_t ability) const {
|
|
for (size_t z = 0; z < this->conditions.size(); z++) {
|
|
const auto& cond = this->conditions[z];
|
|
switch (cond.type) {
|
|
case ConditionType::IMMOBILE:
|
|
case ConditionType::FREEZE:
|
|
ability = 0;
|
|
break;
|
|
case ConditionType::SET_MV_COST_TO_0:
|
|
ability = 99;
|
|
break;
|
|
case ConditionType::ADD_1_TO_MV_COST:
|
|
ability--;
|
|
break;
|
|
case ConditionType::SCALE_MV_COST:
|
|
if (cond.value == 0) {
|
|
ability = 99;
|
|
} else {
|
|
ability /= cond.value;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return ability;
|
|
}
|
|
|
|
ActionChainWithCondsTrial::ActionChainWithCondsTrial(const ActionChainWithConds& src)
|
|
: effective_ap(src.chain.effective_ap),
|
|
effective_tp(src.chain.effective_tp),
|
|
ap_effect_bonus(src.chain.ap_effect_bonus),
|
|
damage(src.chain.damage),
|
|
acting_card_ref(src.chain.acting_card_ref),
|
|
unknown_card_ref_a3(src.chain.unknown_card_ref_a3),
|
|
attack_action_card_refs(src.chain.attack_action_card_refs),
|
|
attack_action_card_ref_count(src.chain.attack_action_card_ref_count),
|
|
attack_medium(src.chain.attack_medium),
|
|
target_card_ref_count(src.chain.target_card_ref_count),
|
|
action_subphase(src.chain.action_subphase),
|
|
strike_count(src.chain.strike_count),
|
|
damage_multiplier(src.chain.damage_multiplier),
|
|
attack_number(src.chain.attack_number),
|
|
tp_effect_bonus(src.chain.tp_effect_bonus),
|
|
physical_attack_bonus_nte(src.chain.physical_attack_bonus_nte),
|
|
tech_attack_bonus_nte(src.chain.tech_attack_bonus_nte),
|
|
card_ap(src.chain.card_ap),
|
|
card_tp(src.chain.card_tp),
|
|
flags(src.chain.flags),
|
|
conditions(src.conditions),
|
|
target_card_refs(src.chain.target_card_refs) {}
|
|
|
|
ActionChainWithCondsTrial::operator ActionChainWithConds() const {
|
|
ActionChainWithConds ret;
|
|
ret.chain.effective_ap = this->effective_ap;
|
|
ret.chain.effective_tp = this->effective_tp;
|
|
ret.chain.ap_effect_bonus = this->ap_effect_bonus;
|
|
ret.chain.damage = this->damage;
|
|
ret.chain.acting_card_ref = this->acting_card_ref;
|
|
ret.chain.unknown_card_ref_a3 = this->unknown_card_ref_a3;
|
|
ret.chain.attack_action_card_refs = this->attack_action_card_refs;
|
|
ret.chain.attack_action_card_ref_count = this->attack_action_card_ref_count;
|
|
ret.chain.attack_medium = this->attack_medium;
|
|
ret.chain.target_card_ref_count = this->target_card_ref_count;
|
|
ret.chain.action_subphase = this->action_subphase;
|
|
ret.chain.strike_count = this->strike_count;
|
|
ret.chain.damage_multiplier = this->damage_multiplier;
|
|
ret.chain.attack_number = this->attack_number;
|
|
ret.chain.tp_effect_bonus = this->tp_effect_bonus;
|
|
ret.chain.physical_attack_bonus_nte = this->physical_attack_bonus_nte;
|
|
ret.chain.tech_attack_bonus_nte = this->tech_attack_bonus_nte;
|
|
ret.chain.card_ap = this->card_ap;
|
|
ret.chain.card_tp = this->card_tp;
|
|
ret.chain.flags = this->flags;
|
|
ret.chain.target_card_refs = this->target_card_refs;
|
|
ret.conditions = this->conditions;
|
|
return ret;
|
|
}
|
|
|
|
ActionMetadata::ActionMetadata() {
|
|
this->clear();
|
|
}
|
|
|
|
bool ActionMetadata::operator==(const ActionMetadata& other) const {
|
|
return (this->card_ref == other.card_ref) &&
|
|
(this->target_card_ref_count == other.target_card_ref_count) &&
|
|
(this->defense_card_ref_count == other.defense_card_ref_count) &&
|
|
(this->action_subphase == other.action_subphase) &&
|
|
(this->defense_power == other.defense_power) &&
|
|
(this->defense_bonus == other.defense_bonus) &&
|
|
(this->attack_bonus == other.attack_bonus) &&
|
|
(this->flags == other.flags) &&
|
|
(this->target_card_refs == other.target_card_refs) &&
|
|
(this->defense_card_refs == other.defense_card_refs) &&
|
|
(this->original_attacker_card_refs == other.original_attacker_card_refs);
|
|
}
|
|
bool ActionMetadata::operator!=(const ActionMetadata& other) const {
|
|
return !this->operator==(other);
|
|
}
|
|
|
|
std::string ActionMetadata::str(shared_ptr<const Server> s) const {
|
|
string card_ref_s = s->debug_str_for_card_ref(this->card_ref);
|
|
string target_card_refs_s = s->debug_str_for_card_refs(this->target_card_refs);
|
|
string defense_card_refs_s = s->debug_str_for_card_refs(this->defense_card_refs);
|
|
string original_attacker_card_refs_s = s->debug_str_for_card_refs(this->original_attacker_card_refs);
|
|
return std::format(
|
|
"ActionMetadata[ref={}, target_ref_count={}, def_ref_count={}, "
|
|
"subphase={}, def_power={}, def_bonus={}, "
|
|
"att_bonus={}, flags={:08X}, target_refs={}, "
|
|
"defense_refs={}, original_attacker_refs={}]",
|
|
card_ref_s,
|
|
this->target_card_ref_count,
|
|
this->defense_card_ref_count,
|
|
phosg::name_for_enum(this->action_subphase),
|
|
this->defense_power,
|
|
this->defense_bonus,
|
|
this->attack_bonus,
|
|
this->flags,
|
|
target_card_refs_s,
|
|
defense_card_refs_s,
|
|
original_attacker_card_refs_s);
|
|
}
|
|
|
|
void ActionMetadata::clear() {
|
|
this->card_ref = 0xFFFF;
|
|
this->target_card_ref_count = 0;
|
|
this->defense_card_ref_count = 0;
|
|
this->action_subphase = ActionSubphase::INVALID_FF;
|
|
this->defense_power = 0;
|
|
this->defense_bonus = 0;
|
|
// TODO: Ep3 NTE doesn't set attack_bonus to zero here. Is the field just
|
|
// unused in NTE?
|
|
this->attack_bonus = 0;
|
|
this->flags = 0;
|
|
this->target_card_refs.clear(0xFFFF);
|
|
this->defense_card_refs.clear(0xFFFF);
|
|
this->original_attacker_card_refs.clear(0xFFFF);
|
|
}
|
|
|
|
void ActionMetadata::clear_FF() {
|
|
this->card_ref = 0xFFFF;
|
|
this->target_card_ref_count = 0xFF;
|
|
this->defense_card_ref_count = 0xFF;
|
|
this->action_subphase = ActionSubphase::INVALID_FF;
|
|
this->defense_power = -1;
|
|
this->defense_bonus = -1;
|
|
this->attack_bonus = -1;
|
|
this->flags = 0xFFFFFFFF;
|
|
this->target_card_refs.clear(0xFFFF);
|
|
this->defense_card_refs.clear(0xFFFF);
|
|
this->original_attacker_card_refs.clear(0xFFFF);
|
|
}
|
|
|
|
bool ActionMetadata::check_flag(uint32_t mask) const {
|
|
return (this->flags & mask) != 0;
|
|
}
|
|
|
|
void ActionMetadata::set_flags(uint32_t flags) {
|
|
this->flags |= flags;
|
|
}
|
|
|
|
void ActionMetadata::clear_flags(uint32_t flags) {
|
|
this->flags &= ~flags;
|
|
}
|
|
|
|
void ActionMetadata::clear_defense_and_attacker_card_refs() {
|
|
this->defense_card_ref_count = 0;
|
|
this->defense_card_refs.clear(0xFFFF);
|
|
this->original_attacker_card_refs.clear(0xFFFF);
|
|
}
|
|
|
|
void ActionMetadata::clear_target_card_refs() {
|
|
this->target_card_ref_count = 0;
|
|
this->target_card_refs.clear(0xFFFF);
|
|
}
|
|
|
|
void ActionMetadata::add_target_card_ref(uint16_t card_ref) {
|
|
if (card_ref != 0xFFFF &&
|
|
this->target_card_ref_count < this->target_card_refs.size()) {
|
|
this->target_card_refs[this->target_card_ref_count++] = card_ref;
|
|
}
|
|
}
|
|
|
|
void ActionMetadata::add_defense_card_ref(
|
|
uint16_t defense_card_ref,
|
|
shared_ptr<Card> card,
|
|
uint16_t original_attacker_card_ref) {
|
|
if ((defense_card_ref != 0xFFFF) && (this->defense_card_ref_count < 8)) {
|
|
this->defense_card_refs[this->defense_card_ref_count] = defense_card_ref;
|
|
this->original_attacker_card_refs[this->defense_card_ref_count] = original_attacker_card_ref;
|
|
this->defense_card_ref_count++;
|
|
this->action_subphase = card->server()->get_current_action_subphase();
|
|
}
|
|
}
|
|
|
|
HandAndEquipState::HandAndEquipState() {
|
|
this->clear();
|
|
}
|
|
|
|
std::string HandAndEquipState::str(shared_ptr<const Server> s) const {
|
|
string assist_card_ref_s = s->debug_str_for_card_ref(this->assist_card_ref);
|
|
string assist_card_ref2_s = s->debug_str_for_card_ref(this->assist_card_ref2);
|
|
string assist_card_id_s = s->debug_str_for_card_id(this->assist_card_id);
|
|
string sc_card_ref_s = s->debug_str_for_card_ref(this->sc_card_ref);
|
|
string hand_card_refs_s = s->debug_str_for_card_refs(this->hand_card_refs);
|
|
string set_card_refs_s = s->debug_str_for_card_refs(this->set_card_refs);
|
|
string hand_card_refs2_s = s->debug_str_for_card_refs(this->hand_card_refs2);
|
|
string set_card_refs2_s = s->debug_str_for_card_refs(this->set_card_refs2);
|
|
return std::format(
|
|
"HandAndEquipState[dice=[{}, {}], atk={}, def={}, atk2={}, "
|
|
"a1={}, total_set_cost={}, is_cpu={}, assist_flags={:08X}, "
|
|
"hand_refs={}, assist_ref={}, set_refs={}, sc_ref={}, hand_refs2={}, "
|
|
"set_refs2={}, assist_ref2={}, assist_set_num={}, assist_card_id={}, "
|
|
"assist_turns={}, assist_delay={}, atk_bonus={}, def_bonus={}, "
|
|
"u2=[{}, {}]]",
|
|
this->dice_results[0],
|
|
this->dice_results[1],
|
|
this->atk_points,
|
|
this->def_points,
|
|
this->atk_points2,
|
|
this->unknown_a1,
|
|
this->total_set_cards_cost,
|
|
this->is_cpu_player,
|
|
this->assist_flags,
|
|
hand_card_refs_s,
|
|
assist_card_ref_s,
|
|
set_card_refs_s,
|
|
sc_card_ref_s,
|
|
hand_card_refs2_s,
|
|
set_card_refs2_s,
|
|
assist_card_ref2_s,
|
|
this->assist_card_set_number,
|
|
assist_card_id_s,
|
|
this->assist_remaining_turns,
|
|
this->assist_delay_turns,
|
|
this->atk_bonuses,
|
|
this->def_bonuses,
|
|
this->unused2[0],
|
|
this->unused2[1]);
|
|
}
|
|
|
|
void HandAndEquipState::clear() {
|
|
this->dice_results.clear(0);
|
|
this->atk_points = 0;
|
|
this->def_points = 0;
|
|
this->atk_points2 = 0;
|
|
this->unknown_a1 = 0;
|
|
this->total_set_cards_cost = 0;
|
|
this->is_cpu_player = 0;
|
|
this->assist_flags = 0;
|
|
this->hand_card_refs.clear(0xFFFF);
|
|
this->assist_card_ref = 0xFFFF;
|
|
this->set_card_refs.clear(0xFFFF);
|
|
this->sc_card_ref = 0xFFFF;
|
|
this->hand_card_refs2.clear(0xFFFF);
|
|
this->set_card_refs2.clear(0xFFFF);
|
|
this->assist_card_ref2 = 0xFFFF;
|
|
this->assist_card_set_number = 0;
|
|
this->assist_card_id = 0xFFFF;
|
|
this->assist_remaining_turns = 0;
|
|
this->assist_delay_turns = 0;
|
|
this->atk_bonuses = 0;
|
|
this->def_bonuses = 0;
|
|
this->unused2.clear(0);
|
|
}
|
|
|
|
void HandAndEquipState::clear_FF() {
|
|
this->dice_results.clear(0xFF);
|
|
this->atk_points = 0xFF;
|
|
this->def_points = 0xFF;
|
|
this->atk_points2 = 0xFF;
|
|
this->unknown_a1 = 0xFF;
|
|
this->total_set_cards_cost = 0xFF;
|
|
this->is_cpu_player = 0xFF;
|
|
this->assist_flags = 0xFFFFFFFF;
|
|
this->hand_card_refs.clear(0xFFFF);
|
|
this->assist_card_ref = 0xFFFF;
|
|
this->set_card_refs.clear(0xFFFF);
|
|
this->sc_card_ref = 0xFFFF;
|
|
this->hand_card_refs2.clear(0xFFFF);
|
|
this->set_card_refs2.clear(0xFFFF);
|
|
this->assist_card_ref2 = 0xFFFF;
|
|
this->assist_card_set_number = 0xFFFF;
|
|
this->assist_card_id = 0xFFFF;
|
|
this->assist_remaining_turns = 0xFF;
|
|
this->assist_delay_turns = 0xFF;
|
|
this->atk_bonuses = 0xFF;
|
|
this->def_bonuses = 0xFF;
|
|
this->unused2.clear(0xFF);
|
|
}
|
|
|
|
PlayerBattleStats::PlayerBattleStats() {
|
|
this->clear();
|
|
}
|
|
|
|
void PlayerBattleStats::clear() {
|
|
this->damage_given = 0;
|
|
this->damage_taken = 0;
|
|
this->num_opponent_cards_destroyed = 0;
|
|
this->num_owned_cards_destroyed = 0;
|
|
this->total_move_distance = 0;
|
|
this->num_cards_set = 0;
|
|
this->num_item_or_creature_cards_set = 0;
|
|
this->num_attack_actions_set = 0;
|
|
this->num_tech_cards_set = 0;
|
|
this->num_assist_cards_set = 0;
|
|
this->defense_actions_set_on_self = 0;
|
|
this->defense_actions_set_on_ally = 0;
|
|
this->num_cards_drawn = 0;
|
|
this->max_attack_damage = 0;
|
|
this->max_attack_combo_size = 0;
|
|
this->num_attacks_given = 0;
|
|
this->num_attacks_taken = 0;
|
|
this->sc_damage_taken = 0;
|
|
this->action_card_negated_damage = 0;
|
|
this->unused = 0;
|
|
}
|
|
|
|
float PlayerBattleStats::score(size_t num_rounds) const {
|
|
// Note: This formula doesn't match the formula on PSO-World, which is:
|
|
// 35
|
|
// + (Attack Damage - Damage Taken)
|
|
// + (Max Card Combo x 3)
|
|
// - (Story Character Damage x 1.8)
|
|
// - (Turns x 2.7)
|
|
// + (Action Card Negated Damage x 0.8)
|
|
// I don't know where that formula came from, but this one came from the USA
|
|
// Ep3 PsoV3.dol, so it's presumably correct. Is the PSO-World formula simply
|
|
// incorrect, or is it from e.g. the Japanese version, which may have a
|
|
// different rank calculation function?
|
|
return 38.0f + 0.8f * this->action_card_negated_damage - 2.3f * num_rounds - 1.8f * this->sc_damage_taken + 3.0f * this->max_attack_combo_size + (this->damage_given - this->damage_taken);
|
|
}
|
|
|
|
uint8_t PlayerBattleStats::rank(size_t num_rounds) const {
|
|
return this->rank_for_score(this->score(num_rounds));
|
|
}
|
|
|
|
const char* PlayerBattleStats::rank_name(size_t num_rounds) const {
|
|
return this->name_for_rank(this->rank_for_score(this->score(num_rounds)));
|
|
}
|
|
|
|
constexpr size_t RANK_THRESHOLD_COUNT = 9;
|
|
static const float RANK_THRESHOLDS[RANK_THRESHOLD_COUNT] = {
|
|
15.0f, 25.0f, 30.0f, 40.0f, 50.0f, 60.0f, 65.0f, 75.0f, 85.0f};
|
|
static const char* RANK_NAMES[RANK_THRESHOLD_COUNT + 1] = {
|
|
"E", "D", "D+", "C", "C+", "B", "B+", "A", "A+", "S"};
|
|
|
|
uint8_t PlayerBattleStats::rank_for_score(float score) {
|
|
size_t rank = 0;
|
|
while (rank < RANK_THRESHOLD_COUNT && RANK_THRESHOLDS[rank] <= score) {
|
|
rank++;
|
|
}
|
|
return rank;
|
|
}
|
|
|
|
const char* PlayerBattleStats::name_for_rank(uint8_t rank) {
|
|
if (rank >= RANK_THRESHOLD_COUNT + 1) {
|
|
throw invalid_argument("invalid rank");
|
|
}
|
|
return RANK_NAMES[rank];
|
|
}
|
|
|
|
PlayerBattleStatsTrial::PlayerBattleStatsTrial(const PlayerBattleStats& data)
|
|
: damage_given(data.damage_given),
|
|
damage_taken(data.damage_taken),
|
|
num_opponent_cards_destroyed(data.num_opponent_cards_destroyed),
|
|
num_owned_cards_destroyed(data.num_owned_cards_destroyed),
|
|
total_move_distance(data.total_move_distance) {}
|
|
|
|
PlayerBattleStatsTrial::operator PlayerBattleStats() const {
|
|
PlayerBattleStats ret;
|
|
ret.damage_given = this->damage_given;
|
|
ret.damage_taken = this->damage_taken;
|
|
ret.num_opponent_cards_destroyed = this->num_opponent_cards_destroyed;
|
|
ret.num_owned_cards_destroyed = this->num_owned_cards_destroyed;
|
|
ret.total_move_distance = this->total_move_distance;
|
|
return ret;
|
|
}
|
|
|
|
static bool is_card_within_range(
|
|
const parray<uint8_t, 9 * 9>& range,
|
|
const Location& anchor_loc,
|
|
const CardShortStatus& ss,
|
|
phosg::PrefixedLogger* log) {
|
|
if (ss.card_ref == 0xFFFF) {
|
|
if (log) {
|
|
log->debug_f("is_card_within_range: (false) ss.card_ref missing");
|
|
}
|
|
return false;
|
|
}
|
|
if (range[0] == 2) {
|
|
if (log) {
|
|
log->debug_f("is_card_within_range: (true) range is entire field");
|
|
}
|
|
return true;
|
|
}
|
|
|
|
if ((ss.loc.x < anchor_loc.x - 4) || (ss.loc.x > anchor_loc.x + 4)) {
|
|
if (log) {
|
|
log->debug_f("is_card_within_range: (false) outside x range (ss.loc.x={}, anchor_loc.x={})", ss.loc.x, anchor_loc.x);
|
|
}
|
|
return false;
|
|
}
|
|
if ((ss.loc.y < anchor_loc.y - 4) || (ss.loc.y > anchor_loc.y + 4)) {
|
|
if (log) {
|
|
log->debug_f("is_card_within_range: (false) outside y range (ss.loc.y={}, anchor_loc.y={})", ss.loc.y, anchor_loc.y);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
uint8_t y_index = (ss.loc.y - anchor_loc.y) + 4;
|
|
uint8_t x_index = (ss.loc.x - anchor_loc.x) + 4;
|
|
bool ret = (range[y_index * 9 + x_index] != 0);
|
|
if (log) {
|
|
log->debug_f("is_card_within_range: ({}) (ss.loc=({},{}), anchor_loc=({},{}), indexes=({},{}))",
|
|
ret ? "true" : "false", ss.loc.x, ss.loc.y, anchor_loc.x, anchor_loc.y, x_index, y_index);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
vector<uint16_t> get_card_refs_within_range(
|
|
const parray<uint8_t, 9 * 9>& range,
|
|
const Location& loc,
|
|
const parray<CardShortStatus, 0x10>& short_statuses,
|
|
phosg::PrefixedLogger* log) {
|
|
vector<uint16_t> ret;
|
|
if (is_card_within_range(range, loc, short_statuses[0], log)) {
|
|
if (log) {
|
|
log->debug_f("get_card_refs_within_range: sc card @{:04X} within range", short_statuses[0].card_ref);
|
|
}
|
|
ret.emplace_back(short_statuses[0].card_ref);
|
|
} else {
|
|
if (log) {
|
|
log->debug_f("get_card_refs_within_range: sc card @{:04X} not within range", short_statuses[0].card_ref);
|
|
}
|
|
}
|
|
for (size_t card_index = 7; card_index < 15; card_index++) {
|
|
const auto& ss = short_statuses[card_index];
|
|
if (is_card_within_range(range, loc, ss, log)) {
|
|
if (log) {
|
|
log->debug_f("get_card_refs_within_range: card @{:04X} within range", ss.card_ref);
|
|
}
|
|
ret.emplace_back(ss.card_ref);
|
|
} else {
|
|
if (log) {
|
|
log->debug_f("get_card_refs_within_range: card @{:04X} not within range", ss.card_ref);
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
} // namespace Episode3
|