Skip to content

Commit

Permalink
Merge pull request #2380 from fmatthew5876/skilluse
Browse files Browse the repository at this point in the history
Refactor Skill and Item usability and fix some incompatibilities
  • Loading branch information
Ghabry authored Oct 11, 2020
2 parents 76feca0 + 3cf4bd6 commit b0eba25
Show file tree
Hide file tree
Showing 9 changed files with 139 additions and 100 deletions.
52 changes: 52 additions & 0 deletions src/algo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,16 @@
#include "game_battler.h"
#include "game_actor.h"
#include "game_enemy.h"
#include "game_system.h"
#include "main_data.h"
#include "game_player.h"
#include "game_targets.h"
#include "game_battle.h"
#include "attribute.h"
#include "player.h"
#include "rand.h"
#include <lcf/rpg/skill.h>
#include <lcf/reader_util.h>

#include <algorithm>

Expand Down Expand Up @@ -257,4 +263,50 @@ int CalcSkillCost(const lcf::rpg::Skill& skill, int max_sp, bool half_sp_cost) {
? max_sp * skill.sp_percent / 100 / div
: (skill.sp_cost + static_cast<int>(half_sp_cost)) / div;
}

bool IsSkillUsable(const lcf::rpg::Skill& skill,
bool require_states_persist)
{
const auto in_battle = Game_Battle::IsBattleRunning();

if (skill.type == lcf::rpg::Skill::Type_escape) {
return !in_battle && Main_Data::game_system->GetAllowEscape() && Main_Data::game_targets->HasEscapeTarget() && !Main_Data::game_player->IsFlying();
}

if (skill.type == lcf::rpg::Skill::Type_teleport) {
return !in_battle && Main_Data::game_system->GetAllowTeleport() && Main_Data::game_targets->HasTeleportTargets() && !Main_Data::game_player->IsFlying();
}

if (skill.type == lcf::rpg::Skill::Type_switch) {
return in_battle ? skill.occasion_battle : skill.occasion_field;
}

if (in_battle) {
return true;
}

if (skill.scope == lcf::rpg::Skill::Scope_enemy
|| skill.scope == lcf::rpg::Skill::Scope_enemies) {
return false;
}

if (skill.affect_hp || skill.affect_sp) {
return true;
}

bool affects_state = false;
for (int i = 0; i < static_cast<int>(skill.state_effects.size()); ++i) {
const bool inflict = skill.state_effects[i];
if (inflict) {
const auto* state = lcf::ReaderUtil::GetElement(lcf::Data::states, i + 1);
if (state && (!require_states_persist || state->type == lcf::rpg::State::Persistence_persists)) {
affects_state = true;
break;
}
}
}

return affects_state;
}

} // namespace Algo
22 changes: 22 additions & 0 deletions src/algo.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <lcf/rpg/fwd.h>
#include <lcf/rpg/system.h>
#include <lcf/rpg/saveactor.h>
#include <lcf/rpg/skill.h>
#include "game_battler.h"

class Game_Actor;
Expand Down Expand Up @@ -181,6 +182,27 @@ int CalcSelfDestructEffect(const Game_Battler& source,
*/
int CalcSkillCost(const lcf::rpg::Skill& skill, int max_sp, bool half_sp_cost);

/*
* Determine whether a skill is usable.
*
* @param skill the skill to check
* @param require_states_persist If we should require persistent states for non-battle.
* @return Whether the skill can be used.
*/
bool IsSkillUsable(const lcf::rpg::Skill& skill,
bool require_states_persist);

/**
* Checks if the skill is a normal or subskill type.
*
* @param skill the skill to check
* @return true if a normal skill or a 2k3 subskill.
*/
inline bool IsNormalOrSubskill(const lcf::rpg::Skill& skill) {
return skill.type == lcf::rpg::Skill::Type_normal
|| skill.type >= lcf::rpg::Skill::Type_subskill;
}

} // namespace Algo


Expand Down
28 changes: 15 additions & 13 deletions src/game_actor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -189,20 +189,22 @@ bool Game_Actor::IsSkillUsable(int skill_id) const {
return false;
}

// Actor must have all attributes of the skill equipped as weapons
const lcf::rpg::Item* item = GetEquipment(lcf::rpg::Item::Type_weapon);
const lcf::rpg::Item* item2 = HasTwoWeapons() ? GetEquipment(lcf::rpg::Item::Type_weapon + 1) : nullptr;

