All pastes #1921516 Raw Copy code Copy link Edit

patch awards

public text v1 · immutable
#1921516 ·published 2010-08-20 18:04 UTC
rendered paste body
Index: src/server/adminmanager.h
===================================================================
--- src/server/adminmanager.h	(revision 6170)
+++ src/server/adminmanager.h	(working copy)
@@ -51,6 +51,7 @@
 class psAdminGameEvent;
 class psNPCDialog;
 class psItem;
+class psSkillInfo;
 struct Result;
 class iResultSet;
 class Client;
@@ -75,6 +76,104 @@
     GM_TESTER  = 10
 };
 
+/// Reward Data is an abstract structure for all kinds of rewards
+class psRewardData
+{
+public:
+    /// The different types of rewards that can be assigned
+    enum Reward_Type
+    {
+        REWARD_EXPERIENCE,
+        REWARD_FACTION,
+        REWARD_SKILL,
+        REWARD_MONEY,
+        REWARD_ITEM
+    };
+
+    Reward_Type rewardType; ///< /see Reward_Type
+
+    psRewardData(Reward_Type prewardType);
+
+    virtual bool IsZero(); ///< checks if the reward is zero
+
+    /** Awards the reward to the specified client.
+     * @param gmClientnum ClientID of the issuer
+     * @param target pointer to the Client of the target
+     */
+    //virtual void Award(int gmClientNum, Client* target);
+};
+
+/// RewardDataExperience holds experience reward data
+class psRewardDataExperience : public psRewardData
+{
+public:
+    int expDelta; ///< relative value to adjust exp by
+
+    psRewardDataExperience(int pexpDelta);
+
+    virtual bool IsZero(); ///< checks if the reward is zero
+    
+};
+
+/// RewardDataFaction holds faction reward data
+class psRewardDataFaction : public psRewardData
+{
+public:
+    csString factionName; ///< name of the faction to adjust
+    int factionDelta; ///< relative value to adjust faction by
+
+    psRewardDataFaction(csString pfactionName, int pfactionDelta);
+
+    virtual bool IsZero(); ///< checks if the reward is zero
+};
+
+/// RewardDataSkill holds skill reward data
+class psRewardDataSkill : public psRewardData
+{
+public:
+    /** name of the skill to adjust.
+      * may be "all" to adjust all skills.
+      * may be "copy" to set the values of the target
+      */
+    csString skillName;
+    int skillDelta; ///< value to adjust the skill by/set it to
+    unsigned int skillCap; ///< maximum value the skill should have
+    
+    /** determines whether the value to adjust the skill is relative or not */
+    bool relativeSkill;
+
+    psRewardDataSkill(csString pskillName, int pskillDelta, int pskillCap, bool prelativeSkill);
+
+    virtual bool IsZero(); ///< checks if the reward is zero
+};
+
+/// RewardDataMoney holds money reward data
+class psRewardDataMoney : public psRewardData
+{
+public:
+    csString moneyType; ///< name of the money type to award (circles, hexas, ...)
+    /** relativ amount of money to adjust by
+      * if this is 0, a random value is used
+      */
+    int moneyCount;
+    
+    psRewardDataMoney(csString pmoneyType, int pmoneyCount);
+
+    virtual bool IsZero(); ///< checks if the reward is zero
+};
+
+/// RewardDataItem holds item reward data
+class psRewardDataItem : public psRewardData
+{
+public:   
+    csString itemName; ///< name of the item to award
+    unsigned short stackCount; ///< number of items to award
+    
+    psRewardDataItem(csString pitemName, int pstackCount);
+
+    virtual bool IsZero(); ///< checks if the reward is zero
+};
+
 /** Admin manager that handles GM commands and general game control.
  */
 class AdminManager : public MessageManager
@@ -83,7 +182,7 @@
     AdminManager();
     virtual ~AdminManager();
 
-	virtual void HandleMessage(MsgEntry *pMsg,Client *client) {};
+    virtual void HandleMessage(MsgEntry *pMsg,Client *client) {};
 
     /** This is called when a player does /admin.
       * This builds up the list of commands that are available to the player
@@ -97,12 +196,42 @@
       * admin commands they should have for their level.
       *
       */
-
     void AdminCreateNewNPC(csString& data);
 
-    void AwardExperienceToTarget(int gmClientnum, Client* target, csString recipient, int ppAward);
+    /** This awards a certain amount of exp to the target.
+      * @param gmClientnum ClientID of the issuer
+      * @param target pointer to the Client of the target
+      * @param ppAward amount of exp to award. might be negative to punish instead
+      */
+    void AwardExperienceToTarget(int gmClientnum, Client* target, int ppAward);
+    
+    /** adjusts a faction standing of the target by a given value.
+      * @param gmClientnum ClientID of the issuer
+      * @param target pointer to the Client of the target
+      * @param factionName name of the faction to adjust
+      * @param standingDelta value to adjust the standing by
+      */
     void AdjustFactionStandingOfTarget(int gmClientnum, Client* target, csString factionName, int standingDelta);
 
+    /** adjusts a skill of the target by a given value.
+      * @param gmClientnum ClientID of the issuer
+      * @param target pointer to the Client of the target
+      * @param skill pointer to the info about the skill to adjust
+      * @param value amount to set/adjust by
+      * @param relative determines whether the value is absolute or relative
+      * @param cap if nonzero the skill will be set to a value smaller or equal to the specified one
+      * @return bool FALSE if an error occured
+      */
+    bool ApplySkill(int gmClientNum, Client* target, psSkillInfo* skill, int value, bool relative = false, unsigned int cap = 0);
+    
+    /** universal function to award a target.
+      * @param gmClientNum ClientID of the issuer
+      * @param target pointer to the Client of the target
+      * @param data struct holding the awards to apply
+      * @see psRewardData
+      */
+    void AwardToTarget(int gmClientNum, Client* target, psRewardData& data);
+
     /** Get sector and coordinates of starting point of a map. Returns success. */
     bool GetStartOfMap(int clientnum, const csString & map, iSector * & targetSector,  csVector3 & targetPoint);
     
@@ -128,7 +257,7 @@
         csString type,name; ///< Used by: /location
         csString sourceplayer;
 
-        int value, interval, random;
+        int value, value2, interval, random;
         int rainDrops, density, fade;
         unsigned int mins, hours, days;
         float amt, x, y, z, rot;
@@ -349,6 +478,9 @@
     /** Awards experience to a player, by a GM */
     void AwardExperience(MsgEntry* me, psAdminCmdMessage& msg, AdminCmdData& data, Client* client, Client* target);
 
+    /** Awards something to a player, by a GM */
+    void Award(AdminCmdData& data, Client* client, Client* target);
+
     /** Transfers an item from one client to another */
     void TransferItem(MsgEntry* me, psAdminCmdMessage& msg, AdminCmdData& data, Client* source, Client* target);
 
