Joyson wrote: Thu Jul 23, 2026 10:30 pm
you would have to program into the game experience gain on crafting/gathering which is not currently in the game, and then turn it off unless you have the challenge active, as well as creating a spell effect like [Expansive Mind] that is granted at character creation upon choosing the challenge that turns off exp for certain quests but not others. this would require serious programming knowledge outside of my expertise. the katana challenge for example is simply a spell effect on character creation that lists weapon ids that you can equip. very simple
I am a coding layman (no coding jobs, no professional coding experiences, very little personal experience), however I utilize AI at work and have extensive experience building code-based projects and apps. I was able to generate the below code by simply inputting the original idea seen above into the prompt window and siloing out the experience gain on skill up segment of the idea (it probably won't look right on a forum post though). In my workflows, I've successfully used these sorts of generate code outputs as a baseline and with Claude-directed feedback & prompting I have been able to produce the apps and projects that I mentioned. I've seen very little error in these projects and apps with my direction and oversight, with full on troubleshooting and after-creation revisions. Surely the expertise needed can be self-guided to some extent if I am able to do it as a layman.
Would it take time and resources to build this out? --> Yes
Would it be a challenge to incorporate it into the server? --> Yes
Is it possible to create and implement a challenge like this? --> Yes (based on my assessment)
Sample Claude-coded experience gain on skill up code
/*
* mod_gathering_xp.cpp
* ---------------------
* Standalone AzerothCore module: awards XP for successful gathering
* (Mining, Herbalism, Fishing) and successful crafting (Cooking, First Aid,
* and other tradeskills), independent of any kill.
*
* This is deliberately self-contained — no dependency on any other custom
* module — so it can be dropped into a modules/ folder and compiled on its
* own, or read as a standalone example.
*
* Two hooks do the real work:
*
* OnLootItem -> fires whenever a player loots an item from anything.
* We only award XP when the loot source is a GameObject
* (a mining vein, an herb node, a fishing bobber) rather
* than a creature corpse, and only when the item's
* required skill is a gathering skill. This is what keeps
* "loot from a kill" out of the XP path.
*
* OnCreateItem -> fires when a player crafts an item via a profession
* spell (cooking, first aid, tailoring, etc.). XP is
* awarded with diminishing returns the more times that
* specific recipe has been crafted, so people can't just
* spam-craft one cheap item to 60.
*
* Tuning constants are grouped at the top — the formulas here are simple
* starting points, not balanced numbers. You will want to adjust them
* against your server's leveling curve.
*/
#include "ScriptMgr.h"
#include "Player.h"
#include "Item.h"
#include "ItemTemplate.h"
#include "ObjectMgr.h"
#include "SharedDefines.h"
#include "DatabaseEnv.h"
#include <unordered_map>
#include <unordered_set>
// ---------------------------------------------------------------------
// Tuning
// ---------------------------------------------------------------------
namespace GatheringXP
{
// Fraction of "XP needed for next level" awarded per successful gather.
constexpr float GATHER_XP_PCT_OF_LEVEL = 0.004f; // 0.4%
// Fraction of "XP needed for next level" awarded per craft, before
// the diminishing-returns falloff below is applied.
constexpr float CRAFT_XP_PCT_OF_LEVEL = 0.006f; // 0.6%
// Each repeat craft of the same recipe multiplies the award by this,
// down to a floor, so the 50th batch of Cooked Crab Meat isn't
// still worth full XP.
constexpr float CRAFT_REPEAT_DECAY = 0.85f;
constexpr float CRAFT_MIN_MULTIPLIER = 0.15f;
// Skills that count as "gathering" for the OnLootItem hook.
inline bool IsGatheringSkill(uint32 skill)
{
return skill == SKILL_MINING || skill == SKILL_HERBALISM || skill == SKILL_FISHING;
}
// Skills that count as "crafting" for the OnCreateItem hook.
// (Cooking / First Aid explicitly called out in the design doc;
// the rest of the primary/secondary professions are included too
// since the same "reward the craft, not the kill" logic applies.)
inline bool IsCraftingSkill(uint32 skill)
{
switch (skill)
{
case SKILL_COOKING:
case SKILL_FIRST_AID:
case SKILL_ALCHEMY:
case SKILL_BLACKSMITHING:
case SKILL_LEATHERWORKING:
case SKILL_TAILORING:
case SKILL_ENGINEERING:
case SKILL_JEWELCRAFTING:
case SKILL_ENCHANTING:
return true;
default:
return false;
}
}
}
// ---------------------------------------------------------------------
// Repeat-craft tracking: guid -> (item entry -> times crafted)
// In-memory only in this example; see NOTES at the bottom for persisting it.
// ---------------------------------------------------------------------
static std::unordered_map<uint32, std::unordered_map<uint32, uint32>> CraftCounts;
static uint32 ComputeGatherXP(Player* player)
{
uint32 xpToNextLevel = player->GetXPForNextLevel();
return static_cast<uint32>(xpToNextLevel * GatheringXP::GATHER_XP_PCT_OF_LEVEL);
}
static uint32 ComputeCraftXP(Player* player, uint32 itemEntry)
{
uint32 xpToNextLevel = player->GetXPForNextLevel();
float base = xpToNextLevel * GatheringXP::CRAFT_XP_PCT_OF_LEVEL;
uint32& timesCrafted = CraftCounts[player->GetGUID().GetCounter()][itemEntry];
float multiplier = std::pow(GatheringXP::CRAFT_REPEAT_DECAY, static_cast<float>(timesCrafted));
if (multiplier < GatheringXP::CRAFT_MIN_MULTIPLIER)
multiplier = GatheringXP::CRAFT_MIN_MULTIPLIER;
++timesCrafted;
return static_cast<uint32>(base * multiplier);
}
// ---------------------------------------------------------------------
// PlayerScript
// ---------------------------------------------------------------------
class GatheringXP_PlayerScript : public PlayerScript
{
public:
GatheringXP_PlayerScript() : PlayerScript("GatheringXP_PlayerScript") {}
// Fires on every successful loot pickup, gathering nodes included.
void OnLootItem(Player* player, Item* item, uint32 /*count*/, ObjectGuid lootguid) override
{
if (!item)
return;
// Only GameObject-sourced loot counts as "gathering" — this is what
// excludes drops looted from a creature's corpse after a kill.
if (lootguid.GetTypeId() != TYPEID_GAMEOBJECT)
return;
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(item->GetEntry());
if (!proto)
return;
if (!GatheringXP::IsGatheringSkill(proto->RequiredSkill))
return;
uint32 xp = ComputeGatherXP(player);
if (xp > 0)
player->GiveXP(xp, nullptr);
}
// Fires when a player successfully crafts an item via a profession spell.
void OnCreateItem(Player* player, Item* item, uint32 /*count*/) override
{
if (!item)
return;
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(item->GetEntry());
if (!proto)
return;
if (!GatheringXP::IsCraftingSkill(proto->RequiredSkill))
return;
uint32 xp = ComputeCraftXP(player, item->GetEntry());
if (xp > 0)
player->GiveXP(xp, nullptr);
}
// Clear this player's repeat-craft memory on logout to bound memory use.
// (Trade-off noted in NOTES: this also means repeat-craft decay resets
// every session unless you persist it — see below.)
void OnLogout(Player* player) override
{
CraftCounts.erase(player->GetGUID().GetCounter());
}
};
void Addmod_gathering_xpScripts()
{
new GatheringXP_PlayerScript();
}
/*
* NOTES / known simplifications
* ------------------------------
* 1. Fishing: SKILL_FISHING loot generally comes through a GameObject
* (the fishing bobber), so it's covered by OnLootItem as written. Fishing
* dailies/tournaments referenced in the design doc are just quests at
* that point and get XP through the normal quest-completion path, not
* this module.
*
* 2. Persisting craft-repeat counts: right now CraftCounts is wiped on
* logout, so decay resets each session. To make it permanent, replace
* the in-memory map with a table like:
* CREATE TABLE character_craft_counts (
* guid INT UNSIGNED, item_entry INT UNSIGNED, times_crafted INT UNSIGNED,
* PRIMARY KEY (guid, item_entry)
* );
* and read/write it in OnLogin / OnCreateItem instead of the map.
*
* 3. GetXPForNextLevel()-based percentages mean gathering scales with
* level automatically, but doesn't scale with the *gathered material's*
* level (a level 60 skinning a level 1 critter yields the same XP as
* skinning a level 60 mob). If you want tier-appropriate rewards, key
* the multiplier off proto->RequiredLevel or an item quality check
* instead of a flat percentage.
*
* 4. This module has no on/off toggle of its own. If you're combining it
* with the Pacifist challenge module from before, gate both hooks behind
* that module's IsPacifist(player) check (or make gathering XP universal
* for all players — that's a design choice, not a technical requirement).
*/