Skip to content

feat: flower month & thawing world changes#3991

Open
Paco161315 wants to merge 6 commits into
opentibiabr:mainfrom
Paco161315:feat-flower-month-&-thawing-world-changes
Open

feat: flower month & thawing world changes#3991
Paco161315 wants to merge 6 commits into
opentibiabr:mainfrom
Paco161315:feat-flower-month-&-thawing-world-changes

Conversation

@Paco161315

@Paco161315 Paco161315 commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

1.- Created the watering plants mechanics.
2.- Added Achievements
3.- Updated Rosemarie
4.- World Board Updated
5.- Added new global storage
6.- Deleted Dryads from spawns
7.- Added items and decay logic
8.- Achievements
9.- Dryad Raids (Flower Month)
10.- Ice Flower Spawns (Thawing)

Everything has been debuged and tested at https://tibiatales.com

Summary by CodeRabbit

Release Notes

  • New Features
    • Added new world events: Thawing MiniWorldChange (ice flower mechanic) and Flower Month (Dryad raid with Rosemarie NPC).
    • Introduced ice flower harvesting and seed planting system with item transformation mechanics.
    • Enhanced watering can with improved plant growth progression and multiple plant stages.
    • Updated Dryad monsters with expanded loot drops and new combat abilities.
    • Added three new achievements: Ice Harvester, Preservationist, and Green Thumb.

1.- Created the watering plants mechanics.
2.- Added Achievements
3.- Updated Rosemarie
4.- World Board Updated
5.- Added new global storage
6.- Deleted Dryads from spawns
7.- Added items and decay logic
8.- Achievements
9.- Dryad Raids (Flower Month)
10.- Ice Flower Spawns (Thawing)

Everything has been debuged and tested at https://tibiatales.com using canary/tfs crystal server distribution.
@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 79c2427a-32e2-4078-a71e-f470e036cfe2

📥 Commits

Reviewing files that changed from the base of the PR and between 09eab9a and 97a7bc6.

📒 Files selected for processing (1)
  • data-otservbr-global/monster/fey/dryad.lua

📝 Walkthrough

Walkthrough

Adds a thawing world event and June dryad raid, introduces ice-flower items/seed mechanics, watering-can plant growth, NPC seed trade, map monster spawn rebalancing, and item decay chain adjustments.

Changes

World Changes and Plant Interaction System

Layer / File(s) Summary
World Change Storage and Thawing Event
data-otservbr-global/lib/core/storages.lua, data-otservbr-global/scripts/world_changes/thawing.lua
Adds ThawingMiniWorldChange storage key (67000) and a GlobalEvent that may place ICE_FLOWER items at predefined coordinates on startup.
Flower Month Raid Event
data-otservbr-global/scripts/raids/world_changes/flower_month.lua
Adds FlowerMonth GlobalEvent for June that spawns Rosemarie NPC and schedules repeated Dryad waves across configured areas.
Rosemarie NPC with Dialogue and Seed Trade
data-otservbr-global/npc/rosemarie.lua
Consolidates NPC config, adds voices/keywords, and a topic-based seed exchange that trades 5 seeds (647) for a flowerpot (306) on confirmation.
NPC Integration: Towncryer and World Board
data-otservbr-global/npc/towncryer.lua, data-otservbr-global/scripts/actions/other/world_board.lua
Adds towncryer announcement and world-board communicate entry tied to ThawingMiniWorldChange storage value.
Ice Flower Item Actions
data/scripts/actions/items/ice_flower.lua, data/scripts/actions/items/ice_flower_seeds.lua
Implements ice flower use (13842 → 13843) with seed spawn chance and seed item (13844) behavior that transforms pots or grants achievements.
Item Decay Chain Configuration
data/items/items.xml
Updates multiple flower-pot decay mappings/durations, adds ice-plant items (13842/13843), and reworks winterblossom progression (14030 → 14031 → 14032 → 306).
Plant Growth via Watering Can
data/scripts/actions/tools/watering_can.lua
Adds watering-can (650) logic with plant stage tables, ITEM_ACTION mapping, random growth/drying/wilting outcomes, transforms, decay handling, and achievement tracking.
Seed Movement Event Handler
data/scripts/movements/seeds.lua
Registers MoveEvent for item 306 to handle seed placement (647, 13844) transforming/decaying pots and consuming seeds.
Monster Spawn Rebalancing
data-otservbr-global/world/otservbr-monster.xml, data-otservbr-global/monster/fey/dryad.lua
Rebalances several map clusters (removing Dryad entries, adding Haunted Treeling/Forest Fury/Deer/Emerald Damselfly/Wilting Leaf Golem) and updates Dryad monster corpse, loot, attacks, and defenses.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • dudantas

Poem

🐇 In thawed glades the ice-flowers gleam,
Seeds traded kindly for a pot and dream,
Watering cans coax petals to rise,
Rosemarie smiles beneath June skies,
Dryads dance while the town cryer sings.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title 'feat: flower month & thawing world changes' accurately summarizes the main changes: introduction of flower month mechanics and thawing world changes, which are reflected in the addition of world events, NPC updates, items, and raid/event systems across multiple files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements the Flower Month and Thawing world changes, adding the Rosemarie NPC, dryad raids, ice flower harvesting, and plant watering mechanics. The review feedback highlights several critical issues: the scheduleWave function in flower_month.lua recursively schedules itself indefinitely after June ends; the thawing global storage value is not reset to 0 when the event is inactive; and several scripts lack defensive checks for valid items or send magic effects to virtual container positions (x = 0xffff), which can cause Lua runtime errors or console warnings. Additionally, a duplicate "plant" keyword registration in rosemarie.lua should be removed.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +49 to +55
local function scheduleWave(index)
spawnDryads(WAVES[index])
local next = index < #WAVES and index + 1 or 1
addEvent(function()
scheduleWave(next)
end, WAVE_INTERVAL)
end

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The scheduleWave function recursively schedules itself indefinitely via addEvent even after the month of June ends. Checking the current month inside scheduleWave before rescheduling ensures the event gracefully stops when the month changes.