Index: src/server/adminmanager.cpp
===================================================================
--- src/server/adminmanager.cpp	(revision 6170)
+++ src/server/adminmanager.cpp	(working copy)
@@ -138,7 +138,77 @@
         MessageManager* msgmanager;
 };
 
+psRewardData::psRewardData(Reward_Type prewardType)
+{
+    rewardType = prewardType;
+}
 
+bool psRewardData::IsZero()
+{
+    return true;
+}
+
+psRewardDataExperience::psRewardDataExperience(int pexpDelta) 
+: psRewardData(REWARD_EXPERIENCE)
+{
+    expDelta = pexpDelta;
+}
+
+bool psRewardDataExperience::IsZero()
+{
+    return ( expDelta == 0 );
+}
+
+psRewardDataFaction::psRewardDataFaction(csString pfactionName, int pfactionDelta) 
+: psRewardData(REWARD_FACTION)
+{
+    factionName = pfactionName;
+    factionDelta = pfactionDelta;
+}
+
+bool psRewardDataFaction::IsZero()
+{
+    return (factionName.IsEmpty() || factionDelta == 0);
+}
+
+psRewardDataSkill::psRewardDataSkill(csString pskillName, int pskillDelta, int pskillCap, bool prelativeSkill)
+: psRewardData(REWARD_SKILL)
+{
+    skillName = pskillName;
+    skillDelta = pskillDelta;
+    skillCap = pskillCap;
+    relativeSkill = prelativeSkill;
+}
+
+bool psRewardDataSkill::IsZero()
+{
+    return (skillName.IsEmpty() || skillDelta == 0);
+}
+
+psRewardDataMoney::psRewardDataMoney(csString pmoneyType, int pmoneyCount)
+: psRewardData(REWARD_MONEY)
+{
+    moneyType = pmoneyType;
+    moneyCount = pmoneyCount;
+}
+
+bool psRewardDataMoney::IsZero()
+{
+    return (moneyType.IsEmpty() || moneyCount == 0);
+}
+
+psRewardDataItem::psRewardDataItem(csString pitemName, int pstackCount)
+: psRewardData(REWARD_ITEM)
+{
+    itemName = pitemName;
+    stackCount = pstackCount;
+}
+
+bool psRewardDataItem::IsZero()
+{
+    return (itemName.IsEmpty() || stackCount == 0);
+}
+
 AdminManager::AdminManager()
 {
     clients = psserver->GetNetManager()->GetConnections();
@@ -460,10 +530,77 @@
         player = words[1];
         return true;
     }
-    else if (command == "/awardexp")
+    else if (command == "/award")
     {
+	// use standard targeting
         player = words[1];
-        value = words.GetInt(2);
+
+        if (words.GetCount() < 2)
+        {
+            // invalid arguments
+            help = true;
+            return true;
+        }
+
+        // initialize PODs
+        value = 0;
+        stackCount = 0;
+        value2 = 0;
+        interval = 0;
+        random = 0;
+        density = 0;
+        item.Clear();
+        skill.Clear();
+        type.Clear();
+        name.Clear();
+
+        size_t index = 2;
+        size_t remaining;
+        // doesn't include a check for duplicate award types
+        while (index < words.GetCount())
+        {
+            subCmd = words[index++];
+            remaining = words.GetCount() - index;
+
+            if (subCmd == "exp" && remaining >= 1)
+            {
+                value = words.GetInt(index++);
+            }
+            else if (subCmd == "item" && remaining >= 2)
+            {
+                stackCount = words.GetInt(index++);
+                item = words[index++];
+            }
+            else if (subCmd == "skill" && remaining >= 2)
+            {
+                skill = words[index++];
+
+                // check for relative value
+                if (words[index][0] == '+' || words[index][0] == '-')
+                    insert = true;
+                value2 = words.GetInt(index++);
+
+                // check for optional maximum
+                interval = words.GetInt(index);
+                if (interval)
+                    index++;
+            }
+            else if (subCmd == "money" && remaining >= 2)
+            {
+                type = words[index++];
+                random = words.GetInt(index++);
+            }
+            else if (subCmd == "faction" && remaining >= 2)
+            {
+                name = words[index++];
+                density = words.GetInt(index++);
+            }
+            else // invalid arguments
+            {
+                help = true;
+                break;
+            }
+        }
         return true;
     }
     else if (command == "/giveitem" ||
@@ -946,55 +1083,94 @@
         else if (subCmd == "reward")
         {
             // "/event reward [range # | all | [player_name]] <#> item"
-            int rewardIndex = 3;
-            stackCount = 0;
+            size_t index = 3;
 
-            if (strspn(words[2].GetDataSafe(), "-0123456789") == words[2].Length())
+            if (words[2] == "target")
             {
-                commandMod.Empty();
-                stackCount = words.GetInt(2);
                 rangeSpecifier = INDIVIDUAL;  // expecting a player by target
             }
+            else if (words[2] == "all")
+            {
+                commandMod = "all";
+                rangeSpecifier = ALL;
+            }
+            else if (words[2] == "range")
+            {
+                commandMod = "range";
+                rangeSpecifier = IN_RANGE;
+                range = words.GetFloat(index++);
+                //if (strspn(words[3].GetDataSafe(), "0123456789.") == words[3].Length())
+            }
             else
             {
-                commandMod = words[2];    // 'range' or 'all'
-                range = 0;
-                if (commandMod == "range")
+                rangeSpecifier = INDIVIDUAL;
+                player = words[2];
+            }
+
+            size_t remaining = words.GetCount() - index;
+            if (remaining < 2) // invalid arguments
+            {
+                subCmd = "help";
+                help = true;
+            }
+
+            // initialize PODs
+            value = 0;
+            stackCount = 0;
+            value2 = 0;
+            interval = 0;
+            random = 0;
+            density = 0;
+            item.Clear();
+            skill.Clear();
+            type.Clear();
+            name.Clear();
+
+            // doesn't include a check for duplicate award types
+            while (index < words.GetCount() && !help) // retrieve award types and values
+            {
+                subCmd = words[index++];
+                remaining = words.GetCount() - index;
+                if (subCmd == "exp" && remaining >= 1)
                 {
-                    rangeSpecifier = IN_RANGE;
-                    if (strspn(words[3].GetDataSafe(), "0123456789.") == words[3].Length())
-                    {
-                        range = words.GetFloat(3);
-                        rewardIndex = 4;
-                    }
+                    value = words.GetInt(index++);
                 }
-                else if (commandMod == "all")
+                else if (subCmd == "item" && remaining >= 2)
                 {
-                    rangeSpecifier = ALL;
+                    stackCount = words.GetInt(index++);
+                    item = words[index++];
                 }
-                else
+                else if (subCmd == "skill" && remaining >= 2)
                 {
-                    rangeSpecifier = INDIVIDUAL;
-                    player = words[2];
-                    commandMod.Empty();
-                }
+                    skill = words[index++];
+                    // check for relative value
+                    if (words[index][0] == '+' || words[index][0] == '-')
+                        insert = true;
+                    value2 = words.GetInt(index++);
 
-                // next 'word' should be numeric: number of items.
-                if (strspn(words[rewardIndex].GetDataSafe(), "-0123456789") == words[rewardIndex].Length())
+                    // check for optional maximum
+                    interval = words.GetInt(index);
+                    if (interval)
+                        index++;
+                } 
+                else if (subCmd == "money" && remaining >= 2)
                 {
-                    stackCount = words.GetInt(rewardIndex);
-                    rewardIndex++;
+                    type = words[index++];
+                    random = words.GetInt(index++);
                 }
-                else
+                else if (subCmd == "faction" && remaining >= 2)
                 {
+                    name = words[index++];
+                    density = words.GetInt(index++);
+                }
+                else // invalid arguments
+                {
                     subCmd = "help";
                     help = true;
-                    return true;
                 }
             }
 
-            // last bit is the item name itself!
-            item = words.GetTail(rewardIndex);
+            subCmd = "reward";
         }
         else if (subCmd == "remove")
         {
@@ -1414,9 +1590,9 @@
     {
         UnbanAdvisor( me, msg, data, client );
     }
-    else if (data.command == "/awardexp" )
+    else if (data.command == "/award" )
     {
-        AwardExperience(me, msg, data, client, targetclient);
+        Award(data, client, targetclient);
     }
     else if (data.command == "/giveitem" )
     {
@@ -4220,39 +4396,9 @@
     bool valid = true;
     Money_Slots type;
 
-    if(data.item == "trias")
-        type = MONEY_TRIAS;
-    else if(data.item == "hexas")
-        type = MONEY_HEXAS;
-    else if(data.item == "octas")
-        type = MONEY_OCTAS;
-    else if(data.item == "circles")
-        type = MONEY_CIRCLES;
-    else
-        valid = false;
-
-    if (!valid || (data.value == 0 && data.random == 0))
-    {
-        psserver->SendSystemError(me->clientnum, "Syntax: /money <circles|hexas|octas|trias> <random|quantity>");
-        return;
-    }
-
-
-    psCharacter* charData = client->GetCharacterData();
-
-    int quantity = data.value;
-    if(data.random > 0)
-        quantity = psserver->rng->Get(1000) + 1;
-
-    psMoney money;
-
-    money.Set(type, quantity);
-
-    Debug4(LOG_ADMIN,me->clientnum,  "Created %d %s for %s\n", quantity, data.item.GetDataSafe(), charData->GetCharName());
-
-    charData->AdjustMoney(money, false);
-    psserver->GetCharManager()->SendPlayerMoney(client);
-
+    psRewardDataMoney rewardDataMoney(data.item, data.value);
+   
+    AwardToTarget(client->GetClientNum(), client, rewardDataMoney);
 }
 
 void AdminManager::RunScript(MsgEntry *me, psAdminCmdMessage& msg, AdminCmdData& data,Client *client, gemObject* object)
@@ -6365,24 +6511,222 @@
     psserver->GetCharManager()->UpdateItemViews(me->clientnum);
 }
 
