Skip to content

Commit

Permalink
implement monster attack spell grouping with random selection
Browse files Browse the repository at this point in the history
Currently, creature attacks ignore group cooldowns. The changes implement spell grouping, and if multiple spells from the same group are selected, one will be chosen at random.
  • Loading branch information
pudimtibia committed Jan 21, 2025
1 parent 5781de7 commit b91bb0b
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 59 deletions.
3 changes: 3 additions & 0 deletions data/scripts/lib/register_monster_type.lua
Original file line number Diff line number Diff line change
Expand Up @@ -897,6 +897,9 @@ function readSpell(incomingLua, mtype)
end
if incomingLua.chance then
spell:setChance(incomingLua.chance)
end
if incomingLua.group then
spell:setGroup(incomingLua.group)
end
if incomingLua.range then
spell:setRange(incomingLua.range)
Expand Down
121 changes: 62 additions & 59 deletions src/creatures/monsters/monster.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1115,65 +1115,68 @@ void Monster::onThink_async() {
}

void Monster::doAttacking(uint32_t interval) {
const auto &attackedCreature = getAttackedCreature();
if (!attackedCreature || attackedCreature->isLifeless() || (isSummon() && attackedCreature.get() == this)) {
return;
}

const auto &player = attackedCreature->getPlayer();
if (player && player->isLoginProtected()) {
return;
}

bool updateLook = true;
bool resetTicks = interval != 0;
attackTicks += interval;

const Position &myPos = getPosition();
const Position &targetPos = attackedCreature->getPosition();

for (const spellBlock_t &spellBlock : mType->info.attackSpells) {
bool inRange = false;

if (spellBlock.spell == nullptr || (spellBlock.isMelee && isFleeing())) {
continue;
}

if (canUseSpell(myPos, targetPos, spellBlock, interval, inRange, resetTicks)) {
if (spellBlock.chance >= static_cast<uint32_t>(uniform_random(1, 100))) {
if (updateLook) {
updateLookDirection();
updateLook = false;
}

minCombatValue = spellBlock.minCombatValue;
maxCombatValue = spellBlock.maxCombatValue;

if (spellBlock.spell == nullptr) {
continue;
}

spellBlock.spell->castSpell(getMonster(), attackedCreature);

if (spellBlock.isMelee) {
extraMeleeAttack = false;
}
}
}

if (!inRange && spellBlock.isMelee) {
// melee swing out of reach
extraMeleeAttack = true;
}
}

if (updateLook) {
updateLookDirection();
}

if (resetTicks) {
attackTicks = 0;
}
const auto &attackedCreature = getAttackedCreature();
if (!attackedCreature || (isSummon() && attackedCreature.get() == this)) {
return;
}

bool updateLook = true;
bool resetTicks = interval != 0;
attackTicks += interval;

const Position &myPos = getPosition();
const Position &targetPos = attackedCreature->getPosition();

std::map<uint32_t, std::vector<const spellBlock_t*>> groupSpells;

for (const spellBlock_t &spellBlock : mType->info.attackSpells) {
if (spellBlock.spell == nullptr || (spellBlock.isMelee && isFleeing())) {
continue;
}

uint32_t group = spellBlock.group > 0 ? spellBlock.group : 0;
bool inRange = false;

if (canUseSpell(myPos, targetPos, spellBlock, interval, inRange, resetTicks)) {
if (spellBlock.chance >= static_cast<uint32_t>(uniform_random(1, 100))) {
groupSpells[group].push_back(&spellBlock);
}
}

if (!inRange && spellBlock.isMelee) {
extraMeleeAttack = true; // melee swing out of reach
}
}

for (const auto &[group, spells] : groupSpells) {
if (!spells.empty()) {
const spellBlock_t* selectedSpell = spells[uniform_random(0, spells.size() - 1)];

if (selectedSpell) {
if (updateLook) {
updateLookDirection();
updateLook = false;
}

minCombatValue = selectedSpell->minCombatValue;
maxCombatValue = selectedSpell->maxCombatValue;

selectedSpell->spell->castSpell(getMonster(), attackedCreature);

if (selectedSpell->isMelee) {
extraMeleeAttack = false;
}
}
}
}

if (updateLook) {
updateLookDirection();
}

if (resetTicks) {
attackTicks = 0;
}
}

