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

feat: multiple creature icons #1585

Merged
merged 4 commits into from
Sep 12, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -95,25 +95,25 @@ end
encounter:register()

local function addShieldStack(player)
local currentIcon = player:getIcon()
local currentIcon = player:getIcon("magma-bubble")
if not currentIcon or currentIcon.category ~= CreatureIconCategory_Quests or currentIcon.icon ~= CreatureIconQuests_GreenShield then
player:setIcon(CreatureIconCategory_Quests, CreatureIconQuests_GreenShield, 5)
player:setIcon("magma-bubble", CreatureIconCategory_Quests, CreatureIconQuests_GreenShield, 5)
return true
end
player:setIcon(CreatureIconCategory_Quests, CreatureIconQuests_GreenShield, currentIcon.count + 5)
player:setIcon("magma-bubble", CreatureIconCategory_Quests, CreatureIconQuests_GreenShield, currentIcon.count + 5)
end

local function tickShields(player)
local currentIcon = player:getIcon()
local currentIcon = player:getIcon("magma-bubble")
if not currentIcon or currentIcon.category ~= CreatureIconCategory_Quests or currentIcon.icon ~= CreatureIconQuests_GreenShield then
return 0
end
if currentIcon.count <= 0 then
player:clearIcon()
player:removeIcon("magma-bubble")
return 0
end
local newCount = currentIcon.count - 1
player:setIcon(CreatureIconCategory_Quests, CreatureIconQuests_GreenShield, newCount)
player:setIcon("magma-bubble", CreatureIconCategory_Quests, CreatureIconQuests_GreenShield, newCount)
return newCount
end

Expand Down Expand Up @@ -294,3 +294,24 @@ function magmaBubbleDeath.onDeath()
end

magmaBubbleDeath:register()

local zoneEvent = ZoneEvent(bossZone)
function zoneEvent.afterEnter(_zone, creature)
local player = creature:getPlayer()
if not player then
return false
end

player:setIcon("magma-bubble", CreatureIconCategory_Quests, CreatureIconQuests_GreenShield, 0)
end

function zoneEvent.afterLeave(_zone, creature)
local player = creature:getPlayer()
if not player then
return false
end

player:removeIcon("magma-bubble")
end

zoneEvent:register()
4 changes: 0 additions & 4 deletions data/libs/functions/creature.lua
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,3 @@ function Creature:addEventStamina(target)
end
end
end

function Creature:clearIcon()
self:setIcon(0, 0)
end
3 changes: 1 addition & 2 deletions src/creatures/creature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1788,8 +1788,7 @@ const phmap::parallel_flat_hash_set<std::shared_ptr<Zone>> Creature::getZones()
return Zone::getZones(getPosition());
}