-void AdminManager::AwardExperience(MsgEntry* me, psAdminCmdMessage& msg, AdminCmdData& data, Client* client, Client* target)
+void AdminManager::Award(AdminCmdData& data, Client* client, Client* target)
 {
+    if (data.help)
+    {
+        psserver->SendSystemInfo(client->GetClientNum(), "Syntax: \"/award <area:[players]:range|me|target|player|eid:EID|pid:PID>\n"
+                                        "['exp' value] ['item' count item] ['skill' <skill|'all'> [+-]value [max]]\n"
+                                        "['money' <circles|hexas|octas|trias> <value|'random'>] ['faction' faction value]\"");
+        return;
+    }
+
+    // unpack reward data
+    psRewardDataExperience RewardExperience(data.value);
+    psRewardDataFaction RewardDataFaction(data.name, data.density);
+    psRewardDataSkill RewardDataSkill(data.skill, data.value2, data.interval,data.insert);
+    psRewardDataMoney RewardDataMoney(data.type, data.random);
+    psRewardDataItem RewardDataItem(data.item, data.stackCount);
+   
+    // natoka: this is uglyness ^3 but thx to the fine struct it's not really
+    // possible to do it otherwise. Once the struct is done for, then this
+    // is history
+    if (RewardExperience.IsZero() && RewardDataFaction.IsZero() &&
+        RewardDataSkill.IsZero() && RewardDataMoney.IsZero() &&
+        RewardDataItem.IsZero())
+    {
+         psserver->SendSystemError(client->GetClientNum(), "Nothing to award.");
+         return;
+    } 
+    else 
+    {
+        if (!RewardExperience.IsZero())
+            AwardToTarget(client->GetClientNum(), target, RewardExperience);
+        if (!RewardDataFaction.IsZero())
+            AwardToTarget(client->GetClientNum(), target, RewardDataFaction);
+        if (!RewardDataSkill.IsZero())
+            AwardToTarget(client->GetClientNum(), target, RewardDataSkill);
+        if (!RewardDataMoney.IsZero())
+            AwardToTarget(client->GetClientNum(), target, RewardDataMoney);
+        if (!RewardDataItem.IsZero())
+            AwardToTarget(client->GetClientNum(), target, RewardDataItem);
+    }
+}
+
+void AdminManager::AwardToTarget(int gmClientNum, Client* target, psRewardData& data)
+{
+    if (data.IsZero())
+    {
+         psserver->SendSystemError(gmClientNum, "Nothing to award.");
+         return;
+    }
+
     if (!target || !target->GetCharacterData())
     {
-        psserver->SendSystemError(me->clientnum, "Invalid target to award experience to");
+        psserver->SendSystemError(gmClientNum, "Invalid target to award");
         return;
     }
+ 
+    psCharacter * pchar = target->GetCharacterData();
+    if (pchar->IsNPC())
+    {
+        psserver->SendSystemError(gmClientNum, "You can't use this command on npcs!");
+        return;
+    }
 
+    if (data.rewardType == psRewardData::REWARD_EXPERIENCE)
+    {
+        psRewardDataExperience rewardDataExperience = (psRewardDataExperience&) data;
+        AwardExperienceToTarget(gmClientNum, target, rewardDataExperience.expDelta);
+    }
+
+    if (data.rewardType == psRewardData::REWARD_ITEM) // award item
+    {
+        psRewardDataItem rewardDataItem = (psRewardDataItem&) data;
+
+        csString text;
+        psItemStats* stats = psserver->GetCacheManager()->GetBasicItemStatsByName(rewardDataItem.itemName);
+        if (!stats)
+        {
+            psserver->SendSystemError(gmClientNum, "You have to specify a valid item name");
+        }
+        else if (stats->IsMoney())
+        {
+            psserver->SendSystemError(gmClientNum, "Use the 'money' award to award money, not the 'item' one.");
+        }
+        else if (stats->GetBuyPersonalise())
+        {
+            psserver->SendSystemError(gmClientNum, "You cannot award personalized items.");
+        }
+        else if (rewardDataItem.stackCount > MAX_STACK_COUNT)
+        {
+            text.Format("The value for the stackCount has to be between 0 and %u", MAX_STACK_COUNT);
+            psserver->SendSystemError(gmClientNum, text);
+        }
+        else // eveyrthing alright - create item
+        {
+            psItem* item = stats->InstantiateBasicItem(true);
+            item->SetStackCount(rewardDataItem.stackCount);
+            item->SetItemQuality(stats->GetQuality());
+            item->SetLoaded();
+            pchar->Inventory().AddOrDrop(item);
+            psserver->GetCharManager()->UpdateItemViews(target->GetClientNum());
+
+            // notify target
+            text.Format("You have been awarded %d %s.", rewardDataItem.stackCount, rewardDataItem.itemName.GetDataSafe());
+            psserver->SendSystemInfo(target->GetClientNum(), text);
+
+            if (gmClientNum != target->GetClientNum()) // notify issuer
+            {
+                text.Format("%s has been awarded %d %s.", target->GetName(), rewardDataItem.stackCount, rewardDataItem.itemName.GetDataSafe());
+                psserver->SendSystemInfo(gmClientNum, text);
+            }
+
+            Debug4(LOG_ADMIN, gmClientNum, "Created %d %s for %s\n", rewardDataItem.stackCount, rewardDataItem.itemName.GetDataSafe(), target->GetName());
+        }
+    }
+
+    if (data.rewardType == psRewardData::REWARD_FACTION)
+     // award faction
+    {
+        psRewardDataFaction rewardDataFaction = (psRewardDataFaction&) data;
+        AdjustFactionStandingOfTarget(gmClientNum, target, rewardDataFaction.factionName, rewardDataFaction.factionDelta);
+    }
+
+    if (data.rewardType == psRewardData::REWARD_SKILL) // award skill
+    {
+        psRewardDataSkill rewardDataSkill = (psRewardDataSkill&) data;
+
+        bool modified = false;
+        if (rewardDataSkill.skillName == "all") // update all skills
+        {
+            for (int i=0; i<PSSKILL_COUNT; i++)
+            {
+                psSkillInfo* skill = psserver->GetCacheManager()->GetSkillByID(i);
+                if (!skill) continue; // skill doesn't exist -> this should not happen
+                modified |= ApplySkill(gmClientNum, target, skill, rewardDataSkill.skillDelta, rewardDataSkill.relativeSkill, rewardDataSkill.skillCap);
+            }
+        }
+        else // update a certain one
+        {
+            psSkillInfo* skill = psserver->GetCacheManager()->GetSkillByName(rewardDataSkill.skillName);
+            modified |= ApplySkill(gmClientNum, target, skill, rewardDataSkill.skillDelta, rewardDataSkill.relativeSkill, rewardDataSkill.skillCap);
+        }
+
+        if (modified && target) // update client view if we changed something
+        {
+            psserver->GetProgressionManager()->SendSkillList(target, false);
+        }
+    }
+
+    if (data.rewardType == psRewardData::REWARD_MONEY) // award money
+    {
+        psRewardDataMoney rewardDataMoney = (psRewardDataMoney&) data;
+        bool valid = true;
+        
+        // determine money type
+        Money_Slots type;
+        if(rewardDataMoney.moneyType == "trias")
+            type = MONEY_TRIAS;
+        else if(rewardDataMoney.moneyType == "hexas")
+            type = MONEY_HEXAS;
+        else if(rewardDataMoney.moneyType == "octas")
+            type = MONEY_OCTAS;
+        else if(rewardDataMoney.moneyType == "circles")
+            type = MONEY_CIRCLES;
+        else
+            valid = false;
+        
+        if (valid)
+        {
+            int value;
+            if (rewardDataMoney.moneyCount) // fixed amount
+                value = rewardDataMoney.moneyCount;
+            else // random amount
+                value = psserver->rng->Get(1000)+1;
+            psMoney money;
+            money.Set(type, value);
+            pchar->AdjustMoney(money, false);
+
+            // update client view
+            psserver->GetCharManager()->SendPlayerMoney(target);
+
+            // notify target
+            csString text;
+            text.Format("You have been awarded %d %s.", value, rewardDataMoney.moneyType.GetDataSafe());
+            psserver->SendSystemInfo(target->GetClientNum(), text);
+
+            if (gmClientNum != target->GetClientNum()) // notify issuer
+            {
+                text.Format("%s has been awarded %d %s.", target->GetName(), value, rewardDataMoney.moneyType.GetDataSafe());
+                psserver->SendSystemInfo(gmClientNum, text);
+            }
+            
+            Debug4(LOG_ADMIN, gmClientNum, "Created %d %s for %s\n", value, rewardDataMoney.moneyType.GetDataSafe(), pchar->GetCharName());
+        }
+        else
+        {
+            psserver->SendSystemError(gmClientNum, "Invalid money type");
+        }
+    }
+}
+
+void AdminManager::AwardExperience(MsgEntry* me, psAdminCmdMessage& msg, AdminCmdData& data, Client* client, Client* target)
+{
     if (data.value == 0)
     {
         psserver->SendSystemError(me->clientnum, "Invalid experience specified");
         return;
     }
 
-    AwardExperienceToTarget(me->clientnum, target, data.player, data.value);
+    // unpack argument
+    psRewardDataExperience rewardData(data.value);
+
+    AwardToTarget(me->clientnum, target, rewardData);
 }
 