for (size_t i = 0; i < skill->attribute_effects.size(); ++i) {
bool required = skill->attribute_effects[i] && lcf::Data::attributes[i].type == lcf::rpg::Attribute::Type_physical;
if (required) {
if (item && i < item->attribute_set.size() && item->attribute_set[i]) {
continue;
}
if (item2 && i < item2->attribute_set.size() && item2->attribute_set[i]) {
continue;
if (!skill->affect_attr_defence) {
// Actor must have all attributes of the skill equipped as weapons
const auto* w1 = GetWeapon();
const auto* w2 = Get2ndWeapon();

for (size_t i = 0; i < skill->attribute_effects.size(); ++i) {
bool required = skill->attribute_effects[i] && lcf::Data::attributes[i].type == lcf::rpg::Attribute::Type_physical;
if (required) {
if (w1 && i < w1->attribute_set.size() && w1->attribute_set[i]) {
continue;
}
if (w2 && i < w2->attribute_set.size() && w2->attribute_set[i]) {
continue;
}
return false;
}
return false;
}
}

Expand Down
4 changes: 1 addition & 3 deletions src/game_battlealgorithm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1152,9 +1152,7 @@ bool Game_BattleAlgorithm::Skill::Execute() {
return this->success;
}

if (!(skill.type == lcf::rpg::Skill::Type_normal ||
skill.type >= lcf::rpg::Skill::Type_subskill)) {
assert(false && "Unsupported skill type");
if (!Algo::IsNormalOrSubskill(skill)) {
this->success = false;
return this->success;
}
Expand Down
53 changes: 12 additions & 41 deletions src/game_battler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "game_battle.h"
#include "game_party.h"
#include "game_party_base.h"
#include "game_player.h"
#include "game_switches.h"
#include "game_system.h"
#include "game_targets.h"
Expand Down Expand Up @@ -129,49 +130,19 @@ bool Game_Battler::IsSkillUsable(int skill_id) const {
return false;
}

if (skill->type == lcf::rpg::Skill::Type_escape) {
return !Game_Battle::IsBattleRunning() && Main_Data::game_system->GetAllowEscape() && Main_Data::game_targets->HasEscapeTarget();
}

if (skill->type == lcf::rpg::Skill::Type_teleport) {
return !Game_Battle::IsBattleRunning() && Main_Data::game_system->GetAllowTeleport() && Main_Data::game_targets->HasTeleportTargets();
}

if (skill->type == lcf::rpg::Skill::Type_switch) {
if (Game_Battle::IsBattleRunning()) {
return skill->occasion_battle;
} else {
return skill->occasion_field;
}
}

// > 10 makes any skill usable
int32_t smallest_physical_rate = 11;
int32_t smallest_magical_rate = 11;

const std::vector<int16_t> states = GetInflictedStates();
for (std::vector<int16_t>::const_iterator it = states.begin();
it != states.end(); ++it) {
// States are guaranteed to be valid
const lcf::rpg::State& state = *lcf::ReaderUtil::GetElement(lcf::Data::states, (*it));

if (state.restrict_skill) {
smallest_physical_rate = std::min(state.restrict_skill_level, smallest_physical_rate);
}

if (state.restrict_magic) {
smallest_magical_rate = std::min(state.restrict_magic_level, smallest_magical_rate);
for (auto state_id: GetInflictedStates()) {
const auto* state = lcf::ReaderUtil::GetElement(lcf::Data::states, state_id);
if (state) {
if (state->restrict_skill && skill->physical_rate >= state->restrict_skill_level) {
return false;
}
if (state->restrict_magic && skill->magical_rate >= state->restrict_magic_level) {
return false;
}
}
}

if (skill->physical_rate >= smallest_physical_rate) {
return false;
}
if (skill->magical_rate >= smallest_magical_rate) {
return false;
}

return true;
return Algo::IsSkillUsable(*skill, true);
}

bool Game_Battler::UseItem(int item_id, const Game_Battler* source) {
Expand Down Expand Up @@ -256,7 +227,7 @@ bool Game_Battler::UseSkill(int skill_id, const Game_Battler* source) {
bool was_used = false;
int revived = 0;

if (skill->type == lcf::rpg::Skill::Type_normal || skill->type >= lcf::rpg::Skill::Type_subskill) {
if (Algo::IsNormalOrSubskill(*skill)) {
// Only takes care of healing skills outside of battle,
// the other skill logic is in Game_BattleAlgorithm

Expand Down
73 changes: 34 additions & 39 deletions src/game_party.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include "game_system.h"
#include <lcf/reader_util.h>
#include "output.h"
#include "algo.h"

Game_Party::Game_Party() {
}
Expand Down Expand Up @@ -254,49 +255,44 @@ bool Game_Party::IsItemUsable(int item_id, const Game_Actor* target) const {
return false;
}

if (data.party.size() == 0) {
return false;
const auto* skill = lcf::ReaderUtil::GetElement(lcf::Data::skills, item->skill_id);
const bool in_battle = Game_Battle::IsBattleRunning();

if (item->use_skill) {
// RPG_RT BUG: Does not check if skill is usable.
return skill &&
(in_battle
|| skill->scope == lcf::rpg::Skill::Scope_self
|| skill->scope == lcf::rpg::Skill::Scope_ally
|| skill->scope == lcf::rpg::Skill::Scope_party);
}

switch (item->type) {
case lcf::rpg::Item::Type_weapon:
case lcf::rpg::Item::Type_shield:
case lcf::rpg::Item::Type_armor:
case lcf::rpg::Item::Type_helmet:
case lcf::rpg::Item::Type_accessory:
if (item->use_skill) {
auto* skill = lcf::ReaderUtil::GetElement(lcf::Data::skills, item->skill_id);
if (skill && (
skill->type == lcf::rpg::Skill::Type_escape
|| skill->type == lcf::rpg::Skill::Type_teleport
|| skill->type == lcf::rpg::Skill::Type_switch
)
) {
return false;
case lcf::rpg::Item::Type_medicine:
return !in_battle || !item->occasion_field1;
case lcf::rpg::Item::Type_material:
case lcf::rpg::Item::Type_book:
return !in_battle;
case lcf::rpg::Item::Type_switch:
return in_battle ? item->occasion_battle : item->occasion_field2;
case lcf::rpg::Item::Type_special:
if (skill && Algo::IsSkillUsable(*skill, false)) {
// RPG_RT requires one actor in the party and alive who can use the item.
// But only if the item invokes a normal or subskill. This check is
// not performed for escape, teleport, or switch skills!
if (!Algo::IsNormalOrSubskill(*skill)) {
return true;
} else {
for (auto* actor: GetActors()) {
if (actor->CanAct() && actor->IsItemUsable(item_id)) {
return true;
}
}
}
return IsSkillUsable(item->skill_id, nullptr, true);
}
return false;
case lcf::rpg::Item::Type_special:
return IsSkillUsable(item->skill_id, nullptr, true);
}

if (Game_Battle::IsBattleRunning()) {
switch (item->type) {
case lcf::rpg::Item::Type_medicine:
return !item->occasion_field1;
case lcf::rpg::Item::Type_switch:
return item->occasion_battle;
}
} else {
switch (item->type) {
case lcf::rpg::Item::Type_medicine:
case lcf::rpg::Item::Type_material:
case lcf::rpg::Item::Type_book:
return true;
case lcf::rpg::Item::Type_switch:
return item->occasion_field2;
}
default:
break;
}

return false;
Expand Down Expand Up @@ -378,8 +374,7 @@ bool Game_Party::IsSkillUsable(int skill_id, const Game_Actor* target, bool from
return !Game_Battle::IsBattleRunning() && Main_Data::game_system->GetAllowEscape() && Main_Data::game_targets->HasEscapeTarget() && !Main_Data::game_player->IsFlying();
} else if (skill->type == lcf::rpg::Skill::Type_teleport) {
return !Game_Battle::IsBattleRunning() && Main_Data::game_system->GetAllowTeleport() && Main_Data::game_targets->HasTeleportTargets() && !Main_Data::game_player->IsFlying();
} else if (skill->type == lcf::rpg::Skill::Type_normal ||
skill->type >= lcf::rpg::Skill::Type_subskill) {
} else if (Algo::IsNormalOrSubskill(*skill)) {
int scope = skill->scope;

if (Game_Battle::IsBattleRunning()) {
Expand Down
2 changes: 0 additions & 2 deletions src/scene_battle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -439,8 +439,6 @@ void Scene_Battle::AssignSkill(const lcf::rpg::Skill* skill, const lcf::rpg::Ite
ActionSelectedCallback(active_actor);
return;
}
case lcf::rpg::Skill::Type_normal:
case lcf::rpg::Skill::Type_subskill:
default:
break;
}
Expand Down
3 changes: 2 additions & 1 deletion src/scene_skill.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

// Headers
#include "scene_skill.h"
#include "algo.h"
#include "game_map.h"
#include "game_party.h"
#include "game_player.h"
Expand Down Expand Up @@ -65,7 +66,7 @@ void Scene_Skill::Update() {
Main_Data::game_party->UseSkill(skill_id, actor, actor);
Scene::PopUntil(Scene::Map);
Game_Map::SetNeedRefresh(true);
} else if (skill->type == lcf::rpg::Skill::Type_normal || skill->type >= lcf::rpg::Skill::Type_subskill) {
} else if (Algo::IsNormalOrSubskill(*skill)) {
Main_Data::game_system->SePlay(Main_Data::game_system->GetSystemSE(Main_Data::game_system->SFX_Decision));
Scene::Push(std::make_shared<Scene_ActorTarget>(skill_id, actor_index));
skill_index = skill_window->GetIndex();
Expand Down
2 changes: 1 addition & 1 deletion src/window_skill.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ bool Window_Skill::CheckInclude(int skill_id) {
bool Window_Skill::CheckEnable(int skill_id) {
const Game_Actor* actor = Main_Data::game_actors->GetActor(actor_id);

return actor->IsSkillLearned(skill_id) && Main_Data::game_party->IsSkillUsable(skill_id, actor);
return actor->IsSkillLearned(skill_id) && actor->IsSkillUsable(skill_id);
}

void Window_Skill::SetSubsetFilter(int subset) {
Expand Down

0 comments on commit b0eba25

Please sign in to comment.