local function scheduleWave(index)
    local t = os.date("*t")
    if t.month ~= 6 then
        return
    end
    spawnDryads(WAVES[index])
    local next = index < #WAVES and index + 1 or 1
    addEvent(function()
        scheduleWave(next)
    end, WAVE_INTERVAL)
end

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mr. Bot pelase read the full code! LOL

Comment on lines +31 to +46
function thawingMiniWorldChange.onStartup()
local eventChance = math.random(1, 100)
if eventChance <= CHANCE_PERCENT then
for _, pos in ipairs(POSITIONS) do
createThawingItem(pos)
end

Game.setStorageValue(GlobalStorage.WorldBoard.ThawingMiniWorldChange, 1)

local t = os.date("*t")
print(string.format("[%04d-%02d-%02d %02d:%02d:%02d.000] [info] [World Change] Thawing World Change event is active.", t.year, t.month, t.day, t.hour, t.min, t.sec))
end

return true
end

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The global storage value GlobalStorage.WorldBoard.ThawingMiniWorldChange is persistent in the database. If the thawing world change is active on one server startup, but the 10% chance is not met on a subsequent startup, the storage value will remain 1 from the previous run. It must be explicitly reset to 0 when the event is not active.

function thawingMiniWorldChange.onStartup()
    local eventChance = math.random(1, 100)
    if eventChance <= CHANCE_PERCENT then
        for _, pos in ipairs(POSITIONS) do
            createThawingItem(pos)
        end
        
        Game.setStorageValue(GlobalStorage.WorldBoard.ThawingMiniWorldChange, 1)
        
        local t = os.date("*t")
        print(string.format("[%04d-%02d-%02d %02d:%02d:%02d.000] [info] [World Change] Thawing World Change event is active.",
            t.year, t.month, t.day, t.hour, t.min, t.sec))
    else
        Game.setStorageValue(GlobalStorage.WorldBoard.ThawingMiniWorldChange, 0)
    end
    
    return true
end

Comment on lines +42 to +51
function wateringCan.onUse(player, item, fromPosition, target, toPosition, isHotkey)
local targetId = target.itemid
local action = ITEM_ACTION[targetId]
local roll = math.random(100)
local advanced = roll <= CHANCES

if targetId == EMPTY_POT then
player:say("You should plant some seeds first.", TALKTYPE_MONSTER_SAY)
return true
elseif targetId == SEED_POT then

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

If a player uses the watering can on themselves or another creature, target will be a Creature object which does not have an itemid property, causing a Lua runtime error. Adding a defensive check to ensure target is a valid item prevents this crash.

function wateringCan.onUse(player, item, fromPosition, target, toPosition, isHotkey)
    if not target or not target:isItem() then
        return false
    end
    local targetId = target.itemid
    local action   = ITEM_ACTION[targetId]
    local roll     = math.random(100)
    local advanced = roll <= CHANCES
    
    if targetId == EMPTY_POT then
        player:say("You should plant some seeds first.", TALKTYPE_MONSTER_SAY)
        return true
    elseif targetId == SEED_POT then

Comment on lines +3 to +17
function iceFlowerSeeds.onUse(player, item, fromPosition, target, toPosition, isHotkey)
if target.itemid == 14029 then
item:remove(1)
toPosition:sendMagicEffect(CONST_ME_POFF)
if not player:hasAchievement("Preservationist") then
player:addAchievement("Preservationist")
end
elseif target.itemid == 306 then
item:remove(1)
target:transform(14031)
target:decay()
toPosition:sendMagicEffect(CONST_ME_POFF)
end
return true
end

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This action script is missing a defensive check to ensure target is a valid item, which will cause a Lua runtime error if used on a creature or empty space. Additionally, it should return false for unhandled items to allow default engine messages, and check toPosition.x ~= 0xffff before sending magic effects to avoid console warnings when used inside containers.

function iceFlowerSeeds.onUse(player, item, fromPosition, target, toPosition, isHotkey)
    if not target or not target:isItem() then
        return false
    end

    if target.itemid == 14029 then
        item:remove(1)
        if toPosition.x ~= 0xffff then
            toPosition:sendMagicEffect(CONST_ME_POFF)
        end
        if not player:hasAchievement("Preservationist") then
            player:addAchievement("Preservationist")
        end
        return true
    elseif target.itemid == 306 then
        item:remove(1)
        target:transform(14031)
        target:decay()
        if toPosition.x ~= 0xffff then
            toPosition:sendMagicEffect(CONST_ME_POFF)
        end
        return true
    end
    return false
end

