Index: src/server/gem.cpp =================================================================== --- src/server/gem.cpp (Revision 1785) +++ src/server/gem.cpp (Arbeitskopie) @@ -1300,109 +1300,124 @@ } else if ( msg_id == "pickup") { - // If the player is in range of the item AND the item may be picked up or securityOveride, and check for dead user - if (actor->IsAlive() && RangeTo(actor) < RANGE_TO_SELECT && IsPickable()) + // Check if the char is dead + if (!actor->IsAlive()) { - gemActor* gActor = dynamic_cast(actor); - psCharacter* chardata = NULL; - if (gActor) chardata = gActor->GetCharacterData(); + psserver->SendSystemInfo(clientnum,"You can't pick up items when you're dead."); + return; + } + // Check if the item is pickupable + if (!IsPickable()) + { + psserver->SendSystemInfo(clientnum,"You can't pick up %s.", GetName()); + return; + } - psItem* item = GetItem(); + // Check if the item is in range + if (!(RangeTo(actor) < RANGE_TO_SELECT)) + { + if (!CacheManager::GetSingleton().GetCommandManager()->Validate(actor->GetClient()->GetSecurityLevel(), "pickup override")) + { + psserver->SendSystemInfo(clientnum,"You're too far away to pick up %s.", GetName()); + return; + } + } - if (actor->GetClient()->GetSecurityLevel() < GM_LEVEL_2) + psItem* item = GetItem(); + + // Check if item is guarded + if (!CacheManager::GetSingleton().GetCommandManager()->Validate(actor->GetClient()->GetSecurityLevel(), "pickup override")) + { + unsigned int guardCharacterID = item->GetGuardingCharacterID(); + gemActor* guardActor = GEMSupervisor::GetSingleton().FindPlayerEntity(guardCharacterID); + if (guardCharacterID && + guardCharacterID != actor->GetCharacterData()->GetCharacterID() && + guardActor && + guardActor->RangeTo(item->GetGemObject()) < RANGE_TO_SELECT) { - unsigned int guard = item->GetGuardingCharacterID(); - gemActor* guardActor = GEMSupervisor::GetSingleton().FindPlayerEntity(guard); - if (guard && - guard != actor->GetCharacterData()->GetCharacterID() && - guardActor && - guardActor->RangeTo(item->GetGemObject()) < RANGE_TO_SELECT) - { - psserver->SendSystemInfo(clientnum,"You notice that the item is being guarded by %s", - guardActor->GetCharacterData()->GetCharFullName()); - - return; - } + psserver->SendSystemInfo(clientnum,"You notice that the item is being guarded by %s", + guardActor->GetCharacterData()->GetCharFullName()); + return; } + } - item->ScheduleRespawn(); + // Cache values from item, because item might be deleted by Add + csString qname = item->GetQuantityName(); + gemContainer *container = dynamic_cast (this); - // Cache values from item, because item might be deleted by Add - csString qname = item->GetQuantityName(); - gemContainer *container = dynamic_cast (this); + uint32 origUID = item->GetUID(); + unsigned short origStackCount = item->GetStackCount(); - uint32 origUID = item->GetUID(); - unsigned short origStackCount = item->GetStackCount(); - - if (chardata && chardata->Inventory().Add(item,false, true, PSCHARACTER_SLOT_NONE, container)) + gemActor* gActor = dynamic_cast(actor); + psCharacter* chardata = NULL; + if (gActor) chardata = gActor->GetCharacterData(); + if (chardata && chardata->Inventory().Add(item,false, true, PSCHARACTER_SLOT_NONE, container)) + { + Client* client = actor->GetClient(); + if (!client) { - // item is deleted if we pickup money - if(item) - { - psPickupEvent evt( + Debug2(LOG_ANY,clientnum,"User action from unknown client! Clientnum:%d\n",clientnum); + return; + } + client->SetTargetObject(NULL); + // item is deleted if we pickup money + if(item) + { + item->ScheduleRespawn(); + psPickupEvent evt( + chardata->GetCharacterID(), + item->GetUID(), + item->GetStackCount(), + (int)item->GetCurrentStats()->GetQuality(), + 0 + ); + evt.FireEvent(); + } + else + { + psPickupEvent evt( chardata->GetCharacterID(), - item->GetUID(), - item->GetStackCount(), - (int)item->GetCurrentStats()->GetQuality(), + origUID, + origStackCount, + 0, 0 - ); - evt.FireEvent(); - } - else - { - psPickupEvent evt( - chardata->GetCharacterID(), - origUID, - origStackCount, - 0, - 0 - ); - evt.FireEvent(); - } - - psSystemMessage newmsg(clientnum, MSG_INFO_BASE, "%s picked up %s", actor->GetName(), qname.GetData() ); - newmsg.Multicast(actor->GetMulticastClients(),0,RANGE_TO_SELECT); + ); + evt.FireEvent(); + } + + psSystemMessage newmsg(clientnum, MSG_INFO_BASE, "%s picked up %s", actor->GetName(), qname.GetData() ); + newmsg.Multicast(actor->GetMulticastClients(),0,RANGE_TO_SELECT); - psserver->GetCharManager()->UpdateItemViews(clientnum); + psserver->GetCharManager()->UpdateItemViews(clientnum); - EntityManager::GetSingleton().RemoveActor(this); // Destroy this - } - else - { + EntityManager::GetSingleton().RemoveActor(this); // Destroy this + } + else + { - /* TODO: Include support for partial pickup of stacks + /* TODO: Include support for partial pickup of stacks - // Check if part of a stack where picked up - if (item && count > item->GetStackCount()) - { - count = count - item->GetStackCount(); - qname = psItem::GetQuantityName(item->GetName(),count); - - psSystemMessage newmsg(client, MSG_INFO, "%s picked up %s", actor->GetName(), qname.GetData() ); - newmsg.Multicast(actor->GetMulticastClients(),0,RANGE_TO_SELECT); - - psserver->GetCharManager()->UpdateItemViews(clientnum); + // Check if part of a stack where picked up + if (item && count > item->GetStackCount()) + { + count = count - item->GetStackCount(); + qname = psItem::GetQuantityName(item->GetName(),count); - csString buf; - buf.Format("%s, %s, %s, \"%s\", %d, %d", actor->GetName(), "World", "Pickup", GetName(), 0, 0); - psserver->GetLogCSV()->Write(CSV_EXCHANGES, buf); - - } - */ + psSystemMessage newmsg(client, MSG_INFO, "%s picked up %s", actor->GetName(), qname.GetData() ); + newmsg.Multicast(actor->GetMulticastClients(),0,RANGE_TO_SELECT); - // Assume inventory is full so tell player about that to. - psserver->SendSystemInfo(clientnum, "You can't carry anymore %s",GetName()); + psserver->GetCharManager()->UpdateItemViews(clientnum); + + csString buf; + buf.Format("%s, %s, %s, \"%s\", %d, %d", actor->GetName(), "World", "Pickup", GetName(), 0, 0); + psserver->GetLogCSV()->Write(CSV_EXCHANGES, buf); + } + */ + + // Assume inventory is full so tell player about that to. + psserver->SendSystemInfo(clientnum, "You can't carry anymore %s",GetName()); } - else - { - if (!actor->IsAlive()) - psserver->SendSystemInfo(clientnum,"You can't pick up items when you're dead."); - else if (!IsPickable()) - psserver->SendSystemInfo(clientnum,"You can't pick up this item."); - else - psserver->SendSystemInfo(clientnum,"You're too far away to pick up that item."); - } } else if (msg_id == "use") Index: src/server/usermanager.cpp =================================================================== --- src/server/usermanager.cpp (Revision 1785) +++ src/server/usermanager.cpp (Arbeitskopie) @@ -37,6 +37,7 @@ #include "util/serverconsole.h" #include "util/pserror.h" #include "util/psconst.h" +#include "util/strutil.h" #include "util/log.h" #include "util/eventmanager.h" @@ -374,12 +375,167 @@ { HandleBanking(client, msg.action); } + else if (msg.command == "/pickup") + { + HandlePickup(client, msg.target); + } else { psserver->SendSystemError(me->clientnum,"Command not supported by server yet."); } } +csArray UserManager::DecodeCommandArea(Client *client, + csString target) +{ + csArray result; + + if (!CacheManager::GetSingleton().GetCommandManager()->Validate(client->GetSecurityLevel(), "command area")) + { + psserver->SendSystemError(client->GetClientNum(), + "You are not allowed to use area", target.GetData()); + return result; + } + + csArray splitTarget = psSplit(target, ':'); + size_t splitSize = splitTarget.GetSize(); + + if ((2>splitSize) || (splitSize>4)) + { + psserver->SendSystemError(client->GetClientNum(), + "Try /$CMD area:item[:range[:name]]"); + return result; + } + + csString itemName = splitTarget[1]; + csString* rangeName = splitSize > 2 ? &splitTarget[2] : 0; + csString* nameFilter = splitSize > 3 ? &splitTarget[3] : 0; + + int range = 99999; /* TODO: check is this value is large enough */ + if (rangeName) + range = atoi(rangeName->GetData()); + + bool allNames = true; + if (nameFilter && (*nameFilter!="all")) + allNames = false; + + gemActor* self = client->GetActor(); + if (!self) + { + psserver->SendSystemError(client->GetClientNum(), "You do not exist..."); + return result; + } + + int mode = 0; + if (itemName == "players") + mode = 0; + else if (itemName == "actors") + mode = 1; + else if (itemName == "items") + mode = 2; + else if (itemName == "npcs") + mode = 3; + else if (itemName == "entities") + mode = 4; + else + { + psserver->SendSystemError(client->GetClientNum(), + "item must be players|actors|items|npcs|entities"); + return result; + } + + csVector3 pos; + iSector* sector; + self->GetPosition(pos, sector); + + GEMSupervisor* gem = GEMSupervisor::GetSingletonPtr(); + if (!gem) + return result; + + csRef nearlist = gem->pl->FindNearbyEntities(sector, pos, + range); + size_t count = nearlist->GetCount(); + size_t handled_entities = 0; + + csArray results; + + for (size_t i=0; iGetObjectFromEntityList(nearlist, i); + if (!nearobj) + continue; + + if (!allNames) + { + csString nearobjName = nearobj->GetName(); + if (!nearobjName.StartsWith(*nameFilter->GetData(), true)) + continue; + } + + csString newTarget; + + switch (mode) + { + case 0: // Target players + { + if (nearobj->GetClientID()) + { + newTarget.Format("pid:%d", nearobj->GetPlayerID()); + break; + } + else + continue; + } + case 1: // Target actors + { + if (nearobj->GetPlayerID()) + { + newTarget.Format("pid:%d", nearobj->GetPlayerID()); + break; + } + else + continue; + } + case 2: // Target items + { + if (nearobj->GetItem()) + { + newTarget.Format("eid:%u", nearobj->GetEntityID()); + break; + } + else + continue; + } + case 3: // Target NPCs + { + if (nearobj->GetNPCPtr()) + { + newTarget.Format("pid:%d", nearobj->GetPlayerID()); + break; + } + else + continue; + } + case 4: // Target everything + { + newTarget.Format("eid:%u", nearobj->GetEntityID()); + break; + } + } + + // Run this once for every target in range (each one will be verified and logged seperately) + result.Push(newTarget); + handled_entities++; + } + + if (!handled_entities) + { + psserver->SendSystemError(client->GetClientNum(), + "Nothing of specified type in range."); + } + return result; +} + bool UserManager::CheckForEmote(csString command, bool execute, MsgEntry *me, Client *client) { for(unsigned int i=0; i < emoteList.GetSize(); i++) @@ -875,7 +1031,6 @@ void UserManager::Who(psUserCmdMessage& msg, Client* client, int clientnum) { csString message((size_t) 1024); - csString temp((size_t) 1024); csString headerMsg("Players Currently Online"); if (!msg.filter.IsEmpty()) @@ -899,65 +1054,98 @@ unsigned count = 0; - // Guild rank, guild and title should come from acraig's player prop class. - ClientIterator i(*clients); - while(i.HasNext()) + if (msg.filter.StartsWith("area:")) { - Client *curr = i.Next(); - csString playerName(curr->GetName()); - csString guildTitle; - csString guildName; - csString format("%s"); // Player name. - - if (curr->IsSuperClient() || !curr->GetActor()) - continue; - - psGuildInfo* guild = curr->GetActor()->GetGuild(); - if (guild != NULL) + csArray filters = DecodeCommandArea(client, msg.filter); + csArray::Iterator it(filters.GetIterator()); + while (it.HasNext()) { - if (guild->id && (!guild->IsSecret() - || guild->id == client->GetGuildID())) + csString pid = it.Next(); + gemObject *object = psserver->GetAdminManager()->FindObjectByString(pid,client->GetActor()); + Client* thisclient = object->GetClient(); + if (thisclient) { - psGuildLevel* level = curr->GetActor()->GetGuildLevel(); - if (level) - { - format.Append(", %s in %s"); // Guild level title. - guildTitle = level->title; - } - else - { - format.Append(", %s"); - } - guildName = guild->name; + if (!WhoProcessClient(thisclient, client->GetGuildID(), &message, msg.filter, + false, &count)) + break; } } - temp.Format(format.GetData(), curr->GetName(), guildTitle.GetData(), - guildName.GetData()); - - csString lower(temp); - StrToLowerCase(lower); - if (!msg.filter.IsEmpty() && lower.FindStr(msg.filter) == (size_t)-1) - continue; - - if (temp.Length() + message.Length() > MAXPACKETSIZE) - break; - else - message.Append('\n'); - message.Append(temp); - - count++; } + else + { + // Guild rank, guild and title should come from acraig's player prop class. + ClientIterator i(*clients); + while (i.HasNext()) + { + Client *curr = i.Next(); + if (!WhoProcessClient(curr, client->GetGuildID(), &message, msg.filter, true, + &count)) + break; + } + } // Could be about to overflow by now, so check. + csString temp; temp.Format("%u shown from %zu players online\n", count, clients->Count()); message.Append('\n'); message.Append(temp); - psSystemMessageSafe newmsg(clientnum ,MSG_WHO, message); + psSystemMessageSafe newmsg(clientnum, MSG_WHO, message); newmsg.SendMessage(); } +bool UserManager::WhoProcessClient(Client *curr, int guildId, csString* message, csString filter, bool check, unsigned * count) +{ + if (curr->IsSuperClient() || !curr->GetActor()) + return true; + + csString temp((size_t) 1024); + csString playerName(curr->GetName()); + csString guildTitle; + csString guildName; + csString format("%s"); // Player name. + + psGuildInfo* guild = curr->GetActor()->GetGuild(); + if (guild != NULL) + { + if (guild->id && (!guild->IsSecret() || guild->id + == guildId)) + { + psGuildLevel* level = curr->GetActor()->GetGuildLevel(); + if (level) + { + format.Append(", %s in %s"); // Guild level title. + guildTitle = level->title; + } + else + { + format.Append(", %s"); + } + guildName = guild->name; + } + } + temp.Format(format.GetData(), curr->GetName(), guildTitle.GetData(), + guildName.GetData()); + + if (check) + { + csString lower(temp); + StrToLowerCase(lower); + if (!filter.IsEmpty() && lower.FindStr(filter) == (size_t) -1) + return true; + } + + if (temp.Length() + message->Length() > MAXPACKETSIZE) + return false; + else + message->Append('\n'); + message->Append(temp); + + (*count)++; + return true; +} + void UserManager::StrToLowerCase(csString& str) { for (register size_t i = 0; i < str.Length(); ++i) @@ -1155,6 +1343,17 @@ void UserManager::ReportPosition(psUserCmdMessage& msg,Client *client,int clientnum) { + + if (msg.player.StartsWith("area:")) + { + csArray filters = DecodeCommandArea(client, msg.player); + csArray::Iterator it(filters.GetIterator()); + while (it.HasNext()) { + msg.player = it.Next(); + ReportPosition(msg, client, clientnum); + } + } + gemObject *object = NULL; bool self = true; @@ -1762,6 +1961,41 @@ } } +void UserManager::HandlePickup(Client *client, csString target) +{ + if (target.StartsWith("area:")) + { + csArray filters = DecodeCommandArea(client, target); + csArray::Iterator it(filters.GetIterator()); + while (it.HasNext()) { + HandlePickup(client, it.Next()); + } + } + else if (target.StartsWith("eid:", true)) + { + gemObject* object= NULL; + GEMSupervisor *gem = GEMSupervisor::GetSingletonPtr(); + csString eid_str = target.Slice(4); + PS_ID eID = (PS_ID)atoi(eid_str.GetDataSafe() ); + if (eID) + { + object = gem->FindObject(eID); + if (object && object->GetItem()) + { + object->SendBehaviorMessage("pickup", client->GetActor() ); + } + else + { + psserver->SendSystemError(client->GetClientNum(), + "Item not found.", target.GetData()); + } + } + } + else + psserver->SendSystemError(client->GetClientNum(), + "You can't pickup %s", target.GetData()); +} + void UserManager::GiveTip(int id) { unsigned int max=CacheManager::GetSingleton().GetTipLength(); Index: src/server/entitymanager.cpp =================================================================== --- src/server/entitymanager.cpp (Revision 1785) +++ src/server/entitymanager.cpp (Arbeitskopie) @@ -1147,17 +1147,6 @@ object->SendTargetStatDR(client); } - // Check to see if this client has the admin level to move this particular item - bool securityOverride = CacheManager::GetSingleton().GetCommandManager()->Validate(client->GetSecurityLevel(), - "move unpickupables/spawns"); - if ( action == "pickup" - && dynamic_cast (object) - && ((dynamic_cast (object))->IsPickable() - || securityOverride)) - { - client->SetTargetObject(NULL); - } - Debug5(LOG_USER,client->GetClientNum(), "User Action: %s %s %s:%d\n",client->GetName(), (const char *)action, (object)?object->GetName():"None", Index: src/server/database/mysql/command_access.sql =================================================================== --- src/server/database/mysql/command_access.sql (Revision 1785) +++ src/server/database/mysql/command_access.sql (Arbeitskopie) @@ -239,6 +239,11 @@ INSERT INTO command_group_assignment VALUES( "quest list others", 24 ); INSERT INTO command_group_assignment VALUES( "quest list others", 23 ); INSERT INTO command_group_assignment VALUES( "quest list others", 22 ); +INSERT INTO command_group_assignment VALUES( "pickup override", 30 ); +INSERT INTO command_group_assignment VALUES( "pickup override", 25 ); +INSERT INTO command_group_assignment VALUES( "pickup override", 24 ); +INSERT INTO command_group_assignment VALUES( "pickup override", 23 ); +INSERT INTO command_group_assignment VALUES( "pickup override", 22 ); # GM1 and above Index: src/server/usermanager.h =================================================================== --- src/server/usermanager.h (Revision 1785) +++ src/server/usermanager.h (Arbeitskopie) @@ -131,7 +131,19 @@ * @param clientnum The client id number of the requesting client. */ void Who(psUserCmdMessage& msg, Client* client, int clientnum); + + /** Formats output of a player and adds it to a message. + * + * @param client The client that is to be formated + * @param guildId The guildId of the caller + * @param message The target csString object + * @param filter What clients + * @param check If a check against filter should be done + * @param count COunter to be increased in case of a successfull match + */ + bool WhoProcessClient(Client *curr, int guildId, csString* message, csString filter, bool check, unsigned * count); + /** Converts a string to lowercase. * * @param str The string. @@ -269,6 +281,12 @@ * @param client The client that issued the command. */ void HandleBanking(Client *client, csString accountType); + + /** Handles a player command to pickup an item. + * @param client The client that issued the command. + * @target description of the item to be picked up. + */ + void HandlePickup(Client *client, csString target); /** Handles a player request to 'use' the targeted item. * @@ -301,6 +319,13 @@ void HandleTargetEvent(MsgEntry *me); void HandleEntranceMessage( MsgEntry* me, Client *client ); + /** Decodes an area: expression + * + * @param client The client of the caller + * @param target The area: expression + * */ + csArray DecodeCommandArea(Client *client, csString target); + void SwitchAttackTarget(Client *targeter, Client *targeted); /** Process an emote command. Index: src/common/net/messages.cpp =================================================================== --- src/common/net/messages.cpp (Revision 1785) +++ src/common/net/messages.cpp (Arbeitskopie) @@ -1328,6 +1328,11 @@ action = words.Get(1); return; } + if ( command == "/pickup" ) + { + target = words.Get(1); + return; + } valid = false; } Index: src/common/net/messages.h =================================================================== --- src/common/net/messages.h (Revision 1785) +++ src/common/net/messages.h (Arbeitskopie) @@ -1092,7 +1092,7 @@ class psUserCmdMessage : public psMessageCracker { public: - csString command,player,filter,action,text; + csString command,player,filter,action,text,target; int dice,sides; int level; csString stance; Index: src/client/cmdusers.cpp =================================================================== --- src/client/cmdusers.cpp (Revision 1785) +++ src/client/cmdusers.cpp (Arbeitskopie) @@ -657,7 +657,19 @@ else object = FindEntityWithName(words[1]); if (object) - psengine->GetCharManager()->SetTarget(object,"pickup"); + { + psengine->GetCharManager()->SetTarget(object,"select"); + PS_ID mappedID = object->GetID(); + csString newCmd; + newCmd.Format("/pickup eid:%d", mappedID); + psUserCmdMessage cmdmsg(newCmd); + msgqueue->SendMessage(cmdmsg.msg); + } + else + { + psUserCmdMessage cmdmsg(cmd); + msgqueue->SendMessage(cmdmsg.msg); + } } else if ( words[0] == "/game" )