Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[core] Monstrosity part 2: Battle & Skills #4618

Draft
wants to merge 11 commits into
base: base
Choose a base branch
from
30 changes: 30 additions & 0 deletions scripts/actions/mobskills/wild_ginseng.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
-----------------------------------
-- Wild Ginseng
-- Description: Grants the effects of Haste, Protect, Shell, Regen, and Blink on the caster.
-- Buff potencies:
-- Despite the description, also grants Protect (60 Defense).
-- Regen effect is 30HP/tick and does not scale.
-- It will not overwrite itself and must be canceled before being reapplied.
-- Haste effect is 20% Haste.
-- Shell effect is -?/256 Magic Damage Taken.
-- Blink has three shadows.
-- All buffs have a random duration between approximately 3.5 and 4.5 minutes.
-----------------------------------
local mobskillObject = {}

mobskillObject.onMobSkillCheck = function(target, mob, skill)
return 0
end

mobskillObject.onMobWeaponSkill = function(target, mob, skill)
xi.mobskills.mobBuffMove(mob, xi.effect.PROTECT, 60, 0, math.random(utils.minutes(3.5), utils.minutes(4.5)))
xi.mobskills.mobBuffMove(mob, xi.effect.SHELL, 2000, 0, math.random(utils.minutes(3.5), utils.minutes(4.5)))
xi.mobskills.mobBuffMove(mob, xi.effect.REGEN, 30, 0, math.random(utils.minutes(3.5), utils.minutes(4.5)))
xi.mobskills.mobBuffMove(mob, xi.effect.BLINK, 3, 0, math.random(utils.minutes(3.5), utils.minutes(4.5)))
xi.mobskills.mobBuffMove(mob, xi.effect.HASTE, 1000, 0, math.random(utils.minutes(3.5), utils.minutes(4.5)))

skill:setMsg(xi.msg.basic.NONE)
return 0
end

return mobskillObject
2 changes: 1 addition & 1 deletion sql/mob_skills.sql
Original file line number Diff line number Diff line change
Expand Up @@ -1375,7 +1375,7 @@ INSERT INTO `mob_skills` VALUES (1358,1009,'plasma_charge',0,7.0,2000,1000,1,0,0
-- INSERT INTO `mob_skills` VALUES (1359,855,'chthonian_ray',0,7.0,2000,1500,4,0,0,0,0,0,0);
INSERT INTO `mob_skills` VALUES (1360,855,'apocalyptic_ray',0,7.0,2000,1500,4,0,0,0,0,0,0);
-- INSERT INTO `mob_skills` VALUES (1361,1105,'viscid_secretion',0,7.0,2000,1500,4,0,0,0,0,0,0);
-- INSERT INTO `mob_skills` VALUES (1362,1106,'wild_ginseng',0,7.0,2000,1500,4,0,0,0,0,0,0);
INSERT INTO `mob_skills` VALUES (1362,37,'wild_ginseng',0,7.0,2000,1500,1,0,0,0,0,0,0);
-- INSERT INTO `mob_skills` VALUES (1363,1107,'hungry_crunch',0,7.0,2000,1500,4,0,0,0,0,0,0);
INSERT INTO `mob_skills` VALUES (1364,848,'mighty_snort',0,7.0,2000,1500,4,0,0,0,0,0,0);
INSERT INTO `mob_skills` VALUES (1365,1043,'tail_thrust',0,7.0,2000,1000,4,0,0,0,0,0,0);
Expand Down
1,033 changes: 1,033 additions & 0 deletions sql/monstrosity_tp_skills.sql

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/common/console_service.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ ConsoleService::ConsoleService()
// Remove "lua" from the front of the inputs
inputs = std::vector<std::string>(inputs.begin() + 1, inputs.end());

auto input = fmt::format("local var = {}; if type(var) ~= \"nil\" then print(var) end", fmt::join(inputs, " "));
auto input = fmt::format("{}", fmt::join(inputs, " "));
lua.safe_script(input);
}
});
Expand Down
4 changes: 2 additions & 2 deletions src/map/ai/ai_container.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ bool CAIContainer::Internal_WeaponSkill(uint16 targid, uint16 wsid)
return false;
}