Comment on lines +56 to +64
keywordHandler:addKeyword({ "plant" }, StdModule.say, {
npcHandler = npcHandler,
text = {
"You will have to water it regularly, then it will flourish and grow. There are several stages of growth that your plant has to pass ...",
"With good care and luck, it will grow into the next stage until it finally becomes a fully blooming flower. ...",
"Of course plants won't grow in the darkness of a depot box. On the other hand, it won't dry out there either ...",
"So if you know you can't take care of your plant for a longer time, it might be a good idea to store it into your depot box.",
},
})

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The "plant" keyword is registered twice. The first registration (lines 56-64) is redundant and gets overridden by the second registration (lines 79-87), which correctly highlights the {flower} keyword in curly braces. This duplicate registration should be removed.

Comment on lines +130 to +132
toPosition:sendMagicEffect(CONST_ME_LOSEENERGY)
return true
end

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

If a player waters a plant inside their backpack (a very common action), toPosition will be a virtual container position (x = 0xffff). Sending a magic effect to a container position is invalid and will spam warnings in the console. Checking toPosition.x ~= 0xffff prevents this.

    if toPosition.x ~= 0xffff then
        toPosition:sendMagicEffect(CONST_ME_LOSEENERGY)
    end
    return true
end

Comment on lines +3 to +18
function seeds.onAddItem(moveitem, tileitem, position, creature)
if tileitem.itemid == 306 then
if moveitem.itemid == 647 then
tileitem:transform(324)
tileitem:decay()
moveitem:remove(1)
position:sendMagicEffect(CONST_ME_POFF)
elseif moveitem.itemid == 13844 then
tileitem:transform(14031)
tileitem:decay()
moveitem:remove(1)
position:sendMagicEffect(CONST_ME_POFF)
end
end
return true
end

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

If a player drags a seed onto a flowerpot inside a container (like a backpack), position will be a virtual container position (x = 0xffff). Sending a magic effect to a container position is invalid and causes console warnings. Checking position.x ~= 0xffff prevents this.

function seeds.onAddItem(moveitem, tileitem, position, creature)
    if tileitem.itemid == 306 then
        if moveitem.itemid == 647 then
            tileitem:transform(324)
            tileitem:decay()
            moveitem:remove(1)
            if position.x ~= 0xffff then
                position:sendMagicEffect(CONST_ME_POFF)
            end
        elseif moveitem.itemid == 13844 then
            tileitem:transform(14031)
            tileitem:decay()
            moveitem:remove(1)
            if position.x ~= 0xffff then
                position:sendMagicEffect(CONST_ME_POFF)
            end
        end
    end
    return true
end

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🧹 Nitpick comments (2)
data-otservbr-global/scripts/world_changes/thawing.lua (1)

21-27: 💤 Low value

Optional: guard against duplicate flowers on repeated activations.