bool Monster::hasExtraSwing() {
Expand Down
1 change: 1 addition & 0 deletions src/creatures/monsters/monsters.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ bool Monsters::deserializeSpell(const std::shared_ptr<MonsterSpell> &spell, spel

sb.speed = spell->interval;
sb.chance = std::min(static_cast<int>(spell->chance), 100);
sb.group = spell->group > 0 ? spell->group : 0;
sb.range = std::min(static_cast<int>(spell->range), MAP_MAX_VIEW_PORT_X * 2);
sb.minCombatValue = std::min(spell->minCombatValue, spell->maxCombatValue);
sb.maxCombatValue = std::max(spell->minCombatValue, spell->maxCombatValue);
Expand Down
3 changes: 3 additions & 0 deletions src/creatures/monsters/monsters.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ struct spellBlock_t {
spellBlock_t(spellBlock_t &&other) noexcept :
spell(std::move(other.spell)),
chance(other.chance),
group(other.group),
speed(other.speed),
range(other.range),
minCombatValue(other.minCombatValue),
Expand All @@ -48,6 +49,7 @@ struct spellBlock_t {

std::shared_ptr<BaseSpell> spell = nullptr;
uint32_t chance = 100;
uint32_t group = 0;
uint32_t speed = 2000;
uint32_t range = 0;
int32_t minCombatValue = 0;
Expand Down Expand Up @@ -230,6 +232,7 @@ class MonsterSpell {
int32_t tickInterval = 0;
int32_t speedChange = 0;
int32_t duration = 0;
int32_t group = 0;

bool isScripted = false;
bool needTarget = false;
Expand Down
13 changes: 13 additions & 0 deletions src/lua/functions/creatures/monster/monster_spell_functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ void MonsterSpellFunctions::init(lua_State* L) {
Lua::registerMethod(L, "MonsterSpell", "setType", MonsterSpellFunctions::luaMonsterSpellSetType);
Lua::registerMethod(L, "MonsterSpell", "setScriptName", MonsterSpellFunctions::luaMonsterSpellSetScriptName);
Lua::registerMethod(L, "MonsterSpell", "setChance", MonsterSpellFunctions::luaMonsterSpellSetChance);
Lua::registerMethod(L, "MonsterSpell", "setGroup", MonsterSpellFunctions::luaMonsterSpellSetGroup);
Lua::registerMethod(L, "MonsterSpell", "setInterval", MonsterSpellFunctions::luaMonsterSpellSetInterval);
Lua::registerMethod(L, "MonsterSpell", "setRange", MonsterSpellFunctions::luaMonsterSpellSetRange);
Lua::registerMethod(L, "MonsterSpell", "setCombatValue", MonsterSpellFunctions::luaMonsterSpellSetCombatValue);
Expand Down Expand Up @@ -95,6 +96,18 @@ int MonsterSpellFunctions::luaMonsterSpellSetInterval(lua_State* L) {
return 1;
}

int MonsterSpellFunctions::luaMonsterSpellSetGroup(lua_State* L) {
// monsterSpell:setGroup(group)
const auto &spell = Lua::getUserdataShared<MonsterSpell>(L, 1);
if (spell) {
spell->group = Lua::getNumber<uint16_t>(L, 2);
Lua::pushBoolean(L, true);
} else {
lua_pushnil(L);
}
return 1;
}

int MonsterSpellFunctions::luaMonsterSpellSetRange(lua_State* L) {
// monsterSpell:setRange(range)
const auto &spell = Lua::getUserdataShared<MonsterSpell>(L, 1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class MonsterSpellFunctions {
static int luaMonsterSpellSetType(lua_State* L);
static int luaMonsterSpellSetScriptName(lua_State* L);
static int luaMonsterSpellSetChance(lua_State* L);
static int luaMonsterSpellSetGroup(lua_State* L);
static int luaMonsterSpellSetInterval(lua_State* L);
static int luaMonsterSpellSetRange(lua_State* L);
static int luaMonsterSpellSetCombatValue(lua_State* L);
Expand Down

0 comments on commit b91bb0b

Please sign in to comment.