From 2713cae31313b9f7d8353fb11989cafa66a8919e Mon Sep 17 00:00:00 2001 From: Albeleon Date: Sat, 23 Jun 2018 23:09:06 +0200 Subject: [PATCH 01/16] "3.9: RM2000: If an absorb skill doesn't take anything, it automatically fails ignoring whether it has some conditions/states to cause." Solution: If a negative absorb skill fails, it skips the rest of the "Execute". We also delete that Absorb SP code at the end. That code, if it's an absorb skill that doesn't absorb SP, and the enemy has 0 SP, it makes the skill miss; which is not true. --- src/game_battlealgorithm.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/game_battlealgorithm.cpp b/src/game_battlealgorithm.cpp index 25fbba4638..6a12d5aea8 100644 --- a/src/game_battlealgorithm.cpp +++ b/src/game_battlealgorithm.cpp @@ -1021,8 +1021,12 @@ bool Game_BattleAlgorithm::Skill::Execute() { this->success = (GetAffectedHp() != -1 && !IsAbsorb()) || (GetAffectedHp() > 0 && IsAbsorb()) || GetAffectedSp() > 0 || GetAffectedAttack() > 0 || GetAffectedDefense() > 0 || GetAffectedSpirit() > 0 || GetAffectedAgility() > 0; + + if (IsAbsorb() && !success) + return this->success; } + // Conditions: for (int i = 0; i < (int) skill.state_effects.size(); i++) { if (!skill.state_effects[i]) continue; @@ -1044,12 +1048,6 @@ bool Game_BattleAlgorithm::Skill::Execute() { assert(false && "Unsupported skill type"); } - if (IsAbsorb() && sp != -1) { - if (GetTarget()->GetSp() == 0) { - this->success = false; - } - } - return this->success; } From b941b60d9e8b271c6745a2b3a44cc640a8905a59 Mon Sep 17 00:00:00 2001 From: Albeleon Date: Sat, 23 Jun 2018 23:17:37 +0200 Subject: [PATCH 02/16] "4.10: RM2000: If a technique misses, it doesn't generate EVASION sound unless the "Dodge technique" message is the same as the "physical miss"." Solution: Override GetResultSe with Skill, so: if the attack fails and it's not the failure message 3 (the physical one), it returns NULL (no sound). Otherwise, it does AlgorithmBase::GetResultSe. --- src/game_battlealgorithm.cpp | 4 ++++ src/game_battlealgorithm.h | 1 + 2 files changed, 5 insertions(+) diff --git a/src/game_battlealgorithm.cpp b/src/game_battlealgorithm.cpp index 6a12d5aea8..cbeeea6705 100644 --- a/src/game_battlealgorithm.cpp +++ b/src/game_battlealgorithm.cpp @@ -1127,6 +1127,10 @@ const RPG::Sound* Game_BattleAlgorithm::Skill::GetStartSe() const { } } +const RPG::Sound* Game_BattleAlgorithm::Skill::GetResultSe() const { + return !success && skill.failure_message != 3 ? NULL : AlgorithmBase::GetResultSe(); +} + void Game_BattleAlgorithm::Skill::GetResultMessages(std::vector& out) const { if (!success) { switch (skill.failure_message) { diff --git a/src/game_battlealgorithm.h b/src/game_battlealgorithm.h index 3de8f43cf3..1fc3c068e6 100644 --- a/src/game_battlealgorithm.h +++ b/src/game_battlealgorithm.h @@ -391,6 +391,7 @@ class Skill : public AlgorithmBase { std::string GetStartMessage() const override; int GetSourceAnimationState() const override; const RPG::Sound* GetStartSe() const override; + const RPG::Sound* GetResultSe() const override; void GetResultMessages(std::vector& out) const override; int GetPhysicalDamageRate() const override; bool IsReflected() const override; From 506d9f38ec76e81ada66e682c784f36be05490ed Mon Sep 17 00:00:00 2001 From: Albeleon Date: Sat, 23 Jun 2018 23:20:02 +0200 Subject: [PATCH 03/16] "4.11: RM2000: If you keep CANCEL pressed while in State_Battle, the BattleActionState doesn't advance, it keeps paused." Solution: After battle_action_wait has finished, we put an Input that returns false if CANCEL is pressed. --- src/scene_battle_rpg2k.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/scene_battle_rpg2k.cpp b/src/scene_battle_rpg2k.cpp index af65071efe..cabffccdc0 100644 --- a/src/scene_battle_rpg2k.cpp +++ b/src/scene_battle_rpg2k.cpp @@ -362,6 +362,10 @@ bool Scene_Battle_Rpg2k::ProcessBattleAction(Game_BattleAlgorithm::AlgorithmBase } } + if (Input::IsPressed(Input::CANCEL)) { + return false; + } + switch (battle_action_state) { case BattleActionState_Start: battle_action_wait = GetDelayForWindow(); From bc40101a6f8de176906123969a0c12fcfae9373d Mon Sep 17 00:00:00 2001 From: Albeleon Date: Sat, 23 Jun 2018 23:38:21 +0200 Subject: [PATCH 04/16] 2.6 + 3.7 "2.6: RM2000 (issue #1102 ): When an enemy dies, a message should be displayed right after the attack inside the same message." "3.7: RM2000: If an enemy dies with an attack that takes away many stats HP included (or puts conditions), the enemy should die right after the HP damage is done, and the other messages are ignored. This also includes absorb attacks." Solution: We push that messages after the damage if the enemy is dead, and it skips the rest of result messages. --- src/game_battlealgorithm.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/game_battlealgorithm.cpp b/src/game_battlealgorithm.cpp index cbeeea6705..e111cf5f50 100644 --- a/src/game_battlealgorithm.cpp +++ b/src/game_battlealgorithm.cpp @@ -421,6 +421,12 @@ void Game_BattleAlgorithm::AlgorithmBase::GetResultMessages(std::vector Date: Sun, 24 Jun 2018 21:26:12 +0200 Subject: [PATCH 05/16] "2.23: RM2000 + RM2003: When screen shakes in a battle, the borders should be black." Solution: Override Scene::DrawBackground and CleanDisplay. --- src/scene_battle.cpp | 4 ++++ src/scene_battle.h | 1 + 2 files changed, 5 insertions(+) diff --git a/src/scene_battle.cpp b/src/scene_battle.cpp index e18d7c80ef..77ea236390 100644 --- a/src/scene_battle.cpp +++ b/src/scene_battle.cpp @@ -114,6 +114,10 @@ void Scene_Battle::TransitionOut() { } } +void Scene_Battle::DrawBackground() { + DisplayUi->CleanDisplay(); +} + void Scene_Battle::CreateUi() { std::vector commands; commands.push_back(Data::terms.battle_fight); diff --git a/src/scene_battle.h b/src/scene_battle.h index d2b8459281..0cf3f746e6 100644 --- a/src/scene_battle.h +++ b/src/scene_battle.h @@ -62,6 +62,7 @@ class Scene_Battle : public Scene { void TransitionIn() override; void TransitionOut() override; + void DrawBackground() override; enum State { /** Battle has started (Display encounter message) */ From 7278e7c75cd654b5daf6a33774224572da28afec Mon Sep 17 00:00:00 2001 From: Albeleon Date: Fri, 29 Jun 2018 16:19:06 +0200 Subject: [PATCH 06/16] "2.25: RM2000: Skills and items that affect whole groups should only reduce the item count or SP once. Check too if a weapon that decreases SP reduces when it hits double, everyone or misses." Solution: For reducing items or skill cost, all of them should be inside "if(FirstAttack())". --- src/game_battlealgorithm.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/game_battlealgorithm.cpp b/src/game_battlealgorithm.cpp index e111cf5f50..f8a4f662c2 100644 --- a/src/game_battlealgorithm.cpp +++ b/src/game_battlealgorithm.cpp @@ -1060,11 +1060,11 @@ bool Game_BattleAlgorithm::Skill::Execute() { void Game_BattleAlgorithm::Skill::Apply() { AlgorithmBase::Apply(); - if (item) { - Main_Data::game_party->ConsumeItemUse(item->ID); - } - else { - if (first_attack) { + if (IsFirstAttack()) { + if (item) { + Main_Data::game_party->ConsumeItemUse(item->ID); + } + else { source->ChangeSp(-source->CalculateSkillCost(skill.ID)); } } From 956d66f2fd397bf674bbf5f8a9651e4656778d2d Mon Sep 17 00:00:00 2001 From: Albeleon Date: Wed, 4 Jul 2018 18:48:31 +0200 Subject: [PATCH 07/16] "2.26: RM2000 + RM2003: When calculating an actor's state probability, the armor state resistance should be also taken. If there are many armors with state protection of the same type, only the one with the most resistance will be taken, they don't stack." Solution: In Game_Actor::GetStateProbability, check all the armor, see if they resist the current state, and if they do, save the resistance percentage. It will only take the most resistent. After that, it is multiplied to the result and divided by 100. --- src/game_actor.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/game_actor.cpp b/src/game_actor.cpp index 0a2b480db5..430ded09d1 100644 --- a/src/game_actor.cpp +++ b/src/game_actor.cpp @@ -573,15 +573,25 @@ int Game_Actor::GetNextExp(int level) const { } int Game_Actor::GetStateProbability(int state_id) const { - int rate = 2; // C - default + int rate = 2, mul = 100; // C - default const uint8_t* r = ReaderUtil::GetElement(GetActor().state_ranks, state_id); if (r) { rate = *r; } + // This takes the armor of the character with the most resistance for that particular state + for (const auto equipment : GetWholeEquipment()) { + RPG::Item* item = ReaderUtil::GetElement(Data::items, equipment); + if (item != nullptr && (item->type == RPG::Item::Type_shield || item->type == RPG::Item::Type_armor + || item->type == RPG::Item::Type_helmet || item->type == RPG::Item::Type_accessory) + && state_id <= item->state_set.size() && item->state_set[state_id - 1]) { + mul = std::min(mul, 100 - item->state_chance); + } + } + // GetStateRate verifies the state_id - return GetStateRate(state_id, rate); + return GetStateRate(state_id, rate) * mul / 100; } int Game_Actor::GetAttributeModifier(int attribute_id) const { From 5b84c364fd64ee89e98ec3421e5f295eff5f02be Mon Sep 17 00:00:00 2001 From: Albeleon Date: Wed, 4 Jul 2018 18:50:54 +0200 Subject: [PATCH 08/16] "2.27: RM2000 + RM2003: If all the player party is in a state that has Restriction NoMove and has a 0% chance of recovery (the physical recovery doesn't matter), it's a game over." Solution: In CheckLose, in case there is a member active, it checks for each active member whether they are in a state with Restriction NoMove and 0% recovery. If the number of members like that are the same as the active members, it's game over. --- src/game_battle.cpp | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/game_battle.cpp b/src/game_battle.cpp index 36b8e381d1..5ed2fc2bc7 100644 --- a/src/game_battle.cpp +++ b/src/game_battle.cpp @@ -153,7 +153,26 @@ bool Game_Battle::CheckWin() { } bool Game_Battle::CheckLose() { - return !Main_Data::game_party->IsAnyActive(); + if (!Main_Data::game_party->IsAnyActive()) + return true; + + // If there are active characters, but all of them are in a state with Restriction "Do Nothing" and 0% recovery probability, it's game over + // Physical recovery doesn't matter in this case + int character_number = 0; + std::vector actors; + + Main_Data::game_party->GetActiveBattlers(actors); + for (auto actor : actors) { + for (auto id_state : actor->GetInflictedStates()) { + RPG::State *state = ReaderUtil::GetElement(Data::states, id_state); + if (state->restriction == RPG::State::Restriction_do_nothing && state->auto_release_prob == 0) { + ++character_number; + break; + } + } + } + + return character_number == actors.size(); } Spriteset_Battle& Game_Battle::GetSpriteset() { From aa171101b6cf23ce936dc80f21d4bf38c771135f Mon Sep 17 00:00:00 2001 From: Albeleon Date: Wed, 4 Jul 2018 20:03:39 +0200 Subject: [PATCH 09/16] "2.31: RM2000: Items that miss shouldn't print any message at all." Solution: In GetResultMessages, only summon AlgorithmBase if the attack is a success. --- src/game_battlealgorithm.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/game_battlealgorithm.cpp b/src/game_battlealgorithm.cpp index f8a4f662c2..4a121bcde5 100644 --- a/src/game_battlealgorithm.cpp +++ b/src/game_battlealgorithm.cpp @@ -1312,7 +1312,8 @@ int Game_BattleAlgorithm::Item::GetSourceAnimationState() const { } void Game_BattleAlgorithm::Item::GetResultMessages(std::vector& out) const { - AlgorithmBase::GetResultMessages(out); + if (success) + AlgorithmBase::GetResultMessages(out); } const RPG::Sound* Game_BattleAlgorithm::Item::GetStartSe() const { From 1605f7cffebc243a1257963a1e585ff1caf856fb Mon Sep 17 00:00:00 2001 From: Albeleon Date: Sat, 7 Jul 2018 01:46:40 +0200 Subject: [PATCH 10/16] "2.38: RM2000 + RM2003: Skills with physical attributes can only be thrown if the character's weapons (one or two) have all the attributes." Solution: Check if it has at least one weapon, an that weapon has that particular physical attribute. If one is missing for that technique, it cannot use it. --- src/game_actor.cpp | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/game_actor.cpp b/src/game_actor.cpp index 430ded09d1..e4b00e6aba 100644 --- a/src/game_actor.cpp +++ b/src/game_actor.cpp @@ -156,17 +156,13 @@ bool Game_Actor::IsSkillUsable(int skill_id) const { for (size_t i = 0; i < skill->attribute_effects.size(); ++i) { bool required = skill->attribute_effects[i] && Data::attributes[i].type == RPG::Attribute::Type_physical; if (required) { - if (item && i < item->attribute_set.size()) { - if (!item->attribute_set[i]) { - return false; - } - } else if (item2 && i < item2->attribute_set.size()) { - if (!item2->attribute_set[i]) { - return false; - } - } else { - return false; + if (item && i < item->attribute_set.size() && item->attribute_set[i]) { + continue; } + if (item2 && i < item2->attribute_set.size() && item2->attribute_set[i]) { + continue; + } + return false; } } From cbd34e99f42c6376b2e49f8df8833f1ff45061fe Mon Sep 17 00:00:00 2001 From: Albeleon Date: Sat, 7 Jul 2018 01:51:34 +0200 Subject: [PATCH 11/16] "2.39: RM2000 + RM2003: Half Cost SP is always rounded to the ceil number." Solution: Ceil the result of the Half Cost SP for the instances where it's divided. --- src/game_actor.cpp | 2 +- src/game_battlealgorithm.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/game_actor.cpp b/src/game_actor.cpp index e4b00e6aba..71fc0db27a 100644 --- a/src/game_actor.cpp +++ b/src/game_actor.cpp @@ -186,7 +186,7 @@ int Game_Actor::GetSpCostModifier() const { } int Game_Actor::CalculateSkillCost(int skill_id) const { - return Game_Battler::CalculateSkillCost(skill_id) / GetSpCostModifier(); + return std::ceil(Game_Battler::CalculateSkillCost(skill_id) / (float) GetSpCostModifier()); } bool Game_Actor::LearnSkill(int skill_id) { diff --git a/src/game_battlealgorithm.cpp b/src/game_battlealgorithm.cpp index 4a121bcde5..2f36176b00 100644 --- a/src/game_battlealgorithm.cpp +++ b/src/game_battlealgorithm.cpp @@ -849,7 +849,7 @@ void Game_BattleAlgorithm::Normal::Apply() { Game_Actor* src = static_cast(source); const RPG::Item* weapon = ReaderUtil::GetElement(Data::items, src->GetWeaponId()); if (weapon) { - source->ChangeSp(-weapon->sp_cost / src->GetSpCostModifier()); + source->ChangeSp(std::ceil(-weapon->sp_cost / (float) src->GetSpCostModifier())); } } } From 6c3a01c37ce663cc599df95383df59e0e52b6cd5 Mon Sep 17 00:00:00 2001 From: Albeleon Date: Sat, 7 Jul 2018 02:20:05 +0200 Subject: [PATCH 12/16] "3.12: RM2000 + RM2003: Protection equipment against an element should increase the rate for 1 (at most). This can be combined with Shift attribute." Solution: Add in Game_Actor::GetAttributeModifier a check for all the protections. If it protects against that element, increase rate (only once). --- src/game_actor.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/game_actor.cpp b/src/game_actor.cpp index 71fc0db27a..006b4fffaa 100644 --- a/src/game_actor.cpp +++ b/src/game_actor.cpp @@ -608,6 +608,16 @@ int Game_Actor::GetAttributeModifier(int attribute_id) const { } rate += *shift; + for (auto id_object : GetWholeEquipment()) { + RPG::Item *object = ReaderUtil::GetElement(Data::items, id_object); + if (object != nullptr && (object->type == RPG::Item::Type_shield || object->type == RPG::Item::Type_armor + || object->type == RPG::Item::Type_helmet || object->type == RPG::Item::Type_accessory) + && object->attribute_set.size() >= attribute_id && object->attribute_set[attribute_id - 1]) { + rate++; + break; + } + } + if (rate < 0) { rate = 0; } else if (rate > 4) { From eb8a6ec83369fa437a138c365c6ae70ebb031c2c Mon Sep 17 00:00:00 2001 From: Albeleon Date: Sat, 7 Jul 2018 02:46:00 +0200 Subject: [PATCH 13/16] "3.14: RM2000 + RM2003: After a battle is over, heal all the states that despite being "Persist after battle", still have more than 0% chance of recovery (turns and physical recovery don't matter)." Solution: Put that condition in RemoveBattleStates, which removes states when the battle is over. --- src/game_battler.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/game_battler.cpp b/src/game_battler.cpp index 8f6e503f29..853b4424e0 100644 --- a/src/game_battler.cpp +++ b/src/game_battler.cpp @@ -458,11 +458,11 @@ void Game_Battler::RemoveBattleStates() { // If death is non-permanent change HP to 1 if (IsDead() && non_permanent(1)) { - ChangeHp(1); + RemoveState(1); } - for (size_t i = 0; i < states.size(); ++i) { - if (non_permanent(i + 1)) { + for (size_t i = 1; i < states.size(); ++i) { + if (non_permanent(i + 1) || ReaderUtil::GetElement(Data::states, i + 1)->auto_release_prob > 0) { states[i] = 0; } } From a0fcb1b28868b05d2d6f435c10b57dfbbabeaa7d Mon Sep 17 00:00:00 2001 From: Albeleon Date: Sat, 7 Jul 2018 03:25:42 +0200 Subject: [PATCH 14/16] "4.13: RM2000: If you keep pressed DECISION while in State_Battle, the waiting time fast-forwards." Solution: If DECISION is pressed in ProcessBattleAction, it goes twice as fast - it reduces "battle_wait_action" by another unity. To avoid that "if (battle_wait_action)" doesn't detect -1 if that's the case, we change it to "if (battle_wait_action > 0)". --- src/scene_battle_rpg2k.cpp | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/scene_battle_rpg2k.cpp b/src/scene_battle_rpg2k.cpp index cabffccdc0..110df7da03 100644 --- a/src/scene_battle_rpg2k.cpp +++ b/src/scene_battle_rpg2k.cpp @@ -335,7 +335,11 @@ bool Scene_Battle_Rpg2k::ProcessBattleAction(Game_BattleAlgorithm::AlgorithmBase Sprite_Battler* source_sprite; Sprite_Battler* target_sprite; - if (battle_action_wait) { + if (Input::IsPressed(Input::DECISION)) { + --battle_action_wait; + } + + if (battle_action_wait > 0) { if (--battle_action_wait) { return false; } @@ -543,8 +547,7 @@ void Scene_Battle_Rpg2k::ProcessInput() { if (Input::IsTriggered(Input::DECISION)) { switch (state) { case State_Start: - // Skip current message - encounter_message_sleep_until = Player::GetFrames(); + // no-op break; case State_SelectOption: // Interpreter message boxes pop up in this state @@ -690,6 +693,10 @@ void Scene_Battle_Rpg2k::Escape() { begin_escape = false; } else { + if (Input::IsPressed(Input::DECISION)) { + ++escape_counter; + } + ++escape_counter; if (escape_counter > 60) { @@ -878,6 +885,10 @@ bool Scene_Battle_Rpg2k::DisplayMonstersInMessageWindow() { encounter_message_first_monster = false; } + if (Input::IsPressed(Input::DECISION)) { + --encounter_message_sleep_until; + } + if (encounter_message_sleep_until > -1) { if (Player::GetFrames() >= encounter_message_sleep_until) { // Sleep over From d38d6e52d270be4c51bd7c94b241c9c8a6ec904a Mon Sep 17 00:00:00 2001 From: Albeleon Date: Mon, 16 Jul 2018 02:02:51 +0200 Subject: [PATCH 15/16] Not show EXP line in victory message if there wasn't. Solution: Add an "If". --- src/scene_battle_rpg2k.cpp | 4 +++- src/scene_battle_rpg2k3.cpp | 6 ++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/scene_battle_rpg2k.cpp b/src/scene_battle_rpg2k.cpp index 110df7da03..0e178f32a4 100644 --- a/src/scene_battle_rpg2k.cpp +++ b/src/scene_battle_rpg2k.cpp @@ -1015,7 +1015,9 @@ bool Scene_Battle_Rpg2k::CheckWin() { Game_Message::texts.push_back(Data::terms.victory + Player::escape_symbol + "|"); std::stringstream ss; - PushExperienceGainedMessage(exp); + if (exp > 0) { + PushExperienceGainedMessage(exp); + } if (money > 0) { PushGoldReceivedMessage(money); } diff --git a/src/scene_battle_rpg2k3.cpp b/src/scene_battle_rpg2k3.cpp index ceef6cd6bb..6ea61a5ad1 100644 --- a/src/scene_battle_rpg2k3.cpp +++ b/src/scene_battle_rpg2k3.cpp @@ -983,8 +983,10 @@ bool Scene_Battle_Rpg2k3::CheckWin() { std::string space = Player::IsRPG2k3E() ? " " : ""; std::stringstream ss; - ss << exp << space << Data::terms.exp_received; - Game_Message::texts.push_back(ss.str()); + if (exp > 0) { + ss << exp << space << Data::terms.exp_received; + Game_Message::texts.push_back(ss.str()); + } if (money > 0) { ss.str(""); ss << Data::terms.gold_recieved_a << " " << money << Data::terms.gold << Data::terms.gold_recieved_b; From d8445c4ca2a3f79aaa40b244222adab669bee22d Mon Sep 17 00:00:00 2001 From: Albeleon Date: Mon, 16 Jul 2018 16:12:41 +0200 Subject: [PATCH 16/16] Make an "All enemy" attack not affect hidden enemies. Problem: Attack that hit all enemies also hits a hidden character. Solution: In IsTargetValid(), use Exists() --- src/game_battlealgorithm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game_battlealgorithm.cpp b/src/game_battlealgorithm.cpp index 2f36176b00..0d53f95271 100644 --- a/src/game_battlealgorithm.cpp +++ b/src/game_battlealgorithm.cpp @@ -641,7 +641,7 @@ bool Game_BattleAlgorithm::AlgorithmBase::IsTargetValid() const { return false; } - return (!GetTarget()->IsDead()); + return GetTarget()->Exists(); } int Game_BattleAlgorithm::AlgorithmBase::GetSourceAnimationState() const {