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 "); - 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 \n" + "['exp' value] ['item' count item] ['skill' [+-]value [max]]\n" + "['money' ] ['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; iGetCacheManager()->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 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; iGetCacheManager()->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; iGetCacheManager()->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 \n" "/event register [range | ]\n" - "/event reward [all | range | ] # \n" + "/event reward | > ['skill' [+-]value [max]]\n" + "['exp' value] ['item' count item] ['money' ]\n" + "['faction' faction value]\n" "/event remove \n" "/event complete []\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 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 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); /** @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);