Files
psopeeps-newserv/src/Episode3/PlayerStateSubordinates.cc
T
2025-12-06 00:18:53 -08:00

878 lines
29 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 {
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,
s->debug_str_for_card_ref(this->card_ref),
this->value,
s->debug_str_for_card_ref(this->condition_giver_card_ref),
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 {
return std::format(
"EffectResult[att_ref={}, target_ref={}, value={}, cur_hp={}, ap={}, tp={}, flags={:02X}, op={}, cond_index={}, dice={}]",
s->debug_str_for_card_ref(this->attacker_card_ref),
s->debug_str_for_card_ref(this->target_card_ref),
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 {
return std::format(
"CardShortStatus[ref={}, cur_hp={}, flags={:08X}, loc={}, u1={:04X}, max_hp={}, u2={}]",
s->debug_str_for_card_ref(this->card_ref),
this->current_hp,
this->card_flags,
this->loc.str(),
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 {
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),
s->debug_str_for_card_ref(this->attacker_card_ref),
s->debug_str_for_card_ref(this->defense_card_ref),
s->debug_str_for_card_refs(this->target_card_refs),
s->debug_str_for_card_refs(this->action_card_refs),
s->debug_str_for_card_ref(this->original_attacker_card_ref));
}
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 {
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,
s->debug_str_for_card_ref(this->acting_card_ref),
s->debug_str_for_card_ref(this->unknown_card_ref_a3),
s->debug_str_for_card_refs(this->attack_action_card_refs),
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,
s->debug_str_for_card_refs(this->target_card_refs));
}
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 {
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={}]",
s->debug_str_for_card_ref(this->card_ref),
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,
s->debug_str_for_card_refs(this->target_card_refs),
s->debug_str_for_card_refs(this->defense_card_refs),
s->debug_str_for_card_refs(this->original_attacker_card_refs));
}
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 {
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,
s->debug_str_for_card_refs(this->hand_card_refs),
s->debug_str_for_card_ref(this->assist_card_ref),
s->debug_str_for_card_refs(this->set_card_refs),
s->debug_str_for_card_ref(this->sc_card_ref),
s->debug_str_for_card_refs(this->hand_card_refs2),
s->debug_str_for_card_refs(this->set_card_refs2),
s->debug_str_for_card_ref(this->assist_card_ref2),
this->assist_card_set_number,
s->debug_str_for_card_id(this->assist_card_id),
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, 25, 30, 40, 50, 60, 65, 75, 85};
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