bool CAIContainer::Internal_MobSkill(uint16 targid, uint16 wsid)
bool CAIContainer::Internal_MobSkill(uint16 targid, uint16 wsid, uint16 manualTPCost)
{
auto* entity = dynamic_cast<CBattleEntity*>(PEntity);
if (entity)
Expand All @@ -287,7 +287,7 @@ bool CAIContainer::Internal_MobSkill(uint16 targid, uint16 wsid)
{
return false;
}
return ChangeState<CMobSkillState>(entity, targid, wsid);
return ChangeState<CMobSkillState>(entity, targid, wsid, manualTPCost);
}
return false;
}
Expand Down
2 changes: 1 addition & 1 deletion src/map/ai/ai_container.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ class CAIContainer
bool Internal_ChangeTarget(uint16 targetid);
bool Internal_Disengage();
bool Internal_WeaponSkill(uint16 targid, uint16 wsid);
bool Internal_MobSkill(uint16 targid, uint16 wsid);
bool Internal_MobSkill(uint16 targid, uint16 wsid, uint16 manualTPCost = 0);
bool Internal_PetSkill(uint16 targid, uint16 abilityid);
bool Internal_Ability(uint16 targetid, uint16 abilityid);
bool Internal_RangedAttack(uint16 targetid);
Expand Down
11 changes: 8 additions & 3 deletions src/map/ai/states/mobskill_state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,11 @@ along with this program. If not, see http://www.gnu.org/licenses/
#include "status_effect_container.h"
#include "utils/battleutils.h"

CMobSkillState::CMobSkillState(CBattleEntity* PEntity, uint16 targid, uint16 wsid)
CMobSkillState::CMobSkillState(CBattleEntity* PEntity, uint16 targid, uint16 wsid, uint16 manualTPCost)
: CState(PEntity, targid)
, m_PEntity(PEntity)
, m_spentTP(0)
, m_manualTPCost(manualTPCost)
{
auto* skill = battleutils::GetMobSkill(wsid);
if (!skill)
Expand Down Expand Up @@ -92,10 +93,13 @@ void CMobSkillState::SpendCost()
m_spentTP = m_PEntity->addTP(-1000);
m_PEntity->StatusEffectContainer->DelStatusEffect(EFFECT_SEKKANOKI);
}
else if (m_manualTPCost > 0)
{
m_spentTP = m_PEntity->addTP(-m_manualTPCost);
}
else
{
m_spentTP = m_PEntity->health.tp;
m_PEntity->health.tp = 0;
m_spentTP = m_PEntity->addTP(-m_PEntity->health.tp);
}
}
}
Expand All @@ -111,6 +115,7 @@ bool CMobSkillState::Update(time_point tick)
m_finishTime = tick + delay;
Complete();
}

if (IsCompleted() && tick > m_finishTime)
{
auto* PTarget = GetTarget();
Expand Down
3 changes: 2 additions & 1 deletion src/map/ai/states/mobskill_state.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class CBattleEntity;
class CMobSkillState : public CState
{
public:
CMobSkillState(CBattleEntity* PEntity, uint16 targid, uint16 wsid);
CMobSkillState(CBattleEntity* PEntity, uint16 targid, uint16 wsid, uint16 manualTPCost = 0);

CMobSkill* GetSkill();

Expand Down Expand Up @@ -62,6 +62,7 @@ class CMobSkillState : public CState
time_point m_finishTime;
duration m_castTime{};
int16 m_spentTP;
uint16 m_manualTPCost;
};

#endif
67 changes: 66 additions & 1 deletion src/map/monstrosity.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,21 @@ struct MonstrosityInstinctRow
std::vector<CModifier> mods;
};

struct MonstrositySkillRow
{
uint16 monstrositySpeciesCode;
uint16 monstrositySkillId;
uint16 monsterSkillId;
uint8 levelUnlocked;
uint16 tpCost;
};

namespace
{
std::unordered_map<uint16, MonstrositySpeciesRow> gMonstrositySpeciesMap;
std::unordered_map<uint16, MonstrosityInstinctRow> gMonstrosityInstinctMap;
std::unordered_map<uint16, MonstrositySkillRow> gMonstrositySkillMap;
std::unordered_map<uint8, uint32> gMonstrosityExpMap;
} // namespace

monstrosity::MonstrosityData_t::MonstrosityData_t()
Expand Down Expand Up @@ -150,6 +161,39 @@ void monstrosity::LoadStaticData()
}
}
}