-void AdminManager::AwardExperienceToTarget(int gmClientnum, Client* target, csString recipient, int ppAward)
+
+void AdminManager::AwardExperienceToTarget(int gmClientnum, Client* target, int ppAward)
 {
     unsigned int pp = target->GetCharacterData()->GetProgressionPoints();
 
@@ -6420,7 +6764,7 @@
         psserver->SendSystemInfo(target->GetClientNum(),"You lost %d progression points.", -ppAward);
     }
 
-    psserver->SendSystemInfo(gmClientnum, "You awarded %s %d progression points.", recipient.GetData(), ppAward);
+    psserver->SendSystemInfo(gmClientnum, "You awarded %s %d progression points.", target->GetName(), ppAward);
 }
 
 void AdminManager::AdjustFactionStandingOfTarget(int gmClientnum, Client* target, csString factionName, int standingDelta)
@@ -6715,70 +7059,70 @@
 
 void AdminManager::SetSkill(MsgEntry* me, psAdminCmdMessage& msg, AdminCmdData& data, Client* client, gemActor *target)
 {
+    if (data.skill.IsEmpty() || data.help)
+    {
+        psserver->SendSystemError(me->clientnum, "Syntax: /setskill [target] [skill|'all'] [+-][value] | [target] copy [source]");
+        return;
+    }
+    
+    if (!target || target->GetClient() != client && !psserver->CheckAccess(client, "setskill others"))
+    {
+        psserver->SendSystemError(me->clientnum, "You have to specify a valid target");
+        return;
+    }
+
+    if (data.skill != "copy")
+    {
+        psRewardDataSkill rewardData(data.skill,data.value,data.insert,data.interval);
+        
+        AwardToTarget(me->clientnum, target->GetClient(), rewardData);
+        return;
+    }
+
     Client* sourceclient = NULL;
     gemActor* source = NULL;
     gemObject* sourceobject = NULL;
     psCharacter * schar = NULL;
 
-    // Try to find the source of skills if we're doing a copy
-    if (data.skill == "copy")
+    // First try and find the source client by name
+    if (data.sourceplayer == "me")
     {
-        // First try and find the source client by name
-        if (data.sourceplayer == "me")
-        {
-            sourceclient = client;
-        }
-        else
-        {
-            sourceclient = FindPlayerClient(data.sourceplayer);
-        }
+        sourceclient = client;
+    }
+    else
+    {
+        sourceclient = FindPlayerClient(data.sourceplayer);
+    }
 
-        if (sourceclient)
+    if (sourceclient)
+    {
+        source = sourceclient->GetActor();
+    }
+    // If a search by name didn't work, try and search by pid or eid
+    else
+    {
+        sourceobject = FindObjectByString(data.sourceplayer,client->GetActor());
+        if (sourceobject)
         {
-            source = sourceclient->GetActor();
+            source = sourceobject->GetActorPtr();
         }
-        // If a search by name didn't work, try and search by pid or eid
-        else
-        {
-            sourceobject = FindObjectByString(data.sourceplayer,client->GetActor());
-            if (sourceobject)
-            {
-                source = sourceobject->GetActorPtr();
-            }
-        }
+    }
 
-        if (source == NULL)
-        {
-            psserver->SendSystemError(me->clientnum, "Invalid skill source");
-            return;
-        }
-        else
-        {
-            schar = source->GetCharacterData();
-            if (!schar)
-            {
-                psserver->SendSystemError(me->clientnum, "No source character data!");
-                return;
-            }
-        }
-    }
-    // Not a copy, just proceed as normal
-    else if (data.skill.IsEmpty() || data.value == -2)
+    if (source == NULL)
     {
-        psserver->SendSystemError(me->clientnum, "Syntax: /setskill [target] [skill|'all'] [value|-1] | [target] copy [source]");
+        psserver->SendSystemError(me->clientnum, "Invalid skill source");
         return;
     }