createThawingItem creates an ICE_FLOWER unconditionally on any tile that exists. If the event re-activates on a later startup without the previous flowers having been harvested/removed, items can stack at the same positions. Consider skipping creation when the position already holds an ICE_FLOWER.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@data-otservbr-global/scripts/world_changes/thawing.lua` around lines 21 - 27,
createThawingItem currently always spawns an ICE_FLOWER on any existing Tile,
causing duplicate flowers on repeated activations; modify createThawingItem to
first inspect the tile for an existing ICE_FLOWER (e.g. use
Tile(pos):getItemById(ICE_FLOWER) or iterate Tile(pos):getItems() and compare
item:getId() to ICE_FLOWER) and only call Game.createItem(ICE_FLOWER, 1, pos)
when no ICE_FLOWER is present, otherwise return false or the found item.
data/scripts/actions/tools/watering_can.lua (1)

74-129: ⚖️ Poor tradeoff

Optional: collapse the repetitive growth branches.

The drying1, drying2, wilting, winterblossom_grow, and advance branches all follow the same shape (say message → transform to success/fail id → decay()), differing only in target ids and the achievement hook. Encoding growMsg/failMsg/successId/failId/awardAchievement in each ITEM_ACTION entry would let a single block handle all of them and reduce drift risk.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@data/scripts/actions/tools/watering_can.lua` around lines 74 - 129, The
repeated branches for action.type values
("drying1","drying2","wilting","winterblossom_grow","advance") all perform the
same pattern (say message → transform to success/fail id → target:decay(),
sometimes call player:addAchievementProgress or scan PLANTS), so refactor into
one unified handler: add per-action metadata fields to the ITEM_ACTION entries
(e.g. growMsg, failMsg, successId, failId, awardAchievement/achievementAmount),
then replace the separate elseif blocks with a single block that checks
action.type, uses action.successId/action.failId (or resolves random picks like
STAGE1_POOL when needed) to call target:transform, uses
action.growMsg/action.failMsg for player:say, and conditionally calls
player:addAchievementProgress or the PLANTS scan when action.awardAchievement is
set; ensure existing symbols (player:say, target:transform, target:decay,
player:addAchievementProgress, STAGE1_POOL, SEED_POT, PLANTS) are used and
preserved.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@data-otservbr-global/npc/rosemarie.lua`:
- Around line 90-91: Player(creature) can be nil; after creating local player =
Player(creature) add a nil guard before calling player:getId() (e.g., if not
player then return false or exit the event handler) so you never invoke
player:getId() on nil; update any downstream logic that assumes playerId to
early-return or skip processing when player is nil to avoid crashes.
- Around line 104-107: The exchange currently assumes removeItem/addItem
succeed; update the logic to validate both operations: call
player:removeItem(647, 5) and check its return value before proceeding, then
call player:addItem(306, 1) and check its return value; only call npcHandler:say
success text when both return true; if removeItem succeeds but addItem fails,
attempt to restore the originals by calling player:addItem(647, 5) (and handle
that result/log failure) and send a failure message instead of the success text.
Ensure you reference the existing functions player:getItemCount,
player:removeItem, player:addItem and npcHandler:say in the updated flow.
- Around line 79-87: There is a duplicate registration of the "plant" keyword
via keywordHandler:addKeyword that re-defines the same response and overrides
the earlier one; remove the redundant second block (the
keywordHandler:addKeyword call that uses npcHandler and the identical text
array) so only the first "plant" registration remains, ensuring npcHandler and
StdModule.say usage from the original definition are preserved.

In `@data-otservbr-global/scripts/raids/world_changes/flower_month.lua`:
- Around line 49-55: scheduleWave currently reschedules itself unconditionally
causing infinite events; modify scheduleWave (which calls spawnDryads,
references WAVES and WAVE_INTERVAL) to check the current month before calling
addEvent—retrieve the current month (e.g. via os.date("*t").month) and only
schedule the next wave if the month equals June (6); if not June, do not call
addEvent (optionally perform any cleanup) so waves stop after June ends.
- Around line 42-43: The spawn counter is incremented unconditionally even if
Game.createMonster fails (it can return nil); update the block around
Game.createMonster("Dryad", pos, false, true) to check the returned value and
only increment spawned when a valid monster object is returned (and optionally
log or warn when creation fails) so spawned correctly reflects actual Dryads
spawned toward DRYADS_PER_WAVE.
- Line 64: Game.createNpc("Rosemarie", ROSEMARIE_POS, true) is called without
checking its return value; update the event code (the call to Game.createNpc) to
capture the return (e.g., local npc = Game.createNpc(...)) and validate it
before proceeding: if nil or false, log an error via the server logger (or game
logger) including the NPC name and ROSEMARIE_POS and abort or cleanly handle the
event (do not continue as if the NPC exists). Ensure you reference the returned
npc variable in subsequent logic (or bail out) so blocked spawns are detected
and reported.

In `@data-otservbr-global/scripts/world_changes/thawing.lua`:
- Around line 31-45: The code in thawingMiniWorldChange.onStartup currently only
sets GlobalStorage.WorldBoard.ThawingMiniWorldChange = 1 when the random roll
succeeds, leaving the storage stuck on 1 across restarts; update onStartup so
that after the roll it explicitly sets
Game.setStorageValue(GlobalStorage.WorldBoard.ThawingMiniWorldChange, 1) when
activating and sets
Game.setStorageValue(GlobalStorage.WorldBoard.ThawingMiniWorldChange, 0) (or
clears it) when the event does not activate, keeping the existing
createThawingItem(POSITIONS) logic and CHANCE_PERCENT check intact; this ensures
towncryer/world_board >0 checks reflect the real active state.

In `@data/items/items.xml`:
- Around line 31578-31581: The harvested ice flower item (id="13843") currently
has an attribute decayTo="13842" which causes it to regenerate into the
unharvested ice flower; remove that regeneration by either deleting the decayTo
attribute or changing it to decayTo="0" so the harvested item disappears after
duration expires; edit the <item id="13843"> element (and its <attribute
key="decayTo">) to implement the chosen behavior while keeping the duration
attribute intact.

---

Nitpick comments:
In `@data-otservbr-global/scripts/world_changes/thawing.lua`:
- Around line 21-27: createThawingItem currently always spawns an ICE_FLOWER on
any existing Tile, causing duplicate flowers on repeated activations; modify
createThawingItem to first inspect the tile for an existing ICE_FLOWER (e.g. use
Tile(pos):getItemById(ICE_FLOWER) or iterate Tile(pos):getItems() and compare
item:getId() to ICE_FLOWER) and only call Game.createItem(ICE_FLOWER, 1, pos)
when no ICE_FLOWER is present, otherwise return false or the found item.

In `@data/scripts/actions/tools/watering_can.lua`:
- Around line 74-129: The repeated branches for action.type values
("drying1","drying2","wilting","winterblossom_grow","advance") all perform the
same pattern (say message → transform to success/fail id → target:decay(),
sometimes call player:addAchievementProgress or scan PLANTS), so refactor into
one unified handler: add per-action metadata fields to the ITEM_ACTION entries
(e.g. growMsg, failMsg, successId, failId, awardAchievement/achievementAmount),
then replace the separate elseif blocks with a single block that checks
action.type, uses action.successId/action.failId (or resolves random picks like
STAGE1_POOL when needed) to call target:transform, uses
action.growMsg/action.failMsg for player:say, and conditionally calls
player:addAchievementProgress or the PLANTS scan when action.awardAchievement is
set; ensure existing symbols (player:say, target:transform, target:decay,
player:addAchievementProgress, STAGE1_POOL, SEED_POT, PLANTS) are used and
preserved.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a060c007-604c-4565-8700-8340da13731f

📥 Commits

Reviewing files that changed from the base of the PR and between bd2601f and 09eab9a.

📒 Files selected for processing (12)
  • data-otservbr-global/lib/core/storages.lua
  • data-otservbr-global/npc/rosemarie.lua
  • data-otservbr-global/npc/towncryer.lua
  • data-otservbr-global/scripts/actions/other/world_board.lua
  • data-otservbr-global/scripts/raids/world_changes/flower_month.lua
  • data-otservbr-global/scripts/world_changes/thawing.lua
  • data-otservbr-global/world/otservbr-monster.xml
  • data/items/items.xml
  • data/scripts/actions/items/ice_flower.lua
  • data/scripts/actions/items/ice_flower_seeds.lua
  • data/scripts/actions/tools/watering_can.lua
  • data/scripts/movements/seeds.lua
💤 Files with no reviewable changes (1)
  • data-otservbr-global/world/otservbr-monster.xml

Comment on lines +79 to +87
keywordHandler:addKeyword({ "plant" }, StdModule.say, {
npcHandler = npcHandler,
text = {
"You will have to water it regularly, then it will flourish and grow. There are several stages of growth that your plant has to pass ...",
"With good care and luck, it will grow into the next stage until it finally becomes a fully blooming {flower}. ...",
"Of course plants won't grow in the darkness of a depot box. On the other hand, it won't dry out there either ...",
"So if you know you can't take care of your plant for a longer time, it might be a good idea to store it into your depot box.",
},
})

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Remove duplicate "plant" keyword definition.

The "plant" keyword is already defined at lines 56-64. This second definition at lines 79-87 is identical and will override the first registration, causing unnecessary processing.

♻️ Proposed fix to remove duplicate
 keywordHandler:addKeyword({ "how are you" }, StdModule.say, { npcHandler = npcHandler, text = "Well, how do I look? Beautiful isn't?" })
-keywordHandler:addKeyword({ "plant" }, StdModule.say, {
-	npcHandler = npcHandler,
-	text = {
-		"You will have to water it regularly, then it will flourish and grow. There are several stages of growth that your plant has to pass ...",
-		"With good care and luck, it will grow into the next stage until it finally becomes a fully blooming {flower}. ...",
-		"Of course plants won't grow in the darkness of a depot box. On the other hand, it won't dry out there either ...",
-		"So if you know you can't take care of your plant for a longer time, it might be a good idea to store it into your depot box.",
-	},
-})

 local function creatureSayCallback(npc, creature, type, message)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
keywordHandler:addKeyword({ "plant" }, StdModule.say, {
npcHandler = npcHandler,
text = {
"You will have to water it regularly, then it will flourish and grow. There are several stages of growth that your plant has to pass ...",
"With good care and luck, it will grow into the next stage until it finally becomes a fully blooming {flower}. ...",
"Of course plants won't grow in the darkness of a depot box. On the other hand, it won't dry out there either ...",
"So if you know you can't take care of your plant for a longer time, it might be a good idea to store it into your depot box.",
},
})
keywordHandler:addKeyword({ "how are you" }, StdModule.say, { npcHandler = npcHandler, text = "Well, how do I look? Beautiful isn't?" })
local function creatureSayCallback(npc, creature, type, message)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@data-otservbr-global/npc/rosemarie.lua` around lines 79 - 87, There is a
duplicate registration of the "plant" keyword via keywordHandler:addKeyword that
re-defines the same response and overrides the earlier one; remove the redundant
second block (the keywordHandler:addKeyword call that uses npcHandler and the
identical text array) so only the first "plant" registration remains, ensuring
npcHandler and StdModule.say usage from the original definition are preserved.