void Creature::setIcon(CreatureIcon icon) {
creatureIcon = icon;
void Creature::iconChanged() const {
if (!tile) {
return;
}
Expand Down
38 changes: 32 additions & 6 deletions src/creatures/creature.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -222,16 +222,41 @@ class Creature : virtual public Thing {
varBuffs[buff] += modifier;
}

virtual CreatureIcon getIcon() const {
return creatureIcon;
virtual const std::vector<CreatureIcon> getIcons() const {
std::vector<CreatureIcon> icons;
icons.reserve(creatureIcons.size());
for (const auto &[_, icon] : creatureIcons) {
if (icon.isSet()) {
icons.push_back(icon);
}
}
return icons;
}

virtual CreatureIcon getIcon(const std::string &key) const {
if (!creatureIcons.contains(key)) {
return CreatureIcon();
}
return creatureIcons.at(key);
}

void setIcon(CreatureIcon icon);
void setIcon(const std::string &key, CreatureIcon icon) {
creatureIcons[key] = icon;
iconChanged();
}

void removeIcon(const std::string &key) {
creatureIcons.erase(key);
iconChanged();
}

void clearIcon() {
setIcon(CreatureIcon());
void clearIcons() {
creatureIcons.clear();
iconChanged();
}

void iconChanged() const;

const Outfit_t getCurrentOutfit() const {
return currentOutfit;
}
Expand Down Expand Up @@ -726,7 +751,8 @@ class Creature : virtual public Thing {

uint8_t wheelOfDestinyDrainBodyDebuff = 0;

CreatureIcon creatureIcon = CreatureIcon();
// use map here instead of phmap to keep the keys in a predictable order
std::map<std::string, CreatureIcon> creatureIcons = {};

// creature script events
bool hasEventRegistered(CreatureEventType_t event) const {
Expand Down
6 changes: 3 additions & 3 deletions src/creatures/monsters/monster.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2122,12 +2122,12 @@ void Monster::configureForgeSystem() {

if (monsterForgeClassification == ForgeClassifications_t::FORGE_FIENDISH_MONSTER) {
setForgeStack(15);
setIcon(CreatureIcon(CreatureIconModifications_t::Fiendish, 0 /* don't show stacks on fiends */));
setIcon("forge", CreatureIcon(CreatureIconModifications_t::Fiendish, 0 /* don't show stacks on fiends */));
g_game().updateCreatureIcon(this);
} else if (monsterForgeClassification == ForgeClassifications_t::FORGE_INFLUENCED_MONSTER) {
auto stack = static_cast<uint16_t>(normal_random(1, 5));
setForgeStack(stack);
setIcon(CreatureIcon(CreatureIconModifications_t::Influenced, stack));
setIcon("forge", CreatureIcon(CreatureIconModifications_t::Influenced, stack));
g_game().updateCreatureIcon(this);
}

Expand All @@ -2153,7 +2153,7 @@ void Monster::clearFiendishStatus() {
health = mType->info.health * mType->getHealthMultiplier();
healthMax = mType->info.healthMax * mType->getHealthMultiplier();

clearIcon();
removeIcon("forge");
g_game().updateCreatureIcon(this);
g_game().sendUpdateCreature(this);
}
Expand Down
15 changes: 8 additions & 7 deletions src/creatures/monsters/monster.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -160,18 +160,19 @@ class Monster final : public Creature {
return challengeFocusDuration > 0;
}

CreatureIcon getIcon() const override {
if (creatureIcon.isSet()) {
return creatureIcon;
const std::vector<CreatureIcon> getIcons() const override {
const auto creatureIcons = Creature::getIcons();
if (!creatureIcons.empty()) {
return creatureIcons;
}
if (challengeMeleeDuration > 0 && mType->info.targetDistance > targetDistance) {
return CreatureIcon(CreatureIconModifications_t::TurnedMelee);
return { CreatureIcon(CreatureIconModifications_t::TurnedMelee) };
} else if (varBuffs[BUFF_DAMAGERECEIVED] > 100) {
return CreatureIcon(CreatureIconModifications_t::HigherDamageReceived);
return { CreatureIcon(CreatureIconModifications_t::HigherDamageReceived) };
} else if (varBuffs[BUFF_DAMAGEDEALT] < 100) {
return CreatureIcon(CreatureIconModifications_t::LowerDamageDealt);
return { CreatureIcon(CreatureIconModifications_t::LowerDamageDealt) };
}
return CreatureIcon();
return {};
}

void setNormalCreatureLight() override;
Expand Down
4 changes: 2 additions & 2 deletions src/creatures/players/player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7569,9 +7569,9 @@ void Player::setHazardSystemPoints(int32_t count) {
addStorageValue(STORAGEVALUE_HAZARDCOUNT, std::max<int32_t>(0, std::min<int32_t>(0xFFFF, count)), true);
reloadHazardSystemPointsCounter = true;
if (count > 0) {
setIcon(CreatureIcon(CreatureIconQuests_t::Hazard, count));
setIcon("hazard", CreatureIcon(CreatureIconQuests_t::Hazard, count));
} else {
clearIcon();
removeIcon("hazard");
}
}

Expand Down
81 changes: 65 additions & 16 deletions src/lua/functions/creatures/creature_functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -982,46 +982,95 @@ int CreatureFunctions::luaCreatureGetZones(lua_State* L) {
}

int CreatureFunctions::luaCreatureSetIcon(lua_State* L) {
// creature:setIcon(category, icon[, number])
Creature* creature = getUserdata<Creature>(L, 1);
// creature:setIcon(key, category, icon[, number])
auto creature = getUserdata<Creature>(L, 1);
if (!creature) {
reportErrorFunc(getErrorDesc(LUA_ERROR_CREATURE_NOT_FOUND));
pushBoolean(L, false);
return 1;
}
auto category = getNumber<CreatureIconCategory_t>(L, 2);
auto count = getNumber<uint16_t>(L, 4, 0);
const auto key = getString(L, 2);
auto category = getNumber<CreatureIconCategory_t>(L, 3);
auto count = getNumber<uint16_t>(L, 5, 0);
CreatureIcon creatureIcon;
if (category == CreatureIconCategory_t::Modifications) {
auto icon = getNumber<CreatureIconModifications_t>(L, 3);
auto icon = getNumber<CreatureIconModifications_t>(L, 5);
creatureIcon = CreatureIcon(icon, count);
} else {
auto icon = getNumber<CreatureIconQuests_t>(L, 3);
auto icon = getNumber<CreatureIconQuests_t>(L, 4);
creatureIcon = CreatureIcon(icon, count);
}

creature->setIcon(creatureIcon);
creature->setIcon(key, creatureIcon);
pushBoolean(L, true);
return 1;
}

int CreatureFunctions::luaCreatureGetIcon(lua_State* L) {
// creature:getIcon()
Creature* creature = getUserdata<Creature>(L, 1);
int CreatureFunctions::luaCreatureGetIcons(lua_State* L) {
// creature:getIcons()
const auto creature = getUserdata<Creature>(L, 1);
if (!creature) {
reportErrorFunc(getErrorDesc(LUA_ERROR_CREATURE_NOT_FOUND));
pushBoolean(L, false);
return 1;
}

auto creatureIcon = creature->getIcon();
if (creatureIcon.isNone()) {
auto icons = creature->getIcons();
lua_createtable(L, static_cast<int>(icons.size()), 0);
for (auto &icon : icons) {
lua_createtable(L, 0, 3);
setField(L, "category", static_cast<uint8_t>(icon.category));
setField(L, "icon", icon.serialize());
setField(L, "count", icon.count);
lua_rawseti(L, -2, static_cast<int>(icon.category));
}
return 1;
}

int CreatureFunctions::luaCreatureGetIcon(lua_State* L) {
// creature:getIcon(key)
const auto creature = getUserdata<Creature>(L, 1);
if (!creature) {
reportErrorFunc(getErrorDesc(LUA_ERROR_CREATURE_NOT_FOUND));
pushBoolean(L, false);
return 1;
}
const auto key = getString(L, 2);
auto icon = creature->getIcon(key);
if (icon.isSet()) {
lua_createtable(L, 0, 3);
setField(L, "category", static_cast<uint8_t>(icon.category));
setField(L, "icon", icon.serialize());
setField(L, "count", icon.count);
} else {
lua_pushnil(L);
}
return 1;
}

int CreatureFunctions::luaCreatureRemoveIcon(lua_State* L) {
// creature:removeIcon(key)
auto creature = getUserdata<Creature>(L, 1);
if (!creature) {
reportErrorFunc(getErrorDesc(LUA_ERROR_CREATURE_NOT_FOUND));
pushBoolean(L, false);
return 1;
}
lua_createtable(L, 0, 3);
setField(L, "category", static_cast<uint8_t>(creatureIcon.category));
setField(L, "icon", creatureIcon.serialize());
setField(L, "count", creatureIcon.count);
const auto key = getString(L, 2);
creature->removeIcon(key);
pushBoolean(L, true);
return 1;
}

int CreatureFunctions::luaCreatureClearIcons(lua_State* L) {
// creature:clearIcons()
auto creature = getUserdata<Creature>(L, 1);
if (!creature) {
reportErrorFunc(getErrorDesc(LUA_ERROR_CREATURE_NOT_FOUND));
pushBoolean(L, false);
return 1;
}
creature->clearIcons();
pushBoolean(L, true);
return 1;
}
6 changes: 6 additions & 0 deletions src/lua/functions/creatures/creature_functions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ class CreatureFunctions final : LuaScriptInterface {
registerMethod(L, "Creature", "getZones", CreatureFunctions::luaCreatureGetZones);
registerMethod(L, "Creature", "setIcon", CreatureFunctions::luaCreatureSetIcon);
registerMethod(L, "Creature", "getIcon", CreatureFunctions::luaCreatureGetIcon);
registerMethod(L, "Creature", "getIcons", CreatureFunctions::luaCreatureGetIcons);
registerMethod(L, "Creature", "removeIcon", CreatureFunctions::luaCreatureRemoveIcon);
registerMethod(L, "Creature", "clearIcons", CreatureFunctions::luaCreatureClearIcons);

CombatFunctions::init(L);
MonsterFunctions::init(L);
Expand Down Expand Up @@ -178,5 +181,8 @@ class CreatureFunctions final : LuaScriptInterface {
static int luaCreatureGetZones(lua_State* L);

static int luaCreatureSetIcon(lua_State* L);
static int luaCreatureGetIcons(lua_State* L);
static int luaCreatureGetIcon(lua_State* L);
static int luaCreatureRemoveIcon(lua_State* L);
static int luaCreatureClearIcons(lua_State* L);
};
7 changes: 1 addition & 6 deletions src/lua/functions/creatures/monster/monster_functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -470,12 +470,7 @@ int MonsterFunctions::luaMonsterSetForgeStack(lua_State* L) {
auto icon = stack < 15
? CreatureIconModifications_t::Influenced
: CreatureIconModifications_t::Fiendish;
monster->setIcon(CreatureIcon(
icon,
icon == CreatureIconModifications_t::Influenced
? static_cast<uint8_t>(stack)
: 0 // don't show the stack for fiendish
));
monster->setIcon("forge", CreatureIcon(icon, icon == CreatureIconModifications_t::Influenced ? static_cast<uint8_t>(stack) : 0));
g_game().updateCreatureIcon(monster);
g_game().sendUpdateCreature(monster);
return 1;
Expand Down
12 changes: 6 additions & 6 deletions src/server/network/protocol/protocolgame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3175,12 +3175,12 @@ void ProtocolGame::addCreatureIcon(NetworkMessage &msg, const Creature* creature
return;
}

const auto icon = creature->getIcon();
// 0 = no icon, 1 = we'll send an icon
if (icon.isNone()) {
msg.addByte(0);
} else {
msg.addByte(1);
const auto icons = creature->getIcons();
// client only supports 3 icons, otherwise it will crash
const auto count = icons.size() > 3 ? 3 : icons.size();
msg.addByte(count);
for (uint8_t i = 0; i < count; ++i) {
const auto &icon = icons[i];
msg.addByte(icon.serialize());
msg.addByte(static_cast<uint8_t>(icon.category));
msg.add<uint16_t>(icon.count);
Expand Down
Loading