-
-    if (target == NULL)
+    else
     {
-        psserver->SendSystemError(me->clientnum, "Invalid target for setting skills");
-        return;
+        schar = source->GetCharacterData();
+        if (!schar)
+        {
+            psserver->SendSystemError(me->clientnum, "No source character data!");
+            return;
+        }
     }
 
-    // Check the permission to set skills for other characters
-    if (target->GetClient() != client && !psserver->CheckAccess(client, "setskill others"))
-        return;
-
     psCharacter * pchar = target->GetCharacterData();
     if (!pchar)
     {
@@ -6792,176 +7136,91 @@
         return;
     }
 
-    // use unsigned int since skills are never negative (this also takes care of overflows)
-    unsigned int value = data.value;
-    unsigned int max = MAX(MAX_SKILL, MAX_STAT);
-    if (data.skill == "all")
+    bool modified = false;
+    for (int i=0; i<PSSKILL_COUNT; i++)
     {
-        // if the value is out of range, send an error
-        if (data.value != -1 && (value < 0 || value > max))
-        {
-            psserver->SendSystemError(me->clientnum, "Valid values are between 0 and %u", max);
-            return;
-        }
+        psSkillInfo * skill = psserver->GetCacheManager()->GetSkillByID(i);
+        if (skill == NULL) continue;
+        modified |= ApplySkill(me->clientnum, target->GetClient(), skill, schar->Skills().GetSkillRank(skill->id).Current());
+    }
 
-        for (size_t i=0; i<psserver->GetCacheManager()->GetSkillAmount(); i++)
-        {
-            psSkillInfo * skill = psserver->GetCacheManager()->GetSkillByID(i);
-            if (skill == NULL) continue;
+    // Send updated skill list to client
+    if(modified && target->GetClient())
+        psserver->GetProgressionManager()->SendSkillList(target->GetClient(), false);
+}
 
-            unsigned int old_value = pchar->Skills().GetSkillRank(skill->id).Current();
+bool AdminManager::ApplySkill(int client, Client* target, psSkillInfo* skill, int value, bool relative, unsigned int cap)
+{
+    // perform sanity checks
+    if (!skill)
+    {
+        psserver->SendSystemError(client, "Invalid Skill");
+        return false;
+    }
 
-            if(data.value == -1)
-            {
-                PSITEMSTATS_STAT stat = skillToStat(skill->id);
-                if (stat != PSITEMSTATS_STAT_NONE)
-                {
-                    //Handle stats differently to pickup buffs/debuffs
-                    int base = pchar->Stats()[stat].Base();
-                    int current = pchar->Stats()[stat].Current();
-                    if (base == current)
-                        psserver->SendSystemInfo(me->clientnum, "Current '%s' of '%s' is %u", skill->name.GetDataSafe(), target->GetName(), base);
-                    else
-                        psserver->SendSystemInfo(me->clientnum, "Current '%s' of '%s' is %u (%u)", skill->name.GetDataSafe(), target->GetName(), base, current);
-                } else {
-                    int base = pchar->Skills().GetSkillRank(skill->id).Base();
-                    int current = pchar->Skills().GetSkillRank(skill->id).Current();
-                    if (base == current)
-                        psserver->SendSystemInfo(me->clientnum, "Current '%s' of '%s' is %u", skill->name.GetDataSafe(), target->GetName(), base);
-                    else
-                        psserver->SendSystemInfo(me->clientnum, "Current '%s' of '%s' is %u (%u)", skill->name.GetDataSafe(), target->GetName(), base, current);
-                }
-            }
-            else
-            {
-                pchar->SetSkillRank(skill->id, value);
-                psserver->SendSystemInfo(me->clientnum, "Changed '%s' of '%s' from %u to %u", skill->name.GetDataSafe(), target->GetName(), old_value,data.value);
-            }
-        }
-
-        if(data.value != -1)
-            return;
-
-        if (target->GetClient() && target->GetClient() != client)
+    if (!target || !client) // no target or issuer
+        return false;
+    
+    psCharacter * pchar = target->GetCharacterData();
+    
+    if (!pchar) // target is no player
+        return false;
+    
+    if (relative && !value) // +0 or -0: show current status and return
+    {
+        int base, current;
+        if (skill->category == PSSKILLS_CATEGORY_STATS) // handle stats explicitly
         {
-            // Inform the other player.
-            psserver->SendSystemOK(target->GetClientID(), "All your skills were set to %d by a GM", data.value);
+            PSITEMSTATS_STAT stat = skillToStat(skill->id);
+            base = pchar->Stats()[stat].Base();
+            current = pchar->Stats()[stat].Current();
+        } 
+        else
+        {
+            base = pchar->Skills().GetSkillRank(skill->id).Base();
+            current = pchar->Skills().GetSkillRank(skill->id).Current();
         }
+        // notify issuer
+        if (base == current)
+            psserver->SendSystemInfo(client, "Current '%s' of '%s' is %d", skill->name.GetDataSafe(), target->GetName(), base);
+        else
+            psserver->SendSystemInfo(client, "Current '%s' of '%s' is %d (%d)", skill->name.GetDataSafe(), target->GetName(), base, current);
+        
+        return false;
     }