Comment on lines +90 to +91
local player = Player(creature)
local playerId = player:getId()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Critical: Add nil check for Player constructor.

Player(creature) can return nil if the creature is not a valid player or has disconnected. Calling getId() on nil will crash the script.

🐛 Proposed fix to add nil guard
 local function creatureSayCallback(npc, creature, type, message)
 	local player = Player(creature)
+	if not player then
+		return false
+	end
 	local playerId = player:getId()

 	if not npcHandler:checkInteraction(npc, creature) then
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@data-otservbr-global/npc/rosemarie.lua` around lines 90 - 91,
Player(creature) can be nil; after creating local player = Player(creature) add
a nil guard before calling player:getId() (e.g., if not player then return false
or exit the event handler) so you never invoke player:getId() on nil; update any
downstream logic that assumes playerId to early-return or skip processing when
player is nil to avoid crashes.

Comment on lines +104 to +107
if player:getItemCount(647) >= 5 then
player:removeItem(647, 5)
player:addItem(306, 1)
npcHandler:say("Here, I planted this little baby in a flowerpot for you. Don't forget to water it regularly - buy a watering can somewhere!", npc, creature)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Validate item transaction success.

Both removeItem and addItem can fail (e.g., if inventory state changes between the count check and removal). The NPC should verify the transaction succeeded before confirming the exchange.

🛡️ Proposed fix to check transaction results
 	elseif MsgContains(message, "yes") and npcHandler:getTopic(playerId) == 1 then
 		if player:getItemCount(647) >= 5 then
-			player:removeItem(647, 5)
-			player:addItem(306, 1)
-			npcHandler:say("Here, I planted this little baby in a flowerpot for you. Don't forget to water it regularly - buy a watering can somewhere!", npc, creature)
+			if player:removeItem(647, 5) then
+				if player:addItem(306, 1) then
+					npcHandler:say("Here, I planted this little baby in a flowerpot for you. Don't forget to water it regularly - buy a watering can somewhere!", npc, creature)
+				else
+					player:addItem(647, 5) -- Refund seeds if flowerpot can't be added
+					npcHandler:say("I couldn't give you the flowerpot. Your inventory might be full.", npc, creature)
+				end
+			else
+				npcHandler:say("You don't have enough seeds.", npc, creature)
+			end
 		else
 			npcHandler:say("You don't have enough seeds.", npc, creature)
 		end
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@data-otservbr-global/npc/rosemarie.lua` around lines 104 - 107, The exchange
currently assumes removeItem/addItem succeed; update the logic to validate both
operations: call player:removeItem(647, 5) and check its return value before
proceeding, then call player:addItem(306, 1) and check its return value; only
call npcHandler:say success text when both return true; if removeItem succeeds
but addItem fails, attempt to restore the originals by calling
player:addItem(647, 5) (and handle that result/log failure) and send a failure
message instead of the success text. Ensure you reference the existing functions
player:getItemCount, player:removeItem, player:addItem and npcHandler:say in the
updated flow.

