diff --git a/src/engine/core/obj_handler.cpp b/src/engine/core/obj_handler.cpp index caa0c85d9..7cb23b5f8 100644 --- a/src/engine/core/obj_handler.cpp +++ b/src/engine/core/obj_handler.cpp @@ -293,30 +293,56 @@ RoomVnum get_room_where_obj(ObjData *obj, bool deep) { // issue #3563: единый лог о пропаже вещи у игрока. Ищет игрока-владельца // (инвентарь/экипировка/его контейнер) и пишет в syslog причину. Звать НАДО // до отвязки вещи от владельца, иначе get_carried_by/get_worn_by уже пустые. +// issue #3563: описание держателя вещи для лога (родительный падеж) -- игрок или +// чармис игрока. "" -> обычный моб/никто (не логируем). Для чармиса поднимаемся +// по цепочке мастеров до игрока-владельца. +std::string ObjHolderLogDesc(CharData *holder) { + if (!holder) { + return ""; + } + char buf[kMaxStringLength]; + if (!holder->IsNpc()) { + snprintf(buf, sizeof(buf), "игрока %s", GET_NAME(holder)); + return buf; + } + if (IsCharmice(holder)) { + // чармис висит напрямую на игроке (конвенция: pk.cpp, equipment.cpp и т.п.) + CharData *m = holder->get_master(); + if (m && !m->IsNpc()) { + snprintf(buf, sizeof(buf), "чармиса '%s' игрока %s", GET_NAME(holder), GET_NAME(m)); + } else { + snprintf(buf, sizeof(buf), "чармиса '%s' (потерявшего владельца)", GET_NAME(holder)); + } + return buf; + } + return ""; +} + void LogPlayerObjLoss(ObjData *obj, const char *reason) { if (!obj) { return; } - CharData *owner = obj->get_carried_by() ? obj->get_carried_by() + CharData *holder = obj->get_carried_by() ? obj->get_carried_by() : (obj->get_worn_by() ? obj->get_worn_by() : nullptr); - if (!owner) { - // вещь в контейнере -- ищем игрока-владельца контейнера + if (!holder) { + // вещь в контейнере -- ищем владельца контейнера for (ObjData *cont = obj->get_in_obj(); cont; cont = cont->get_in_obj()) { if (cont->get_carried_by()) { - owner = cont->get_carried_by(); + holder = cont->get_carried_by(); break; } if (cont->get_worn_by()) { - owner = cont->get_worn_by(); + holder = cont->get_worn_by(); break; } } } - if (!owner || owner->IsNpc()) { + const std::string who = ObjHolderLogDesc(holder); + if (who.empty()) { // не игрок и не чармис игрока -- не логируем return; } - log("[Obj loss] у игрока %s пропала вещь '%s' vnum == %d (%s), timer == %d, прочность == %d/%d", - GET_NAME(owner), obj->get_PName(grammar::ECase::kNom).c_str(), GET_OBJ_VNUM(obj), + log("[Obj loss] у %s пропала вещь '%s' vnum == %d (%s), timer == %d, прочность == %d/%d", + who.c_str(), obj->get_PName(grammar::ECase::kNom).c_str(), GET_OBJ_VNUM(obj), reason, obj->get_timer(), obj->get_current_durability(), obj->get_maximum_durability()); } diff --git a/src/engine/core/obj_handler.h b/src/engine/core/obj_handler.h index c2cd6788d..b11b656b6 100644 --- a/src/engine/core/obj_handler.h +++ b/src/engine/core/obj_handler.h @@ -8,6 +8,8 @@ #include "engine/structs/structs.h" // RoomRnum / RoomVnum +#include + class CharData; class ObjData; @@ -22,6 +24,8 @@ RoomVnum get_room_where_obj(ObjData *obj, bool deep = false); void ExtractObjFromWorld(ObjData *obj, bool showlog = false); // issue #3563: пишет в syslog о пропаже вещи у игрока; звать до отвязки от владельца. void LogPlayerObjLoss(ObjData *obj, const char *reason); +// issue #3563: описание держателя для лога -- "игрока X" / "чармиса 'Y' игрока X" / "" (моб). +std::string ObjHolderLogDesc(CharData *holder); void UpdateCharObjects(CharData *ch); void DropObjOnZoneReset(CharData *ch, ObjData *obj, bool inv, bool zone_reset); int get_object_low_rent(ObjData *obj); diff --git a/src/engine/ui/cmd/do_steal.cpp b/src/engine/ui/cmd/do_steal.cpp index 3137a3750..d1d53348f 100644 --- a/src/engine/ui/cmd/do_steal.cpp +++ b/src/engine/ui/cmd/do_steal.cpp @@ -14,6 +14,7 @@ #include "gameplay/mechanics/mount.h" #include "engine/entities/obj_data.h" #include "engine/core/char_equip_flags.h" +#include "engine/core/obj_handler.h" // ObjHolderLogDesc (issue #3563) #include "gameplay/mechanics/equipment.h" #include "gameplay/mechanics/inventory.h" #include "gameplay/skills/skills.h" @@ -99,9 +100,11 @@ void go_steal(CharData *ch, CharData *vict, char *obj_name) { act("$n украл$g $o3 у $N1.", false, ch, obj, vict, kToNotVict | kToArenaListen); // issue #3563: кража НАДЕТОЙ вещи (имм или у спящей жертвы) -- она уходит // через UnequipChar, без obj_from_char, поэтому логируем отдельно. + // Для чармиса-жертвы в описании будет имя чармиса и его владельца. + const std::string who = ObjHolderLogDesc(vict); log("[Steal eq] %s украл надетую вещь '%s' (vnum %d) у %s", GET_NAME(ch), obj->get_PName(grammar::ECase::kNom).c_str(), - GET_OBJ_VNUM(obj), GET_NAME(vict)); + GET_OBJ_VNUM(obj), who.empty() ? GET_NAME(vict) : who.c_str()); PlaceObjToInventory(UnequipChar(vict, eq_pos, CharEquipFlags()), ch); } } diff --git a/src/gameplay/skills/punctual_style.cpp b/src/gameplay/skills/punctual_style.cpp index 3fd61ce8f..d6a36754a 100644 --- a/src/gameplay/skills/punctual_style.cpp +++ b/src/gameplay/skills/punctual_style.cpp @@ -648,13 +648,14 @@ void PerformPunctualHit(CharData *ch, CharData *victim, HitData &hit_data) { if (!victim->IsNpc() && ROOM_FLAGGED(victim->in_room, ERoomFlag::kArena)) { PlaceObjToInventory(obj, victim); } else { - // issue #3563: точный стиль выбил вещь НА ЗЕМЛЮ (не в инвентарь). Для игрока - // логируем -- иначе потом непонятно, куда делось оружие (валяется в комнате, - // владелец не находит). Мобов не логируем, чтобы не шуметь. - if (!victim->IsNpc()) { + // issue #3563: точный стиль выбил вещь НА ЗЕМЛЮ (не в инвентарь). Логируем для + // игрока и чармиса игрока -- иначе непонятно, куда делось оружие (валяется в + // комнате, владелец не находит). Обычных мобов не логируем, чтобы не шуметь. + const std::string who = ObjHolderLogDesc(victim); + if (!who.empty()) { log("[Punctual -> ground] %s выбил точным стилем '%s' (vnum %d) из рук %s -> комната [%d] %s", GET_NAME(ch), obj->get_PName(grammar::ECase::kNom).c_str(), GET_OBJ_VNUM(obj), - GET_NAME(victim), world[victim->in_room]->vnum, world[victim->in_room]->name); + who.c_str(), world[victim->in_room]->vnum, world[victim->in_room]->name); } PlaceObjToRoom(obj, victim->in_room); CheckObjDecay(obj);