-    else if (data.skill == "copy")
+    else // modify skill
     {
-        for (size_t i=0; i<psserver->GetCacheManager()->GetSkillAmount(); i++)
-        {
-            psSkillInfo * skill = psserver->GetCacheManager()->GetSkillByID(i);
-            if (skill == NULL) continue;
+        int old_value = pchar->Skills().GetSkillRank(skill->id).Current(); // backup current value
+        int new_value = value;
+        int max = MAX_SKILL;
 
-            unsigned int old_value = pchar->Skills().GetSkillRank(skill->id).Current();
-            unsigned int new_value = schar->Skills().GetSkillRank(skill->id).Current();
+        if (relative)
+            new_value += old_value;
 
-            pchar->SetSkillRank(skill->id, new_value);
-            psserver->SendSystemInfo(me->clientnum, "Changed '%s' of '%s' from %u to %u", skill->name.GetDataSafe(), target->GetName(), old_value, new_value);
+        if (skill->category == PSSKILLS_CATEGORY_STATS) // stats have an own maximum
+            max = MAX_STAT;
 
-            if (target->GetClient() &&  target->GetClient() != client)
-            {
-                // Inform the other player.
-                psserver->SendSystemOK(target->GetClientID(), "Your '%s' level was set to %d by a GM", data.skill.GetDataSafe(), new_value);
-            }
-        }
-    }
-    else
-    {
-        psSkillInfo * skill = psserver->GetCacheManager()->GetSkillByName(data.skill);
-        if (skill == NULL)
-        {
-            psserver->SendSystemError(me->clientnum, "Skill not found");
-            return;
-        }
+        if (cap && cap < max) // check whether the issuer specified a maximum
+            max = cap;
 
-        unsigned int old_value = pchar->Skills().GetSkillRank(skill->id).Current();
-        if (data.value == -1)
-        {
-            PSITEMSTATS_STAT stat = skillToStat(skill->id);
-            if (stat != PSITEMSTATS_STAT_NONE)
-            {
-                //Handle stats differently to pickup buffs/debuffs
-                int base = pchar->Stats()[stat].Base();
-                int current = pchar->Stats()[stat].Current();
-                if (base == current)
-                    psserver->SendSystemInfo(me->clientnum, "Current '%s' of '%s' is %u", skill->name.GetDataSafe(), target->GetName(), base);
-                else
-                    psserver->SendSystemInfo(me->clientnum, "Current '%s' of '%s' is %u (%u)", skill->name.GetDataSafe(), target->GetName(), base, current);
-            } else {
-                int base = pchar->Skills().GetSkillRank(skill->id).Base();
-                int current = pchar->Skills().GetSkillRank(skill->id).Current();
-                if (base == current)
-                    psserver->SendSystemInfo(me->clientnum, "Current '%s' of '%s' is %u", skill->name.GetDataSafe(), target->GetName(), base);
-                else
-                    psserver->SendSystemInfo(me->clientnum, "Current '%s' of '%s' is %u (%u)", skill->name.GetDataSafe(), target->GetName(), base, current);
-            }
-            return;
-        }
-        else if (skill->category == PSSKILLS_CATEGORY_STATS && (value < 0 || value > MAX_STAT))
-        {
-            psserver->SendSystemError(me->clientnum, "Stat values are between 0 and %u", MAX_STAT);
-            return;
-        }
-        else if (skill->category != PSSKILLS_CATEGORY_STATS && (value < 0 || value > MAX_SKILL))
-        {
-            psserver->SendSystemError(me->clientnum, "Skill values are between 0 and %u", MAX_SKILL);
-            return;
-        }
+        // adjust the value to be between 0 and the maximum if needed
+        if (new_value > max)
+            new_value = max;
+        else if (new_value < 0)
+            new_value = 0;
 
-        pchar->SetSkillRank(skill->id, value);
-        psserver->SendSystemInfo(me->clientnum, "Changed '%s' of '%s' from %u to %u", skill->name.GetDataSafe(), target->GetName(), old_value,data.value);
+        pchar->SetSkillRank(skill->id, new_value);
 
-        if (target->GetClient() &&  target->GetClient() != client)
-        {
-            // Inform the other player.
-            psserver->SendSystemOK(target->GetClientID(), "Your '%s' level was set to %d by a GM", data.skill.GetDataSafe(), data.value);
-        }
-    }
+        // notify issuer
+        psserver->SendSystemInfo(client, "Changed '%s' of '%s' from %d to %d", skill->name.GetDataSafe(), target->GetName(), old_value, new_value);
 