Comment on lines +42 to +43
Game.createMonster("Dryad", pos, false, true)
spawned = spawned + 1

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Verify monster creation succeeded before incrementing spawn count.

Game.createMonster can return nil if the monster type doesn't exist or spawn fails. Currently, the counter increments regardless, potentially causing fewer than DRYADS_PER_WAVE monsters to spawn while reporting success.

🛡️ Proposed fix to validate monster creation
 		local tile = Tile(pos)
 		if tile and tile:isWalkable() then
-			Game.createMonster("Dryad", pos, false, true)
-			spawned = spawned + 1
+			if Game.createMonster("Dryad", pos, false, true) then
+				spawned = spawned + 1
+			end
 		end
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@data-otservbr-global/scripts/raids/world_changes/flower_month.lua` around
lines 42 - 43, The spawn counter is incremented unconditionally even if
Game.createMonster fails (it can return nil); update the block around
Game.createMonster("Dryad", pos, false, true) to check the returned value and
only increment spawned when a valid monster object is returned (and optionally
log or warn when creation fails) so spawned correctly reflects actual Dryads
spawned toward DRYADS_PER_WAVE.

Comment on lines +49 to +55
local function scheduleWave(index)
spawnDryads(WAVES[index])
local next = index < #WAVES and index + 1 or 1
addEvent(function()
scheduleWave(next)
end, WAVE_INTERVAL)
end

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Critical: Infinite event scheduling beyond June.

The scheduleWave function recursively schedules itself indefinitely without checking if the current month is still June. Once started, waves will continue spawning every 15 minutes forever, even after June ends, causing a memory leak and unintended behavior.

🐛 Proposed fix to add month check before rescheduling
 local function scheduleWave(index)
+	local t = os.date("*t")
+	if t.month ~= 6 then
+		return
+	end
+
 	spawnDryads(WAVES[index])
 	local next = index < `#WAVES` and index + 1 or 1
 	addEvent(function()
 		scheduleWave(next)
 	end, WAVE_INTERVAL)
 end
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
local function scheduleWave(index)
spawnDryads(WAVES[index])
local next = index < #WAVES and index + 1 or 1
addEvent(function()
scheduleWave(next)
end, WAVE_INTERVAL)
end
local function scheduleWave(index)
local t = os.date("*t")
if t.month ~= 6 then
return
end
spawnDryads(WAVES[index])
local next = index < `#WAVES` and index + 1 or 1
addEvent(function()
scheduleWave(next)
end, WAVE_INTERVAL)
end
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@data-otservbr-global/scripts/raids/world_changes/flower_month.lua` around
lines 49 - 55, scheduleWave currently reschedules itself unconditionally causing
infinite events; modify scheduleWave (which calls spawnDryads, references WAVES
and WAVE_INTERVAL) to check the current month before calling addEvent—retrieve
the current month (e.g. via os.date("*t").month) and only schedule the next wave
if the month equals June (6); if not June, do not call addEvent (optionally
perform any cleanup) so waves stop after June ends.

local month = t.month

if month == 6 then
Game.createNpc("Rosemarie", ROSEMARIE_POS, true)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Verify Rosemarie NPC is defined and matches the spawn expectation

# Search for NPC definition (excluding this raid file)
rg -n --type=lua 'createNpcType.*[Rr]osemarie' -g '!**/flower_month.lua'

Repository: opentibiabr/canary

Length of output: 44


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Show relevant lines around the createNpc call in the target file
target="data-otservbr-global/scripts/raids/world_changes/flower_month.lua"
echo "---- $target (around lines 50-90) ----"
nl -ba "$target" | sed -n '40,90p'

# 2) Find all occurrences of Rosemarie across Lua files
echo "---- rg Rosemarie across Lua ----"
rg -n --type=lua --hidden --no-ignore -S 'Rosemarie' .

# 3) Check how Game.createNpc is used elsewhere (any return-value handling)
echo "---- rg Game.createNpc patterns (handling nil) ----"
rg -n --type=lua --hidden --no-ignore -S 'local\s+npc\s*=\s*Game\.createNpc|Game\.createNpc\([^)]*\)\s*or|if\s+not\s+.*Game\.createNpc' .

echo "---- rg Game.createNpc simple usage (context) ----"
rg -n --type=lua --hidden --no-ignore -S 'Game\.createNpc\s*\(' data-otservbr-global/scripts | head -n 50

Repository: opentibiabr/canary

Length of output: 220


🏁 Script executed:

#!/bin/bash
set -euo pipefail

target="data-otservbr-global/scripts/raids/world_changes/flower_month.lua"
echo "---- $target (around lines 50-90) ----"
nl -ba "$target" | sed -n '50,80p'

echo "---- rg Rosemarie across Lua ----"
rg -n --type=lua --hidden --no-ignore -S 'Rosemarie' .

echo "---- rg Game.createNpc patterns (handling nil) ----"
rg -n --type=lua --hidden --no-ignore -S 'local\s+npc\s*=\s*Game\.createNpc|if\s+not\s+.*Game\.createNpc|local\s+\w+\s*=\s*Game\.createNpc\(|Game\.createNpc\([^)]*\)\s*or' data-otservbr-global/scripts || true

echo "---- rg Game.createNpc simple usage (context) ----"
rg -n --type=lua --hidden --no-ignore -S 'Game\.createNpc\s*\(' data-otservbr-global/scripts | head -n 50

Repository: opentibiabr/canary

Length of output: 220


🏁 Script executed:

#!/bin/bash
set -euo pipefail

target="data-otservbr-global/scripts/raids/world_changes/flower_month.lua"
echo "---- $target (around lines 50-90) ----"
nl -ba "$target" | sed -n '50,90p'

echo "---- rg Rosemarie across Lua ----"
rg -n --type=lua --hidden --no-ignore -S 'Rosemarie' .

echo "---- rg Game.createNpc patterns (handling nil) ----"
rg -n --type=lua --hidden --no-ignore -S 'local\s+\w+\s*=\s*Game\.createNpc|if\s+not\s+.*Game\.createNpc|Game\.createNpc\([^)]*\)\s*or' data-otservbr-global/scripts || true

echo "---- rg Game.createNpc simple usage (context) ----"
rg -n --type=lua --hidden --no-ignore -S 'Game\.createNpc\s*\(' data-otservbr-global/scripts | head -n 50

Repository: opentibiabr/canary

Length of output: 220


🏁 Script executed:

#!/bin/bash
set -euo pipefail

target="data-otservbr-global/scripts/raids/world_changes/flower_month.lua"
echo "---- $target (around lines 50-90) ----"
nl -ba "$target" | sed -n '50,90p'

echo "---- rg Rosemarie across Lua ----"
rg -n --type=lua --hidden --no-ignore -S 'Rosemarie' .

echo "---- rg Game.createNpc patterns (handling nil) ----"
rg -n --type=lua --hidden --no-ignore -S 'local\s+\w+\s*=\s*Game\.createNpc|if\s+not\s+.*Game\.createNpc|Game\.createNpc\([^)]*\)\s*or' data-otservbr-global/scripts || true

echo "---- rg Game.createNpc simple usage (context) ----"
rg -n --type=lua --hidden --no-ignore -S 'Game\.createNpc\s*\(' data-otservbr-global/scripts | head -n 50

Repository: opentibiabr/canary

Length of output: 220


🏁 Script executed:

#!/bin/bash
set -euo pipefail

target="data-otservbr-global/scripts/raids/world_changes/flower_month.lua"

echo "---- $target (lines 55-75 with numbers) ----"
python3 - <<PY
path="$target"
start=55
end=75
with open(path,'r',encoding='utf-8',errors='ignore') as f:
    for i,line in enumerate(f, start=1):
        if start <= i <= end:
            print(f"{i:4d}\t{line.rstrip()}")
        if i > end: break
PY

echo "---- occurrences of Rosemarie in repo (Lua) ----"
rg -n --type=lua --hidden --no-ignore 'Rosemarie' .

echo "---- occurrences of Game.createNpc('Rosemarie' ... ) ----"
rg -n --type=lua --hidden --no-ignore 'Game\.createNpc\s*\(\s*["'\'']Rosemarie["'\'']' .

echo "---- sample of Game.createNpc nil-handling patterns in world_changes scripts ----"
rg -n --type=lua --hidden --no-ignore \
  'local\s+\w+\s*=\s*Game\.createNpc|if\s+not\s+\w+\s*then|if\s+not\s+Game\.createNpc|or\s+nil|~= *nil' \
  data-otservbr-global/scripts/world_changes data-otservbr-global/scripts/raids 2>/dev/null || true

echo "---- search: Game.createNpc( usage with surrounding context ----"
rg -n --type=lua --hidden --no-ignore 'Game\.createNpc\s*\(' data-otservbr-global/scripts/raids | head -n 50

Repository: opentibiabr/canary

Length of output: 2551


Check NPC creation return value.

Game.createNpc("Rosemarie", ROSEMARIE_POS, true) (flower_month.lua:64) isn’t checked. If the spawn fails (e.g., blocked position), the event continues without logging—though the NPC’s definition exists at data-otservbr-global/npc/rosemarie.lua.

🛡️ Proposed fix to validate NPC creation
-		Game.createNpc("Rosemarie", ROSEMARIE_POS, true)
+		local npc = Game.createNpc("Rosemarie", ROSEMARIE_POS, true)
+		if not npc then
+			print(string.format("[%04d-%02d-%02d %02d:%02d:%02d.000] [warning] [World Change] Failed to spawn Rosemarie NPC.", t.year, t.month, t.day, t.hour, t.min, t.sec))
+		end
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Game.createNpc("Rosemarie", ROSEMARIE_POS, true)
local npc = Game.createNpc("Rosemarie", ROSEMARIE_POS, true)
if not npc then
print(string.format("[%04d-%02d-%02d %02d:%02d:%02d.000] [warning] [World Change] Failed to spawn Rosemarie NPC.", t.year, t.month, t.day, t.hour, t.min, t.sec))
end
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@data-otservbr-global/scripts/raids/world_changes/flower_month.lua` at line
64, Game.createNpc("Rosemarie", ROSEMARIE_POS, true) is called without checking
its return value; update the event code (the call to Game.createNpc) to capture
the return (e.g., local npc = Game.createNpc(...)) and validate it before
proceeding: if nil or false, log an error via the server logger (or game logger)
including the NPC name and ROSEMARIE_POS and abort or cleanly handle the event
(do not continue as if the NPC exists). Ensure you reference the returned npc
variable in subsequent logic (or bail out) so blocked spawns are detected and
reported.