ret = sql->Query("SELECT monstrosity_species_id, dat_skill_id, mob_skill_id, unlock_level, tp_cost FROM monstrosity_tp_skills;");
if (ret != SQL_ERROR && sql->NumRows() != 0)
{
while (sql->NextRow() == SQL_SUCCESS)
{
MonstrositySkillRow row;

row.monstrositySpeciesCode = static_cast<uint16>(sql->GetUIntData(0));
row.monstrositySkillId = static_cast<uint16>(sql->GetUIntData(1));
row.monsterSkillId = static_cast<uint16>(sql->GetUIntData(2));
row.levelUnlocked = static_cast<uint8>(sql->GetUIntData(3));
row.tpCost = static_cast<uint16>(sql->GetUIntData(4));

// NOTE: Only keep the first results
if (gMonstrositySkillMap.find(row.monstrositySkillId) == gMonstrositySkillMap.end())
{
gMonstrositySkillMap[row.monstrositySkillId] = row;
}
}
}

ret = sql->Query("SELECT level, amount FROM monstrosity_exp_table;");
if (ret != SQL_ERROR && sql->NumRows() != 0)
{
while (sql->NextRow() == SQL_SUCCESS)
{
auto level = static_cast<uint8>(sql->GetUIntData(0));
auto amount = static_cast<uint32>(sql->GetUIntData(1));

gMonstrosityExpMap[level] = amount;
}
}
}

void monstrosity::ReadMonstrosityData(CCharEntity* PChar)
Expand Down Expand Up @@ -409,10 +453,31 @@ void monstrosity::HandleMonsterSkillActionPacket(CCharEntity* PChar, CBasicPacke
uint16 targId = data.ref<uint16>(0x08);
uint16 skillId = data.ref<uint16>(0x0C);

// If not found, bail out
if (gMonstrositySkillMap.find(skillId) == gMonstrositySkillMap.end())
{
ShowWarning(fmt::format("Monstrosity skill {} not implemented.", skillId));
PChar->pushPacket(std::make_unique<CMessageBasicPacket>(PChar, PChar, 0, 0, MSGBASIC_CANNOT_PERFORM_ACTION));
return;
}

// Translate skillId from the dat-based Monstrosity ID to the ID's we use for regular mob skills
auto skill = gMonstrositySkillMap[skillId];

skillId = skill.monsterSkillId;

// TODO: Validate that this move is available at this level, for this species, and that
// we're capable of using it (state, TP, etc.).

PChar->PAI->Internal_MobSkill(targId, skillId);
if (PChar->health.tp >= skill.tpCost)
{
PChar->PAI->Internal_MobSkill(targId, skillId, skill.tpCost);
}
else
{
// TODO: Correct rejection message
PChar->pushPacket(std::make_unique<CMessageBasicPacket>(PChar, PChar, 0, 0, MSGBASIC_CANNOT_PERFORM_ACTION));
}
}

void monstrosity::HandleEquipChangePacket(CCharEntity* PChar, CBasicPacket& data)
Expand Down
7 changes: 7 additions & 0 deletions src/map/packets/entity_update.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,9 @@ void CEntityUpdatePacket::updateWith(CBaseEntity* PEntity, ENTITYUPDATE type, ui
ref<uint8>(0x28) |= 0x40;
}

// Other flag options for 0x29:
// 0x08: Sword and Shield (Belligerency etc.)
// 0x20: Two heads butting on red background
ref<uint8>(0x29) = static_cast<uint8>(PEntity->allegiance);
ref<uint8>(0x2B) = PEntity->namevis;
}
Expand Down Expand Up @@ -167,6 +170,10 @@ void CEntityUpdatePacket::updateWith(CBaseEntity* PEntity, ENTITYUPDATE type, ui
ref<uint8>(0x28) |= PMob->StatusEffectContainer->HasStatusEffect(EFFECT_TERROR) ? 0x10 : 0x00;
ref<uint8>(0x28) |= PMob->health.hp > 0 && PMob->animation == ANIMATION_DEATH ? 0x08 : 0;
ref<uint8>(0x28) |= PMob->status == STATUS_TYPE::NORMAL && PMob->objtype == TYPE_MOB ? 0x40 : 0; // Make the entity triggerable if a mob and normal status

// Other flag options for 0x29:
// 0x08: Sword and Shield (Belligerency etc.)
// 0x20: Two heads butting on red background
ref<uint8>(0x29) = static_cast<uint8>(PEntity->allegiance);
ref<uint8>(0x2B) = PEntity->namevis;
}
Expand Down