-    // Send updated skill list to client
-    if(target->GetClient())
-        psserver->GetProgressionManager()->SendSkillList(target->GetClient(), false);
-}
-/*
-void AdminManager::AwardSkillToTarget(int gmClientnum, Client* target, csSring skillName, csString recipient, int levelAward, int limit, bool relative)
-{
-    psCharacter* charData = player->GetCharacterData();
-    unsigned int value = abs(data.value);
-    unsigned int max = MAX(MAX_SKILL, MAX_STAT);
-    
-    if(skillName == "all")
-    {
-        
-    }
-    else
-    {
-        psSkillInfo * skill = psserver->GetCacheManager()->GetSkillByName(skillName);
-        if (skill == NULL)
-        {
-            psserver->SendSystemError(me->clientnum, "Skill not found");
-            return;
-        }
-        unsigned int oldValue = charData->Skills().GetSkillRank(skill->id).Current();
-                charData->SetSkillRank(skill->id, value);
-        psserver->SendSystemInfo(me->clientnum, "Changed '%s' of '%s' from %u to %u", skill->name.GetDataSafe(), target->GetName(), old_value,data.value);
+        if (target && target->GetClientNum() != client) // notify target
+            psserver->SendSystemOK(target->GetClientNum(), "Your '%s' level was set to %d by a GM", skill->name.GetDataSafe(), new_value);
 
-        if (target->GetClient() &&  target->GetClient() != client)
-        {
-            // Inform the other player.
-            psserver->SendSystemOK(target->GetClientID(), "Your '%s' level was set to %d by a GM", data.skill.GetDataSafe(), data.value);
-        }
     }
+    return true;
+}
 
-        // Send updated skill list to client
-    if(target->GetClient())
-        psserver->GetProgressionManager()->SendSkillList(target->GetClient(), false);
-    
-
-}*/
-
 void AdminManager::UpdateRespawn(AdminCmdData& data, Client* client, gemActor* target)
 {
     if (!target)
@@ -7812,7 +8071,6 @@
         (data.gmeventName.Length() == 0 || data.gmeventDesc.Length() == 0)) ||
         (data.subCmd == "register" && data.player.Length() == 0 && data.rangeSpecifier == INDIVIDUAL) ||
         (data.subCmd == "remove" && data.player.Length() == 0) ||
-        (data.subCmd == "reward" && data.item.Length() == 0 && data.stackCount == 0) ||
         (data.subCmd == "control" && data.gmeventName.Length() == 0) ||
         (data.subCmd == "discard" && data.gmeventName.Length() == 0))
     {
@@ -7825,7 +8083,9 @@
         psserver->SendSystemInfo( me->clientnum, "/event help\n"
                                   "/event create <name> <description>\n"
                                   "/event register [range <range> | <player>]\n"
-                                  "/event reward [all | range <range> | <player>] # <item>\n"
+                                  "/event reward <target | all | range <range> | <player>> ['skill' <skill|'all'> [+-]value [max]]\n"
+                                  "['exp' value] ['item' count item] ['money' <circles|hexas|octas|trias> <value|'random'>]\n"
+                                  "['faction' faction value]\n"
                                   "/event remove <player>\n"
                                   "/event complete [<name>]\n"
                                   "/event list\n"
@@ -7879,12 +8139,37 @@
     // reward player(s)
     if (data.subCmd == "reward")
     {
-        gmeventResult = gmeventManager->RewardPlayersInGMEvent(client,
-                                                               data.rangeSpecifier,
-                                                               data.range,
-                                                               target,
-                                                               data.stackCount,
-                                                               data.item);
+        // unpack arguments
+        psRewardDataExperience RewardExperience(data.value);
+        psRewardDataFaction RewardDataFaction(data.name, data.density);
+        psRewardDataSkill RewardDataSkill(data.skill, data.value2, data.interval,data.insert);
+        psRewardDataMoney RewardDataMoney(data.type, data.random);
+        psRewardDataItem RewardDataItem(data.item, data.stackCount);
+
+        csArray<psRewardData> rewards;
+
+        if (RewardExperience.IsZero() && RewardDataFaction.IsZero() &&
+            RewardDataSkill.IsZero() && RewardDataMoney.IsZero() &&
+            RewardDataItem.IsZero())
+        {
+            psserver->SendSystemError(client->GetClientNum(), "Nothing to award.");
+            return;
+        } 
+        else 
+        {
+            if (!RewardExperience.IsZero())
+                rewards.Push(RewardExperience);
+            if (!RewardDataFaction.IsZero())
+                rewards.Push(RewardDataFaction);
+            if (!RewardDataSkill.IsZero())
+                rewards.Push(RewardDataSkill);
+            if (!RewardDataMoney.IsZero())
+                rewards.Push(RewardDataMoney);
+            if (!RewardDataItem.IsZero())
+                rewards.Push(RewardDataItem);
+        }
+
+        gmeventResult = gmeventManager->RewardPlayersInGMEvent(client, data.rangeSpecifier, data.range, target, rewards);
         return;
     }
 
@@ -8539,13 +8824,10 @@
         psserver->SendSystemInfo(client->GetClientNum(),"Syntax: \"/assignfaction [me/target/eid/pid/area/name] [factionname] [points]\"");
         return;
     }
-    if(!target)
-    {
-        psserver->SendSystemInfo(client->GetClientNum(),"Unable to find the player to assign faction points to.");
-        return;
-    }
 
-    AdjustFactionStandingOfTarget(client->GetClientNum(), target, data.name, data.value);
+    psRewardDataFaction rewardData(data.name, data.value);
+
+    AwardToTarget(me->clientnum, target, rewardData);
 }
 
 void AdminManager::HandleServerQuit(MsgEntry* me, psAdminCmdMessage& msg, AdminCmdData& data, Client *client )
Index: src/server/gmeventmanager.cpp
===================================================================
--- src/server/gmeventmanager.cpp	(revision 6170)
+++ src/server/gmeventmanager.cpp	(working copy)
@@ -422,13 +422,10 @@
                                              RangeSpecifier rewardRecipient,
                                              float range,
                                              Client* target,
-                                             short stackCount,
-                                             csString itemName)
+					     csArray<psRewardData> rewardData)
 {
     GMEvent* gmEvent;
     int clientnum = client->GetClientNum(), zero = 0;
-    WordArray rewardDesc(itemName);
-    RewardType rewardType = REWARD_ITEM;
     PID gmID = client->GetPID();
 
     // make sure GM is running (or assisting) an event
@@ -450,50 +447,6 @@
        return false;
     }
 
-    // identify reward type: experience, faction points or an item
-    if (rewardDesc[0] == "exp" && rewardDesc.GetCount() == 1)
-        rewardType = REWARD_EXPERIENCE;
-    else if (rewardDesc[0] == "faction" && rewardDesc.GetCount() > 1)
-        rewardType = REWARD_FACTION_POINTS;
-
-    // retrieve base stats item
-    psItemStats *basestats;
-    if (rewardType == REWARD_ITEM)
-    {
-        basestats = psserver->GetCacheManager()->GetBasicItemStatsByName(itemName.GetDataSafe());
-        if (basestats == NULL)
-        {
-            psserver->SendSystemInfo(clientnum, "Reward \'%s\' not recognised.", itemName.GetDataSafe());
-            Error2("'%s' was not found as a valid base item.", itemName.GetDataSafe());
-            return false;
-        }
-
-        if (stackCount <= 0)
-        {
-           psserver->SendSystemInfo(clientnum,
-                                "You must reward at least 1 item to participant(s).");
-           return false;
-        }
-        
-        if (stackCount >= 65 && !basestats->IsMoney())
-        {
-           psserver->SendSystemInfo(clientnum,
-                                "You can't reward that amount of items to partecipant(s).");
-           return false;
-        }
-
-        // cant reward personalised or unique items
-        if (basestats->GetBuyPersonalise() || basestats->GetUnique())
-        {
-           psserver->SendSystemInfo(clientnum,
-                                "You cannot reward personalised / unique items.");
-           return false;
-        }
-    }
-    else
-        basestats = NULL;
-
-
     if (rewardRecipient == INDIVIDUAL)
     {
         if (!target)
@@ -504,12 +457,10 @@
             GMEvent* playersEvent = GetGMEventByPlayer(target->GetPID(), RUNNING, zero);
             if (playersEvent && playersEvent->id == gmEvent->id)
             {
-                if (rewardType == REWARD_EXPERIENCE)
-                    psserver->GetAdminManager()->AwardExperienceToTarget(clientnum, target, target->GetName(), stackCount);
-                else if (rewardType == REWARD_FACTION_POINTS)
-                    psserver->GetAdminManager()->AdjustFactionStandingOfTarget(clientnum, target, rewardDesc.GetTail(1), stackCount);
-                else
-                    RewardPlayer(clientnum, target, stackCount, basestats);
+		for (int i=0; i < rewardData.GetSize(); i++) 
+		{
+                    psserver->GetAdminManager()->AwardToTarget(clientnum, target, rewardData.Get(i));
+		}
             }
             else
             {
@@ -530,13 +481,11 @@
             if ((target = clientConnections->FindPlayer(gmEvent->Player[p].PlayerID)))
             {
                 if (rewardRecipient == ALL || clientActor->RangeTo(target->GetActor()) <= range)
-                {
-                    if (rewardType == REWARD_EXPERIENCE)
-                        psserver->GetAdminManager()->AwardExperienceToTarget(clientnum, target, target->GetName(), stackCount);
-                    else if (rewardType == REWARD_FACTION_POINTS)
-                        psserver->GetAdminManager()->AdjustFactionStandingOfTarget(clientnum, target, rewardDesc.GetTail(1), stackCount);
-                    else
-                        RewardPlayer(clientnum, target, stackCount, basestats);
+		{
+		    for (int i=0; i < rewardData.GetSize(); i++) 
+		    {
+		        psserver->GetAdminManager()->AwardToTarget(clientnum, target, rewardData.Get(i));
+		    }
                 }
             }
         }
@@ -981,51 +930,6 @@
     return SIZET_NOT_FOUND;
 }
 
-void GMEventManager::RewardPlayer(int clientnum, Client* target, short stackCount, psItemStats* basestats)
-{
-    // If it's money don't instantiate them just directly give them.
-    if(basestats->IsMoney())
-    {
-        target->GetActor()->GetCharacterData()->SetMoney(basestats, stackCount);
-        psserver->SendSystemInfo(target->GetClientNum(),
-                                 "You have been rewarded %d %s%s for participating in this event.",
-                                 stackCount, basestats->GetName(), (stackCount>1)?"s":"");
-        psserver->SendSystemInfo(clientnum, "%s has been rewarded %d %s%s.",
-                                 target->GetName(), stackCount,
-                                 basestats->GetName(), (stackCount>1)?"s":"");
-        return;
-    }
-
-    // generate the prize item
-    psItem* newitem = basestats->InstantiateBasicItem(true);
-    if (newitem == NULL)
-    {
-        Error1("Could not instantiate from base item.");
-        psserver->SendSystemInfo(clientnum, "%s has not been rewarded.", target->GetName());
-        return;
-    }
-    newitem->SetItemQuality(basestats->GetQuality());
-    newitem->SetStackCount(stackCount);
-
-    // spawn the item into the recipients inventory
-    newitem->SetLoaded();  // Item is fully created
-    if (target->GetActor()->GetCharacterData()->Inventory().Add(newitem))
-    {
-        // inform recipient of their prize
-        psserver->SendSystemInfo(target->GetClientNum(),
-                                 "You have been rewarded %d %s%s for participating in this event.",
-                                 stackCount, basestats->GetName(), (stackCount>1)?"s":"");
-        psserver->SendSystemInfo(clientnum, "%s has been rewarded %d %s%s.",
-                                 target->GetName(), stackCount,
-                                 basestats->GetName(), (stackCount>1)?"s":"");
-        return;
-    }
-
-    // failed to stash item, so remove it
-    psserver->GetCacheManager()->RemoveInstance(newitem);
-    psserver->SendSystemInfo(clientnum, "%s has not been rewarded.", target->GetName());
-}
-
 int GMEventManager::GetNextEventID(void)
 {
     // TODO this is just too simple
Index: src/server/database/mysql/command_access.sql
===================================================================
--- src/server/database/mysql/command_access.sql	(revision 6170)
+++ src/server/database/mysql/command_access.sql	(working copy)
@@ -100,6 +100,9 @@
 INSERT INTO command_group_assignment VALUES( "/awardexp", 30 );
 INSERT INTO command_group_assignment VALUES( "/awardexp", 25 );
 INSERT INTO command_group_assignment VALUES( "/awardexp", 24 );
+INSERT INTO command_group_assignment VALUES( "/award", 30 );
+INSERT INTO command_group_assignment VALUES( "/award", 25 );
+INSERT INTO command_group_assignment VALUES( "/award", 24 );
 INSERT INTO command_group_assignment VALUES( "quest change others", 30 );
 INSERT INTO command_group_assignment VALUES( "quest change others", 25 );
 INSERT INTO command_group_assignment VALUES( "quest change others", 24 );
Index: src/server/gmeventmanager.h
===================================================================
--- src/server/gmeventmanager.h	(revision 6170)
+++ src/server/gmeventmanager.h	(working copy)
@@ -44,6 +44,7 @@
 #define SUPPORT_GM_LEVEL      GM_LEVEL_4
 
 class psItemStats;
+class psRewardData;
 
 enum GMEventStatus
 {
@@ -146,8 +147,7 @@
                                  RangeSpecifier rewardRecipient,
                                  float range,
                                  Client *target,
-                                 short stackCount,
-                                 csString itemName);
+				 csArray<psRewardData>);
 
     /** @brief Returns all events for a player.
      *
@@ -288,21 +288,6 @@
      */
     void SetEvalStatus(PID PlayerID, GMEvent *Event, bool NewStatus);
 
-    /** @brief Reward player in event.
-     *
-     *  @param clientnum: GM client number.
-     *  @param target: client pointer to recipient.
-     *  @param stackCount: stack count # items in reward.
-     *  @param basestats: base stats of reward item.
-     */
-    void RewardPlayer(int clientnum, Client* target, short stackCount, psItemStats* basestats);
-    enum RewardType
-    {
-        REWARD_ITEM,
-        REWARD_EXPERIENCE,
-        REWARD_FACTION_POINTS
-    };
-
     /** Get next free event id number.
      */
     int GetNextEventID(void);