Comment on lines +31 to +45
function thawingMiniWorldChange.onStartup()
local eventChance = math.random(1, 100)
if eventChance <= CHANCE_PERCENT then
for _, pos in ipairs(POSITIONS) do
createThawingItem(pos)
end

Game.setStorageValue(GlobalStorage.WorldBoard.ThawingMiniWorldChange, 1)

local t = os.date("*t")
print(string.format("[%04d-%02d-%02d %02d:%02d:%02d.000] [info] [World Change] Thawing World Change event is active.", t.year, t.month, t.day, t.hour, t.min, t.sec))
end

return true
end

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Reset the storage when the event does not activate, otherwise the thaw announcements persist forever.

Global storage values are persisted across server restarts. onStartup only ever sets ThawingMiniWorldChange to 1 when the 10% roll succeeds, and the key is not in startupGlobalStorages (storages.lua L3081). So once the event activates, the value stays 1 on every subsequent restart, and both the towncryer voice line and the world board message keep broadcasting the thaw even when the event is no longer active. The downstream > 0 checks in towncryer.lua and world_board.lua are correct; the root cause is the missing reset here.

🐛 Proposed fix: reset on the inactive path
 function thawingMiniWorldChange.onStartup()
 	local eventChance = math.random(1, 100)
 	if eventChance <= CHANCE_PERCENT then
 		for _, pos in ipairs(POSITIONS) do
 			createThawingItem(pos)
 		end
 
 		Game.setStorageValue(GlobalStorage.WorldBoard.ThawingMiniWorldChange, 1)
 
 		local t = os.date("*t")
 		print(string.format("[%04d-%02d-%02d %02d:%02d:%02d.000] [info] [World Change] Thawing World Change event is active.", t.year, t.month, t.day, t.hour, t.min, t.sec))
+	else
+		Game.setStorageValue(GlobalStorage.WorldBoard.ThawingMiniWorldChange, 0)
 	end
 
 	return true
 end
Alternatively, add the key to `startupGlobalStorages` so it is reset on every startup before the roll.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@data-otservbr-global/scripts/world_changes/thawing.lua` around lines 31 - 45,
The code in thawingMiniWorldChange.onStartup currently only sets
GlobalStorage.WorldBoard.ThawingMiniWorldChange = 1 when the random roll
succeeds, leaving the storage stuck on 1 across restarts; update onStartup so
that after the roll it explicitly sets
Game.setStorageValue(GlobalStorage.WorldBoard.ThawingMiniWorldChange, 1) when
activating and sets
Game.setStorageValue(GlobalStorage.WorldBoard.ThawingMiniWorldChange, 0) (or
clears it) when the event does not activate, keeping the existing
createThawingItem(POSITIONS) logic and CHANCE_PERCENT check intact; this ensures
towncryer/world_board >0 checks reflect the real active state.

Comment thread data/items/items.xml
Comment on lines +31578 to +31581
<item id="13843" article="a" name="harvested ice flower">
<attribute key="duration" value="14400" />
<attribute key="decayTo" value="13842" />
</item>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚖️ Poor tradeoff

Logic error: Harvested ice flower decays back to unharvested state.

The harvested ice flower (13843) decays back to ice flower (13842), creating a regeneration loop. Once harvested, the flower should not regrow. This allows a player to harvest the same flower repeatedly.

The decay should either:

  1. Be removed (no decay for harvested state), or
  2. Decay to 0 (item disappears), or
  3. Decay to a "wilted harvested flower" state

Based on the plant mechanics, harvested flowers should likely disappear after the duration expires, not regenerate.

🔄 Proposed fix to prevent regeneration
 <item id="13843" article="a" name="harvested ice flower">
   <attribute key="duration" value="14400" />
-  <attribute key="decayTo" value="13842" />
+  <attribute key="decayTo" value="0" />
 </item>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<item id="13843" article="a" name="harvested ice flower">
<attribute key="duration" value="14400" />
<attribute key="decayTo" value="13842" />
</item>
<item id="13843" article="a" name="harvested ice flower">
<attribute key="duration" value="14400" />
<attribute key="decayTo" value="0" />
</item>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@data/items/items.xml` around lines 31578 - 31581, The harvested ice flower
item (id="13843") currently has an attribute decayTo="13842" which causes it to
regenerate into the unharvested ice flower; remove that regeneration by either
deleting the decayTo attribute or changing it to decayTo="0" so the harvested
item disappears after duration expires; edit the <item id="13843"> element (and
its <attribute key="decayTo">) to implement the chosen behavior while keeping
the duration attribute intact.

@kaleohanopahala

Copy link
Copy Markdown
Contributor

Very Nice 👯

@sonarqubecloud

sonarqubecloud Bot commented Jun 5, 2026

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants