From ffbd31ecd822ab665dbb0e439df675bab0549691 Mon Sep 17 00:00:00 2001 From: bikbai Date: Sun, 4 Oct 2020 17:16:01 +0300 Subject: [PATCH 01/40] groupfight#1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Обособил нужные функции для дальнейших доделок-переделок --- CMakeLists.txt | 2 +- src/act.other.cpp | 758 ++------------------------------------ src/comm.h | 1 + src/groups/group.cpp | 710 +++++++++++++++++++++++++++++++++++ src/groups/group.h | 11 + src/groups/grp.member.cpp | 1 + src/groups/grp.member.h | 15 + src/interpreter.cpp | 113 +++--- 8 files changed, 821 insertions(+), 790 deletions(-) create mode 100644 src/groups/group.cpp create mode 100644 src/groups/group.h create mode 100644 src/groups/grp.member.cpp create mode 100644 src/groups/grp.member.h diff --git a/CMakeLists.txt b/CMakeLists.txt index f44e3b68c..ae7e60369 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -203,7 +203,7 @@ set(SOURCES src/core/affect_data.cpp src/cmd/track.cpp src/cmd/telegram.cpp - src/cmd/hire.cpp) + src/cmd/hire.cpp src/groups/grp.member.cpp src/groups/grp.member.h src/groups/group.cpp src/groups/group.h) set(HEADERS src/cmd/telegram.h diff --git a/src/act.other.cpp b/src/act.other.cpp index 7eb1f0222..7f43ad00f 100644 --- a/src/act.other.cpp +++ b/src/act.other.cpp @@ -14,42 +14,43 @@ #include "act.other.hpp" -#include "world.objects.hpp" -#include "object.prototypes.hpp" -#include "logger.hpp" -#include "obj.hpp" +#include "char_obj_utils.inl" +#include "chars/char.hpp" +#include "chars/char_player.hpp" #include "comm.h" -#include "interpreter.h" -#include "handler.h" -#include "db.h" -#include "spells.h" -#include "skills.h" -#include "screen.h" -#include "house.h" +#include "conf.h" #include "constants.h" +#include "db.h" +#include "depot.hpp" #include "dg_scripts.h" -#include "fightsystem/pk.h" +#include "features.hpp" #include "fightsystem/fight.h" #include "fightsystem/fight_hit.hpp" +#include "fightsystem/pk.h" +#include "handler.h" +#include "house.h" +#include "interpreter.h" +#include "logger.hpp" #include "magic.h" -#include "features.hpp" -#include "depot.hpp" +#include "msdp.constants.hpp" +#include "noob.hpp" +#include "obj.hpp" +#include "object.prototypes.hpp" +#include "objsave.h" #include "privilege.hpp" #include "random.hpp" -#include "chars/char.hpp" -#include "chars/char_player.hpp" #include "remember.hpp" #include "room.hpp" -#include "objsave.h" +#include "screen.h" #include "shop_ext.hpp" -#include "noob.hpp" +#include "skills.h" +#include "spells.h" #include "structs.h" -#include "conf.h" #include "sysdep.h" -#include "char_obj_utils.inl" -#include "msdp.constants.hpp" -#include +#include "world.objects.hpp" + +#include #include #include #include @@ -73,12 +74,12 @@ extern struct skillvariables_insgem insgem_vars; void list_feats(CHAR_DATA * ch, CHAR_DATA * vict, bool all_feats); void list_skills(CHAR_DATA * ch, CHAR_DATA * vict, const char* filter = NULL); void list_spells(CHAR_DATA * ch, CHAR_DATA * vict, int all_spells); -void appear(CHAR_DATA * ch); + void write_aliases(CHAR_DATA * ch); void perform_immort_vis(CHAR_DATA * ch); void do_gen_comm(CHAR_DATA *ch, char *argument, int cmd, int subcmd); -extern char *color_value(CHAR_DATA * ch, int real, int max); -int posi_value(int real, int max); + + int invalid_no_class(CHAR_DATA * ch, const OBJ_DATA * obj); extern void split_or_clan_tax(CHAR_DATA *ch, long amount); extern bool is_wear_light(CHAR_DATA *ch); @@ -94,10 +95,6 @@ void do_spells(CHAR_DATA *ch, char *argument, int cmd, int subcmd); void do_features(CHAR_DATA *ch, char *argument, int cmd, int subcmd); void do_skills(CHAR_DATA *ch, char *argument, int cmd, int subcmd); void do_visible(CHAR_DATA *ch, char *argument, int cmd, int subcmd); -void print_group(CHAR_DATA * ch); -void do_group(CHAR_DATA *ch, char *argument, int cmd, int subcmd); -void do_ungroup(CHAR_DATA *ch, char *argument, int cmd, int subcmd); -void do_report(CHAR_DATA *ch, char *argument, int cmd, int subcmd); void do_split(CHAR_DATA *ch, char *argument, int cmd, int subcmd); void do_split(CHAR_DATA *ch, char *argument, int cmd, int subcmd,int currency); void do_use(CHAR_DATA *ch, char *argument, int cmd, int subcmd); @@ -110,7 +107,6 @@ void do_color(CHAR_DATA *ch, char *argument, int cmd, int subcmd); void do_recall(CHAR_DATA *ch, char *argument, int cmd, int subcmd); void do_dig(CHAR_DATA *ch, char *argument, int cmd, int subcmd); void do_summon(CHAR_DATA *ch, char *argument, int cmd, int subcmd); -bool is_dark(room_rnum room); void do_antigods(CHAR_DATA *ch, char* /*argument*/, int/* cmd*/, int/* subcmd*/) { @@ -920,709 +916,7 @@ void do_courage(CHAR_DATA *ch, char* /*argument*/, int/* cmd*/, int/* subcmd*/) act(buf, FALSE, ch, obj, 0, TO_ROOM | TO_ARENA_LISTEN); } -int max_group_size(CHAR_DATA *ch) -{ - int bonus_commander = 0; - if (AFF_FLAGGED(ch, EAffectFlag::AFF_COMMANDER)) bonus_commander = VPOSI((ch->get_skill(SKILL_LEADERSHIP) - 120) / 10, 0 , 8); - - return MAX_GROUPED_FOLLOWERS + (int) VPOSI((ch->get_skill(SKILL_LEADERSHIP) - 80) / 5, 0, 4) + bonus_commander; -} -bool is_group_member(CHAR_DATA *ch, CHAR_DATA *vict) -{ - if (IS_NPC(vict) - || !AFF_FLAGGED(vict, EAffectFlag::AFF_GROUP) - || vict->get_master() != ch) - { - return false; - } - else - { - return true; - } -} - -int perform_group(CHAR_DATA * ch, CHAR_DATA * vict) -{ - if (AFF_FLAGGED(vict, EAffectFlag::AFF_GROUP) - || AFF_FLAGGED(vict, EAffectFlag::AFF_CHARM) - || MOB_FLAGGED(vict, MOB_ANGEL) - || MOB_FLAGGED(vict, MOB_GHOST) - || IS_HORSE(vict)) - { - return (FALSE); - } - - AFF_FLAGS(vict).set(EAffectFlag::AFF_GROUP); - if (ch != vict) - { - act("$N принят$A в члены вашего кружка (тьфу-ты, группы :).", FALSE, ch, 0, vict, TO_CHAR); - act("Вы приняты в группу $n1.", FALSE, ch, 0, vict, TO_VICT); - act("$N принят$A в группу $n1.", FALSE, ch, 0, vict, TO_NOTVICT | TO_ARENA_LISTEN); - } - return (TRUE); -} - -/** -* Смена лидера группы на персонажа с макс лидеркой. -* Сам лидер при этом остается в группе, если он живой. -* \param vict - новый лидер, если смена происходит по команде 'гр лидер имя', -* старый лидер соответственно группится обратно, если нулевой, то считаем, что -* произошла смерть старого лидера и новый выбирается по наибольшей лидерке. -*/ -void change_leader(CHAR_DATA *ch, CHAR_DATA *vict) -{ - if (IS_NPC(ch) - || ch->has_master() - || !ch->followers) - { - return; - } - - CHAR_DATA *leader = vict; - if (!leader) - { - // лидер умер, ищем согрупника с максимальным скиллом лидерки - for (struct follow_type *l = ch->followers; l; l = l->next) - { - if (!is_group_member(ch, l->follower)) - continue; - if (!leader) - leader = l->follower; - else if (l->follower->get_skill(SKILL_LEADERSHIP) > leader->get_skill(SKILL_LEADERSHIP)) - leader = l->follower; - } - } - - if (!leader) - { - return; - } - - // для реследования используем стандартные функции - std::vector temp_list; - for (struct follow_type *n = 0, *l = ch->followers; l; l = n) - { - n = l->next; - if (!is_group_member(ch, l->follower)) - { - continue; - } - else - { - CHAR_DATA *temp_vict = l->follower; - if (temp_vict->has_master() - && stop_follower(temp_vict, SF_SILENCE)) - { - continue; - } - - if (temp_vict != leader) - { - temp_list.push_back(temp_vict); - } - } - } - - // вся эта фигня только для того, чтобы при реследовании пройтись по списку в обратном - // направлении и сохранить относительный порядок следования в группе - if (!temp_list.empty()) - { - for (std::vector::reverse_iterator it = temp_list.rbegin(); it != temp_list.rend(); ++it) - { - leader->add_follower_silently(*it); - } - } - - // бывшего лидера последним закидываем обратно в группу, если он живой - if (vict) - { - // флаг группы надо снять, иначе при регрупе не будет писаться о старом лидере - //AFF_FLAGS(ch).unset(EAffectFlag::AFF_GROUP); - ch->removeGroupFlags(); - leader->add_follower_silently(ch); - } - - if (!leader->followers) - { - return; - } - - ch->dps_copy(leader); - perform_group(leader, leader); - int followers = 0; - for (struct follow_type *f = leader->followers; f; f = f->next) - { - if (followers < max_group_size(leader)) - { - if (perform_group(leader, f->follower)) - ++followers; - } - else - { - send_to_char("Вы больше никого не можете принять в группу.\r\n", ch); - return; - } - } -} - -void print_one_line(CHAR_DATA * ch, CHAR_DATA * k, int leader, int header) -{ - int ok, ok2, div; - const char *WORD_STATE[] = { "При смерти", - "Оч.тяж.ран", - "Оч.тяж.ран", - " Тяж.ранен", - " Тяж.ранен", - " Ранен ", - " Ранен ", - " Ранен ", - "Лег.ранен ", - "Лег.ранен ", - "Слег.ранен", - " Невредим " - }; - const char *MOVE_STATE[] = { "Истощен", - "Истощен", - "О.устал", - " Устал ", - " Устал ", - "Л.устал", - "Л.устал", - "Хорошо ", - "Хорошо ", - "Хорошо ", - "Отдохн.", - " Полон " - }; - const char *POS_STATE[] = { "Умер", - "Истекает кровью", - "При смерти", - "Без сознания", - "Спит", - "Отдыхает", - "Сидит", - "Сражается", - "Стоит" - }; - - if (IS_NPC(k)) - { - if (!header) -// send_to_char("Персонаж | Здоровье |Рядом| Доп | Положение | Лояльн.\r\n",ch); - send_to_char("Персонаж | Здоровье |Рядом| Аффект | Положение\r\n", ch); - std::string name = GET_NAME(k); - name[0] = UPPER(name[0]); - sprintf(buf, "%s%-20s%s|", CCIBLU(ch, C_NRM), - name.substr(0, 20).c_str(), CCNRM(ch, C_NRM)); - sprintf(buf + strlen(buf), "%s%10s%s|", - color_value(ch, GET_HIT(k), GET_REAL_MAX_HIT(k)), - WORD_STATE[posi_value(GET_HIT(k), GET_REAL_MAX_HIT(k)) + 1], CCNRM(ch, C_NRM)); - - ok = ch->in_room == IN_ROOM(k); - sprintf(buf + strlen(buf), "%s%5s%s|", - ok ? CCGRN(ch, C_NRM) : CCRED(ch, C_NRM), ok ? " Да " : " Нет ", CCNRM(ch, C_NRM)); - - sprintf(buf + strlen(buf), " %s%s%s%s%s%s%s%s%s%s%s%s%s |", - CCIRED(ch, C_NRM), - AFF_FLAGGED(k, EAffectFlag::AFF_SANCTUARY) ? "О" : (AFF_FLAGGED(k, EAffectFlag::AFF_PRISMATICAURA) ? "П" : " "), - CCGRN(ch, C_NRM), - AFF_FLAGGED(k, EAffectFlag::AFF_WATERBREATH) ? "Д" : " ", CCICYN(ch, C_NRM), - AFF_FLAGGED(k, EAffectFlag::AFF_INVISIBLE) ? "Н" : " ", CCIYEL(ch, C_NRM), - (AFF_FLAGGED(k, EAffectFlag::AFF_SINGLELIGHT) - || AFF_FLAGGED(k, EAffectFlag::AFF_HOLYLIGHT) - || (GET_EQ(k, WEAR_LIGHT) - && GET_OBJ_VAL(GET_EQ(k, WEAR_LIGHT), 2))) ? "С" : " ", - CCIBLU(ch, C_NRM), AFF_FLAGGED(k, EAffectFlag::AFF_FLY) ? "Л" : " ", CCYEL(ch, C_NRM), - k->low_charm() ? "Т" : " ", CCNRM(ch, C_NRM)); - - sprintf(buf + strlen(buf), "%-15s", POS_STATE[(int) GET_POS(k)]); - - act(buf, FALSE, ch, 0, k, TO_CHAR); - } - else - { - if (!header) - send_to_char - ("Персонаж | Здоровье |Энергия|Рядом|Учить| Аффект | Кто | Держит строй | Положение \r\n", ch); - - std::string name = GET_NAME(k); - name[0] = UPPER(name[0]); - sprintf(buf, "%s%-20s%s|", CCIBLU(ch, C_NRM), name.c_str(), CCNRM(ch, C_NRM)); - sprintf(buf + strlen(buf), "%s%10s%s|", - color_value(ch, GET_HIT(k), GET_REAL_MAX_HIT(k)), - WORD_STATE[posi_value(GET_HIT(k), GET_REAL_MAX_HIT(k)) + 1], CCNRM(ch, C_NRM)); - - sprintf(buf + strlen(buf), "%s%7s%s|", - color_value(ch, GET_MOVE(k), GET_REAL_MAX_MOVE(k)), - MOVE_STATE[posi_value(GET_MOVE(k), GET_REAL_MAX_MOVE(k)) + 1], CCNRM(ch, C_NRM)); - - ok = ch->in_room == IN_ROOM(k); - sprintf(buf + strlen(buf), "%s%5s%s|", - ok ? CCGRN(ch, C_NRM) : CCRED(ch, C_NRM), ok ? " Да " : " Нет ", CCNRM(ch, C_NRM)); - - if ((!IS_MANA_CASTER(k) && !MEMQUEUE_EMPTY(k)) || - (IS_MANA_CASTER(k) && GET_MANA_STORED(k) < GET_MAX_MANA(k))) - { - div = mana_gain(k); - if (div > 0) - { - if (!IS_MANA_CASTER(k)) - { - ok2 = MAX(0, 1 + GET_MEM_TOTAL(k) - GET_MEM_COMPLETED(k)); - ok2 = ok2 * 60 / div; // время мема в сек - } - else - { - ok2 = MAX(0, 1 + GET_MAX_MANA(k) - GET_MANA_STORED(k)); - ok2 = ok2 / div; // время восстановления в секундах - } - ok = ok2 / 60; - ok2 %= 60; - if (ok > 99) - sprintf(buf + strlen(buf), "&g%5d&n|", ok); - else - sprintf(buf + strlen(buf), "&g%2d:%02d&n|", ok, ok2); - } - else - { - sprintf(buf + strlen(buf), "&r -&n|"); - } - } - else - sprintf(buf + strlen(buf), " |"); - - sprintf(buf + strlen(buf), " %s%s%s%s%s%s%s%s%s%s%s%s%s |", - CCIRED(ch, C_NRM), AFF_FLAGGED(k, EAffectFlag::AFF_SANCTUARY) ? "О" : (AFF_FLAGGED(k, EAffectFlag::AFF_PRISMATICAURA) - ? "П" : " "), CCGRN(ch, - C_NRM), - AFF_FLAGGED(k, EAffectFlag::AFF_WATERBREATH) ? "Д" : " ", CCICYN(ch, - C_NRM), - AFF_FLAGGED(k, EAffectFlag::AFF_INVISIBLE) ? "Н" : " ", CCIYEL(ch, C_NRM), (AFF_FLAGGED(k, EAffectFlag::AFF_SINGLELIGHT) - || AFF_FLAGGED(k, EAffectFlag::AFF_HOLYLIGHT) - || (GET_EQ(k, WEAR_LIGHT) - && - GET_OBJ_VAL(GET_EQ - (k, WEAR_LIGHT), - 2))) ? "С" : " ", - CCIBLU(ch, C_NRM), AFF_FLAGGED(k, EAffectFlag::AFF_FLY) ? "Л" : " ", CCYEL(ch, C_NRM), - k->ahorse() ? "В" : " ", CCNRM(ch, C_NRM)); - - sprintf(buf + strlen(buf), "%5s|", leader ? "Лидер" : ""); - ok = PRF_FLAGGED(k, PRF_SKIRMISHER); - sprintf(buf + strlen(buf), "%s%-14s%s|", ok ? CCGRN(ch, C_NRM) : CCNRM(ch, C_NRM), ok ? " Да " : " Нет ", CCNRM(ch, C_NRM)); - sprintf(buf + strlen(buf), " %s", POS_STATE[(int) GET_POS(k)]); - act(buf, FALSE, ch, 0, k, TO_CHAR); - } -} - -void print_list_group(CHAR_DATA *ch) -{ - CHAR_DATA *k; - struct follow_type *f; - int count = 1; - k = (ch->has_master() ? ch->get_master() : ch); - if (AFF_FLAGGED(ch, EAffectFlag::AFF_GROUP)) - { - send_to_char("Ваша группа состоит из:\r\n", ch); - if (AFF_FLAGGED(k, EAffectFlag::AFF_GROUP)) - { - sprintf(buf1, "Лидер: %s\r\n", GET_NAME(k)); - send_to_char(buf1, ch); - } - - for (f = k->followers; f; f = f->next) - { - if (!AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP)) - { - continue; - } - sprintf(buf1, "%d. Согруппник: %s\r\n", count, GET_NAME(f->follower)); - send_to_char(buf1, ch); - count++; - } - } - else - { - send_to_char("Но вы же не член (в лучшем смысле этого слова) группы!\r\n", ch); - } -} - -void print_group(CHAR_DATA * ch) -{ - int gfound = 0, cfound = 0; - CHAR_DATA *k; - struct follow_type *f, *g; - - k = ch->has_master() ? ch->get_master() : ch; - if (!IS_NPC(ch)) - ch->desc->msdp_report(msdp::constants::GROUP); - - if (AFF_FLAGGED(ch, EAffectFlag::AFF_GROUP)) - { - send_to_char("Ваша группа состоит из:\r\n", ch); - if (AFF_FLAGGED(k, EAffectFlag::AFF_GROUP)) - { - print_one_line(ch, k, TRUE, gfound++); - } - - for (f = k->followers; f; f = f->next) - { - if (!AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP)) - { - continue; - } - print_one_line(ch, f->follower, FALSE, gfound++); - } - } - - for (f = ch->followers; f; f = f->next) - { - if (!(AFF_FLAGGED(f->follower, EAffectFlag::AFF_CHARM) - || MOB_FLAGGED(f->follower, MOB_ANGEL)|| MOB_FLAGGED(f->follower, MOB_GHOST))) - { - continue; - } - if (!cfound) - send_to_char("Ваши последователи:\r\n", ch); - print_one_line(ch, f->follower, FALSE, cfound++); - } - if (!gfound && !cfound) - { - send_to_char("Но вы же не член (в лучшем смысле этого слова) группы!\r\n", ch); - return; - } - if (PRF_FLAGGED(ch, PRF_SHOWGROUP)) - { - for (g = k->followers, cfound = 0; g; g = g->next) - { - for (f = g->follower->followers; f; f = f->next) - { - if (!(AFF_FLAGGED(f->follower, EAffectFlag::AFF_CHARM) - || MOB_FLAGGED(f->follower, MOB_ANGEL) || MOB_FLAGGED(f->follower, MOB_GHOST)) - || !AFF_FLAGGED(ch, EAffectFlag::AFF_GROUP)) - { - continue; - } - - if (f->follower->get_master() == ch - || !AFF_FLAGGED(f->follower->get_master(), EAffectFlag::AFF_GROUP)) - { - continue; - } - - // shapirus: при включенном режиме не показываем клонов и хранителей - if (PRF_FLAGGED(ch, PRF_NOCLONES) - && IS_NPC(f->follower) - && (MOB_FLAGGED(f->follower, MOB_CLONE) - || GET_MOB_VNUM(f->follower) == MOB_KEEPER)) - { - continue; - } - - if (!cfound) - { - send_to_char("Последователи членов вашей группы:\r\n", ch); - } - print_one_line(ch, f->follower, FALSE, cfound++); - } - - if (ch->has_master()) - { - if (!(AFF_FLAGGED(g->follower, EAffectFlag::AFF_CHARM) - || MOB_FLAGGED(g->follower, MOB_ANGEL) || MOB_FLAGGED(g->follower, MOB_GHOST)) - || !AFF_FLAGGED(ch, EAffectFlag::AFF_GROUP)) - { - continue; - } - - // shapirus: при включенном режиме не показываем клонов и хранителей - if (PRF_FLAGGED(ch, PRF_NOCLONES) - && IS_NPC(g->follower) - && (MOB_FLAGGED(g->follower, MOB_CLONE) - || GET_MOB_VNUM(g->follower) == MOB_KEEPER)) - { - continue; - } - - if (!cfound) - { - send_to_char("Последователи членов вашей группы:\r\n", ch); - } - print_one_line(ch, g->follower, FALSE, cfound++); - } - } - } -} - -void do_group(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) -{ - CHAR_DATA *vict; - struct follow_type *f; - int found, f_number; - - argument = one_argument(argument, buf); - - if (!*buf) - { - print_group(ch); - return; - } - - if (!str_cmp(buf, "список")) - { - print_list_group(ch); - return; - } - - if (GET_POS(ch) < POS_RESTING) - { - send_to_char("Трудно управлять группой в таком состоянии.\r\n", ch); - return; - } - - if (ch->has_master()) - { - act("Вы не можете управлять группой. Вы еще не ведущий.", FALSE, ch, 0, 0, TO_CHAR); - return; - } - - if (!ch->followers) - { - send_to_char("За вами никто не следует.\r\n", ch); - return; - } - - - -// вычисляем количество последователей - for (f_number = 0, f = ch->followers; f; f = f->next) - { - if (AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP)) - { - f_number++; - } - } - - if (!str_cmp(buf, "all") - || !str_cmp(buf, "все")) - { - perform_group(ch, ch); - for (found = 0, f = ch->followers; f; f = f->next) - { - if ((f_number + found) >= max_group_size(ch)) - { - send_to_char("Вы больше никого не можете принять в группу.\r\n", ch); - return; - } - found += perform_group(ch, f->follower); - } - - if (!found) - { - send_to_char("Все, кто за вами следуют, уже включены в вашу группу.\r\n", ch); - } - - return; - } - else if (!str_cmp(buf, "leader") || !str_cmp(buf, "лидер")) - { - vict = get_player_vis(ch, argument, FIND_CHAR_WORLD); - // added by WorM (Видолюб) Если найден клон и его хозяин персонаж - // а то чото как-то глючно Двойник %1 не является членом вашей группы. - if (vict - && IS_NPC(vict) - && MOB_FLAGGED(vict, MOB_CLONE) - && AFF_FLAGGED(vict, EAffectFlag::AFF_CHARM) - && vict->has_master() - && !IS_NPC(vict->get_master())) - { - if (CAN_SEE(ch, vict->get_master())) - { - vict = vict->get_master(); - } - else - { - vict = NULL; - } - } - - // end by WorM - if (!vict) - { - send_to_char("Нет такого персонажа.\r\n", ch); - return; - } - else if (vict == ch) - { - send_to_char("Вы и так лидер группы...\r\n", ch); - return; - } - else if (!AFF_FLAGGED(vict, EAffectFlag::AFF_GROUP) - || vict->get_master() != ch) - { - send_to_char(ch, "%s не является членом вашей группы.\r\n", GET_NAME(vict)); - return; - } - change_leader(ch, vict); - return; - } - - if (!(vict = get_char_vis(ch, buf, FIND_CHAR_ROOM))) - { - send_to_char(NOPERSON, ch); - } - else if ((vict->get_master() != ch) && (vict != ch)) - { - act("$N2 нужно следовать за вами, чтобы стать членом вашей группы.", FALSE, ch, 0, vict, TO_CHAR); - } - else - { - if (!AFF_FLAGGED(vict, EAffectFlag::AFF_GROUP)) - { - if (AFF_FLAGGED(vict, EAffectFlag::AFF_CHARM) || MOB_FLAGGED(vict, MOB_ANGEL) || MOB_FLAGGED(vict, MOB_GHOST) || IS_HORSE(vict)) - { - send_to_char("Только равноправные персонажи могут быть включены в группу.\r\n", ch); - send_to_char("Только равноправные персонажи могут быть включены в группу.\r\n", vict); - }; - if (f_number >= max_group_size(ch)) - { - send_to_char("Вы больше никого не можете принять в группу.\r\n", ch); - return; - } - perform_group(ch, ch); - perform_group(ch, vict); - } - else if (ch != vict) - { - act("$N исключен$A из состава вашей группы.", FALSE, ch, 0, vict, TO_CHAR); - act("Вы исключены из группы $n1!", FALSE, ch, 0, vict, TO_VICT); - act("$N был$G исключен$A из группы $n1!", FALSE, ch, 0, vict, TO_NOTVICT | TO_ARENA_LISTEN); - //AFF_FLAGS(vict).unset(EAffectFlag::AFF_GROUP); - vict->removeGroupFlags(); - } - } -} - -void do_ungroup(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) -{ - struct follow_type *f, *next_fol; - CHAR_DATA *tch; - - one_argument(argument, buf); - - if (ch->has_master() - || !(AFF_FLAGGED(ch, EAffectFlag::AFF_GROUP))) - { - send_to_char("Вы же не лидер группы!\r\n", ch); - return; - } - - if (!*buf) - { - sprintf(buf2, "Вы исключены из группы %s.\r\n", GET_PAD(ch, 1)); - for (f = ch->followers; f; f = next_fol) - { - next_fol = f->next; - if (AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP)) - { - //AFF_FLAGS(f->follower).unset(EAffectFlag::AFF_GROUP); - f->follower->removeGroupFlags(); - send_to_char(buf2, f->follower); - if (!AFF_FLAGGED(f->follower, EAffectFlag::AFF_CHARM) - && !(IS_NPC(f->follower) - && AFF_FLAGGED(f->follower, EAffectFlag::AFF_HORSE))) - { - stop_follower(f->follower, SF_EMPTY); - } - } - } - AFF_FLAGS(ch).unset(EAffectFlag::AFF_GROUP); - ch->removeGroupFlags(); - send_to_char("Вы распустили группу.\r\n", ch); - return; - } - for (f = ch->followers; f; f = next_fol) - { - next_fol = f->next; - tch = f->follower; - if (isname(buf, tch->get_pc_name()) - && !AFF_FLAGGED(tch, EAffectFlag::AFF_CHARM) - && !IS_HORSE(tch)) - { - //AFF_FLAGS(tch).unset(EAffectFlag::AFF_GROUP); - tch->removeGroupFlags(); - act("$N более не член вашей группы.", FALSE, ch, 0, tch, TO_CHAR); - act("Вы исключены из группы $n1!", FALSE, ch, 0, tch, TO_VICT); - act("$N был$G изгнан$A из группы $n1!", FALSE, ch, 0, tch, TO_NOTVICT | TO_ARENA_LISTEN); - stop_follower(tch, SF_EMPTY); - return; - } - } - send_to_char("Этот игрок не входит в состав вашей группы.\r\n", ch); - return; -} - -void do_report(CHAR_DATA *ch, char* /*argument*/, int/* cmd*/, int/* subcmd*/) -{ - CHAR_DATA *k; - struct follow_type *f; - - if (!AFF_FLAGGED(ch, EAffectFlag::AFF_GROUP) && !AFF_FLAGGED(ch, EAffectFlag::AFF_CHARM)) - { - send_to_char("И перед кем вы отчитываетесь?\r\n", ch); - return; - } - if (ch->is_druid()) - { - sprintf(buf, "%s доложил%s : %d(%d)H, %d(%d)V, %d(%d)M\r\n", - GET_NAME(ch), GET_CH_SUF_1(ch), - GET_HIT(ch), GET_REAL_MAX_HIT(ch), - GET_MOVE(ch), GET_REAL_MAX_MOVE(ch), - GET_MANA_STORED(ch), GET_MAX_MANA(ch)); - } - else if (AFF_FLAGGED(ch, EAffectFlag::AFF_CHARM)) - { - int loyalty = 0; - for (const auto& aff : ch->affected) - { - if (aff->type == SPELL_CHARM) - { - loyalty = aff->duration / 2; - break; - } - } - sprintf(buf, "%s доложил%s : %d(%d)H, %d(%d)V, %dL\r\n", - GET_NAME(ch), GET_CH_SUF_1(ch), - GET_HIT(ch), GET_REAL_MAX_HIT(ch), - GET_MOVE(ch), GET_REAL_MAX_MOVE(ch), - loyalty); - } - else - { - sprintf(buf, "%s доложил%s : %d(%d)H, %d(%d)V\r\n", - GET_NAME(ch), GET_CH_SUF_1(ch), - GET_HIT(ch), GET_REAL_MAX_HIT(ch), - GET_MOVE(ch), GET_REAL_MAX_MOVE(ch)); - } - CAP(buf); - k = ch->has_master() ? ch->get_master() : ch; - for (f = k->followers; f; f = f->next) - { - if (AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP) - && f->follower != ch - && !AFF_FLAGGED(f->follower, EAffectFlag::AFF_DEAFNESS)) - { - send_to_char(buf, f->follower); - } - } - - if (k != ch && !AFF_FLAGGED(k, EAffectFlag::AFF_DEAFNESS)) - { - send_to_char(buf, k); - } - send_to_char("Вы доложили о состоянии всем членам вашей группы.\r\n", ch); -} void do_split(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) { do_split(ch,argument,0,0,0); } diff --git a/src/comm.h b/src/comm.h index d07be8256..bba0454cb 100644 --- a/src/comm.h +++ b/src/comm.h @@ -36,6 +36,7 @@ void send_to_room(const char *messg, room_rnum room, int to_awake); void send_to_outdoor(const char *messg, int control); void send_to_gods(const char *messg); void perform_to_all(const char *messg, CHAR_DATA * ch); +char *color_value(CHAR_DATA* /*ch*/, int real, int max); #ifdef HAS_EPOLL void close_socket(DESCRIPTOR_DATA * d, int direct, int epoll, struct epoll_event *events, int n_ev); #else diff --git a/src/groups/group.cpp b/src/groups/group.cpp new file mode 100644 index 000000000..11da30b8a --- /dev/null +++ b/src/groups/group.cpp @@ -0,0 +1,710 @@ +#include "group.h" + +#include "comm.h" +#include "handler.h" +#include "magic.h" +#include "msdp.constants.hpp" +#include "screen.h" + +bool is_group_member(CHAR_DATA *ch, CHAR_DATA *vict) +{ + if (IS_NPC(vict) || !AFF_FLAGGED(vict, EAffectFlag::AFF_GROUP) || vict->get_master() != ch) + { + return false; + } + else + { + return true; + } +} + +void print_one_line(CHAR_DATA * ch, CHAR_DATA * k, int leader, int header) +{ + int ok, ok2, div; + const char *WORD_STATE[] = { "При смерти", + "Оч.тяж.ран", + "Оч.тяж.ран", + " Тяж.ранен", + " Тяж.ранен", + " Ранен ", + " Ранен ", + " Ранен ", + "Лег.ранен ", + "Лег.ранен ", + "Слег.ранен", + " Невредим " + }; + const char *MOVE_STATE[] = { "Истощен", + "Истощен", + "О.устал", + " Устал ", + " Устал ", + "Л.устал", + "Л.устал", + "Хорошо ", + "Хорошо ", + "Хорошо ", + "Отдохн.", + " Полон " + }; + const char *POS_STATE[] = { "Умер", + "Истекает кровью", + "При смерти", + "Без сознания", + "Спит", + "Отдыхает", + "Сидит", + "Сражается", + "Стоит" + }; + + if (IS_NPC(k)) + { + if (!header) +// send_to_char("Персонаж | Здоровье |Рядом| Доп | Положение | Лояльн.\r\n",ch); + send_to_char("Персонаж | Здоровье |Рядом| Аффект | Положение\r\n", ch); + std::string name = GET_NAME(k); + name[0] = UPPER(name[0]); + sprintf(buf, "%s%-20s%s|", CCIBLU(ch, C_NRM), + name.substr(0, 20).c_str(), CCNRM(ch, C_NRM)); + sprintf(buf + strlen(buf), "%s%10s%s|", + color_value(ch, GET_HIT(k), GET_REAL_MAX_HIT(k)), + WORD_STATE[posi_value(GET_HIT(k), GET_REAL_MAX_HIT(k)) + 1], CCNRM(ch, C_NRM)); + + ok = ch->in_room == IN_ROOM(k); + sprintf(buf + strlen(buf), "%s%5s%s|", + ok ? CCGRN(ch, C_NRM) : CCRED(ch, C_NRM), ok ? " Да " : " Нет ", CCNRM(ch, C_NRM)); + + sprintf(buf + strlen(buf), " %s%s%s%s%s%s%s%s%s%s%s%s%s |", + CCIRED(ch, C_NRM), + AFF_FLAGGED(k, EAffectFlag::AFF_SANCTUARY) ? "О" : (AFF_FLAGGED(k, EAffectFlag::AFF_PRISMATICAURA) ? "П" : " "), + CCGRN(ch, C_NRM), + AFF_FLAGGED(k, EAffectFlag::AFF_WATERBREATH) ? "Д" : " ", CCICYN(ch, C_NRM), + AFF_FLAGGED(k, EAffectFlag::AFF_INVISIBLE) ? "Н" : " ", CCIYEL(ch, C_NRM), + (AFF_FLAGGED(k, EAffectFlag::AFF_SINGLELIGHT) + || AFF_FLAGGED(k, EAffectFlag::AFF_HOLYLIGHT) + || (GET_EQ(k, WEAR_LIGHT) + && GET_OBJ_VAL(GET_EQ(k, WEAR_LIGHT), 2))) ? "С" : " ", + CCIBLU(ch, C_NRM), AFF_FLAGGED(k, EAffectFlag::AFF_FLY) ? "Л" : " ", CCYEL(ch, C_NRM), + k->low_charm() ? "Т" : " ", CCNRM(ch, C_NRM)); + + sprintf(buf + strlen(buf), "%-15s", POS_STATE[(int) GET_POS(k)]); + + act(buf, FALSE, ch, 0, k, TO_CHAR); + } + else + { + if (!header) + send_to_char + ("Персонаж | Здоровье |Энергия|Рядом|Учить| Аффект | Кто | Держит строй | Положение \r\n", ch); + + std::string name = GET_NAME(k); + name[0] = UPPER(name[0]); + sprintf(buf, "%s%-20s%s|", CCIBLU(ch, C_NRM), name.c_str(), CCNRM(ch, C_NRM)); + sprintf(buf + strlen(buf), "%s%10s%s|", + color_value(ch, GET_HIT(k), GET_REAL_MAX_HIT(k)), + WORD_STATE[posi_value(GET_HIT(k), GET_REAL_MAX_HIT(k)) + 1], CCNRM(ch, C_NRM)); + + sprintf(buf + strlen(buf), "%s%7s%s|", + color_value(ch, GET_MOVE(k), GET_REAL_MAX_MOVE(k)), + MOVE_STATE[posi_value(GET_MOVE(k), GET_REAL_MAX_MOVE(k)) + 1], CCNRM(ch, C_NRM)); + + ok = ch->in_room == IN_ROOM(k); + sprintf(buf + strlen(buf), "%s%5s%s|", + ok ? CCGRN(ch, C_NRM) : CCRED(ch, C_NRM), ok ? " Да " : " Нет ", CCNRM(ch, C_NRM)); + + if ((!IS_MANA_CASTER(k) && !MEMQUEUE_EMPTY(k)) || + (IS_MANA_CASTER(k) && GET_MANA_STORED(k) < GET_MAX_MANA(k))) + { + div = mana_gain(k); + if (div > 0) + { + if (!IS_MANA_CASTER(k)) + { + ok2 = MAX(0, 1 + GET_MEM_TOTAL(k) - GET_MEM_COMPLETED(k)); + ok2 = ok2 * 60 / div; // время мема в сек + } + else + { + ok2 = MAX(0, 1 + GET_MAX_MANA(k) - GET_MANA_STORED(k)); + ok2 = ok2 / div; // время восстановления в секундах + } + ok = ok2 / 60; + ok2 %= 60; + if (ok > 99) + sprintf(buf + strlen(buf), "&g%5d&n|", ok); + else + sprintf(buf + strlen(buf), "&g%2d:%02d&n|", ok, ok2); + } + else + { + sprintf(buf + strlen(buf), "&r -&n|"); + } + } + else + sprintf(buf + strlen(buf), " |"); + + sprintf(buf + strlen(buf), " %s%s%s%s%s%s%s%s%s%s%s%s%s |", + CCIRED(ch, C_NRM), AFF_FLAGGED(k, EAffectFlag::AFF_SANCTUARY) ? "О" : (AFF_FLAGGED(k, EAffectFlag::AFF_PRISMATICAURA) + ? "П" : " "), CCGRN(ch, + C_NRM), + AFF_FLAGGED(k, EAffectFlag::AFF_WATERBREATH) ? "Д" : " ", CCICYN(ch, + C_NRM), + AFF_FLAGGED(k, EAffectFlag::AFF_INVISIBLE) ? "Н" : " ", CCIYEL(ch, C_NRM), (AFF_FLAGGED(k, EAffectFlag::AFF_SINGLELIGHT) + || AFF_FLAGGED(k, EAffectFlag::AFF_HOLYLIGHT) + || (GET_EQ(k, WEAR_LIGHT) + && + GET_OBJ_VAL(GET_EQ + (k, WEAR_LIGHT), + 2))) ? "С" : " ", + CCIBLU(ch, C_NRM), AFF_FLAGGED(k, EAffectFlag::AFF_FLY) ? "Л" : " ", CCYEL(ch, C_NRM), + k->ahorse() ? "В" : " ", CCNRM(ch, C_NRM)); + + sprintf(buf + strlen(buf), "%5s|", leader ? "Лидер" : ""); + ok = PRF_FLAGGED(k, PRF_SKIRMISHER); + sprintf(buf + strlen(buf), "%s%-14s%s|", ok ? CCGRN(ch, C_NRM) : CCNRM(ch, C_NRM), ok ? " Да " : " Нет ", CCNRM(ch, C_NRM)); + sprintf(buf + strlen(buf), " %s", POS_STATE[(int) GET_POS(k)]); + act(buf, FALSE, ch, 0, k, TO_CHAR); + } +} + +void print_group(CHAR_DATA * ch) +{ + int gfound = 0, cfound = 0; + CHAR_DATA *k; + struct follow_type *f, *g; + + k = ch->has_master() ? ch->get_master() : ch; + if (!IS_NPC(ch)) + ch->desc->msdp_report(msdp::constants::GROUP); + + if (AFF_FLAGGED(ch, EAffectFlag::AFF_GROUP)) + { + send_to_char("Ваша группа состоит из:\r\n", ch); + if (AFF_FLAGGED(k, EAffectFlag::AFF_GROUP)) + { + print_one_line(ch, k, TRUE, gfound++); + } + + for (f = k->followers; f; f = f->next) + { + if (!AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP)) + { + continue; + } + print_one_line(ch, f->follower, FALSE, gfound++); + } + } + + for (f = ch->followers; f; f = f->next) + { + if (!(AFF_FLAGGED(f->follower, EAffectFlag::AFF_CHARM) + || MOB_FLAGGED(f->follower, MOB_ANGEL)|| MOB_FLAGGED(f->follower, MOB_GHOST))) + { + continue; + } + if (!cfound) + send_to_char("Ваши последователи:\r\n", ch); + print_one_line(ch, f->follower, FALSE, cfound++); + } + if (!gfound && !cfound) + { + send_to_char("Но вы же не член (в лучшем смысле этого слова) группы!\r\n", ch); + return; + } + if (PRF_FLAGGED(ch, PRF_SHOWGROUP)) + { + for (g = k->followers, cfound = 0; g; g = g->next) + { + for (f = g->follower->followers; f; f = f->next) + { + if (!(AFF_FLAGGED(f->follower, EAffectFlag::AFF_CHARM) + || MOB_FLAGGED(f->follower, MOB_ANGEL) || MOB_FLAGGED(f->follower, MOB_GHOST)) + || !AFF_FLAGGED(ch, EAffectFlag::AFF_GROUP)) + { + continue; + } + + if (f->follower->get_master() == ch + || !AFF_FLAGGED(f->follower->get_master(), EAffectFlag::AFF_GROUP)) + { + continue; + } + + // shapirus: при включенном режиме не показываем клонов и хранителей + if (PRF_FLAGGED(ch, PRF_NOCLONES) + && IS_NPC(f->follower) + && (MOB_FLAGGED(f->follower, MOB_CLONE) + || GET_MOB_VNUM(f->follower) == MOB_KEEPER)) + { + continue; + } + + if (!cfound) + { + send_to_char("Последователи членов вашей группы:\r\n", ch); + } + print_one_line(ch, f->follower, FALSE, cfound++); + } + + if (ch->has_master()) + { + if (!(AFF_FLAGGED(g->follower, EAffectFlag::AFF_CHARM) + || MOB_FLAGGED(g->follower, MOB_ANGEL) || MOB_FLAGGED(g->follower, MOB_GHOST)) + || !AFF_FLAGGED(ch, EAffectFlag::AFF_GROUP)) + { + continue; + } + + // shapirus: при включенном режиме не показываем клонов и хранителей + if (PRF_FLAGGED(ch, PRF_NOCLONES) + && IS_NPC(g->follower) + && (MOB_FLAGGED(g->follower, MOB_CLONE) + || GET_MOB_VNUM(g->follower) == MOB_KEEPER)) + { + continue; + } + + if (!cfound) + { + send_to_char("Последователи членов вашей группы:\r\n", ch); + } + print_one_line(ch, g->follower, FALSE, cfound++); + } + } + } +} + +void print_list_group(CHAR_DATA *ch) +{ + CHAR_DATA *k; + struct follow_type *f; + int count = 1; + k = (ch->has_master() ? ch->get_master() : ch); + if (AFF_FLAGGED(ch, EAffectFlag::AFF_GROUP)) + { + send_to_char("Ваша группа состоит из:\r\n", ch); + if (AFF_FLAGGED(k, EAffectFlag::AFF_GROUP)) + { + sprintf(buf1, "Лидер: %s\r\n", GET_NAME(k)); + send_to_char(buf1, ch); + } + + for (f = k->followers; f; f = f->next) + { + if (!AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP)) + { + continue; + } + sprintf(buf1, "%d. Согруппник: %s\r\n", count, GET_NAME(f->follower)); + send_to_char(buf1, ch); + count++; + } + } + else + { + send_to_char("Но вы же не член (в лучшем смысле этого слова) группы!\r\n", ch); + } +} + +int perform_group(CHAR_DATA * ch, CHAR_DATA * vict) +{ + if (AFF_FLAGGED(vict, EAffectFlag::AFF_GROUP) + || AFF_FLAGGED(vict, EAffectFlag::AFF_CHARM) + || MOB_FLAGGED(vict, MOB_ANGEL) + || MOB_FLAGGED(vict, MOB_GHOST) + || IS_HORSE(vict)) + { + return (FALSE); + } + + AFF_FLAGS(vict).set(EAffectFlag::AFF_GROUP); + if (ch != vict) + { + act("$N принят$A в члены вашего кружка (тьфу-ты, группы :).", FALSE, ch, 0, vict, TO_CHAR); + act("Вы приняты в группу $n1.", FALSE, ch, 0, vict, TO_VICT); + act("$N принят$A в группу $n1.", FALSE, ch, 0, vict, TO_NOTVICT | TO_ARENA_LISTEN); + } + return (TRUE); +} + +int max_group_size(CHAR_DATA *ch) +{ + int bonus_commander = 0; + if (AFF_FLAGGED(ch, EAffectFlag::AFF_COMMANDER)) bonus_commander = VPOSI((ch->get_skill(SKILL_LEADERSHIP) - 120) / 10, 0 , 8); + + return MAX_GROUPED_FOLLOWERS + (int) VPOSI((ch->get_skill(SKILL_LEADERSHIP) - 80) / 5, 0, 4) + bonus_commander; +} + +/** +* Смена лидера группы на персонажа с макс лидеркой. +* Сам лидер при этом остается в группе, если он живой. +* \param vict - новый лидер, если смена происходит по команде 'гр лидер имя', +* старый лидер соответственно группится обратно, если нулевой, то считаем, что +* произошла смерть старого лидера и новый выбирается по наибольшей лидерке. +*/ +void change_leader(CHAR_DATA *ch, CHAR_DATA *vict) +{ + if (IS_NPC(ch) + || ch->has_master() + || !ch->followers) + { + return; + } + + CHAR_DATA *leader = vict; + if (!leader) + { + // лидер умер, ищем согрупника с максимальным скиллом лидерки + for (struct follow_type *l = ch->followers; l; l = l->next) + { + if (!is_group_member(ch, l->follower)) + continue; + if (!leader) + leader = l->follower; + else if (l->follower->get_skill(SKILL_LEADERSHIP) > leader->get_skill(SKILL_LEADERSHIP)) + leader = l->follower; + } + } + + if (!leader) + { + return; + } + + // для реследования используем стандартные функции + std::vector temp_list; + for (struct follow_type *n = 0, *l = ch->followers; l; l = n) + { + n = l->next; + if (!is_group_member(ch, l->follower)) + { + continue; + } + else + { + CHAR_DATA *temp_vict = l->follower; + if (temp_vict->has_master() + && stop_follower(temp_vict, SF_SILENCE)) + { + continue; + } + + if (temp_vict != leader) + { + temp_list.push_back(temp_vict); + } + } + } + + // вся эта фигня только для того, чтобы при реследовании пройтись по списку в обратном + // направлении и сохранить относительный порядок следования в группе + if (!temp_list.empty()) + { + for (std::vector::reverse_iterator it = temp_list.rbegin(); it != temp_list.rend(); ++it) + { + leader->add_follower_silently(*it); + } + } + + // бывшего лидера последним закидываем обратно в группу, если он живой + if (vict) + { + // флаг группы надо снять, иначе при регрупе не будет писаться о старом лидере + //AFF_FLAGS(ch).unset(EAffectFlag::AFF_GROUP); + ch->removeGroupFlags(); + leader->add_follower_silently(ch); + } + + if (!leader->followers) + { + return; + } + + ch->dps_copy(leader); + perform_group(leader, leader); + int followers = 0; + for (struct follow_type *f = leader->followers; f; f = f->next) + { + if (followers < max_group_size(leader)) + { + if (perform_group(leader, f->follower)) + ++followers; + } + else + { + send_to_char("Вы больше никого не можете принять в группу.\r\n", ch); + return; + } + } +} + + +void do_group(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) +{ + CHAR_DATA *vict; + struct follow_type *f; + int found, f_number; + + argument = one_argument(argument, buf); + + if (!*buf) + { + print_group(ch); + return; + } + + if (!str_cmp(buf, "список")) + { + print_list_group(ch); + return; + } + + if (GET_POS(ch) < POS_RESTING) + { + send_to_char("Трудно управлять группой в таком состоянии.\r\n", ch); + return; + } + + if (ch->has_master()) + { + act("Вы не можете управлять группой. Вы еще не ведущий.", FALSE, ch, 0, 0, TO_CHAR); + return; + } + + if (!ch->followers) + { + send_to_char("За вами никто не следует.\r\n", ch); + return; + } + + + +// вычисляем количество последователей + for (f_number = 0, f = ch->followers; f; f = f->next) + { + if (AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP)) + { + f_number++; + } + } + + if (!str_cmp(buf, "all") + || !str_cmp(buf, "все")) + { + perform_group(ch, ch); + for (found = 0, f = ch->followers; f; f = f->next) + { + if ((f_number + found) >= max_group_size(ch)) + { + send_to_char("Вы больше никого не можете принять в группу.\r\n", ch); + return; + } + found += perform_group(ch, f->follower); + } + + if (!found) + { + send_to_char("Все, кто за вами следуют, уже включены в вашу группу.\r\n", ch); + } + + return; + } + else if (!str_cmp(buf, "leader") || !str_cmp(buf, "лидер")) + { + vict = get_player_vis(ch, argument, FIND_CHAR_WORLD); + // added by WorM (Видолюб) Если найден клон и его хозяин персонаж + // а то чото как-то глючно Двойник %1 не является членом вашей группы. + if (vict + && IS_NPC(vict) + && MOB_FLAGGED(vict, MOB_CLONE) + && AFF_FLAGGED(vict, EAffectFlag::AFF_CHARM) + && vict->has_master() + && !IS_NPC(vict->get_master())) + { + if (CAN_SEE(ch, vict->get_master())) + { + vict = vict->get_master(); + } + else + { + vict = NULL; + } + } + + // end by WorM + if (!vict) + { + send_to_char("Нет такого персонажа.\r\n", ch); + return; + } + else if (vict == ch) + { + send_to_char("Вы и так лидер группы...\r\n", ch); + return; + } + else if (!AFF_FLAGGED(vict, EAffectFlag::AFF_GROUP) + || vict->get_master() != ch) + { + send_to_char(ch, "%s не является членом вашей группы.\r\n", GET_NAME(vict)); + return; + } + change_leader(ch, vict); + return; + } + + if (!(vict = get_char_vis(ch, buf, FIND_CHAR_ROOM))) + { + send_to_char(NOPERSON, ch); + } + else if ((vict->get_master() != ch) && (vict != ch)) + { + act("$N2 нужно следовать за вами, чтобы стать членом вашей группы.", FALSE, ch, 0, vict, TO_CHAR); + } + else + { + if (!AFF_FLAGGED(vict, EAffectFlag::AFF_GROUP)) + { + if (AFF_FLAGGED(vict, EAffectFlag::AFF_CHARM) || MOB_FLAGGED(vict, MOB_ANGEL) || MOB_FLAGGED(vict, MOB_GHOST) || IS_HORSE(vict)) + { + send_to_char("Только равноправные персонажи могут быть включены в группу.\r\n", ch); + send_to_char("Только равноправные персонажи могут быть включены в группу.\r\n", vict); + }; + if (f_number >= max_group_size(ch)) + { + send_to_char("Вы больше никого не можете принять в группу.\r\n", ch); + return; + } + perform_group(ch, ch); + perform_group(ch, vict); + } + else if (ch != vict) + { + act("$N исключен$A из состава вашей группы.", FALSE, ch, 0, vict, TO_CHAR); + act("Вы исключены из группы $n1!", FALSE, ch, 0, vict, TO_VICT); + act("$N был$G исключен$A из группы $n1!", FALSE, ch, 0, vict, TO_NOTVICT | TO_ARENA_LISTEN); + //AFF_FLAGS(vict).unset(EAffectFlag::AFF_GROUP); + vict->removeGroupFlags(); + } + } +} + +void do_ungroup(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) +{ + struct follow_type *f, *next_fol; + CHAR_DATA *tch; + + one_argument(argument, buf); + + if (ch->has_master() + || !(AFF_FLAGGED(ch, EAffectFlag::AFF_GROUP))) + { + send_to_char("Вы же не лидер группы!\r\n", ch); + return; + } + + if (!*buf) + { + sprintf(buf2, "Вы исключены из группы %s.\r\n", GET_PAD(ch, 1)); + for (f = ch->followers; f; f = next_fol) + { + next_fol = f->next; + if (AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP)) + { + //AFF_FLAGS(f->follower).unset(EAffectFlag::AFF_GROUP); + f->follower->removeGroupFlags(); + send_to_char(buf2, f->follower); + if (!AFF_FLAGGED(f->follower, EAffectFlag::AFF_CHARM) + && !(IS_NPC(f->follower) + && AFF_FLAGGED(f->follower, EAffectFlag::AFF_HORSE))) + { + stop_follower(f->follower, SF_EMPTY); + } + } + } + AFF_FLAGS(ch).unset(EAffectFlag::AFF_GROUP); + ch->removeGroupFlags(); + send_to_char("Вы распустили группу.\r\n", ch); + return; + } + for (f = ch->followers; f; f = next_fol) + { + next_fol = f->next; + tch = f->follower; + if (isname(buf, tch->get_pc_name()) + && !AFF_FLAGGED(tch, EAffectFlag::AFF_CHARM) + && !IS_HORSE(tch)) + { + //AFF_FLAGS(tch).unset(EAffectFlag::AFF_GROUP); + tch->removeGroupFlags(); + act("$N более не член вашей группы.", FALSE, ch, 0, tch, TO_CHAR); + act("Вы исключены из группы $n1!", FALSE, ch, 0, tch, TO_VICT); + act("$N был$G изгнан$A из группы $n1!", FALSE, ch, 0, tch, TO_NOTVICT | TO_ARENA_LISTEN); + stop_follower(tch, SF_EMPTY); + return; + } + } + send_to_char("Этот игрок не входит в состав вашей группы.\r\n", ch); + return; +} + +void do_report(CHAR_DATA *ch, char* /*argument*/, int/* cmd*/, int/* subcmd*/) +{ + CHAR_DATA *k; + struct follow_type *f; + + if (!AFF_FLAGGED(ch, EAffectFlag::AFF_GROUP) && !AFF_FLAGGED(ch, EAffectFlag::AFF_CHARM)) + { + send_to_char("И перед кем вы отчитываетесь?\r\n", ch); + return; + } + if (ch->is_druid()) + { + sprintf(buf, "%s доложил%s : %d(%d)H, %d(%d)V, %d(%d)M\r\n", + GET_NAME(ch), GET_CH_SUF_1(ch), + GET_HIT(ch), GET_REAL_MAX_HIT(ch), + GET_MOVE(ch), GET_REAL_MAX_MOVE(ch), + GET_MANA_STORED(ch), GET_MAX_MANA(ch)); + } + else if (AFF_FLAGGED(ch, EAffectFlag::AFF_CHARM)) + { + int loyalty = 0; + for (const auto& aff : ch->affected) + { + if (aff->type == SPELL_CHARM) + { + loyalty = aff->duration / 2; + break; + } + } + sprintf(buf, "%s доложил%s : %d(%d)H, %d(%d)V, %dL\r\n", + GET_NAME(ch), GET_CH_SUF_1(ch), + GET_HIT(ch), GET_REAL_MAX_HIT(ch), + GET_MOVE(ch), GET_REAL_MAX_MOVE(ch), + loyalty); + } + else + { + sprintf(buf, "%s доложил%s : %d(%d)H, %d(%d)V\r\n", + GET_NAME(ch), GET_CH_SUF_1(ch), + GET_HIT(ch), GET_REAL_MAX_HIT(ch), + GET_MOVE(ch), GET_REAL_MAX_MOVE(ch)); + } + CAP(buf); + k = ch->has_master() ? ch->get_master() : ch; + for (f = k->followers; f; f = f->next) + { + if (AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP) + && f->follower != ch + && !AFF_FLAGGED(f->follower, EAffectFlag::AFF_DEAFNESS)) + { + send_to_char(buf, f->follower); + } + } + + if (k != ch && !AFF_FLAGGED(k, EAffectFlag::AFF_DEAFNESS)) + { + send_to_char(buf, k); + } + send_to_char("Вы доложили о состоянии всем членам вашей группы.\r\n", ch); +} \ No newline at end of file diff --git a/src/groups/group.h b/src/groups/group.h new file mode 100644 index 000000000..5378161aa --- /dev/null +++ b/src/groups/group.h @@ -0,0 +1,11 @@ +#ifndef BYLINS_GROUP_H +#define BYLINS_GROUP_H + +#include "chars/char.hpp" + +void change_leader(CHAR_DATA *ch, CHAR_DATA *vict); +void do_group(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/); +void do_ungroup(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/); +void do_report(CHAR_DATA *ch, char* /*argument*/, int/* cmd*/, int/* subcmd*/); + +#endif //BYLINS_GROUP_H diff --git a/src/groups/grp.member.cpp b/src/groups/grp.member.cpp new file mode 100644 index 000000000..9bf3f9c37 --- /dev/null +++ b/src/groups/grp.member.cpp @@ -0,0 +1 @@ +#include "grp.member.h" diff --git a/src/groups/grp.member.h b/src/groups/grp.member.h new file mode 100644 index 000000000..7e42fa9bf --- /dev/null +++ b/src/groups/grp.member.h @@ -0,0 +1,15 @@ +#ifndef BYLINS_GRP_MEMBER_H +#define BYLINS_GRP_MEMBER_H + +#include "chars/char.hpp" + +class groupMember { +public: + groupMember(); + +private: + CHAR_DATA *ch; + bool isLeader; +}; + +#endif //BYLINS_GRP_MEMBER_H diff --git a/src/interpreter.cpp b/src/interpreter.cpp index 9a967d165..54005102f 100644 --- a/src/interpreter.cpp +++ b/src/interpreter.cpp @@ -15,75 +15,76 @@ #include "interpreter.h" -#include "cmd/retreat.h" -#include "cmd/order.h" -#include "cmd/track.h" -#include "cmd/telegram.h" -#include "cmd/hire.h" - -#include "skills/manadrain.h" -#include "skills/flee.h" -#include "skills/bash.h" -#include "skills/stun.h" -#include "skills/resque.h" -#include "skills/kick.h" -#include "skills/strangle.h" -#include "skills/chopoff.h" -#include "skills/disarm.h" -#include "skills/stupor.h" -#include "skills/ironwind.h" -#include "skills/throw.h" -#include "skills/mighthit.h" -#include "skills/block.h" -#include "skills/parry.h" -#include "skills/protect.h" -#include "skills/turnundead.h" -#include "fightsystem/assist.h" -#include "fightsystem/start.fight.h" -#include "fightsystem/mobact.hpp" -#include "cmd/mercenary.h" #include "act.movement.hpp" +#include "ban.hpp" +#include "boards.h" +#include "chars/char.hpp" +#include "chars/char_player.hpp" #include "chars/world.characters.hpp" -#include "object.prototypes.hpp" -#include "logger.hpp" -#include "craft.commands.hpp" -#include "heartbeat.commands.hpp" -#include "obj.hpp" +#include "cmd/hire.h" +#include "cmd/mercenary.h" +#include "cmd/order.h" +#include "cmd/retreat.h" +#include "cmd/telegram.h" +#include "cmd/track.h" #include "comm.h" #include "constants.h" +#include "craft.commands.hpp" #include "db.h" -#include "spells.h" -#include "skills.h" -#include "handler.h" -#include "house.h" -#include "mail.h" -#include "screen.h" -#include "olc.h" +#include "depot.hpp" #include "dg_scripts.h" +#include "features.hpp" +#include "fightsystem/assist.h" +#include "fightsystem/mobact.hpp" #include "fightsystem/pk.h" +#include "fightsystem/start.fight.h" #include "genchar.h" -#include "ban.hpp" +#include "glory.hpp" +#include "glory_const.hpp" +#include "glory_misc.hpp" +#include "groups/group.h" +#include "handler.h" +#include "heartbeat.commands.hpp" +#include "house.h" #include "item.creation.hpp" -#include "features.hpp" -#include "boards.h" -#include "top.h" -#include "title.hpp" +#include "liquid.hpp" +#include "logger.hpp" +#include "mail.h" +#include "modify.h" +#include "name_list.hpp" +#include "named_stuff.hpp" #include "names.hpp" +#include "obj.hpp" +#include "object.prototypes.hpp" +#include "olc.h" +#include "parcel.hpp" #include "password.hpp" #include "privilege.hpp" -#include "depot.hpp" -#include "glory.hpp" -#include "chars/char.hpp" -#include "chars/char_player.hpp" -#include "parcel.hpp" -#include "liquid.hpp" -#include "name_list.hpp" -#include "modify.h" #include "room.hpp" -#include "glory_const.hpp" -#include "glory_misc.hpp" -#include "named_stuff.hpp" +#include "screen.h" +#include "skills.h" +#include "skills/bash.h" +#include "skills/block.h" +#include "skills/chopoff.h" +#include "skills/disarm.h" +#include "skills/flee.h" +#include "skills/ironwind.h" +#include "skills/kick.h" +#include "skills/manadrain.h" +#include "skills/mighthit.h" +#include "skills/parry.h" +#include "skills/protect.h" +#include "skills/resque.h" +#include "skills/strangle.h" +#include "skills/stun.h" +#include "skills/stupor.h" +#include "skills/throw.h" +#include "skills/turnundead.h" +#include "spells.h" #include "time.h" +#include "title.hpp" +#include "top.h" + #if defined WITH_SCRIPTING #include "scripting.hpp" #endif @@ -245,7 +246,6 @@ void do_givehorse(CHAR_DATA *ch, char *argument, int cmd, int subcmd); void do_gold(CHAR_DATA *ch, char *argument, int cmd, int subcmd); void do_goto(CHAR_DATA *ch, char *argument, int cmd, int subcmd); void do_grab(CHAR_DATA *ch, char *argument, int cmd, int subcmd); -void do_group(CHAR_DATA *ch, char *argument, int cmd, int subcmd); void do_gsay(CHAR_DATA *ch, char *argument, int cmd, int subcmd); void do_hide(CHAR_DATA *ch, char *argument, int cmd, int subcmd); void do_info(CHAR_DATA *ch, char *argument, int cmd, int subcmd); @@ -318,7 +318,6 @@ void do_time(CHAR_DATA *ch, char *argument, int cmd, int subcmd); void do_toggle(CHAR_DATA *ch, char *argument, int cmd, int subcmd); void do_sense(CHAR_DATA *ch, char *argument, int cmd, int subcmd); void do_unban(CHAR_DATA *ch, char *argument, int cmd, int subcmd); -void do_ungroup(CHAR_DATA *ch, char *argument, int cmd, int subcmd); void do_use(CHAR_DATA *ch, char *argument, int cmd, int subcmd); void do_users(CHAR_DATA *ch, char *argument, int cmd, int subcmd); void do_visible(CHAR_DATA *ch, char *argument, int cmd, int subcmd); From 3f6bc663b90949658328e2c0affd3f309d35b754 Mon Sep 17 00:00:00 2001 From: bikbai Date: Sun, 20 Dec 2020 22:48:48 +0300 Subject: [PATCH 02/40] groupfight --- CMakeLists.txt | 16 +- src/act.movement.cpp | 81 - src/act.other.cpp | 69 - src/chars/char.cpp | 12 +- src/chars/char.hpp | 4 + src/{ => cmd.imm}/act.wizard.cpp | 2 +- src/{ => cmd.imm}/act.wizard.hpp | 0 src/cmd.imm/test.cpp | 27 + src/cmd.imm/test.h | 9 + src/cmd/quit.cpp | 83 + src/cmd/quit.h | 12 + src/comm.cpp | 4 +- src/fightsystem/fight_stuff.hpp | 1 + src/global.objects.cpp | 8 +- src/global.objects.hpp | 5 +- src/groups/group.h | 11 - src/groups/grp.member.cpp | 1 - src/groups/grp.member.h | 15 - src/grp/follow.cpp | 253 +++ src/grp/follow.h | 8 + src/{groups/group.cpp => grp/grp.group.cpp} | 189 ++- src/grp/grp.group.h | 65 + src/grp/grp.roster.cpp | 46 + src/grp/grp.roster.h | 32 + src/interpreter.cpp | 1635 +++++++++---------- src/privilege.cpp | 14 +- src/pugixml.cpp | 6 +- src/structs.h | 1 + src/utils.cpp | 163 -- 29 files changed, 1554 insertions(+), 1218 deletions(-) rename src/{ => cmd.imm}/act.wizard.cpp (99%) rename src/{ => cmd.imm}/act.wizard.hpp (100%) create mode 100644 src/cmd.imm/test.cpp create mode 100644 src/cmd.imm/test.h create mode 100644 src/cmd/quit.cpp create mode 100644 src/cmd/quit.h delete mode 100644 src/groups/group.h delete mode 100644 src/groups/grp.member.cpp delete mode 100644 src/groups/grp.member.h create mode 100644 src/grp/follow.cpp create mode 100644 src/grp/follow.h rename src/{groups/group.cpp => grp/grp.group.cpp} (82%) create mode 100644 src/grp/grp.group.h create mode 100644 src/grp/grp.roster.cpp create mode 100644 src/grp/grp.roster.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ae7e60369..f62804b19 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,7 +22,7 @@ set(SOURCES src/act.movement.cpp src/act.other.cpp src/act.social.cpp - src/act.wizard.cpp + src/cmd.imm/act.wizard.cpp src/AffectHandler.cpp src/alias.cpp src/auction.cpp @@ -203,7 +203,10 @@ set(SOURCES src/core/affect_data.cpp src/cmd/track.cpp src/cmd/telegram.cpp - src/cmd/hire.cpp src/groups/grp.member.cpp src/groups/grp.member.h src/groups/group.cpp src/groups/group.h) + src/cmd/hire.cpp + src/grp/grp.group.cpp + src/grp/grp.roster.cpp + ) set(HEADERS src/cmd/telegram.h @@ -241,7 +244,7 @@ set(HEADERS src/heartbeat.commands.hpp src/weather.hpp src/limits.hpp - src/act.wizard.hpp + src/cmd.imm/act.wizard.hpp src/heartbeat.hpp src/speedwalks.hpp src/chars/world.characters.hpp @@ -390,7 +393,10 @@ set(HEADERS src/skills/strangle.h src/skills/chopoff.h src/skills/stupor.h - src/graph.h) + src/graph.h + src/grp/grp.group.h + src/grp/grp.roster.h + ) set(CONFIGURATION_FILES lib.template/misc/configuration.xml) # Build types @@ -567,7 +573,7 @@ set(MISC_FILES source_group("Misc" FILES ${MISC_FILES}) -set(CIRCLE_FILES ${SOURCES} ${HEADERS} readme.markdown CONTRIBUTING.md ${CRAFT_FILES} ${MISC_FILES}) +set(CIRCLE_FILES ${SOURCES} ${HEADERS} readme.markdown CONTRIBUTING.md ${CRAFT_FILES} ${MISC_FILES} src/cmd.imm/test.cpp src/cmd.imm/test.h src/grp/follow.cpp src/grp/follow.h src/cmd/quit.cpp src/cmd/quit.h) # Sort source and header files. Just to convenience. list(SORT CIRCLE_FILES) diff --git a/src/act.movement.cpp b/src/act.movement.cpp index e5ebd01ca..5965b00d0 100644 --- a/src/act.movement.cpp +++ b/src/act.movement.cpp @@ -2059,85 +2059,4 @@ void do_wake(CHAR_DATA *ch, char *argument, int/* cmd*/, int subcmd) } } -void do_follow(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) -{ - CHAR_DATA *leader; - struct follow_type *f; - one_argument(argument, smallBuf); - - if (IS_NPC(ch) && AFF_FLAGGED(ch, EAffectFlag::AFF_CHARM) && ch->get_fighting()) - return; - if (*smallBuf) - { - if (!str_cmp(smallBuf, "я") || !str_cmp(smallBuf, "self") || !str_cmp(smallBuf, "me")) - { - if (!ch->has_master()) - { - send_to_char("Но вы ведь ни за кем не следуете...\r\n", ch); - } - else - { - stop_follower(ch, SF_EMPTY); - } - return; - } - if (!(leader = get_char_vis(ch, smallBuf, FIND_CHAR_ROOM))) - { - send_to_char(NOPERSON, ch); - return; - } - } - else - { - send_to_char("За кем вы хотите следовать?\r\n", ch); - return; - } - - if (ch->get_master() == leader) - { - act("Вы уже следуете за $N4.", FALSE, ch, 0, leader, TO_CHAR); - return; - } - - if (AFF_FLAGGED(ch, EAffectFlag::AFF_CHARM) - && ch->has_master()) - { - act("Но вы можете следовать только за $N4!", FALSE, ch, 0, ch->get_master(), TO_CHAR); - } - else // Not Charmed follow person - { - if (leader == ch) - { - if (!ch->has_master()) - { - send_to_char("Вы уже следуете за собой.\r\n", ch); - return; - } - stop_follower(ch, SF_EMPTY); - } - else - { - if (circle_follow(ch, leader)) - { - send_to_char("Так у вас целый хоровод получится.\r\n", ch); - return; - } - - if (ch->has_master()) - { - stop_follower(ch, SF_EMPTY); - } - //AFF_FLAGS(ch).unset(EAffectFlag::AFF_GROUP); - ch->removeGroupFlags(); - for (f = ch->followers; f; f = f->next) - { - //AFF_FLAGS(f->follower).unset(EAffectFlag::AFF_GROUP); - f->follower->removeGroupFlags(); - } - - leader->add_follower(ch); - } - } -} - // vim: ts=4 sw=4 tw=0 noet syntax=cpp : diff --git a/src/act.other.cpp b/src/act.other.cpp index 7f43ad00f..86277eca6 100644 --- a/src/act.other.cpp +++ b/src/act.other.cpp @@ -85,7 +85,6 @@ extern void split_or_clan_tax(CHAR_DATA *ch, long amount); extern bool is_wear_light(CHAR_DATA *ch); // local functions void do_antigods(CHAR_DATA *ch, char *argument, int cmd, int subcmd); -void do_quit(CHAR_DATA *ch, char *argument, int cmd, int subcmd); void do_save(CHAR_DATA *ch, char *argument, int cmd, int subcmd); void do_not_here(CHAR_DATA *ch, char *argument, int cmd, int subcmd); void do_sneak(CHAR_DATA *ch, char *argument, int cmd, int subcmd); @@ -127,74 +126,6 @@ void do_antigods(CHAR_DATA *ch, char* /*argument*/, int/* cmd*/, int/* subcmd*/) send_to_char("Вы и так не под защитой богов.\r\n", ch); } -void do_quit(CHAR_DATA *ch, char *argument, int/* cmd*/, int subcmd) -{ - DESCRIPTOR_DATA *d, *next_d; - - if (IS_NPC(ch) || !ch->desc) - return; - - if (subcmd != SCMD_QUIT) - send_to_char("Вам стоит набрать эту команду полностью во избежание недоразумений!\r\n", ch); - else if (GET_POS(ch) == POS_FIGHTING) - send_to_char("Угу! Щаз-з-з! Вы, батенька, деретесь!\r\n", ch); - else if (GET_POS(ch) < POS_STUNNED) - { - send_to_char("Вас пригласила к себе владелица косы...\r\n", ch); - die(ch, NULL); - } - else if (AFF_FLAGGED(ch, EAffectFlag::AFF_SLEEP)) - { - return; - } - else if (*argument) - { - send_to_char("Если вы хотите выйти из игры с потерей всех вещей, то просто наберите 'конец'.\r\n", ch); - } - else - { -// int loadroom = ch->in_room; - if (RENTABLE(ch)) - { - send_to_char("В связи с боевыми действиями эвакуация временно прекращена.\r\n", ch); - return; - } - if (!GET_INVIS_LEV(ch)) - act("$n покинул$g игру.", TRUE, ch, 0, 0, TO_ROOM | TO_ARENA_LISTEN); - sprintf(buf, "%s quit the game.", GET_NAME(ch)); - mudlog(buf, NRM, MAX(LVL_GOD, GET_INVIS_LEV(ch)), SYSLOG, TRUE); - send_to_char("До свидания, странник... Мы ждем тебя снова!\r\n", ch); - - long depot_cost = static_cast(Depot::get_total_cost_per_day(ch)); - if (depot_cost) - { - send_to_char(ch, "За вещи в хранилище придется заплатить %ld %s в день.\r\n", depot_cost, desc_count(depot_cost, WHAT_MONEYu)); - long deadline = ((ch->get_gold() + ch->get_bank()) / depot_cost); - send_to_char(ch, "Твоих денег хватит на %ld %s.\r\n", deadline, desc_count(deadline, WHAT_DAY)); - } - Depot::exit_char(ch); - Clan::clan_invoice(ch, false); - - /* - * kill off all sockets connected to the same player as the one who is - * trying to quit. Helps to maintain sanity as well as prevent duping. - */ - for (d = descriptor_list; d; d = next_d) - { - next_d = d->next; - if (d == ch->desc) - continue; - if (d->character && (GET_IDNUM(d->character) == GET_IDNUM(ch))) - STATE(d) = CON_DISCONNECT; - } - - if (free_rent || IS_GOD(ch)) - { - Crash_rentsave(ch, 0); - } - extract_char(ch, FALSE); - } -} void do_summon(CHAR_DATA *ch, char* /*argument*/, int/* cmd*/, int/* subcmd*/) { diff --git a/src/chars/char.cpp b/src/chars/char.cpp index a4c14d5bb..0ce5c9731 100644 --- a/src/chars/char.cpp +++ b/src/chars/char.cpp @@ -39,9 +39,12 @@ #include #include +#include "grp/grp.group.cpp" + std::string PlayerI::empty_const_str; MapSystem::Options PlayerI::empty_map_options; + namespace { @@ -120,6 +123,7 @@ CHAR_DATA::CHAR_DATA() : current_morph_ = GetNormalMorphNew(this); caching::character_cache.add(this); this->set_skill(SKILL_GLOBAL_COOLDOWN, 1); + this->personGroup = nullptr; } CHAR_DATA::~CHAR_DATA() @@ -2014,8 +2018,12 @@ void CHAR_DATA::msdp_report(const std::string& name) { } void CHAR_DATA::removeGroupFlags() { - AFF_FLAGS(this).unset(EAffectFlag::AFF_GROUP); - PRF_FLAGS(this).unset(PRF_SKIRMISHER); + AFF_FLAGS(this).unset(EAffectFlag::AFF_GROUP); + PRF_FLAGS(this).unset(PRF_SKIRMISHER); + if (!this->personGroup) + return; + this->personGroup->removeMember(this); + this->personGroup = nullptr; } void CHAR_DATA::add_follower(CHAR_DATA* ch) { diff --git a/src/chars/char.hpp b/src/chars/char.hpp index 88a0cb55d..e5e0a3021 100644 --- a/src/chars/char.hpp +++ b/src/chars/char.hpp @@ -24,6 +24,8 @@ #include #include +class Group; + // These data contain information about a players time data struct time_data { @@ -833,6 +835,8 @@ class CHAR_DATA : public ProtectedCharacterData bool drop_from_horse(); bool isHorsePrevents(); void dismount(); +public: + Group* personGroup; }; inline const player_special_data::ignores_t& CHAR_DATA::get_ignores() const diff --git a/src/act.wizard.cpp b/src/cmd.imm/act.wizard.cpp similarity index 99% rename from src/act.wizard.cpp rename to src/cmd.imm/act.wizard.cpp index c08298abd..ceae036c6 100644 --- a/src/act.wizard.cpp +++ b/src/cmd.imm/act.wizard.cpp @@ -12,7 +12,7 @@ * $Revision$ * ************************************************************************ */ -#include "act.wizard.hpp" +#include "cmd.imm/act.wizard.hpp" #include "action.targeting.hpp" #include "object.prototypes.hpp" diff --git a/src/act.wizard.hpp b/src/cmd.imm/act.wizard.hpp similarity index 100% rename from src/act.wizard.hpp rename to src/cmd.imm/act.wizard.hpp diff --git a/src/cmd.imm/test.cpp b/src/cmd.imm/test.cpp new file mode 100644 index 000000000..e69279315 --- /dev/null +++ b/src/cmd.imm/test.cpp @@ -0,0 +1,27 @@ +// +// Created by ubuntu on 19/12/20. +// + +#include "test.h" +#include "comm.h" + +bool handleTestInput(CHAR_DATA *ch, char *arg) { + switch (LOWER(*arg)) { + case '1': + case 'z': + case 'я': + STATE(ch->desc) = CON_PLAYING; + send_to_char("Режим тестирования окончен.\r\n", ch); + return 1; + default: + break; + + } +} + + +void do_test(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) +{ + send_to_char(ch, "Переход в режим тестирования"); + STATE(ch->desc) = CON_IMPLTEST; +} \ No newline at end of file diff --git a/src/cmd.imm/test.h b/src/cmd.imm/test.h new file mode 100644 index 000000000..d6a84e8d3 --- /dev/null +++ b/src/cmd.imm/test.h @@ -0,0 +1,9 @@ +#ifndef BYLINS_TEST_H +#define BYLINS_TEST_H + +#include "chars/char.hpp" + +void do_test(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/); +bool handleTestInput(CHAR_DATA *ch, char *arg); + +#endif //BYLINS_TEST_H diff --git a/src/cmd/quit.cpp b/src/cmd/quit.cpp new file mode 100644 index 000000000..e32b0cc7a --- /dev/null +++ b/src/cmd/quit.cpp @@ -0,0 +1,83 @@ +// +// Created by ubuntu on 20/12/20. +// + +#include "quit.h" +#include "fightsystem/fight_stuff.hpp" +#include "depot.hpp" +#include "handler.h" +#include "objsave.h" +#include "house.h" + +extern int free_rent; + +void do_quit(CHAR_DATA *ch, char *argument, int/* cmd*/, int subcmd) +{ + DESCRIPTOR_DATA *d, *next_d; + + if (IS_NPC(ch) || !ch->desc) + return; + + if (subcmd != SCMD_QUIT) + send_to_char("Вам стоит набрать эту команду полностью во избежание недоразумений!\r\n", ch); + else if (GET_POS(ch) == POS_FIGHTING) + send_to_char("Угу! Щаз-з-з! Вы, батенька, деретесь!\r\n", ch); + else if (GET_POS(ch) < POS_STUNNED) + { + send_to_char("Вас пригласила к себе владелица косы...\r\n", ch); + die(ch, nullptr); + } + else if (AFF_FLAGGED(ch, EAffectFlag::AFF_SLEEP)) + { + return; + } + else if (*argument) + { + send_to_char("Если вы хотите выйти из игры с потерей всех вещей, то просто наберите 'конец'.\r\n", ch); + } + else + { +// int loadroom = ch->in_room; + if (RENTABLE(ch)) + { + send_to_char("В связи с боевыми действиями эвакуация временно прекращена.\r\n", ch); + return; + } + if (!GET_INVIS_LEV(ch)) + act("$n покинул$g игру.", TRUE, ch, 0, 0, TO_ROOM | TO_ARENA_LISTEN); + sprintf(buf, "%s quit the game.", GET_NAME(ch)); + mudlog(buf, NRM, MAX(LVL_GOD, GET_INVIS_LEV(ch)), SYSLOG, TRUE); + send_to_char("До свидания, странник... Мы ждем тебя снова!\r\n", ch); + + long depot_cost = static_cast(Depot::get_total_cost_per_day(ch)); + if (depot_cost) + { + send_to_char(ch, "За вещи в хранилище придется заплатить %ld %s в день.\r\n", depot_cost, desc_count(depot_cost, WHAT_MONEYu)); + long deadline = ((ch->get_gold() + ch->get_bank()) / depot_cost); + send_to_char(ch, "Твоих денег хватит на %ld %s.\r\n", deadline, desc_count(deadline, WHAT_DAY)); + } + Depot::exit_char(ch); + Clan::clan_invoice(ch, false); + if (ch->personGroup) + ch->personGroup->removeMember(ch); + + /* + * kill off all sockets connected to the same player as the one who is + * trying to quit. Helps to maintain sanity as well as prevent duping. + */ + for (d = descriptor_list; d; d = next_d) + { + next_d = d->next; + if (d == ch->desc) + continue; + if (d->character && (GET_IDNUM(d->character) == GET_IDNUM(ch))) + STATE(d) = CON_DISCONNECT; + } + + if (free_rent || IS_GOD(ch)) + { + Crash_rentsave(ch, 0); + } + extract_char(ch, FALSE); + } +} diff --git a/src/cmd/quit.h b/src/cmd/quit.h new file mode 100644 index 000000000..cee5a1240 --- /dev/null +++ b/src/cmd/quit.h @@ -0,0 +1,12 @@ +// +// Created by ubuntu on 20/12/20. +// + +#ifndef BYLINS_QUIT_H +#define BYLINS_QUIT_H + +#include "chars/char.hpp" + +void do_quit(CHAR_DATA *ch, char *argument, int/* cmd*/, int subcmd); + +#endif //BYLINS_QUIT_H diff --git a/src/comm.cpp b/src/comm.cpp index d129ad652..d3fe7d1db 100644 --- a/src/comm.cpp +++ b/src/comm.cpp @@ -3410,7 +3410,9 @@ void close_socket(DESCRIPTOR_DATA * d, int direct) || STATE(d) == CON_NAMED_STUFF || STATE(d) == CON_MAP_MENU || STATE(d) == CON_TORC_EXCH - || STATE(d) == CON_SEDIT || STATE(d) == CON_CONSOLE) + || STATE(d) == CON_SEDIT + || STATE(d) == CON_CONSOLE + || STATE(d) == CON_IMPLTEST) { STATE(d) = CON_PLAYING; } diff --git a/src/fightsystem/fight_stuff.hpp b/src/fightsystem/fight_stuff.hpp index d95326a3c..a8f92cd80 100644 --- a/src/fightsystem/fight_stuff.hpp +++ b/src/fightsystem/fight_stuff.hpp @@ -4,6 +4,7 @@ class CHAR_DATA; // to avoid inclusion "char.hpp" into header file. bool check_tester_death(CHAR_DATA *ch, CHAR_DATA *killer); +void die(CHAR_DATA *ch, CHAR_DATA *killer); #endif // __FIGHT_STUFF_HPP__ diff --git a/src/global.objects.cpp b/src/global.objects.cpp index 18be110fe..6d97e2348 100644 --- a/src/global.objects.cpp +++ b/src/global.objects.cpp @@ -1,3 +1,4 @@ +#include #include "global.objects.hpp" #include "ban.hpp" @@ -39,10 +40,11 @@ namespace DailyQuestMap daily_quests; Strengthening strengthening; obj2trigers_t obj2trigers; + GroupRoster groupRoster; }; GlobalObjectsStorage::GlobalObjectsStorage() : - ban(nullptr) + ban(nullptr), groupRoster() { } @@ -196,4 +198,8 @@ obj2trigers_t& GlobalObjects::obj_trigers() return global_objects().obj2trigers; } +GroupRoster& GlobalObjects::groupRoster() { + return global_objects().groupRoster; +} + // vim: ts=4 sw=4 tw=0 noet syntax=cpp : diff --git a/src/global.objects.hpp b/src/global.objects.hpp index e35c01859..56f5a21b6 100644 --- a/src/global.objects.hpp +++ b/src/global.objects.hpp @@ -10,11 +10,13 @@ #include "shops.implementation.hpp" #include "world.objects.hpp" #include "chars/world.characters.hpp" -#include "act.wizard.hpp" +#include "cmd.imm/act.wizard.hpp" #include "influxdb.hpp" #include "zone.table.hpp" #include "daily_quest.hpp" #include "strengthening.hpp" +#include "grp/grp.roster.h" + class BanList; // to avoid inclusion of ban.hpp /** @@ -60,6 +62,7 @@ class GlobalObjects static DailyQuestMap& daily_quests(); static Strengthening& strengthening(); static obj2trigers_t& obj_trigers(); + static GroupRoster& groupRoster(); }; #endif // __GLOBAL_OBJECTS_HPP__ diff --git a/src/groups/group.h b/src/groups/group.h deleted file mode 100644 index 5378161aa..000000000 --- a/src/groups/group.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef BYLINS_GROUP_H -#define BYLINS_GROUP_H - -#include "chars/char.hpp" - -void change_leader(CHAR_DATA *ch, CHAR_DATA *vict); -void do_group(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/); -void do_ungroup(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/); -void do_report(CHAR_DATA *ch, char* /*argument*/, int/* cmd*/, int/* subcmd*/); - -#endif //BYLINS_GROUP_H diff --git a/src/groups/grp.member.cpp b/src/groups/grp.member.cpp deleted file mode 100644 index 9bf3f9c37..000000000 --- a/src/groups/grp.member.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "grp.member.h" diff --git a/src/groups/grp.member.h b/src/groups/grp.member.h deleted file mode 100644 index 7e42fa9bf..000000000 --- a/src/groups/grp.member.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef BYLINS_GRP_MEMBER_H -#define BYLINS_GRP_MEMBER_H - -#include "chars/char.hpp" - -class groupMember { -public: - groupMember(); - -private: - CHAR_DATA *ch; - bool isLeader; -}; - -#endif //BYLINS_GRP_MEMBER_H diff --git a/src/grp/follow.cpp b/src/grp/follow.cpp new file mode 100644 index 000000000..6d9295a29 --- /dev/null +++ b/src/grp/follow.cpp @@ -0,0 +1,253 @@ +// +// Created by ubuntu on 19/12/20. +// + +#include "follow.h" +#include "fightsystem/fight.h" +#include "handler.h" + +void perform_drop_gold(CHAR_DATA * ch, int amount); + +// Called when stop following persons, or stopping charm // +// This will NOT do if a character quits/dies!! // +// При возврате 1 использовать ch нельзя, т.к. прошли через extract_char +// TODO: по всем вызовам не проходил, может еще где-то коряво вызывается, кроме передачи скакунов -- Krodo +// при персонаже на входе - пуржить не должно полюбому, если начнет, как минимум в change_leader будут глюки +bool stop_follower(CHAR_DATA * ch, int mode) +{ + struct follow_type *j, *k; + int i; + + //log("[Stop follower] Start function(%s->%s)",ch ? GET_NAME(ch) : "none", + // ch->master ? GET_NAME(ch->master) : "none"); + + if (!ch->has_master()) + { + log("SYSERR: stop_follower(%s) without master", GET_NAME(ch)); + return (FALSE); + } + + // для смены лидера без лишнего спама + if (!IS_SET(mode, SF_SILENCE)) + { + act("Вы прекратили следовать за $N4.", FALSE, ch, 0, ch->get_master(), TO_CHAR); + act("$n прекратил$g следовать за $N4.", TRUE, ch, 0, ch->get_master(), TO_NOTVICT | TO_ARENA_LISTEN); + } + + //log("[Stop follower] Stop horse"); + if (ch->get_master()->get_horse() == ch && ch->get_master()->ahorse()) + { + ch->drop_from_horse(); + } + else + { + act("$n прекратил$g следовать за вами.", TRUE, ch, 0, ch->get_master(), TO_VICT); + } + + //log("[Stop follower] Remove from followers list"); + if (!ch->get_master()->followers) + { + log("[Stop follower] SYSERR: Followers absent for %s (master %s).", GET_NAME(ch), GET_NAME(ch->get_master())); + } + else if (ch->get_master()->followers->follower == ch) // Head of follower-list? + { + k = ch->get_master()->followers; + ch->get_master()->followers = k->next; + if (!ch->get_master()->followers + && !ch->get_master()->has_master()) + { + //AFF_FLAGS(ch->get_master()).unset(EAffectFlag::AFF_GROUP); + ch->get_master()->removeGroupFlags(); + } + free(k); + } + else // locate follower who is not head of list + { + for (k = ch->get_master()->followers; k->next && k->next->follower != ch; k = k->next); + if (!k->next) + { + log("[Stop follower] SYSERR: Undefined %s in %s followers list.", GET_NAME(ch), GET_NAME(ch->get_master())); + } + else + { + j = k->next; + k->next = j->next; + free(j); + } + } + + ch->set_master(nullptr); + //AFF_FLAGS(ch).unset(EAffectFlag::AFF_GROUP); + ch->removeGroupFlags(); + + if (AFF_FLAGGED(ch, EAffectFlag::AFF_CHARM) + || AFF_FLAGGED(ch, EAffectFlag::AFF_HELPER) + || IS_SET(mode, SF_CHARMLOST)) + { + if (affected_by_spell(ch, SPELL_CHARM)) + { + affect_from_char(ch, SPELL_CHARM); + } + EXTRACT_TIMER(ch) = 5; + AFF_FLAGS(ch).unset(EAffectFlag::AFF_CHARM); + + if (ch->get_fighting()) + { + stop_fighting(ch, TRUE); + } + + if (IS_NPC(ch)) + { + if (MOB_FLAGGED(ch, MOB_CORPSE)) + { + act("Налетевший ветер развеял $n3, не оставив и следа.", TRUE, ch, 0, 0, TO_ROOM | TO_ARENA_LISTEN); + GET_LASTROOM(ch) = GET_ROOM_VNUM(ch->in_room); + perform_drop_gold(ch, ch->get_gold()); + ch->set_gold(0); + extract_char(ch, FALSE); + return (TRUE); + } + else if (AFF_FLAGGED(ch, EAffectFlag::AFF_HELPER)) + { + AFF_FLAGS(ch).unset(EAffectFlag::AFF_HELPER); + } + } + } + + if (IS_NPC(ch) + && !MOB_FLAGGED(ch, MOB_PLAYER_SUMMON) //Не ресетим флаги, если моб призван игроком + && (i = GET_MOB_RNUM(ch)) >= 0) + { + MOB_FLAGS(ch) = MOB_FLAGS(mob_proto + i); + } + + return (FALSE); +} + + + +// * Called when a character that follows/is followed dies +bool die_follower(CHAR_DATA * ch) +{ + struct follow_type *j, *k = ch->followers; + + if (ch->has_master() && stop_follower(ch, SF_FOLLOWERDIE)) + { + // чармиса спуржили в stop_follower + return true; + } + + if (ch->ahorse()) { + AFF_FLAGS(ch).unset(EAffectFlag::AFF_HORSE); + } + + for (k = ch->followers; k; k = j) + { + j = k->next; + stop_follower(k->follower, SF_MASTERDIE); + } + return false; +} + + +// Check if making CH follow VICTIM will create an illegal // +// Follow "Loop/circle" // +bool circle_follow(CHAR_DATA * ch, CHAR_DATA * victim) +{ + for (auto k = victim; k; k = k->get_master()) + { + if (k->get_master() == k) + { + k->set_master(nullptr); + return false; + } + + if (k == ch) + { + return true; + } + } + + return false; +} + +void do_follow(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) +{ + CHAR_DATA *leader; + struct follow_type *f; + one_argument(argument, smallBuf); + + if (IS_NPC(ch) && AFF_FLAGGED(ch, EAffectFlag::AFF_CHARM) && ch->get_fighting()) + return; + if (*smallBuf) + { + if (!str_cmp(smallBuf, "я") || !str_cmp(smallBuf, "self") || !str_cmp(smallBuf, "me")) + { + if (!ch->has_master()) + { + send_to_char("Но вы ведь ни за кем не следуете...\r\n", ch); + } + else + { + stop_follower(ch, SF_EMPTY); + } + return; + } + if (!(leader = get_char_vis(ch, smallBuf, FIND_CHAR_ROOM))) + { + send_to_char(NOPERSON, ch); + return; + } + } + else + { + send_to_char("За кем вы хотите следовать?\r\n", ch); + return; + } + + if (ch->get_master() == leader) + { + act("Вы уже следуете за $N4.", FALSE, ch, 0, leader, TO_CHAR); + return; + } + + if (AFF_FLAGGED(ch, EAffectFlag::AFF_CHARM) + && ch->has_master()) + { + act("Но вы можете следовать только за $N4!", FALSE, ch, 0, ch->get_master(), TO_CHAR); + } + else // Not Charmed follow person + { + if (leader == ch) + { + if (!ch->has_master()) + { + send_to_char("Вы уже следуете за собой.\r\n", ch); + return; + } + stop_follower(ch, SF_EMPTY); + } + else + { + if (circle_follow(ch, leader)) + { + send_to_char("Так у вас целый хоровод получится.\r\n", ch); + return; + } + + if (ch->has_master()) + { + stop_follower(ch, SF_EMPTY); + } + //AFF_FLAGS(ch).unset(EAffectFlag::AFF_GROUP); + ch->removeGroupFlags(); + for (f = ch->followers; f; f = f->next) + { + //AFF_FLAGS(f->follower).unset(EAffectFlag::AFF_GROUP); + f->follower->removeGroupFlags(); + } + + leader->add_follower(ch); + } + } +} diff --git a/src/grp/follow.h b/src/grp/follow.h new file mode 100644 index 000000000..3afa1c2dc --- /dev/null +++ b/src/grp/follow.h @@ -0,0 +1,8 @@ +#ifndef BYLINS_FOLLOW_H +#define BYLINS_FOLLOW_H + +#include "chars/char.hpp" + +void do_follow(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/); + +#endif //BYLINS_FOLLOW_H diff --git a/src/groups/group.cpp b/src/grp/grp.group.cpp similarity index 82% rename from src/groups/group.cpp rename to src/grp/grp.group.cpp index 11da30b8a..bcb3d6941 100644 --- a/src/groups/group.cpp +++ b/src/grp/grp.group.cpp @@ -1,4 +1,4 @@ -#include "group.h" +#include "grp.group.h" #include "comm.h" #include "handler.h" @@ -6,6 +6,8 @@ #include "msdp.constants.hpp" #include "screen.h" +extern GroupRoster& groupRoster; + bool is_group_member(CHAR_DATA *ch, CHAR_DATA *vict) { if (IS_NPC(vict) || !AFF_FLAGGED(vict, EAffectFlag::AFF_GROUP) || vict->get_master() != ch) @@ -325,6 +327,13 @@ int perform_group(CHAR_DATA * ch, CHAR_DATA * vict) act("Вы приняты в группу $n1.", FALSE, ch, 0, vict, TO_VICT); act("$N принят$A в группу $n1.", FALSE, ch, 0, vict, TO_NOTVICT | TO_ARENA_LISTEN); } + if (ch->personGroup == nullptr) + ch->personGroup = groupRoster.addGroup(ch); + if (vict->personGroup == nullptr) + { + vict->personGroup = ch->personGroup; + vict->personGroup->addMember(ch); + } return (TRUE); } @@ -439,7 +448,6 @@ void change_leader(CHAR_DATA *ch, CHAR_DATA *vict) } } - void do_group(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) { CHAR_DATA *vict; @@ -460,6 +468,13 @@ void do_group(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) return; } + if (!str_cmp(buf, "новый")) + { + if (ch->personGroup) + ch->personGroup->print(ch); + return; + } + if (GET_POS(ch) < POS_RESTING) { send_to_char("Трудно управлять группой в таком состоянии.\r\n", ch); @@ -707,4 +722,172 @@ void do_report(CHAR_DATA *ch, char* /*argument*/, int/* cmd*/, int/* subcmd*/) send_to_char(buf, k); } send_to_char("Вы доложили о состоянии всем членам вашей группы.\r\n", ch); -} \ No newline at end of file +} + + +Group::Group(CHAR_DATA *leader, u_long uid){ + this->_memberCap = IS_NPC(leader)? 255 : max_group_size(leader); + this->_uid = uid; + this->addMember(leader); + this->setLeader(leader); +} + +void Group::addMember(CHAR_DATA *member) { + if (IS_NPC(member)) + return; + this->_memberList.emplace(member->get_uid(), member); +} + +void Group::removeMember(CHAR_DATA *member) { + if (this->_currentMemberCount == 0) { + sprintf(buf, "ROSTER: попытка удалить из группы при текущем индексе 0, id=%lu", this->_uid); + mudlog(buf, BRF, LVL_IMMORT, SYSLOG, TRUE); + return; + } + auto it = this->_memberList.find(member->get_uid()); + if (it != this->_memberList.end()) + this->_memberList.erase(it); +} + +void Group::print(CHAR_DATA *member) { + if (this->_memberList.empty()) { + send_to_char(member, "WTF"); + return; + } + sprintf(buf, "Персонажи в группе, id = %lu\r\n", this->_uid); + for (auto & it : this->_memberList){ + if (it.second->get_uid() != this->_uid) { + sprintf(buf, "%s - %s", it.second->get_pc_name().c_str(), "Игрок"); + } else { + sprintf(buf, "%s - %s", it.second->get_pc_name().c_str(), "Лидер"); + } + } + +} + +CHAR_DATA *Group::getLeader() const { + return _leader; +} + +void Group::setLeader(CHAR_DATA *leader) { + this->_leaderUID = leader->get_uid(); + _leader = leader; +} + +bool Group::isActive() { + return false; +} + +bool Group::checkMember(int uid) { + return false; +} + +u_long Group::getUid() const { + return _uid; +} + +u_short Group::getCurrentMemberCount() const { + return _currentMemberCount; +} + +int Group::getMemberCap() const { + return _memberCap; +} + +Group::~Group() { + // чистим ссылки + for (auto & it : this->_memberList){ + if (it.second->purged()) + continue; + send_to_char(it.second, "Ваша группа была распущена.\r\n"); + it.second->personGroup = nullptr; + } +} + +bool Group::applyRequest(CHAR_DATA *applicant) { + if (this->_requestList.find(applicant->get_uid()) == this->_requestList.end()) { + act("$E подал$A заявку на вступление в группу.", FALSE, this->_leader, 0, applicant, TO_CHAR); + this->_requestList.emplace(applicant->get_uid(), applicant); + return true; + } + return false; +} + +bool Group::approveRequest(CHAR_DATA *applicant) { + if (this->_memberList.size() == this->_memberCap){ + send_to_char(this->_leader, "Чтобы принять кого нужного, надо сперва исключить кого ненужного!\r\n"); + return false; + } +} + +bool Group::denyRequest(CHAR_DATA *applicant) { + return false; +} + +void GCmd::do_gmake(CHAR_DATA *ch, char *argument, int, int) { + if (!ch) + return; + if (ch->personGroup && ch->personGroup->getLeader() == ch){ + send_to_char(ch, "Только древний правитель Цесарь Иулий мог водить много легионов!\r\n"); + return; + } + if (ch->personGroup) // в группе - покидаем + ch->personGroup->removeMember(ch); + groupRoster.addGroup(ch); + send_to_char(ch, "Вы создали группу id:%d, максимальное число последователей: %d\r\n",ch->personGroup->getUid(), ch->personGroup->getMemberCap()); +} + +void GCmd::do_ginvite(CHAR_DATA *ch, char *argument, int, int) { + +} + +void GCmd::do_grequest(CHAR_DATA *ch, char *argument, int, int) { + CHAR_DATA *vict = nullptr; + + if (AFF_FLAGGED(ch, EAffectFlag::AFF_CHARM)) + return; + + if (AFF_FLAGGED(ch, EAffectFlag::AFF_SILENCE) || AFF_FLAGGED(ch, EAffectFlag::AFF_STRANGLED)){ + send_to_char(ch, "Вы немы, как рыба об лед.\r\n"); + return; + } + if (ch->personGroup && ch->personGroup->getLeader() == ch){ + send_to_char(ch, "Негоже лидеру группы напрашиваться в чужую!\r\n"); + return; + } + + one_argument(argument, smallBuf); + + if (!*smallBuf) { + send_to_char("И кому же вы хотите напроситься в группу?.\r\n", ch); + return; + } + if (!(vict = get_player_vis(ch, smallBuf, FIND_CHAR_WORLD)) || IS_NPC(vict)) + { + send_to_char("Необходимо видеть лидера группы.\r\n", ch); + return; + } + if (vict->personGroup->applyRequest(ch)){ + send_to_char("Заявка на вступление в группу отправлена.\r\n", ch); + } else { + send_to_char("Вы уже подавали заявку в группу!.\r\n", ch); + } +} + +void GCmd::do_gleave(CHAR_DATA *ch, char *argument, int, int) { + +} + +void GCmd::do_gabandon(CHAR_DATA *ch, char *argument, int, int) { + if (!ch->personGroup || ch->personGroup->getLeader() != ch){ + send_to_char(ch, "У вас мания величия, вы не управляете группой!\r\n"); + return; + } + send_to_char(ch, "Вы распустили группу id:%d\r\n",ch->personGroup->getUid()); + groupRoster.removeGroup(ch->personGroup->getUid()); + +} + +void GCmd::do_gpromote(CHAR_DATA *ch, char *argument, int, int) { + +} diff --git a/src/grp/grp.group.h b/src/grp/grp.group.h new file mode 100644 index 000000000..abf2a9373 --- /dev/null +++ b/src/grp/grp.group.h @@ -0,0 +1,65 @@ +#ifndef BYLINS_GRP_GROUP_H +#define BYLINS_GRP_GROUP_H + +#include "chars/char.hpp" +#include "grp.roster.h" + +void change_leader(CHAR_DATA *ch, CHAR_DATA *vict); +void do_group(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/); +void do_ungroup(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/); +void do_report(CHAR_DATA *ch, char* /*argument*/, int/* cmd*/, int/* subcmd*/); +int max_group_size(CHAR_DATA *ch); + +class Group { +private: + // ид группы в ростере + u_long _uid = 0; + // текущее количество игроков + u_short _currentMemberCount = 0; + //макс.количество игроков + int _memberCap = 0; + // ид лидера + int _leaderUID; + CHAR_DATA* _leader; + // состав группы, ключом UID персонажа. Дохлые/вышедшие проверяем отдельным методом + std::map _memberList; + // список заявок ключ UID персонажа. При принятии одобряется, при отказе удаляется. + std::map _requestList; +public: + u_long getUid() const; + u_short getCurrentMemberCount() const; + CHAR_DATA *getLeader() const; + void setLeader(CHAR_DATA *leader); + int getMemberCap() const; + + virtual ~Group(); + + Group(CHAR_DATA *leader, u_long uid); + void addMember(CHAR_DATA *member); + void removeMember(CHAR_DATA *member); + bool checkMember(int uid); // проверки валидности персонажа + + void print(CHAR_DATA *member); + bool isActive(); // проверка, что в группе все персонажи онлайн + + bool applyRequest(CHAR_DATA *applicant); + bool approveRequest(CHAR_DATA *applicant); + bool denyRequest(CHAR_DATA *applicant); +}; + +namespace GCmd{ + // команда роспуска группы + void do_gabandon(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/); + // команда приглашения лидером персонажа в группу + void do_ginvite(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/); + // команда выхода из группы + void do_gleave(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/); + // команда создания группы + void do_gmake(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/); + // команда переназначения лидера + void do_gpromote(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/); + // команда запроса персонажем в группу + void do_grequest(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/); +} + +#endif //BYLINS_GRP_GROUP_H diff --git a/src/grp/grp.roster.cpp b/src/grp/grp.roster.cpp new file mode 100644 index 000000000..dc312a7dd --- /dev/null +++ b/src/grp/grp.roster.cpp @@ -0,0 +1,46 @@ +// +// Created by ubuntu on 17/12/20. +// + +#include "grp.roster.h" +#include "global.objects.hpp" + +GroupRoster& groupRoster = GlobalObjects::groupRoster(); + +GroupRoster::GroupRoster() { + this->_currentGroupIndex = 0; +} + +Group* GroupRoster::addGroup(CHAR_DATA * leader) { + ++this->_currentGroupIndex; + auto *group = new Group(leader, _currentGroupIndex); + this->_groupList.emplace(_currentGroupIndex, new GroupRosterRecord(group)); + leader->personGroup = group; // прописываем сразу обратную ссылку персонажа на группу + return group; +} + +void GroupRoster::removeGroup(u_long uid) { + auto grp = _groupList.find(uid); + delete grp->second->member; + _groupList.erase(uid); +} + +void GroupRoster::recalculateRoster() { + return; +} + +void GroupRoster::restorePlayerGroup(CHAR_DATA *ch) { + for (auto & it : this->_groupList){ + if(it.second->member->checkMember(ch->get_uid())){ + ch->personGroup = it.second->member; + ch->send_to_TC(true, false, true, "Restore group: %d", ch->personGroup->getUid()); + break; + } + } +} + + +GroupRosterRecord::GroupRosterRecord(Group *group) { + this->member = group; + this->needRecalc = false; +} diff --git a/src/grp/grp.roster.h b/src/grp/grp.roster.h new file mode 100644 index 000000000..65c6163c2 --- /dev/null +++ b/src/grp/grp.roster.h @@ -0,0 +1,32 @@ +// +// Created by ubuntu on 17/12/20. +// + +#ifndef BYLINS_GRP_ROSTER_H +#define BYLINS_GRP_ROSTER_H + +#include "chars/char.hpp" +#include "grp.group.h" + +class GroupRosterRecord { +public: + explicit GroupRosterRecord(Group * group); + Group *member; + bool needRecalc = false; +}; + + +class GroupRoster { +private: + u_long _currentGroupIndex = 0; + std::map _groupList; +public: + GroupRoster(); + Group* addGroup(CHAR_DATA * leader); + u_long getSize() {return _groupList.size();} + void removeGroup(u_long uid); + void restorePlayerGroup(CHAR_DATA *ch); + void recalculateRoster(); +}; + +#endif //BYLINS_GRP_ROSTER_H diff --git a/src/interpreter.cpp b/src/interpreter.cpp index 54005102f..07df038d6 100644 --- a/src/interpreter.cpp +++ b/src/interpreter.cpp @@ -24,6 +24,7 @@ #include "cmd/hire.h" #include "cmd/mercenary.h" #include "cmd/order.h" +#include "cmd/quit.h" #include "cmd/retreat.h" #include "cmd/telegram.h" #include "cmd/track.h" @@ -42,7 +43,7 @@ #include "glory.hpp" #include "glory_const.hpp" #include "glory_misc.hpp" -#include "groups/group.h" +#include "grp/grp.group.h" #include "handler.h" #include "heartbeat.commands.hpp" #include "house.h" @@ -84,6 +85,7 @@ #include "time.h" #include "title.hpp" #include "top.h" +#include "cmd.imm/test.h" #if defined WITH_SCRIPTING #include "scripting.hpp" @@ -119,6 +121,7 @@ #include #endif +extern GroupRoster& groupRoster; extern room_rnum r_mortal_start_room; extern room_rnum r_immort_start_room; extern room_rnum r_frozen_start_room; @@ -281,7 +284,6 @@ void do_learn(CHAR_DATA *ch, char *argument, int cmd, int subcmd); void do_forget(CHAR_DATA *ch, char *argument, int cmd, int subcmd); void do_purge(CHAR_DATA *ch, char *argument, int cmd, int subcmd); void do_put(CHAR_DATA *ch, char *argument, int cmd, int subcmd); -void do_quit(CHAR_DATA *ch, char *argument, int cmd, int subcmd); void do_reboot(CHAR_DATA *ch, char *argument, int cmd, int subcmd); void do_remove(CHAR_DATA *ch, char *argument, int cmd, int subcmd); void do_rent(CHAR_DATA *ch, char *argument, int cmd, int subcmd); @@ -878,6 +880,12 @@ cpp_extern const struct command_info cmd_info[] = {"group", POS_RESTING, do_group, 1, 0, 500}, {"gsay", POS_SLEEPING, do_gsay, 0, 0, -1}, {"gtell", POS_SLEEPING, do_gsay, 0, 0, -1}, + {"gabandon", POS_SLEEPING, GCmd::do_gabandon, 0, 0, -1}, + {"gmake", POS_SLEEPING, GCmd::do_gmake, 0, 0, -1}, + {"gpromote", POS_SLEEPING, GCmd::do_gpromote, 0, 0, -1}, + {"ginvite", POS_SLEEPING, GCmd::do_ginvite, 0, 0, -1}, + {"grequest", POS_SLEEPING, GCmd::do_grequest, 0, 0, -1}, + {"gleave", POS_SLEEPING, GCmd::do_gleave, 0, 0, -1}, {"handbook", POS_DEAD, do_gen_ps, LVL_IMMORT, SCMD_HANDBOOK, 0}, {"hcontrol", POS_DEAD, DoHcontrol, LVL_GRGOD, 0, 0}, {"help", POS_DEAD, do_help, 0, 0, 0}, @@ -1002,6 +1010,7 @@ cpp_extern const struct command_info cmd_info[] = {"sedit", POS_DEAD, do_sedit, LVL_IMPL, 0, 0}, {"errlog", POS_DEAD, do_syslog, LVL_BUILDER, ERRLOG, 0}, {"imlog", POS_DEAD, do_syslog, LVL_BUILDER, IMLOG, 0}, + {"test", POS_DEAD, do_test, LVL_IMPL, 0, 0}, {"take", POS_RESTING, do_get, 0, 0, 500}, {"taste", POS_RESTING, do_eat, 0, SCMD_TASTE, 500}, {"t2c", POS_RESTING, do_send_text_to_char, LVL_GRGOD, 0, -1 }, @@ -2607,6 +2616,8 @@ void do_entergame(DESCRIPTOR_DATA * d) } Noob::check_help_message(d->character.get()); + // возвращаем персонажа группу + groupRoster.restorePlayerGroup(d->character.get()); } //По кругу проверяем корректность параметров @@ -2989,99 +3000,105 @@ void nanny(DESCRIPTOR_DATA * d, char *arg) if (STATE(d) != CON_CONSOLE) skip_spaces(&arg); - switch (STATE(d)) - { - case CON_INIT: - // just connected - { - int online_players = 0; - for (auto i = descriptor_list; i; i = i->next) - { - online_players++; - } - sprintf(buf, "Online: %d\r\n", online_players); - } - - SEND_TO_Q(buf, d); - ShowEncodingPrompt(d, false); - STATE(d) = CON_GET_KEYTABLE; - break; - + switch (STATE(d)) { + case CON_INIT: { + // just connected + { + int online_players = 0; + for (auto i = descriptor_list; i; i = i->next) { + online_players++; + } + sprintf(buf, "Online: %d\r\n", online_players); + } + + SEND_TO_Q(buf, d); + ShowEncodingPrompt(d, false); + STATE(d) = CON_GET_KEYTABLE; + break; + } //. OLC states . - case CON_OEDIT: - oedit_parse(d, arg); - break; - - case CON_REDIT: - redit_parse(d, arg); - break; - - case CON_ZEDIT: - zedit_parse(d, arg); - break; - - case CON_MEDIT: - medit_parse(d, arg); - break; - - case CON_TRIGEDIT: - trigedit_parse(d, arg); - break; - - case CON_MREDIT: - mredit_parse(d, arg); - break; - - case CON_CLANEDIT: - d->clan_olc->clan->Manage(d, arg); - break; - - case CON_SPEND_GLORY: - if (!Glory::parse_spend_glory_menu(d->character.get(), arg)) - { - Glory::spend_glory_menu(d->character.get()); - } - break; - - case CON_GLORY_CONST: - if (!GloryConst::parse_spend_glory_menu(d->character.get(), arg)) - { - GloryConst::spend_glory_menu(d->character.get()); - } - break; - - case CON_NAMED_STUFF: - if (!NamedStuff::parse_nedit_menu(d->character.get(), arg)) - { - NamedStuff::nedit_menu(d->character.get()); - } - break; - - case CON_MAP_MENU: - d->map_options->parse_menu(d->character.get(), arg); - break; - - case CON_TORC_EXCH: - ExtMoney::torc_exch_parse(d->character.get(), arg); - break; - - case CON_SEDIT: - { - try - { - obj_sets_olc::parse_input(d->character.get(), arg); - } - catch (const std::out_of_range &e) - { - send_to_char(d->character.get(), "Редактирование прервано: %s", e.what()); - d->sedit.reset(); - STATE(d) = CON_PLAYING; - } - break; - } - //. End of OLC states .*/ + case CON_OEDIT: { + oedit_parse(d, arg); + break; + } + + case CON_REDIT: { + redit_parse(d, arg); + break; + } + + case CON_ZEDIT: { + zedit_parse(d, arg); + break; + } + + case CON_MEDIT: { + medit_parse(d, arg); + break; + } + + case CON_TRIGEDIT: { + trigedit_parse(d, arg); + break; + } + + case CON_MREDIT: { + mredit_parse(d, arg); + break; + } - case CON_GET_KEYTABLE: + case CON_SEDIT:{ + try + { + obj_sets_olc::parse_input(d->character.get(), arg); + } + catch (const std::out_of_range &e) + { + send_to_char(d->character.get(), "Редактирование прервано: %s", e.what()); + d->sedit.reset(); + STATE(d) = CON_PLAYING; + } + break; + } + + case CON_CLANEDIT: { + d->clan_olc->clan->Manage(d, arg); + break; + } + + case CON_SPEND_GLORY: { + if (!Glory::parse_spend_glory_menu(d->character.get(), arg)) { + Glory::spend_glory_menu(d->character.get()); + } + break; + } + + case CON_GLORY_CONST: { + if (!GloryConst::parse_spend_glory_menu(d->character.get(), arg)) { + GloryConst::spend_glory_menu(d->character.get()); + } + break; + } + + case CON_NAMED_STUFF: { + if (!NamedStuff::parse_nedit_menu(d->character.get(), arg)) { + NamedStuff::nedit_menu(d->character.get()); + } + break; + } + + case CON_MAP_MENU:{ + d->map_options->parse_menu(d->character.get(), arg); + break; + } + + case CON_TORC_EXCH: { + ExtMoney::torc_exch_parse(d->character.get(), arg); + break; + } + //. End of OLC states .*/ + + case CON_GET_KEYTABLE: { if (strlen(arg) > 0) arg[0] = arg[strlen(arg) - 1]; if (*arg == '9') @@ -3099,509 +3116,445 @@ void nanny(DESCRIPTOR_DATA * d, char *arg) SEND_TO_Q(GREETINGS, d); STATE(d) = CON_GET_NAME; break; + } + // wait for input of name + case CON_GET_NAME: { + if (!d->character) { + CreateChar(d); + } + + if (!*arg) { + STATE(d) = CON_CLOSE; + } else if (!str_cmp("новый", arg)) { + SEND_TO_Q(name_rules, d); + + std::stringstream ss; + ss << "Введите имя"; + if (0 < player_table.free_names_count()) { + ss << " (примеры доступных имен : "; + print_free_names(ss, player_table); + ss << ")"; + } + ss << ": "; + + SEND_TO_Q(ss.str().c_str(), d); + STATE(d) = CON_NEW_CHAR; + return; + } else { + if (sscanf(arg, "%s %s", pwd_name, pwd_pwd) == 2) { + if (parse_exist_name(pwd_name, tmp_name) + || (player_i = load_char(tmp_name, d->character.get())) < 0) { + SEND_TO_Q("Некорректное имя. Повторите, пожалуйста.\r\n" "Имя : ", d); + return; + } + + if (PLR_FLAGGED(d->character, PLR_DELETED) + || !Password::compare_password(d->character.get(), pwd_pwd)) { + SEND_TO_Q("Некорректное имя. Повторите, пожалуйста.\r\n" "Имя : ", d); + if (!PLR_FLAGGED(d->character, PLR_DELETED)) { + sprintf(buf, "Bad PW: %s [%s]", GET_NAME(d->character), d->host); + mudlog(buf, BRF, LVL_IMMORT, SYSLOG, TRUE); + } + + d->character.reset(); + return; + } + + PLR_FLAGS(d->character).unset(PLR_MAILING); + PLR_FLAGS(d->character).unset(PLR_WRITING); + PLR_FLAGS(d->character).unset(PLR_CRYO); + d->character->set_pfilepos(player_i); + GET_ID(d->character) = GET_IDNUM(d->character); + DoAfterPassword(d); + + return; + } else { + if (parse_exist_name(arg, tmp_name) || + strlen(tmp_name) < (MIN_NAME_LENGTH - 1) || // дабы можно было войти чарам с 4 буквами + strlen(tmp_name) > MAX_NAME_LENGTH || + !Is_Valid_Name(tmp_name) || fill_word(tmp_name) || reserved_word(tmp_name)) { + SEND_TO_Q("Некорректное имя. Повторите, пожалуйста.\r\n" "Имя : ", d); + return; + } else if (!Is_Valid_Dc(tmp_name)) { + player_i = load_char(tmp_name, d->character.get()); + d->character->set_pfilepos(player_i); + if (IS_IMMORTAL(d->character) || PRF_FLAGGED(d->character, PRF_CODERINFO)) { + SEND_TO_Q("Игрок с подобным именем является БЕССМЕРТНЫМ в игре.\r\n", d); + } else { + SEND_TO_Q("Игрок с подобным именем находится в игре.\r\n", d); + } + SEND_TO_Q("Во избежание недоразумений введите пару ИМЯ ПАРОЛЬ.\r\n", d); + SEND_TO_Q("Имя и пароль через пробел : ", d); + + d->character.reset(); + return; + } + } + + player_i = load_char(tmp_name, d->character.get()); + if (player_i > -1) { + d->character->set_pfilepos(player_i); + if (PLR_FLAGGED(d->character, + PLR_DELETED)) // We get a false positive from the original deleted character. + { + d->character.reset(); + + // Check for multiple creations... + if (!Valid_Name(tmp_name) || _parse_name(tmp_name, tmp_name)) { + SEND_TO_Q("Некорректное имя. Повторите, пожалуйста.\r\n" "Имя : ", d); + return; + } + + // дополнительная проверка на длину имени чара + if (strlen(tmp_name) < (MIN_NAME_LENGTH)) { + SEND_TO_Q("Некорректное имя. Повторите, пожалуйста.\r\n" "Имя : ", d); + return; + } + + CreateChar(d); + d->character->set_pc_name(CAP(tmp_name)); + d->character->player_data.PNames[0] = std::string(CAP(tmp_name)); + d->character->set_pfilepos(player_i); + sprintf(buf, "Вы действительно выбрали имя %s [ Y(Д) / N(Н) ]? ", tmp_name); + SEND_TO_Q(buf, d); + STATE(d) = CON_NAME_CNFRM; + } else // undo it just in case they are set + { + if (IS_IMMORTAL(d->character) || PRF_FLAGGED(d->character, PRF_CODERINFO)) { + SEND_TO_Q("Игрок с подобным именем является БЕССМЕРТНЫМ в игре.\r\n", d); + SEND_TO_Q("Во избежание недоразумений введите пару ИМЯ ПАРОЛЬ.\r\n", d); + SEND_TO_Q("Имя и пароль через пробел : ", d); + d->character.reset(); + + return; + } + + PLR_FLAGS(d->character).unset(PLR_MAILING); + PLR_FLAGS(d->character).unset(PLR_WRITING); + PLR_FLAGS(d->character).unset(PLR_CRYO); + SEND_TO_Q("Персонаж с таким именем уже существует. Введите пароль : ", d); + d->idle_tics = 0; + STATE(d) = CON_PASSWORD; + } + } else // player unknown -- make new character + { + // еще одна проверка + if (strlen(tmp_name) < (MIN_NAME_LENGTH)) { + SEND_TO_Q("Некорректное имя. Повторите, пожалуйста.\r\n" "Имя : ", d); + return; + } + + // Check for multiple creations of a character. + if (!Valid_Name(tmp_name) || _parse_name(tmp_name, tmp_name)) { + SEND_TO_Q("Некорректное имя. Повторите, пожалуйста.\r\n" "Имя : ", d); + return; + } + + if (cmp_ptable_by_name(tmp_name, MIN_NAME_LENGTH) >= 0) { + SEND_TO_Q + ("Первые символы вашего имени совпадают с уже существующим персонажем.\r\n" + "Для исключения разных недоразумений вам необходимо выбрать другое имя.\r\n" + "Имя : ", d); + return; + } + + d->character->set_pc_name(CAP(tmp_name)); + d->character->player_data.PNames[0] = std::string(CAP(tmp_name)); + SEND_TO_Q(name_rules, d); + sprintf(buf, "Вы действительно выбрали имя %s [ Y(Д) / N(Н) ]? ", tmp_name); + SEND_TO_Q(buf, d); + STATE(d) = CON_NAME_CNFRM; + } + } + break; + } + case CON_NAME_CNFRM:{ // wait for conf. of new name + if (UPPER(*arg) == 'Y' || UPPER(*arg) == 'Д') { + if (ban->is_banned(d->host) >= BanList::BAN_NEW) { + sprintf(buf, "Попытка создания персонажа %s отклонена для [%s] (siteban)", + GET_PC_NAME(d->character), d->host); + mudlog(buf, NRM, LVL_GOD, SYSLOG, TRUE); + SEND_TO_Q("Извините, создание нового персонажа для вашего IP !!! ЗАПРЕЩЕНО !!!\r\n", d); + STATE(d) = CON_CLOSE; + return; + } + + if (circle_restrict) { + SEND_TO_Q("Извините, вы не можете создать новый персонаж в настоящий момент.\r\n", d); + sprintf(buf, "Попытка создания нового персонажа %s отклонена для [%s] (wizlock)", + GET_PC_NAME(d->character), d->host); + mudlog(buf, NRM, LVL_GOD, SYSLOG, TRUE); + STATE(d) = CON_CLOSE; + return; + } + + switch (NewNames::auto_authorize(d)) { + case NewNames::AUTO_ALLOW: + sprintf(buf, + "Введите пароль для %s (не вводите пароли типа '123' или 'qwe', иначе ваших персонажев могут украсть) : ", + GET_PAD(d->character, 1)); + SEND_TO_Q(buf, d); + STATE(d) = CON_NEWPASSWD; + return; + + case NewNames::AUTO_BAN: + STATE(d) = CON_CLOSE; + return; + + default: + break; + } + + SEND_TO_Q("Ваш пол [ М(M)/Ж(F) ]? ", d); + STATE(d) = CON_QSEX; + return; + + } else if (UPPER(*arg) == 'N' || UPPER(*arg) == 'Н') { + SEND_TO_Q("Итак, чего изволите? Учтите, бананов нет :)\r\n" "Имя : ", d); + d->character->set_pc_name(0); + STATE(d) = CON_GET_NAME; + } else { + SEND_TO_Q("Ответьте Yes(Да) or No(Нет) : ", d); + } + break; + } - case CON_GET_NAME: // wait for input of name - if (!d->character) - { - CreateChar(d); - } - - if (!*arg) - { - STATE(d) = CON_CLOSE; - } - else if (!str_cmp("новый", arg)) - { - SEND_TO_Q(name_rules, d); - - std::stringstream ss; - ss << "Введите имя"; - if (0 < player_table.free_names_count()) - { - ss << " (примеры доступных имен : "; - print_free_names(ss, player_table); - ss << ")"; - } - ss << ": "; - - SEND_TO_Q(ss.str().c_str(), d); - STATE(d) = CON_NEW_CHAR; - return; - } - else - { - if (sscanf(arg, "%s %s", pwd_name, pwd_pwd) == 2) - { - if (parse_exist_name(pwd_name, tmp_name) - || (player_i = load_char(tmp_name, d->character.get())) < 0) - { - SEND_TO_Q("Некорректное имя. Повторите, пожалуйста.\r\n" "Имя : ", d); - return; - } - - if (PLR_FLAGGED(d->character, PLR_DELETED) - || !Password::compare_password(d->character.get(), pwd_pwd)) - { - SEND_TO_Q("Некорректное имя. Повторите, пожалуйста.\r\n" "Имя : ", d); - if (!PLR_FLAGGED(d->character, PLR_DELETED)) - { - sprintf(buf, "Bad PW: %s [%s]", GET_NAME(d->character), d->host); - mudlog(buf, BRF, LVL_IMMORT, SYSLOG, TRUE); - } - - d->character.reset(); - return; - } - - PLR_FLAGS(d->character).unset(PLR_MAILING); - PLR_FLAGS(d->character).unset(PLR_WRITING); - PLR_FLAGS(d->character).unset(PLR_CRYO); - d->character->set_pfilepos(player_i); - GET_ID(d->character) = GET_IDNUM(d->character); - DoAfterPassword(d); - - return; - } - else - { - if (parse_exist_name(arg, tmp_name) || - strlen(tmp_name) < (MIN_NAME_LENGTH - 1) || // дабы можно было войти чарам с 4 буквами - strlen(tmp_name) > MAX_NAME_LENGTH || - !Is_Valid_Name(tmp_name) || fill_word(tmp_name) || reserved_word(tmp_name)) - { - SEND_TO_Q("Некорректное имя. Повторите, пожалуйста.\r\n" "Имя : ", d); - return; - } - else if (!Is_Valid_Dc(tmp_name)) - { - player_i = load_char(tmp_name, d->character.get()); - d->character->set_pfilepos(player_i); - if (IS_IMMORTAL(d->character) || PRF_FLAGGED(d->character, PRF_CODERINFO)) - { - SEND_TO_Q("Игрок с подобным именем является БЕССМЕРТНЫМ в игре.\r\n", d); - } - else - { - SEND_TO_Q("Игрок с подобным именем находится в игре.\r\n", d); - } - SEND_TO_Q("Во избежание недоразумений введите пару ИМЯ ПАРОЛЬ.\r\n", d); - SEND_TO_Q("Имя и пароль через пробел : ", d); - - d->character.reset(); - return; - } - } - - player_i = load_char(tmp_name, d->character.get()); - if (player_i > -1) - { - d->character->set_pfilepos(player_i); - if (PLR_FLAGGED(d->character, PLR_DELETED)) // We get a false positive from the original deleted character. - { - d->character.reset(); - - // Check for multiple creations... - if (!Valid_Name(tmp_name) || _parse_name(tmp_name, tmp_name)) - { - SEND_TO_Q("Некорректное имя. Повторите, пожалуйста.\r\n" "Имя : ", d); - return; - } - - // дополнительная проверка на длину имени чара - if (strlen(tmp_name) < (MIN_NAME_LENGTH)) - { - SEND_TO_Q("Некорректное имя. Повторите, пожалуйста.\r\n" "Имя : ", d); - return; - } - - CreateChar(d); - d->character->set_pc_name(CAP(tmp_name)); - d->character->player_data.PNames[0] = std::string(CAP(tmp_name)); - d->character->set_pfilepos(player_i); - sprintf(buf, "Вы действительно выбрали имя %s [ Y(Д) / N(Н) ]? ", tmp_name); - SEND_TO_Q(buf, d); - STATE(d) = CON_NAME_CNFRM; - } - else // undo it just in case they are set - { - if (IS_IMMORTAL(d->character) || PRF_FLAGGED(d->character, PRF_CODERINFO)) - { - SEND_TO_Q("Игрок с подобным именем является БЕССМЕРТНЫМ в игре.\r\n", d); - SEND_TO_Q("Во избежание недоразумений введите пару ИМЯ ПАРОЛЬ.\r\n", d); - SEND_TO_Q("Имя и пароль через пробел : ", d); - d->character.reset(); - - return; - } - - PLR_FLAGS(d->character).unset(PLR_MAILING); - PLR_FLAGS(d->character).unset(PLR_WRITING); - PLR_FLAGS(d->character).unset(PLR_CRYO); - SEND_TO_Q("Персонаж с таким именем уже существует. Введите пароль : ", d); - d->idle_tics = 0; - STATE(d) = CON_PASSWORD; - } - } - else // player unknown -- make new character - { - // еще одна проверка - if (strlen(tmp_name) < (MIN_NAME_LENGTH)) - { - SEND_TO_Q("Некорректное имя. Повторите, пожалуйста.\r\n" "Имя : ", d); - return; - } - - // Check for multiple creations of a character. - if (!Valid_Name(tmp_name) || _parse_name(tmp_name, tmp_name)) - { - SEND_TO_Q("Некорректное имя. Повторите, пожалуйста.\r\n" "Имя : ", d); - return; - } - - if (cmp_ptable_by_name(tmp_name, MIN_NAME_LENGTH) >= 0) - { - SEND_TO_Q - ("Первые символы вашего имени совпадают с уже существующим персонажем.\r\n" - "Для исключения разных недоразумений вам необходимо выбрать другое имя.\r\n" - "Имя : ", d); - return; - } - - d->character->set_pc_name(CAP(tmp_name)); - d->character->player_data.PNames[0] = std::string(CAP(tmp_name)); - SEND_TO_Q(name_rules, d); - sprintf(buf, "Вы действительно выбрали имя %s [ Y(Д) / N(Н) ]? ", tmp_name); - SEND_TO_Q(buf, d); - STATE(d) = CON_NAME_CNFRM; - } - } - break; - - case CON_NAME_CNFRM: // wait for conf. of new name - if (UPPER(*arg) == 'Y' || UPPER(*arg) == 'Д') - { - if (ban->is_banned(d->host) >= BanList::BAN_NEW) - { - sprintf(buf, "Попытка создания персонажа %s отклонена для [%s] (siteban)", - GET_PC_NAME(d->character), d->host); - mudlog(buf, NRM, LVL_GOD, SYSLOG, TRUE); - SEND_TO_Q("Извините, создание нового персонажа для вашего IP !!! ЗАПРЕЩЕНО !!!\r\n", d); - STATE(d) = CON_CLOSE; - return; - } - - if (circle_restrict) - { - SEND_TO_Q("Извините, вы не можете создать новый персонаж в настоящий момент.\r\n", d); - sprintf(buf, "Попытка создания нового персонажа %s отклонена для [%s] (wizlock)", - GET_PC_NAME(d->character), d->host); - mudlog(buf, NRM, LVL_GOD, SYSLOG, TRUE); - STATE(d) = CON_CLOSE; - return; - } - - switch (NewNames::auto_authorize(d)) - { - case NewNames::AUTO_ALLOW: - sprintf(buf, "Введите пароль для %s (не вводите пароли типа '123' или 'qwe', иначе ваших персонажев могут украсть) : ", - GET_PAD(d->character, 1)); - SEND_TO_Q(buf, d); - STATE(d) = CON_NEWPASSWD; - return; - - case NewNames::AUTO_BAN: - STATE(d) = CON_CLOSE; - return; - - default: - break; - } - - SEND_TO_Q("Ваш пол [ М(M)/Ж(F) ]? ", d); - STATE(d) = CON_QSEX; - return; - - } - else if (UPPER(*arg) == 'N' || UPPER(*arg) == 'Н') - { - SEND_TO_Q("Итак, чего изволите? Учтите, бананов нет :)\r\n" "Имя : ", d); - d->character->set_pc_name(0); - STATE(d) = CON_GET_NAME; - } - else - { - SEND_TO_Q("Ответьте Yes(Да) or No(Нет) : ", d); - } - break; - - case CON_NEW_CHAR: - if (!*arg) - { - STATE(d) = CON_CLOSE; - return; - } - - if (!d->character) - { - CreateChar(d); - } - - if (_parse_name(arg, tmp_name) || - strlen(tmp_name) < MIN_NAME_LENGTH || - strlen(tmp_name) > MAX_NAME_LENGTH || - !Is_Valid_Name(tmp_name) || fill_word(tmp_name) || reserved_word(tmp_name)) - { - SEND_TO_Q("Некорректное имя. Повторите, пожалуйста.\r\n" "Имя : ", d); - return; - } - - player_i = load_char(tmp_name, d->character.get()); - if (player_i > -1) - { - if (PLR_FLAGGED(d->character, PLR_DELETED)) - { - d->character.reset(); - CreateChar(d); - } - else - { - SEND_TO_Q("Такой персонаж уже существует. Выберите другое имя : ", d); - d->character.reset(); - - return; - } - } - - if (!Valid_Name(tmp_name)) - { - SEND_TO_Q("Некорректное имя. Повторите, пожалуйста.\r\n" "Имя : ", d); - return; - } - - if (cmp_ptable_by_name(tmp_name, MIN_NAME_LENGTH + 1) >= 0) - { - SEND_TO_Q("Первые символы вашего имени совпадают с уже существующим персонажем.\r\n" - "Для исключения разных недоразумений вам необходимо выбрать другое имя.\r\n" - "Имя : ", d); - return; - } - - d->character->set_pc_name(CAP(tmp_name)); - d->character->player_data.PNames[0] = std::string(CAP(tmp_name)); - if (ban->is_banned(d->host) >= BanList::BAN_NEW) - { - sprintf(buf, "Попытка создания персонажа %s отклонена для [%s] (siteban)", - GET_PC_NAME(d->character), d->host); - mudlog(buf, NRM, LVL_GOD, SYSLOG, TRUE); - SEND_TO_Q("Извините, создание нового персонажа для вашего IP !!!ЗАПРЕЩЕНО!!!\r\n", d); - STATE(d) = CON_CLOSE; - return; - } - - if (circle_restrict) - { - SEND_TO_Q("Извините, вы не можете создать новый персонаж в настоящий момент.\r\n", d); - sprintf(buf, - "Попытка создания нового персонажа %s отклонена для [%s] (wizlock)", - GET_PC_NAME(d->character), d->host); - mudlog(buf, NRM, LVL_GOD, SYSLOG, TRUE); - STATE(d) = CON_CLOSE; - return; - } - - switch (NewNames::auto_authorize(d)) - { - case NewNames::AUTO_ALLOW: - sprintf(buf, - "Введите пароль для %s (не вводите пароли типа '123' или 'qwe', иначе ваших персонажев могут украсть) : ", - GET_PAD(d->character, 1)); - SEND_TO_Q(buf, d); - STATE(d) = CON_NEWPASSWD; - return; - - case NewNames::AUTO_BAN: - d->character.reset(); - SEND_TO_Q("Выберите другое имя : ", d); - return; - - default: - break; - } - - SEND_TO_Q("Ваш пол [ М(M)/Ж(F) ]? ", d); - STATE(d) = CON_QSEX; - return; - - case CON_PASSWORD: // get pwd for known player - /* - * To really prevent duping correctly, the player's record should - * be reloaded from disk at this point (after the password has been - * typed). However I'm afraid that trying to load a character over - * an already loaded character is going to cause some problem down the - * road that I can't see at the moment. So to compensate, I'm going to - * (1) add a 15 or 20-second time limit for entering a password, and (2) - * re-add the code to cut off duplicates when a player quits. JE 6 Feb 96 - */ - - SEND_TO_Q("\r\n", d); - - if (!*arg) - { - STATE(d) = CON_CLOSE; - } - else - { - if (!Password::compare_password(d->character.get(), arg)) - { - sprintf(buf, "Bad PW: %s [%s]", GET_NAME(d->character), d->host); - mudlog(buf, BRF, LVL_IMMORT, SYSLOG, TRUE); - GET_BAD_PWS(d->character)++; - d->character->save_char(); - if (++(d->bad_pws) >= max_bad_pws) // 3 strikes and you're out. - { - SEND_TO_Q("Неверный пароль... Отсоединяемся.\r\n", d); - STATE(d) = CON_CLOSE; - } - else - { - SEND_TO_Q("Неверный пароль.\r\nПароль : ", d); - } - return; - } - DoAfterPassword(d); - } - break; - - case CON_NEWPASSWD: - case CON_CHPWD_GETNEW: - if (!Password::check_password(d->character.get(), arg)) - { - sprintf(buf, "\r\n%s\r\n", Password::BAD_PASSWORD); - SEND_TO_Q(buf, d); - SEND_TO_Q("Пароль : ", d); - return; - } - - Password::set_password(d->character.get(), arg); - - SEND_TO_Q("\r\nПовторите пароль, пожалуйста : ", d); - if (STATE(d) == CON_NEWPASSWD) - { - STATE(d) = CON_CNFPASSWD; - } - else - { - STATE(d) = CON_CHPWD_VRFY; - } - - break; - - case CON_CNFPASSWD: - case CON_CHPWD_VRFY: - if (!Password::compare_password(d->character.get(), arg)) - { - SEND_TO_Q("\r\nПароли не соответствуют... повторим.\r\n", d); - SEND_TO_Q("Пароль: ", d); - if (STATE(d) == CON_CNFPASSWD) - { - STATE(d) = CON_NEWPASSWD; - } - else - { - STATE(d) = CON_CHPWD_GETNEW; - } - return; - } - - if (STATE(d) == CON_CNFPASSWD) - { - GET_KIN(d->character) = 0; // added by WorM: Выставляем расу в Русич(коммент выше) - SEND_TO_Q(class_menu, d); - SEND_TO_Q("\r\nВаша профессия (Для более полной информации вы можете набрать" - " \r\nсправка <интересующая профессия>): ", d); - STATE(d) = CON_QCLASS; - } - else - { - sprintf(buf, "%s заменил себе пароль.", GET_NAME(d->character)); - add_karma(d->character.get(), buf, ""); - d->character->save_char(); - SEND_TO_Q("\r\nГотово.\r\n", d); - SEND_TO_Q(MENU, d); - STATE(d) = CON_MENU; - } - - break; - - case CON_QSEX: // query sex of new user - if (pre_help(d->character.get(), arg)) - { - SEND_TO_Q("\r\nВаш пол [ М(M)/Ж(F) ]? ", d); - STATE(d) = CON_QSEX; - return; - } - - switch (UPPER(*arg)) - { - case 'М': - case 'M': - d->character->set_sex(ESex::SEX_MALE); - break; - - case 'Ж': - case 'F': - d->character->set_sex(ESex::SEX_FEMALE); - break; - - default: - SEND_TO_Q("Это может быть и пол, но явно не ваш :)\r\n" "А какой у ВАС пол? ", d); - return; - } - SEND_TO_Q("Проверьте правильность склонения имени. В случае ошибки введите свой вариант.\r\n", d); - GetCase(GET_PC_NAME(d->character), GET_SEX(d->character), 1, tmp_name); - sprintf(buf, "Имя в родительном падеже (меч КОГО?) [%s]: ", tmp_name); - SEND_TO_Q(buf, d); - STATE(d) = CON_NAME2; - return; - - case CON_QKIN: // query rass - if (pre_help(d->character.get(), arg)) - { + case CON_NEW_CHAR: { + if (!*arg) { + STATE(d) = CON_CLOSE; + return; + } + + if (!d->character) { + CreateChar(d); + } + + if (_parse_name(arg, tmp_name) || + strlen(tmp_name) < MIN_NAME_LENGTH || + strlen(tmp_name) > MAX_NAME_LENGTH || + !Is_Valid_Name(tmp_name) || fill_word(tmp_name) || reserved_word(tmp_name)) { + SEND_TO_Q("Некорректное имя. Повторите, пожалуйста.\r\n" "Имя : ", d); + return; + } + + player_i = load_char(tmp_name, d->character.get()); + if (player_i > -1) { + if (PLR_FLAGGED(d->character, PLR_DELETED)) { + d->character.reset(); + CreateChar(d); + } else { + SEND_TO_Q("Такой персонаж уже существует. Выберите другое имя : ", d); + d->character.reset(); + + return; + } + } + + if (!Valid_Name(tmp_name)) { + SEND_TO_Q("Некорректное имя. Повторите, пожалуйста.\r\n" "Имя : ", d); + return; + } + + if (cmp_ptable_by_name(tmp_name, MIN_NAME_LENGTH + 1) >= 0) { + SEND_TO_Q("Первые символы вашего имени совпадают с уже существующим персонажем.\r\n" + "Для исключения разных недоразумений вам необходимо выбрать другое имя.\r\n" + "Имя : ", d); + return; + } + + d->character->set_pc_name(CAP(tmp_name)); + d->character->player_data.PNames[0] = std::string(CAP(tmp_name)); + if (ban->is_banned(d->host) >= BanList::BAN_NEW) { + sprintf(buf, "Попытка создания персонажа %s отклонена для [%s] (siteban)", + GET_PC_NAME(d->character), d->host); + mudlog(buf, NRM, LVL_GOD, SYSLOG, TRUE); + SEND_TO_Q("Извините, создание нового персонажа для вашего IP !!!ЗАПРЕЩЕНО!!!\r\n", d); + STATE(d) = CON_CLOSE; + return; + } + + if (circle_restrict) { + SEND_TO_Q("Извините, вы не можете создать новый персонаж в настоящий момент.\r\n", d); + sprintf(buf, + "Попытка создания нового персонажа %s отклонена для [%s] (wizlock)", + GET_PC_NAME(d->character), d->host); + mudlog(buf, NRM, LVL_GOD, SYSLOG, TRUE); + STATE(d) = CON_CLOSE; + return; + } + + switch (NewNames::auto_authorize(d)) { + case NewNames::AUTO_ALLOW: + sprintf(buf, + "Введите пароль для %s (не вводите пароли типа '123' или 'qwe', иначе ваших персонажев могут украсть) : ", + GET_PAD(d->character, 1)); + SEND_TO_Q(buf, d); + STATE(d) = CON_NEWPASSWD; + return; + + case NewNames::AUTO_BAN: + d->character.reset(); + SEND_TO_Q("Выберите другое имя : ", d); + return; + + default: + break; + } + + SEND_TO_Q("Ваш пол [ М(M)/Ж(F) ]? ", d); + STATE(d) = CON_QSEX; + return; + } + // get pwd for known player + case CON_PASSWORD: { + /* + * To really prevent duping correctly, the player's record should + * be reloaded from disk at this point (after the password has been + * typed). However I'm afraid that trying to load a character over + * an already loaded character is going to cause some problem down the + * road that I can't see at the moment. So to compensate, I'm going to + * (1) add a 15 or 20-second time limit for entering a password, and (2) + * re-add the code to cut off duplicates when a player quits. JE 6 Feb 96 + */ + + SEND_TO_Q("\r\n", d); + + if (!*arg) { + STATE(d) = CON_CLOSE; + } else { + if (!Password::compare_password(d->character.get(), arg)) { + sprintf(buf, "Bad PW: %s [%s]", GET_NAME(d->character), d->host); + mudlog(buf, BRF, LVL_IMMORT, SYSLOG, TRUE); + GET_BAD_PWS(d->character)++; + d->character->save_char(); + if (++(d->bad_pws) >= max_bad_pws) // 3 strikes and you're out. + { + SEND_TO_Q("Неверный пароль... Отсоединяемся.\r\n", d); + STATE(d) = CON_CLOSE; + } else { + SEND_TO_Q("Неверный пароль.\r\nПароль : ", d); + } + return; + } + DoAfterPassword(d); + } + break; + } + case CON_NEWPASSWD: + case CON_CHPWD_GETNEW: { + if (!Password::check_password(d->character.get(), arg)) { + sprintf(buf, "\r\n%s\r\n", Password::BAD_PASSWORD); + SEND_TO_Q(buf, d); + SEND_TO_Q("Пароль : ", d); + return; + } + + Password::set_password(d->character.get(), arg); + + SEND_TO_Q("\r\nПовторите пароль, пожалуйста : ", d); + if (STATE(d) == CON_NEWPASSWD) { + STATE(d) = CON_CNFPASSWD; + } else { + STATE(d) = CON_CHPWD_VRFY; + } + + break; + } + case CON_CNFPASSWD: + case CON_CHPWD_VRFY: { + if (!Password::compare_password(d->character.get(), arg)) { + SEND_TO_Q("\r\nПароли не соответствуют... повторим.\r\n", d); + SEND_TO_Q("Пароль: ", d); + if (STATE(d) == CON_CNFPASSWD) { + STATE(d) = CON_NEWPASSWD; + } else { + STATE(d) = CON_CHPWD_GETNEW; + } + return; + } + + if (STATE(d) == CON_CNFPASSWD) { + GET_KIN(d->character) = 0; // added by WorM: Выставляем расу в Русич(коммент выше) + SEND_TO_Q(class_menu, d); + SEND_TO_Q("\r\nВаша профессия (Для более полной информации вы можете набрать" + " \r\nсправка <интересующая профессия>): ", d); + STATE(d) = CON_QCLASS; + } else { + sprintf(buf, "%s заменил себе пароль.", GET_NAME(d->character)); + add_karma(d->character.get(), buf, ""); + d->character->save_char(); + SEND_TO_Q("\r\nГотово.\r\n", d); + SEND_TO_Q(MENU, d); + STATE(d) = CON_MENU; + } + + break; + } + case CON_QSEX: { // query sex of new user + if (pre_help(d->character.get(), arg)) { + SEND_TO_Q("\r\nВаш пол [ М(M)/Ж(F) ]? ", d); + STATE(d) = CON_QSEX; + return; + } + + switch (UPPER(*arg)) { + case 'М': + case 'M': + d->character->set_sex(ESex::SEX_MALE); + break; + + case 'Ж': + case 'F': + d->character->set_sex(ESex::SEX_FEMALE); + break; + + default: + SEND_TO_Q("Это может быть и пол, но явно не ваш :)\r\n" "А какой у ВАС пол? ", d); + return; + } + SEND_TO_Q("Проверьте правильность склонения имени. В случае ошибки введите свой вариант.\r\n", d); + GetCase(GET_PC_NAME(d->character), GET_SEX(d->character), 1, tmp_name); + sprintf(buf, "Имя в родительном падеже (меч КОГО?) [%s]: ", tmp_name); + SEND_TO_Q(buf, d); + STATE(d) = CON_NAME2; + return; + } + case CON_QKIN: { // query rass + if (pre_help(d->character.get(), arg)) { SEND_TO_Q("\r\nКакой народ вам ближе по духу:\r\n", d); SEND_TO_Q(string(PlayerRace::ShowKinsMenu()).c_str(), d); - SEND_TO_Q("\r\nПлемя: ", d); - STATE(d) = CON_QKIN; - return; - } + SEND_TO_Q("\r\nПлемя: ", d); + STATE(d) = CON_QKIN; + return; + } load_result = PlayerRace::CheckKin(arg); - if (load_result == KIN_UNDEFINED) - { - SEND_TO_Q("Стыдно не помнить предков.\r\n" - "Какое Племя вам ближе по духу? ", d); - return; - } - - GET_KIN(d->character) = load_result; - /* - Ахтунг-партизанен! - Пока что убраны все вызовы парсилок _классов_ для отличных от русичей _рас_. - Сами парсилки и списки классов оставлены для потомков. - Проверка тоже убрана, так что при создании перса другой расы ему предложат выбрать "русские" классы. - Теоретически это конечно неправильно, но я сомневаюсь, что в ближайшем будущем кто-то станет доделывать расы. - Если же такой садомазо найдется, то для него это всеи пишется. - В таком варианте надо в описания _рас_ в файле playerraces.xml - Ввести список доступных расе классов. И уже от этого списка плясать с названиями и парсом, а не городить все в 3 экземплярах - Сами классы при этом из кода можно и не выносить ж) - Sventovit - */ + if (load_result == KIN_UNDEFINED) { + SEND_TO_Q("Стыдно не помнить предков.\r\n" + "Какое Племя вам ближе по духу? ", d); + return; + } + + GET_KIN(d->character) = load_result; + /* + Ахтунг-партизанен! + Пока что убраны все вызовы парсилок _классов_ для отличных от русичей _рас_. + Сами парсилки и списки классов оставлены для потомков. + Проверка тоже убрана, так что при создании перса другой расы ему предложат выбрать "русские" классы. + Теоретически это конечно неправильно, но я сомневаюсь, что в ближайшем будущем кто-то станет доделывать расы. + Если же такой садомазо найдется, то для него это всеи пишется. + В таком варианте надо в описания _рас_ в файле playerraces.xml + Ввести список доступных расе классов. И уже от этого списка плясать с названиями и парсом, а не городить все в 3 экземплярах + Сами классы при этом из кода можно и не выносить ж) + Sventovit + */ SEND_TO_Q(class_menu, d); - SEND_TO_Q("\r\nВаша профессия (Для более полной информации вы можете набрать" - " \r\nсправка <интересующая профессия>): ", d); - STATE(d) = CON_QCLASS; - break; - - case CON_RELIGION: // query religion of new user + SEND_TO_Q("\r\nВаша профессия (Для более полной информации вы можете набрать" + " \r\nсправка <интересующая профессия>): ", d); + STATE(d) = CON_QCLASS; + break; + } + case CON_RELIGION: { // query religion of new user if (pre_help(d->character.get(), arg)) { SEND_TO_Q(religion_menu, d); @@ -3650,8 +3603,8 @@ void nanny(DESCRIPTOR_DATA * d, char *arg) STATE(d) = CON_RACE; break; - - case CON_QCLASS: + } + case CON_QCLASS: { if (pre_help(d->character.get(), arg)) { SEND_TO_Q(class_menu, d); @@ -3675,61 +3628,53 @@ void nanny(DESCRIPTOR_DATA * d, char *arg) SEND_TO_Q("\n\rРелигия :", d); STATE(d) = CON_RELIGION; break; + } + case CON_QCLASSS: { + if (pre_help(d->character.get(), arg)) { + SEND_TO_Q(class_menu_step, d); + SEND_TO_Q("\r\nВаша профессия : ", d); + STATE(d) = CON_QCLASSS; + return; + } + + load_result = parse_class_step(*arg); + + if (load_result == CLASS_UNDEFINED) { + SEND_TO_Q("\r\nЭто не профессия.\r\nПрофессия : ", d); + return; + } else { + d->character->set_class(load_result); + } - case CON_QCLASSS: - if (pre_help(d->character.get(), arg)) - { - SEND_TO_Q(class_menu_step, d); - SEND_TO_Q("\r\nВаша профессия : ", d); - STATE(d) = CON_QCLASSS; - return; - } - - load_result = parse_class_step(*arg); - - if (load_result == CLASS_UNDEFINED) - { - SEND_TO_Q("\r\nЭто не профессия.\r\nПрофессия : ", d); - return; - } - else - { - d->character->set_class(load_result); - } - - SEND_TO_Q(religion_menu, d); - SEND_TO_Q("\n\rРелигия :", d); - STATE(d) = CON_RELIGION; - - break; - - case CON_QCLASSV: - if (pre_help(d->character.get(), arg)) - { - SEND_TO_Q(class_menu_vik, d); - SEND_TO_Q("\r\nВаша профессия : ", d); - STATE(d) = CON_QCLASSV; - return; - } - - load_result = parse_class_vik(*arg); - - if (load_result == CLASS_UNDEFINED) - { - SEND_TO_Q("\r\nЭто не профессия.\r\nПрофессия : ", d); - return; - } - else - { - d->character->set_class(load_result); - } - - SEND_TO_Q(religion_menu, d); - SEND_TO_Q("\n\rРелигия:", d); - STATE(d) = CON_RELIGION; - - break; + SEND_TO_Q(religion_menu, d); + SEND_TO_Q("\n\rРелигия :", d); + STATE(d) = CON_RELIGION; + break; + } + case CON_QCLASSV: { + if (pre_help(d->character.get(), arg)) { + SEND_TO_Q(class_menu_vik, d); + SEND_TO_Q("\r\nВаша профессия : ", d); + STATE(d) = CON_QCLASSV; + return; + } + + load_result = parse_class_vik(*arg); + + if (load_result == CLASS_UNDEFINED) { + SEND_TO_Q("\r\nЭто не профессия.\r\nПрофессия : ", d); + return; + } else { + d->character->set_class(load_result); + } + + SEND_TO_Q(religion_menu, d); + SEND_TO_Q("\n\rРелигия:", d); + STATE(d) = CON_RELIGION; + + break; + } case CON_RACE: // query race if (pre_help(d->character.get(), arg)) { @@ -3882,217 +3827,195 @@ void nanny(DESCRIPTOR_DATA * d, char *arg) break; } - case CON_MENU: // get selection from main menu - switch (*arg) - { - case '0': - SEND_TO_Q("\r\nДо встречи на земле Киевской.\r\n", d); - - if (GET_REMORT(d->character) == 0 - && GET_LEVEL(d->character) <= 25 - && !PLR_FLAGS(d->character).get(PLR_NODELETE)) - { - int timeout = -1; - for (int ci = 0; GET_LEVEL(d->character) > pclean_criteria[ci].level; ci++) - { - //if (GET_LEVEL(d->character) == pclean_criteria[ci].level) - timeout = pclean_criteria[ci + 1].days; - } - if (timeout > 0) - { - time_t deltime = time(NULL) + timeout * 60 * 60 * 24; - sprintf(buf, "В случае вашего отсутствия персонаж будет храниться до %s нашей эры :).\r\n", - rustime(localtime(&deltime))); - SEND_TO_Q(buf, d); - } - }; - - STATE(d) = CON_CLOSE; - - break; - - case '1': - if (!check_dupes_email(d)) - { - STATE(d) = CON_CLOSE; - break; - } - - do_entergame(d); - - break; - - case '2': - if (d->character->player_data.description != "") - { - SEND_TO_Q("Ваше ТЕКУЩЕЕ описание:\r\n", d); - SEND_TO_Q(d->character->player_data.description.c_str(), d); - /* - * Don't free this now... so that the old description gets loaded - * as the current buffer in the editor. Do setup the ABORT buffer - * here, however. - * - * free(d->character->player_data.description); - * d->character->player_data.description = NULL; - */ - d->backstr = str_dup(d->character->player_data.description.c_str()); - } - - SEND_TO_Q("Введите описание вашего героя, которое будет выводиться по команде <осмотреть>.\r\n", d); - SEND_TO_Q("(/s сохранить /h помощь)\r\n", d); - - d->writer.reset(new DelegatedStdStringWriter(d->character->player_data.description)); - d->max_str = EXDSCR_LENGTH; - STATE(d) = CON_EXDESC; - - break; - - case '3': - page_string(d, background, 0); - STATE(d) = CON_RMOTD; - break; - - case '4': - SEND_TO_Q("\r\nВведите СТАРЫЙ пароль : ", d); - STATE(d) = CON_CHPWD_GETOLD; - break; - - case '5': - if (IS_IMMORTAL(d->character)) - { - SEND_TO_Q("\r\nБоги бессмертны (с) Стрибог, просите чтоб пофризили :)))\r\n", d); - SEND_TO_Q(MENU, d); - break; - } - - if (PLR_FLAGGED(d->character, PLR_HELLED) - || PLR_FLAGGED(d->character, PLR_FROZEN)) - { - SEND_TO_Q("\r\nВы находитесь в АДУ!!! Амнистии подобным образом не будет.\r\n", d); - SEND_TO_Q(MENU, d); - break; - } - - if (GET_REMORT(d->character) > 5) - { - SEND_TO_Q("\r\nНельзя удалить себя достигнув шестого перевоплощения.\r\n", d); - SEND_TO_Q(MENU, d); - break; - } - - SEND_TO_Q("\r\nДля подтверждения введите свой пароль : ", d); - STATE(d) = CON_DELCNF1; - - break; - - case '6': - if (IS_IMMORTAL(d->character)) - { - SEND_TO_Q("\r\nВам это ни к чему...\r\n", d); - SEND_TO_Q(MENU, d); - STATE(d) = CON_MENU; - } - else - { - ResetStats::print_menu(d); - STATE(d) = CON_MENU_STATS; - } - break; - - case '7': - if (!PRF_FLAGGED(d->character, PRF_BLIND)) - { - PRF_FLAGS(d->character).set(PRF_BLIND); - SEND_TO_Q("\r\nСпециальный режим слепого игрока ВКЛЮЧЕН.\r\n", d); - SEND_TO_Q(MENU, d); - STATE(d) = CON_MENU; - } - else - { - PRF_FLAGS(d->character).unset(PRF_BLIND); - SEND_TO_Q("\r\nСпециальный режим слепого игрока ВЫКЛЮЧЕН.\r\n", d); - SEND_TO_Q(MENU, d); - STATE(d) = CON_MENU; - } - - break; - case '8': - d->character->get_account()->show_list_players(d); - break; - - default: - SEND_TO_Q("\r\nЭто не есть правильный ответ!\r\n", d); - SEND_TO_Q(MENU, d); - - break; - } - - break; - - case CON_CHPWD_GETOLD: - if (!Password::compare_password(d->character.get(), arg)) - { - SEND_TO_Q("\r\nНеверный пароль.\r\n", d); - SEND_TO_Q(MENU, d); - STATE(d) = CON_MENU; - } - else - { - SEND_TO_Q("\r\nВведите НОВЫЙ пароль : ", d); - STATE(d) = CON_CHPWD_GETNEW; - } - - return; - - case CON_DELCNF1: - if (!Password::compare_password(d->character.get(), arg)) - { - SEND_TO_Q("\r\nНеверный пароль.\r\n", d); - SEND_TO_Q(MENU, d); - STATE(d) = CON_MENU; - } - else - { - SEND_TO_Q("\r\n!!! ВАШ ПЕРСОНАЖ БУДЕТ УДАЛЕН !!!\r\n" - "Вы АБСОЛЮТНО В ЭТОМ УВЕРЕНЫ?\r\n\r\n" - "Наберите \"YES / ДА\" для подтверждения: ", d); - STATE(d) = CON_DELCNF2; - } - - break; + case CON_MENU: { // get selection from main menu + switch (*arg) { + case '0': + SEND_TO_Q("\r\nДо встречи на земле Киевской.\r\n", d); + + if (GET_REMORT(d->character) == 0 + && GET_LEVEL(d->character) <= 25 + && !PLR_FLAGS(d->character).get(PLR_NODELETE)) { + int timeout = -1; + for (int ci = 0; GET_LEVEL(d->character) > pclean_criteria[ci].level; ci++) { + //if (GET_LEVEL(d->character) == pclean_criteria[ci].level) + timeout = pclean_criteria[ci + 1].days; + } + if (timeout > 0) { + time_t deltime = time(NULL) + timeout * 60 * 60 * 24; + sprintf(buf, "В случае вашего отсутствия персонаж будет храниться до %s нашей эры :).\r\n", + rustime(localtime(&deltime))); + SEND_TO_Q(buf, d); + } + }; + + STATE(d) = CON_CLOSE; + + break; + + case '1': + if (!check_dupes_email(d)) { + STATE(d) = CON_CLOSE; + break; + } + + do_entergame(d); + + break; + + case '2': + if (d->character->player_data.description != "") { + SEND_TO_Q("Ваше ТЕКУЩЕЕ описание:\r\n", d); + SEND_TO_Q(d->character->player_data.description.c_str(), d); + /* + * Don't free this now... so that the old description gets loaded + * as the current buffer in the editor. Do setup the ABORT buffer + * here, however. + * + * free(d->character->player_data.description); + * d->character->player_data.description = NULL; + */ + d->backstr = str_dup(d->character->player_data.description.c_str()); + } + + SEND_TO_Q("Введите описание вашего героя, которое будет выводиться по команде <осмотреть>.\r\n", d); + SEND_TO_Q("(/s сохранить /h помощь)\r\n", d); + + d->writer.reset(new DelegatedStdStringWriter(d->character->player_data.description)); + d->max_str = EXDSCR_LENGTH; + STATE(d) = CON_EXDESC; + + break; + + case '3': + page_string(d, background, 0); + STATE(d) = CON_RMOTD; + break; + + case '4': + SEND_TO_Q("\r\nВведите СТАРЫЙ пароль : ", d); + STATE(d) = CON_CHPWD_GETOLD; + break; + + case '5': + if (IS_IMMORTAL(d->character)) { + SEND_TO_Q("\r\nБоги бессмертны (с) Стрибог, просите чтоб пофризили :)))\r\n", d); + SEND_TO_Q(MENU, d); + break; + } + + if (PLR_FLAGGED(d->character, PLR_HELLED) + || PLR_FLAGGED(d->character, PLR_FROZEN)) { + SEND_TO_Q("\r\nВы находитесь в АДУ!!! Амнистии подобным образом не будет.\r\n", d); + SEND_TO_Q(MENU, d); + break; + } + + if (GET_REMORT(d->character) > 5) { + SEND_TO_Q("\r\nНельзя удалить себя достигнув шестого перевоплощения.\r\n", d); + SEND_TO_Q(MENU, d); + break; + } + + SEND_TO_Q("\r\nДля подтверждения введите свой пароль : ", d); + STATE(d) = CON_DELCNF1; + + break; + + case '6': + if (IS_IMMORTAL(d->character)) { + SEND_TO_Q("\r\nВам это ни к чему...\r\n", d); + SEND_TO_Q(MENU, d); + STATE(d) = CON_MENU; + } else { + ResetStats::print_menu(d); + STATE(d) = CON_MENU_STATS; + } + break; + + case '7': + if (!PRF_FLAGGED(d->character, PRF_BLIND)) { + PRF_FLAGS(d->character).set(PRF_BLIND); + SEND_TO_Q("\r\nСпециальный режим слепого игрока ВКЛЮЧЕН.\r\n", d); + SEND_TO_Q(MENU, d); + STATE(d) = CON_MENU; + } else { + PRF_FLAGS(d->character).unset(PRF_BLIND); + SEND_TO_Q("\r\nСпециальный режим слепого игрока ВЫКЛЮЧЕН.\r\n", d); + SEND_TO_Q(MENU, d); + STATE(d) = CON_MENU; + } + + break; + case '8': + d->character->get_account()->show_list_players(d); + break; + + default: + SEND_TO_Q("\r\nЭто не есть правильный ответ!\r\n", d); + SEND_TO_Q(MENU, d); + + break; + } - case CON_DELCNF2: - if (!strcmp(arg, "yes") - || !strcmp(arg, "YES") - || !strcmp(arg, "да") - || !strcmp(arg, "ДА")) - { - if (PLR_FLAGGED(d->character, PLR_FROZEN)) - { - SEND_TO_Q("Вы решились на суицид, но Боги остановили вас.\r\n", d); - SEND_TO_Q("Персонаж не удален.\r\n", d); - STATE(d) = CON_CLOSE; - return; - } - if (GET_LEVEL(d->character) >= LVL_GRGOD) - return; - delete_char(GET_NAME(d->character)); - sprintf(buf, "Персонаж '%s' удален!\r\n" "До свидания.\r\n", GET_NAME(d->character)); - SEND_TO_Q(buf, d); - sprintf(buf, "%s (lev %d) has self-deleted.", GET_NAME(d->character), GET_LEVEL(d->character)); - mudlog(buf, NRM, LVL_GOD, SYSLOG, TRUE); - d->character->get_account()->remove_player(GetUniqueByName(GET_NAME(d->character))); - STATE(d) = CON_CLOSE; - return; - } - else - { - SEND_TO_Q("\r\nПерсонаж не удален.\r\n", d); - SEND_TO_Q(MENU, d); - STATE(d) = CON_MENU; - } - break; + break; + } + case CON_CHPWD_GETOLD: { + if (!Password::compare_password(d->character.get(), arg)) { + SEND_TO_Q("\r\nНеверный пароль.\r\n", d); + SEND_TO_Q(MENU, d); + STATE(d) = CON_MENU; + } else { + SEND_TO_Q("\r\nВведите НОВЫЙ пароль : ", d); + STATE(d) = CON_CHPWD_GETNEW; + } + + return; + } + + case CON_DELCNF1: { + if (!Password::compare_password(d->character.get(), arg)) { + SEND_TO_Q("\r\nНеверный пароль.\r\n", d); + SEND_TO_Q(MENU, d); + STATE(d) = CON_MENU; + } else { + SEND_TO_Q("\r\n!!! ВАШ ПЕРСОНАЖ БУДЕТ УДАЛЕН !!!\r\n" + "Вы АБСОЛЮТНО В ЭТОМ УВЕРЕНЫ?\r\n\r\n" + "Наберите \"YES / ДА\" для подтверждения: ", d); + STATE(d) = CON_DELCNF2; + } + + break; + } + + case CON_DELCNF2: { + if (!strcmp(arg, "yes") + || !strcmp(arg, "YES") + || !strcmp(arg, "да") + || !strcmp(arg, "ДА")) { + if (PLR_FLAGGED(d->character, PLR_FROZEN)) { + SEND_TO_Q("Вы решились на суицид, но Боги остановили вас.\r\n", d); + SEND_TO_Q("Персонаж не удален.\r\n", d); + STATE(d) = CON_CLOSE; + return; + } + if (GET_LEVEL(d->character) >= LVL_GRGOD) + return; + delete_char(GET_NAME(d->character)); + sprintf(buf, "Персонаж '%s' удален!\r\n" "До свидания.\r\n", GET_NAME(d->character)); + SEND_TO_Q(buf, d); + sprintf(buf, "%s (lev %d) has self-deleted.", GET_NAME(d->character), GET_LEVEL(d->character)); + mudlog(buf, NRM, LVL_GOD, SYSLOG, TRUE); + d->character->get_account()->remove_player(GetUniqueByName(GET_NAME(d->character))); + STATE(d) = CON_CLOSE; + return; + } else { + SEND_TO_Q("\r\nПерсонаж не удален.\r\n", d); + SEND_TO_Q(MENU, d); + STATE(d) = CON_MENU; + } + break; + } case CON_NAME2: skip_spaces(&arg); if (strlen(arg) == 0) @@ -4376,6 +4299,10 @@ void nanny(DESCRIPTOR_DATA * d, char *arg) STATE(d) = CON_RMOTD; break; + case CON_IMPLTEST: { + handleTestInput(d->character.get(), arg); + break; + } default: log("SYSERR: Nanny: illegal state of con'ness (%d) for '%s'; closing connection.", diff --git a/src/privilege.cpp b/src/privilege.cpp index 7d98dc614..d09a830aa 100644 --- a/src/privilege.cpp +++ b/src/privilege.cpp @@ -34,16 +34,16 @@ * # title - одобрение/запрет чужих титулов * # остальные группы вписываете сколько хотите * # иммы: имя уид (0 не канает, теперь надо сразу писать уид) команды/группы -* +* * default = wizhelp wiznet register имя титул title holylight uptime date set (title name) rules show nohassle ; show (punishment stats player) * default_demigod = wizhelp wiznet имя rules * arena = purge * olc = oedit zedit redit olc trigedit * goto = goto прыжок poofin poofout -* +* * -* Йцук 595336650 groups (olc) hell mute dumb ban delete set (bank) -* Фыва 803863739 groups (arena goto olc boards) hell mute dumb ban delete set (bank) +* Йцук 595336650 grp (olc) hell mute dumb ban delete set (bank) +* Фыва 803863739 grp (arena goto olc boards) hell mute dumb ban delete set (bank) * * Формат файла временный, zone.ru там грозится своим форматом на lua, а xml в очередной раз решено не воротить, хотя и хотелось... */ @@ -200,7 +200,7 @@ void parse_command_line(const std::string &commands, int other_flags) fill_mode = 2; continue; } - else if ((*tmp_tok_iter) == "groups") + else if ((*tmp_tok_iter) == "grp") { fill_mode = 3; continue; @@ -237,7 +237,7 @@ void load() ReadEndString(file); continue; } - else if (name == "") + else if (name == "") { while (file >> name) @@ -246,7 +246,7 @@ void load() ReadEndString(file); continue; } - if (name == "") + if (name == "") break; file >> temp; // "=" diff --git a/src/pugixml.cpp b/src/pugixml.cpp index 1e5fc176b..01c19e076 100644 --- a/src/pugixml.cpp +++ b/src/pugixml.cpp @@ -2837,9 +2837,9 @@ PUGI__NS_BEGIN // , , "...", '...' // // - // First group can not contain nested groups - // Second group can contain nested groups of the same type - // Third group can contain all other groups + // First group can not contain nested grp + // Second group can contain nested grp of the same type + // Third group can contain all other grp char_t* parse_doctype_primitive(char_t* s) { if (*s == '"' || *s == '\'') diff --git a/src/structs.h b/src/structs.h index 36c20ea08..d0e412b1e 100644 --- a/src/structs.h +++ b/src/structs.h @@ -791,6 +791,7 @@ typedef std::list affects_list_t; #define CON_RESET_RELIGION 55 // сброс религии из меню сброса статов #define CON_RANDOM_NUMBER 56 // Verification code entry: where player enter in the game from new location #define CON_INIT 57 // just connected +#define CON_IMPLTEST 58 // внутренние тесты имплементора // не забываем отражать новые состояния в connected_types -- Krodo // Character equipment positions: used as index for char_data.equipment[] // diff --git a/src/utils.cpp b/src/utils.cpp index 43ab80133..dc74119a7 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -651,169 +651,6 @@ TIME_INFO_DATA *age(const CHAR_DATA * ch) return (&player_age); } -// Check if making CH follow VICTIM will create an illegal // -// Follow "Loop/circle" // -bool circle_follow(CHAR_DATA * ch, CHAR_DATA * victim) -{ - for (auto k = victim; k; k = k->get_master()) - { - if (k->get_master() == k) - { - k->set_master(nullptr); - return false; - } - - if (k == ch) - { - return true; - } - } - - return false; -} - - -// Called when stop following persons, or stopping charm // -// This will NOT do if a character quits/dies!! // -// При возврате 1 использовать ch нельзя, т.к. прошли через extract_char -// TODO: по всем вызовам не проходил, может еще где-то коряво вызывается, кроме передачи скакунов -- Krodo -// при персонаже на входе - пуржить не должно полюбому, если начнет, как минимум в change_leader будут глюки -bool stop_follower(CHAR_DATA * ch, int mode) -{ - struct follow_type *j, *k; - int i; - - //log("[Stop follower] Start function(%s->%s)",ch ? GET_NAME(ch) : "none", - // ch->master ? GET_NAME(ch->master) : "none"); - - if (!ch->has_master()) - { - log("SYSERR: stop_follower(%s) without master", GET_NAME(ch)); - return (FALSE); - } - - // для смены лидера без лишнего спама - if (!IS_SET(mode, SF_SILENCE)) - { - act("Вы прекратили следовать за $N4.", FALSE, ch, 0, ch->get_master(), TO_CHAR); - act("$n прекратил$g следовать за $N4.", TRUE, ch, 0, ch->get_master(), TO_NOTVICT | TO_ARENA_LISTEN); - } - - //log("[Stop follower] Stop horse"); - if (ch->get_master()->get_horse() == ch && ch->get_master()->ahorse()) - { - ch->drop_from_horse(); - } - else - { - act("$n прекратил$g следовать за вами.", TRUE, ch, 0, ch->get_master(), TO_VICT); - } - - //log("[Stop follower] Remove from followers list"); - if (!ch->get_master()->followers) - { - log("[Stop follower] SYSERR: Followers absent for %s (master %s).", GET_NAME(ch), GET_NAME(ch->get_master())); - } - else if (ch->get_master()->followers->follower == ch) // Head of follower-list? - { - k = ch->get_master()->followers; - ch->get_master()->followers = k->next; - if (!ch->get_master()->followers - && !ch->get_master()->has_master()) - { - //AFF_FLAGS(ch->get_master()).unset(EAffectFlag::AFF_GROUP); - ch->get_master()->removeGroupFlags(); - } - free(k); - } - else // locate follower who is not head of list - { - for (k = ch->get_master()->followers; k->next && k->next->follower != ch; k = k->next); - if (!k->next) - { - log("[Stop follower] SYSERR: Undefined %s in %s followers list.", GET_NAME(ch), GET_NAME(ch->get_master())); - } - else - { - j = k->next; - k->next = j->next; - free(j); - } - } - - ch->set_master(nullptr); - //AFF_FLAGS(ch).unset(EAffectFlag::AFF_GROUP); - ch->removeGroupFlags(); - - if (AFF_FLAGGED(ch, EAffectFlag::AFF_CHARM) - || AFF_FLAGGED(ch, EAffectFlag::AFF_HELPER) - || IS_SET(mode, SF_CHARMLOST)) - { - if (affected_by_spell(ch, SPELL_CHARM)) - { - affect_from_char(ch, SPELL_CHARM); - } - EXTRACT_TIMER(ch) = 5; - AFF_FLAGS(ch).unset(EAffectFlag::AFF_CHARM); - - if (ch->get_fighting()) - { - stop_fighting(ch, TRUE); - } - - if (IS_NPC(ch)) - { - if (MOB_FLAGGED(ch, MOB_CORPSE)) - { - act("Налетевший ветер развеял $n3, не оставив и следа.", TRUE, ch, 0, 0, TO_ROOM | TO_ARENA_LISTEN); - GET_LASTROOM(ch) = GET_ROOM_VNUM(ch->in_room); - perform_drop_gold(ch, ch->get_gold()); - ch->set_gold(0); - extract_char(ch, FALSE); - return (TRUE); - } - else if (AFF_FLAGGED(ch, EAffectFlag::AFF_HELPER)) - { - AFF_FLAGS(ch).unset(EAffectFlag::AFF_HELPER); - } - } - } - - if (IS_NPC(ch) - && !MOB_FLAGGED(ch, MOB_PLAYER_SUMMON) //Не ресетим флаги, если моб призван игроком - && (i = GET_MOB_RNUM(ch)) >= 0) - { - MOB_FLAGS(ch) = MOB_FLAGS(mob_proto + i); - } - - return (FALSE); -} - - - -// * Called when a character that follows/is followed dies -bool die_follower(CHAR_DATA * ch) -{ - struct follow_type *j, *k = ch->followers; - - if (ch->has_master() && stop_follower(ch, SF_FOLLOWERDIE)) - { - // чармиса спуржили в stop_follower - return true; - } - - if (ch->ahorse()) { - AFF_FLAGS(ch).unset(EAffectFlag::AFF_HORSE); - } - - for (k = ch->followers; k; k = j) - { - j = k->next; - stop_follower(k->follower, SF_MASTERDIE); - } - return false; -} - /* * get_line reads the next non-blank line off of the input stream. * The newline character is removed from the input. Lines which begin From c43639e924c28942b1f711e2dbb325a3ca16b749 Mon Sep 17 00:00:00 2001 From: bikbai Date: Wed, 23 Dec 2020 01:32:20 +0300 Subject: [PATCH 03/40] =?UTF-8?q?=D0=BF=D1=80=D0=BE=D0=B4=D0=BE=D0=BB?= =?UTF-8?q?=D0=B6=D0=B0=D0=B5=D0=BC=20=D0=B3=D1=80=D1=83=D0=BF=D0=BF=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 2 +- src/act.informative.cpp | 4 +- src/chars/char.cpp | 4 - src/cmd.imm/test.cpp | 27 ----- src/cmd.imm/test.h | 9 -- src/cmd/quit.cpp | 2 + src/comm.cpp | 3 +- src/constants.cpp | 6 +- src/grp/grp.group.cpp | 219 +++++++++++++++++++++++----------------- src/grp/grp.group.h | 50 +++++---- src/grp/grp.roster.cpp | 150 +++++++++++++++++++++++++-- src/grp/grp.roster.h | 34 ++++++- src/interpreter.cpp | 17 +--- src/structs.h | 5 +- 14 files changed, 347 insertions(+), 185 deletions(-) delete mode 100644 src/cmd.imm/test.cpp delete mode 100644 src/cmd.imm/test.h diff --git a/CMakeLists.txt b/CMakeLists.txt index f62804b19..48c48b9cd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -573,7 +573,7 @@ set(MISC_FILES source_group("Misc" FILES ${MISC_FILES}) -set(CIRCLE_FILES ${SOURCES} ${HEADERS} readme.markdown CONTRIBUTING.md ${CRAFT_FILES} ${MISC_FILES} src/cmd.imm/test.cpp src/cmd.imm/test.h src/grp/follow.cpp src/grp/follow.h src/cmd/quit.cpp src/cmd/quit.h) +set(CIRCLE_FILES ${SOURCES} ${HEADERS} readme.markdown CONTRIBUTING.md ${CRAFT_FILES} ${MISC_FILES} src/grp/follow.cpp src/grp/follow.h src/cmd/quit.cpp src/cmd/quit.h) # Sort source and header files. Just to convenience. list(SORT CIRCLE_FILES) diff --git a/src/act.informative.cpp b/src/act.informative.cpp index 87097729e..7c4b0de77 100644 --- a/src/act.informative.cpp +++ b/src/act.informative.cpp @@ -61,6 +61,7 @@ #include "sysdep.h" #include "bonus.h" #include "conf.h" +#include "grp/grp.group.h" #include #include @@ -5505,7 +5506,8 @@ void sendWhoami(CHAR_DATA *ch) { if (ch->player_specials->saved.telegram_id != 0) { //тут прямое обращение, ибо базовый класс, а не наследник send_to_char(ch, "Подключен Телеграм, chat_id: %lu\r\n", ch->player_specials->saved.telegram_id); } - + if (ch->personGroup) + send_to_char(ch, "Стоит в группе #%d лидера %s\r\n", ch->personGroup->getUid(), ch->personGroup->getLeaderName().c_str()); } // Generic page_string function for displaying text diff --git a/src/chars/char.cpp b/src/chars/char.cpp index 0ce5c9731..6c17feddd 100644 --- a/src/chars/char.cpp +++ b/src/chars/char.cpp @@ -2020,10 +2020,6 @@ void CHAR_DATA::msdp_report(const std::string& name) { void CHAR_DATA::removeGroupFlags() { AFF_FLAGS(this).unset(EAffectFlag::AFF_GROUP); PRF_FLAGS(this).unset(PRF_SKIRMISHER); - if (!this->personGroup) - return; - this->personGroup->removeMember(this); - this->personGroup = nullptr; } void CHAR_DATA::add_follower(CHAR_DATA* ch) { diff --git a/src/cmd.imm/test.cpp b/src/cmd.imm/test.cpp deleted file mode 100644 index e69279315..000000000 --- a/src/cmd.imm/test.cpp +++ /dev/null @@ -1,27 +0,0 @@ -// -// Created by ubuntu on 19/12/20. -// - -#include "test.h" -#include "comm.h" - -bool handleTestInput(CHAR_DATA *ch, char *arg) { - switch (LOWER(*arg)) { - case '1': - case 'z': - case 'я': - STATE(ch->desc) = CON_PLAYING; - send_to_char("Режим тестирования окончен.\r\n", ch); - return 1; - default: - break; - - } -} - - -void do_test(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) -{ - send_to_char(ch, "Переход в режим тестирования"); - STATE(ch->desc) = CON_IMPLTEST; -} \ No newline at end of file diff --git a/src/cmd.imm/test.h b/src/cmd.imm/test.h deleted file mode 100644 index d6a84e8d3..000000000 --- a/src/cmd.imm/test.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef BYLINS_TEST_H -#define BYLINS_TEST_H - -#include "chars/char.hpp" - -void do_test(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/); -bool handleTestInput(CHAR_DATA *ch, char *arg); - -#endif //BYLINS_TEST_H diff --git a/src/cmd/quit.cpp b/src/cmd/quit.cpp index e32b0cc7a..dfca0c662 100644 --- a/src/cmd/quit.cpp +++ b/src/cmd/quit.cpp @@ -8,9 +8,11 @@ #include "handler.h" #include "objsave.h" #include "house.h" +#include "grp/grp.group.h" extern int free_rent; + void do_quit(CHAR_DATA *ch, char *argument, int/* cmd*/, int subcmd) { DESCRIPTOR_DATA *d, *next_d; diff --git a/src/comm.cpp b/src/comm.cpp index d3fe7d1db..1cf0bccb6 100644 --- a/src/comm.cpp +++ b/src/comm.cpp @@ -3411,8 +3411,7 @@ void close_socket(DESCRIPTOR_DATA * d, int direct) || STATE(d) == CON_MAP_MENU || STATE(d) == CON_TORC_EXCH || STATE(d) == CON_SEDIT - || STATE(d) == CON_CONSOLE - || STATE(d) == CON_IMPLTEST) + || STATE(d) == CON_CONSOLE) { STATE(d) = CON_PLAYING; } diff --git a/src/constants.cpp b/src/constants.cpp index 7c9d4c920..18a9d1dd4 100644 --- a/src/constants.cpp +++ b/src/constants.cpp @@ -666,7 +666,7 @@ const char *connected_types[] = "GloryConst OLC", "NamedStuff OLC", "Select new kin", - // 50-57 + // 50-59 "Select new race", "Interactive console", "обмен гривен", @@ -675,6 +675,10 @@ const char *connected_types[] = "select new religion", "Verification", "Just connected", + "ERROR:UNDEF", + "ERROR:UNDEF", + // 60-59 + "ERROR:UNDEF" "\n" }; diff --git a/src/grp/grp.group.cpp b/src/grp/grp.group.cpp index bcb3d6941..d02ce2b29 100644 --- a/src/grp/grp.group.cpp +++ b/src/grp/grp.group.cpp @@ -468,13 +468,6 @@ void do_group(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) return; } - if (!str_cmp(buf, "новый")) - { - if (ch->personGroup) - ch->personGroup->print(ch); - return; - } - if (GET_POS(ch) < POS_RESTING) { send_to_char("Трудно управлять группой в таком состоянии.\r\n", ch); @@ -604,8 +597,11 @@ void do_group(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) } } -void do_ungroup(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) -{ +void do_group2(CHAR_DATA *ch, char *argument, int, int){ + ch->personGroup->processGroupCommands(ch, argument); +} + +void do_ungroup(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) { struct follow_type *f, *next_fol; CHAR_DATA *tch; @@ -724,10 +720,10 @@ void do_report(CHAR_DATA *ch, char* /*argument*/, int/* cmd*/, int/* subcmd*/) send_to_char("Вы доложили о состоянии всем членам вашей группы.\r\n", ch); } - Group::Group(CHAR_DATA *leader, u_long uid){ this->_memberCap = IS_NPC(leader)? 255 : max_group_size(leader); this->_uid = uid; + // заодно иничим остальные поля this->addMember(leader); this->setLeader(leader); } @@ -735,7 +731,19 @@ Group::Group(CHAR_DATA *leader, u_long uid){ void Group::addMember(CHAR_DATA *member) { if (IS_NPC(member)) return; - this->_memberList.emplace(member->get_uid(), member); + if (member->personGroup == this) + return; + auto it = this->_memberList.find(member->get_uid()); + if (it == this->_memberList.end()) + this->_memberList.emplace(member->get_uid(), new char_info(member->get_uid(), + member, + member->get_pc_name()) ); + else { + sprintf(buf, "ROSTER: группа id=%lu, добавление персонажа с тем же uid", this->_uid); + mudlog(buf, BRF, LVL_IMMORT, SYSLOG, TRUE); + return; + } + member->personGroup = this; } void Group::removeMember(CHAR_DATA *member) { @@ -747,22 +755,7 @@ void Group::removeMember(CHAR_DATA *member) { auto it = this->_memberList.find(member->get_uid()); if (it != this->_memberList.end()) this->_memberList.erase(it); -} - -void Group::print(CHAR_DATA *member) { - if (this->_memberList.empty()) { - send_to_char(member, "WTF"); - return; - } - sprintf(buf, "Персонажи в группе, id = %lu\r\n", this->_uid); - for (auto & it : this->_memberList){ - if (it.second->get_uid() != this->_uid) { - sprintf(buf, "%s - %s", it.second->get_pc_name().c_str(), "Игрок"); - } else { - sprintf(buf, "%s - %s", it.second->get_pc_name().c_str(), "Лидер"); - } - } - + member->personGroup = nullptr; } CHAR_DATA *Group::getLeader() const { @@ -772,6 +765,7 @@ CHAR_DATA *Group::getLeader() const { void Group::setLeader(CHAR_DATA *leader) { this->_leaderUID = leader->get_uid(); _leader = leader; + _leaderName = leader->get_pc_name(); } bool Group::isActive() { @@ -797,97 +791,138 @@ int Group::getMemberCap() const { Group::~Group() { // чистим ссылки for (auto & it : this->_memberList){ - if (it.second->purged()) + if (!it.second->member || it.second->member->purged()) continue; - send_to_char(it.second, "Ваша группа была распущена.\r\n"); - it.second->personGroup = nullptr; - } -} - -bool Group::applyRequest(CHAR_DATA *applicant) { - if (this->_requestList.find(applicant->get_uid()) == this->_requestList.end()) { - act("$E подал$A заявку на вступление в группу.", FALSE, this->_leader, 0, applicant, TO_CHAR); - this->_requestList.emplace(applicant->get_uid(), applicant); - return true; + if (it.second->member != this->_leader) + send_to_char(it.second->member, "Ваша группа была распущена.\r\n"); + it.second->member->personGroup = nullptr; } - return false; } -bool Group::approveRequest(CHAR_DATA *applicant) { - if (this->_memberList.size() == this->_memberCap){ +bool Group::approveRequest(char *applicant) { + if ((int)this->_memberList.size() == this->_memberCap){ send_to_char(this->_leader, "Чтобы принять кого нужного, надо сперва исключить кого ненужного!\r\n"); return false; } + return true; } -bool Group::denyRequest(CHAR_DATA *applicant) { - return false; -} +void Group::printGroup(CHAR_DATA *requestor) { -void GCmd::do_gmake(CHAR_DATA *ch, char *argument, int, int) { - if (!ch) - return; - if (ch->personGroup && ch->personGroup->getLeader() == ch){ - send_to_char(ch, "Только древний правитель Цесарь Иулий мог водить много легионов!\r\n"); - return; - } - if (ch->personGroup) // в группе - покидаем - ch->personGroup->removeMember(ch); - groupRoster.addGroup(ch); - send_to_char(ch, "Вы создали группу id:%d, максимальное число последователей: %d\r\n",ch->personGroup->getUid(), ch->personGroup->getMemberCap()); } -void GCmd::do_ginvite(CHAR_DATA *ch, char *argument, int, int) { - -} - -void GCmd::do_grequest(CHAR_DATA *ch, char *argument, int, int) { - CHAR_DATA *vict = nullptr; - - if (AFF_FLAGGED(ch, EAffectFlag::AFF_CHARM)) +void Group::processGroupCommands(CHAR_DATA *ch, char *argument) { + enum SubCmd { PRINT, HELP, LIST, MAKE, INVITE, TAKE, REJECT, EXPELL, LEADER, LEAVE, DISBAND}; + SubCmd mode; + Group * grp; + + const std::string strHELP = "справка помощь"; + const std::string strMAKE = "создать make"; + const std::string strLIST = "список list"; + const std::string strINVITE = "пригласить invite"; + const std::string strTAKE = "принять approve"; + const std::string strREJECT = "отклонить reject"; + const std::string strEXPELL = "выгнать expell"; + const std::string strLEADER = "лидер leader"; + const std::string strLEAVE = "покинуть leave"; + const std::string strDISBAND = "распустить disband"; + char subcmd[MAX_INPUT_LENGTH], target[MAX_INPUT_LENGTH]; + + if (!ch || IS_NPC(ch)) return; - if (AFF_FLAGGED(ch, EAffectFlag::AFF_SILENCE) || AFF_FLAGGED(ch, EAffectFlag::AFF_STRANGLED)){ - send_to_char(ch, "Вы немы, как рыба об лед.\r\n"); + two_arguments(argument, subcmd, target); + + // выбираем режим + if (!*subcmd) + mode = PRINT; + else if (isname(subcmd, strHELP.c_str())) + mode = HELP; + else if (isname(subcmd, strMAKE.c_str())) + mode = MAKE; + else if (isname(subcmd, strLIST.c_str())) + mode = LIST; + else if (isname(subcmd, strINVITE.c_str())) + mode = INVITE; + else if (isname(subcmd, strTAKE.c_str())) + mode = TAKE; + else if (isname(subcmd, strREJECT.c_str())) + mode = REJECT; + else if (isname(subcmd, strEXPELL.c_str())) + mode = EXPELL; + else if (isname(subcmd, strLEADER.c_str())) + mode = LEADER; + else if (isname(subcmd, strLEAVE.c_str())) + mode = LEAVE; + else if (isname(subcmd, strDISBAND.c_str())) + mode = DISBAND; + else { + send_to_char("Уточните команду.\r\n", ch); return; } - if (ch->personGroup && ch->personGroup->getLeader() == ch){ - send_to_char(ch, "Негоже лидеру группы напрашиваться в чужую!\r\n"); + grp = ch->personGroup; + + if (mode == MAKE) { + if (grp && grp->getLeader() == ch){ + send_to_char(ch, "Только древний правитель Цесарь Иулий мог водить много легионов!\r\n"); + return; + } + if (grp) // в группе - покидаем + grp->removeMember(ch); + groupRoster.addGroup(ch); + send_to_char(ch, "Вы создали группу id:%lu, максимальное число последователей: %d\r\n",ch->personGroup->getUid(), ch->personGroup->getMemberCap()); return; } - one_argument(argument, smallBuf); - - if (!*smallBuf) { - send_to_char("И кому же вы хотите напроситься в группу?.\r\n", ch); + if (!grp || grp->getLeader() != ch ) { + send_to_char(ch, "Необходимо быть лидером группы для этого.\r\n"); return; } - if (!(vict = get_player_vis(ch, smallBuf, FIND_CHAR_WORLD)) || IS_NPC(vict)) - { - send_to_char("Необходимо видеть лидера группы.\r\n", ch); + if (GET_POS(ch) < POS_RESTING) { + send_to_char("Трудно управлять группой в таком состоянии.\r\n", ch); return; } - if (vict->personGroup->applyRequest(ch)){ - send_to_char("Заявка на вступление в группу отправлена.\r\n", ch); - } else { - send_to_char("Вы уже подавали заявку в группу!.\r\n", ch); - } -} - -void GCmd::do_gleave(CHAR_DATA *ch, char *argument, int, int) { - -} -void GCmd::do_gabandon(CHAR_DATA *ch, char *argument, int, int) { - if (!ch->personGroup || ch->personGroup->getLeader() != ch){ - send_to_char(ch, "У вас мания величия, вы не управляете группой!\r\n"); - return; + // MAKE был раньше, т.к. он создает группу + switch (mode) { + case PRINT: + grp->printGroup(ch); + break; + case HELP: + break; + case LIST: + //grp->listMembers(ch); + break; + case INVITE: + groupRoster.makeRequest(target); + break; + case TAKE: + //grp->approveRequest(target); + break; + case REJECT: + //grp->denyRequest(target); + break; + case EXPELL: + //grp->expellMember(target); + break; + case LEADER: + //grp->promote(target); + break; + case LEAVE: + //grp->leave(ch); + break; + case DISBAND: + groupRoster.removeGroup(grp->getUid()); + break; } - send_to_char(ch, "Вы распустили группу id:%d\r\n",ch->personGroup->getUid()); - groupRoster.removeGroup(ch->personGroup->getUid()); - } -void GCmd::do_gpromote(CHAR_DATA *ch, char *argument, int, int) { +const std::string &Group::getLeaderName() const { + return _leaderName; +} +bool Group::isFull() { + if (this->_currentMemberCount == this->_memberCap) + return true; + return false; } diff --git a/src/grp/grp.group.h b/src/grp/grp.group.h index abf2a9373..76bc2d5b0 100644 --- a/src/grp/grp.group.h +++ b/src/grp/grp.group.h @@ -6,12 +6,22 @@ void change_leader(CHAR_DATA *ch, CHAR_DATA *vict); void do_group(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/); +void do_group2(CHAR_DATA *ch, char *argument, int, int); void do_ungroup(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/); void do_report(CHAR_DATA *ch, char* /*argument*/, int/* cmd*/, int/* subcmd*/); int max_group_size(CHAR_DATA *ch); + class Group { private: + struct char_info { + int memberUID; + char_info(int memberUid, CHAR_DATA *member, const std::string &memberName) : memberUID(memberUid), + member(member), + memberName(memberName) {} + CHAR_DATA * member; + std::string memberName; + }; // ид группы в ростере u_long _uid = 0; // текущее количество игроков @@ -20,46 +30,42 @@ class Group { int _memberCap = 0; // ид лидера int _leaderUID; + // имя лидера + std::string _leaderName; + // ссылка на персонажа, АХТУНГ! Может меняться и быть невалидным CHAR_DATA* _leader; // состав группы, ключом UID персонажа. Дохлые/вышедшие проверяем отдельным методом - std::map _memberList; - // список заявок ключ UID персонажа. При принятии одобряется, при отказе удаляется. - std::map _requestList; + std::map _memberList; public: u_long getUid() const; u_short getCurrentMemberCount() const; + const std::string &getLeaderName() const; CHAR_DATA *getLeader() const; void setLeader(CHAR_DATA *leader); int getMemberCap() const; virtual ~Group(); + bool isFull(); + Group(CHAR_DATA *leader, u_long uid); void addMember(CHAR_DATA *member); void removeMember(CHAR_DATA *member); bool checkMember(int uid); // проверки валидности персонажа - void print(CHAR_DATA *member); + void printGroup(CHAR_DATA *requestor); + void listMembers(CHAR_DATA *requestor); bool isActive(); // проверка, что в группе все персонажи онлайн - bool applyRequest(CHAR_DATA *applicant); - bool approveRequest(CHAR_DATA *applicant); - bool denyRequest(CHAR_DATA *applicant); -}; + // обработчик команд группы + void processGroupCommands(CHAR_DATA *ch, char *argument); + // методы работы с контентом группы + bool approveRequest(char *applicant); + bool denyRequest(char *applicant); + bool expellMember(char *applicant); + bool promote(char *applicant); + bool leave(CHAR_DATA *ch); -namespace GCmd{ - // команда роспуска группы - void do_gabandon(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/); - // команда приглашения лидером персонажа в группу - void do_ginvite(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/); - // команда выхода из группы - void do_gleave(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/); - // команда создания группы - void do_gmake(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/); - // команда переназначения лидера - void do_gpromote(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/); - // команда запроса персонажем в группу - void do_grequest(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/); -} +}; #endif //BYLINS_GRP_GROUP_H diff --git a/src/grp/grp.roster.cpp b/src/grp/grp.roster.cpp index dc312a7dd..045024c2b 100644 --- a/src/grp/grp.roster.cpp +++ b/src/grp/grp.roster.cpp @@ -4,29 +4,44 @@ #include "grp.roster.h" #include "global.objects.hpp" +#include "handler.h" GroupRoster& groupRoster = GlobalObjects::groupRoster(); - GroupRoster::GroupRoster() { this->_currentGroupIndex = 0; } +Request::Request(CHAR_DATA *author, Group *group) { + if (!author || !group) + return; + _applicant = author; + _applicantName = author->get_pc_name(); + _applicantUID = author->get_uid(); + _group = group; + if (author == group->getLeader()) + _type = RQ_GROUP; + else + _type = RQ_PERSON; +} + Group* GroupRoster::addGroup(CHAR_DATA * leader) { ++this->_currentGroupIndex; auto *group = new Group(leader, _currentGroupIndex); this->_groupList.emplace(_currentGroupIndex, new GroupRosterRecord(group)); - leader->personGroup = group; // прописываем сразу обратную ссылку персонажа на группу return group; } void GroupRoster::removeGroup(u_long uid) { auto grp = _groupList.find(uid); - delete grp->second->member; - _groupList.erase(uid); + if (grp != _groupList.end()) + { + delete grp->second->member; + _groupList.erase(uid); + } } void GroupRoster::recalculateRoster() { - return; + } void GroupRoster::restorePlayerGroup(CHAR_DATA *ch) { @@ -42,5 +57,128 @@ void GroupRoster::restorePlayerGroup(CHAR_DATA *ch) { GroupRosterRecord::GroupRosterRecord(Group *group) { this->member = group; - this->needRecalc = false; +} + +void GroupRoster::printRequestList(CHAR_DATA *ch) { + bool notFound = true; + if (!ch || ch->purged()) + return; + send_to_char(ch, "Активные заявки и приглашения:\r\n"); + for (const auto & it : this->_requestList){ + if (ch->get_uid() == it._applicantUID) + sprintf(smallBuf, "%s%s\r\n", + it._type== RQ_TYPE::RQ_PERSON? "Заявка в группу лидера ": "Приглашение от лидера группы ", + it._group->getLeaderName().c_str()); + if (ch->personGroup != nullptr && ch->personGroup->getUid() == it._group->getUid()) + sprintf(smallBuf, "Приглашение игроку: %s\r\n", it._applicantName.c_str() ); + send_to_char(ch, "%s", smallBuf); + notFound = false; + } + if (notFound) + send_to_char(ch, "Заявок нет.\r\n"); +} + +void GroupRoster::makeRequest(CHAR_DATA *author, char* target) { + if (AFF_FLAGGED(author, EAffectFlag::AFF_CHARM)) + return; + + if (AFF_FLAGGED(author, EAffectFlag::AFF_SILENCE) || AFF_FLAGGED(author, EAffectFlag::AFF_STRANGLED)) { + send_to_char(author, "Вы немы, как рыба об лед.\r\n"); + return; + } + if (author->personGroup && author->personGroup->getLeader() == author) { + send_to_char(author, "Негоже лидеру группы напрашиваться в чужую!\r\n"); + return; + } + + if (!*target) { + send_to_char("И кому же вы хотите напроситься в группу?.\r\n", author); + return; + } + + switch (groupRoster.tryAddRequest(author, target)) { + case OK_SUCCESS: + send_to_char("Заявка на вступление в группу отправлена.\r\n", author); + return; + case GROUP_OVERFLOW: + send_to_char("В группе нет свободных мест, свяжитесь с лидером.\r\n", author); + return; + case NO_GROUP: + send_to_char("Протри глаза, не такой группы!\r\n", author); + return; + case RQ_REFRESH: + send_to_char("Заявка успешно продлена.\r\n", author); + } +} + +void GroupRoster::revokeRequest(CHAR_DATA *ch, char* target) { + if (ch->personGroup != nullptr && ch->personGroup->getLeader() == ch) { + // вызов от имени лидера, отменяем заявку лидера на присоединение к группе + send_to_char(ch, "вызов от имени лидера, отменяем заявку лидера на присоединение к группе\r\n"); + return; + } + for (auto it = this->_requestList.begin(); it != this->_requestList.end(); ++it) + if (it->_applicant == ch && str_cmp(target, it->_group->getLeaderName().c_str())){ + send_to_char(ch, "заявка найдена и удалена\r\n"); + _requestList.erase(it); + return; + } + send_to_char(ch, "заявка не найдена\r\n"); +} + +void GroupRoster::makeRequest(char *targetPerson) { + +} + +RQ_RESULT GroupRoster::tryAddRequest(CHAR_DATA *author, char *targetGroup) { + Group* grp = nullptr; + for (auto & it : this->_groupList) { + if (str_cmp(it.second->member->getLeaderName().c_str(), targetGroup)) + grp = it.second->member; + } + if (grp == nullptr) + return RQ_RESULT::NO_GROUP; + if (grp->isFull()) + return RQ_RESULT::GROUP_OVERFLOW; + + for (auto & it : this->_requestList){ + if (author == it._applicant && grp == it._group){ + it.time = std::chrono::system_clock::now(); + return RQ_REFRESH; + } + } + // не нашли, создаём + this->_requestList.emplace(_requestList.end(), Request(author, grp)); + return RQ_RESULT::OK_SUCCESS; +} + + +void do_grequest(CHAR_DATA *ch, char *argument, int, int){ + char subcmd[MAX_INPUT_LENGTH], target[MAX_INPUT_LENGTH]; + enum SUBCMD {LIST, MAKE, CANCEL}; + RQ_RESULT res; + // гзаявка - список + // гзаявка создать Верий - отправляет заявку в группу + // гзаявка отменить Верий - отменяет заявку в группу + + two_arguments(argument, subcmd, target); + + if (!*subcmd || isname(subcmd, "список list")) + { + groupRoster.printRequestList(ch); + return; + } + else if (isname(subcmd, "создать отправить make send")) { + groupRoster.makeRequest(ch, target); + return; + } + else if (isname(subcmd, "отменить отозвать cancel revoke")){ + groupRoster.revokeRequest(ch, target); + return; + } + else { + send_to_char("Уточните команду.\r\n", ch); + return; + } + } diff --git a/src/grp/grp.roster.h b/src/grp/grp.roster.h index 65c6163c2..f4fb2292c 100644 --- a/src/grp/grp.roster.h +++ b/src/grp/grp.roster.h @@ -8,25 +8,49 @@ #include "chars/char.hpp" #include "grp.group.h" +enum RQ_RESULT {OK_SUCCESS, NO_GROUP, GROUP_OVERFLOW, RQ_REFRESH}; +enum RQ_TYPE {RQ_GROUP, RQ_PERSON}; + +void do_grequest(CHAR_DATA *ch, char *argument, int, int); + class GroupRosterRecord { public: explicit GroupRosterRecord(Group * group); Group *member; - bool needRecalc = false; }; +class Request { +public: + CHAR_DATA *_applicant; + std::string _applicantName; + int _applicantUID; + Group *_group; + RQ_TYPE _type; + Request(CHAR_DATA* author, Group* group); + std::chrono::time_point time; +}; class GroupRoster { private: u_long _currentGroupIndex = 0; - std::map _groupList; + std::map _groupList; + std::list _requestList; + RQ_RESULT tryAddRequest(CHAR_DATA* author, char* targetGroup); public: GroupRoster(); - Group* addGroup(CHAR_DATA * leader); u_long getSize() {return _groupList.size();} - void removeGroup(u_long uid); - void restorePlayerGroup(CHAR_DATA *ch); + void restorePlayerGroup(CHAR_DATA* ch); void recalculateRoster(); + + // манипуляция группой + Group* addGroup(CHAR_DATA* leader); + void removeGroup(u_long uid); + + // методы работы с заявками + void printRequestList(CHAR_DATA* ch); + void makeRequest(char* targetPerson); + void makeRequest(CHAR_DATA* author, char* targetGroup); + void revokeRequest(CHAR_DATA* author, char* targetGroup); }; #endif //BYLINS_GRP_ROSTER_H diff --git a/src/interpreter.cpp b/src/interpreter.cpp index 07df038d6..6a57cb093 100644 --- a/src/interpreter.cpp +++ b/src/interpreter.cpp @@ -85,7 +85,6 @@ #include "time.h" #include "title.hpp" #include "top.h" -#include "cmd.imm/test.h" #if defined WITH_SCRIPTING #include "scripting.hpp" @@ -521,9 +520,10 @@ cpp_extern const struct command_info cmd_info[] = {"гдругам", POS_SLEEPING, DoClanChannel, 0, SCMD_CHANNEL, 0}, {"где", POS_RESTING, do_where, LVL_IMMORT, 0, 0}, {"гдея", POS_RESTING, do_zone, 0, 0, 0}, + {"гзаявка", POS_DEAD, do_grequest, 0, 0, 0 }, {"глоток", POS_RESTING, do_drink, 0, SCMD_SIP, 200}, {"города", POS_DEAD, do_cities, 0, 0, 0 }, - {"группа", POS_SLEEPING, do_group, 1, 0, -1}, + {"группа", POS_SLEEPING, do_group2, 1, 0, -1}, {"гсоюзникам", POS_SLEEPING, DoClanChannel, 0, SCMD_ACHANNEL, 0}, {"гэхо", POS_DEAD, do_gecho, LVL_GOD, 0, 0}, {"гбогам", POS_DEAD, do_wiznet, LVL_IMMORT, 0, 0}, @@ -880,12 +880,8 @@ cpp_extern const struct command_info cmd_info[] = {"group", POS_RESTING, do_group, 1, 0, 500}, {"gsay", POS_SLEEPING, do_gsay, 0, 0, -1}, {"gtell", POS_SLEEPING, do_gsay, 0, 0, -1}, - {"gabandon", POS_SLEEPING, GCmd::do_gabandon, 0, 0, -1}, - {"gmake", POS_SLEEPING, GCmd::do_gmake, 0, 0, -1}, - {"gpromote", POS_SLEEPING, GCmd::do_gpromote, 0, 0, -1}, - {"ginvite", POS_SLEEPING, GCmd::do_ginvite, 0, 0, -1}, - {"grequest", POS_SLEEPING, GCmd::do_grequest, 0, 0, -1}, - {"gleave", POS_SLEEPING, GCmd::do_gleave, 0, 0, -1}, + {"group2", POS_RESTING, do_group2, 1, 0, 500}, + {"grequest", POS_RESTING, do_grequest, 1, 0, 500}, {"handbook", POS_DEAD, do_gen_ps, LVL_IMMORT, SCMD_HANDBOOK, 0}, {"hcontrol", POS_DEAD, DoHcontrol, LVL_GRGOD, 0, 0}, {"help", POS_DEAD, do_help, 0, 0, 0}, @@ -1010,7 +1006,6 @@ cpp_extern const struct command_info cmd_info[] = {"sedit", POS_DEAD, do_sedit, LVL_IMPL, 0, 0}, {"errlog", POS_DEAD, do_syslog, LVL_BUILDER, ERRLOG, 0}, {"imlog", POS_DEAD, do_syslog, LVL_BUILDER, IMLOG, 0}, - {"test", POS_DEAD, do_test, LVL_IMPL, 0, 0}, {"take", POS_RESTING, do_get, 0, 0, 500}, {"taste", POS_RESTING, do_eat, 0, SCMD_TASTE, 500}, {"t2c", POS_RESTING, do_send_text_to_char, LVL_GRGOD, 0, -1 }, @@ -4299,10 +4294,6 @@ void nanny(DESCRIPTOR_DATA * d, char *arg) STATE(d) = CON_RMOTD; break; - case CON_IMPLTEST: { - handleTestInput(d->character.get(), arg); - break; - } default: log("SYSERR: Nanny: illegal state of con'ness (%d) for '%s'; closing connection.", diff --git a/src/structs.h b/src/structs.h index d0e412b1e..54e66d804 100644 --- a/src/structs.h +++ b/src/structs.h @@ -791,7 +791,7 @@ typedef std::list affects_list_t; #define CON_RESET_RELIGION 55 // сброс религии из меню сброса статов #define CON_RANDOM_NUMBER 56 // Verification code entry: where player enter in the game from new location #define CON_INIT 57 // just connected -#define CON_IMPLTEST 58 // внутренние тесты имплементора + // не забываем отражать новые состояния в connected_types -- Krodo // Character equipment positions: used as index for char_data.equipment[] // @@ -1603,6 +1603,8 @@ namespace obj_sets_olc class sedit; } +class GControl; + #ifndef HAVE_ZLIB struct z_stream; #endif @@ -1744,7 +1746,6 @@ struct DESCRIPTOR_DATA std::array ext_money; // обмен доп.денег std::shared_ptr sedit; // редактирование сетов bool mxp; // Для MXP - private: bool m_msdp_support; std::unordered_set m_msdp_requested_report; From f486ddd7fc008f015586e723af22b217c09841b8 Mon Sep 17 00:00:00 2001 From: bikbai Date: Wed, 23 Dec 2020 20:49:01 +0300 Subject: [PATCH 04/40] =?UTF-8?q?=D0=BF=D1=80=D0=BE=D0=B4=D0=BE=D0=BB?= =?UTF-8?q?=D0=B6=D0=B0=D0=B5=D0=BC=20=D0=B3=D1=80=D1=83=D0=BF=D0=BF=D1=8B?= =?UTF-8?q?.=20=D0=95=D1=89=D0=B5=20=D0=BA=D1=83=D1=87=D0=B0=20=D1=80?= =?UTF-8?q?=D0=B5=D1=84=D0=B0=D0=BA=D1=82=D0=BE=D1=80=D0=B8=D0=BD=D0=B3?= =?UTF-8?q?=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 2 +- src/comm.h | 2 + src/grp/grp.cmdprocessor.cpp | 45 +++++++++ src/grp/grp.cmdprocessor.h | 11 +++ src/grp/grp.group.cpp | 29 ++---- src/grp/grp.group.h | 32 ++---- src/grp/grp.groupmanager.cpp | 48 +++++++++ src/grp/grp.groupmanager.h | 26 +++++ src/grp/grp.membermanager.cpp | 27 +++++ src/grp/grp.membermanager.h | 31 ++++++ src/grp/grp.request.cpp | 18 ++++ src/grp/grp.request.h | 20 ++++ src/grp/grp.requestmanager.cpp | 151 ++++++++++++++++++++++++++++ src/grp/grp.requestmanager.h | 28 ++++++ src/grp/grp.roster.cpp | 174 ++------------------------------- src/grp/grp.roster.h | 47 +++------ 16 files changed, 449 insertions(+), 242 deletions(-) create mode 100644 src/grp/grp.cmdprocessor.cpp create mode 100644 src/grp/grp.cmdprocessor.h create mode 100644 src/grp/grp.groupmanager.cpp create mode 100644 src/grp/grp.groupmanager.h create mode 100644 src/grp/grp.membermanager.cpp create mode 100644 src/grp/grp.membermanager.h create mode 100644 src/grp/grp.request.cpp create mode 100644 src/grp/grp.request.h create mode 100644 src/grp/grp.requestmanager.cpp create mode 100644 src/grp/grp.requestmanager.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 48c48b9cd..07bab450f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -573,7 +573,7 @@ set(MISC_FILES source_group("Misc" FILES ${MISC_FILES}) -set(CIRCLE_FILES ${SOURCES} ${HEADERS} readme.markdown CONTRIBUTING.md ${CRAFT_FILES} ${MISC_FILES} src/grp/follow.cpp src/grp/follow.h src/cmd/quit.cpp src/cmd/quit.h) +set(CIRCLE_FILES ${SOURCES} ${HEADERS} readme.markdown CONTRIBUTING.md ${CRAFT_FILES} ${MISC_FILES} src/grp/follow.cpp src/grp/follow.h src/cmd/quit.cpp src/cmd/quit.h src/grp/grp.request.cpp src/grp/grp.request.h src/grp/grp.cmdprocessor.cpp src/grp/grp.cmdprocessor.h src/grp/grp.requestmanager.cpp src/grp/grp.requestmanager.h src/grp/grp.groupmanager.cpp src/grp/grp.groupmanager.h src/grp/grp.membermanager.cpp src/grp/grp.membermanager.h) # Sort source and header files. Just to convenience. list(SORT CIRCLE_FILES) diff --git a/src/comm.h b/src/comm.h index bba0454cb..4d4453592 100644 --- a/src/comm.h +++ b/src/comm.h @@ -72,6 +72,7 @@ unsigned long get_ip(const char *addr); #define TO_NOTVICT 3 #define TO_CHAR 4 #define TO_ROOM_HIDE 5 // В комнату, но только тем, кто чувствует жизнь +#define TO_GROUP 6 // сообщения игрокам в группе #define CHECK_NODEAF 32 // посылать только глухим #define CHECK_DEAF 64 // не посылать глухим #define TO_SLEEP 128 // to char, even if sleeping @@ -79,6 +80,7 @@ unsigned long get_ip(const char *addr); #define TO_BRIEF_SHIELDS 1024 // отсылать только тем, у кого включен режим PRF_BRIEF_SHIELDS #define TO_NO_BRIEF_SHIELDS 2048 // отсылать только тем, у кого нет режима PRF_BRIEF_SHIELDS + // I/O functions int write_to_descriptor(socket_t desc, const char *txt, size_t total); bool write_to_descriptor_with_options(DESCRIPTOR_DATA * t, const char* buffer, size_t byffer_size, int& written); diff --git a/src/grp/grp.cmdprocessor.cpp b/src/grp/grp.cmdprocessor.cpp new file mode 100644 index 000000000..708472bb2 --- /dev/null +++ b/src/grp/grp.cmdprocessor.cpp @@ -0,0 +1,45 @@ +/* + * Функции, подключаемые в движок процессора команд игрока + */ + +#include "grp.cmdprocessor.h" +#include "grp.requestmanager.h" + +extern GroupRoster& groupRoster; + +void do_grequest(CHAR_DATA *ch, char *argument, int, int){ + char subcmd[MAX_INPUT_LENGTH], target[MAX_INPUT_LENGTH]; + // гзаявка список + // гзаявка создать Верий - отправляет заявку в группу + // гзаявка отменить Верий - отменяет заявку в группу + + RequestManager rm = groupRoster.getRequestManager(); + two_arguments(argument, subcmd, target); + /*печать перечня заявок*/ + if (!*subcmd) { + send_to_char(ch, "Формат команды:\r\n"); + send_to_char(ch, "гзаявка список - для получения списка заявок\r\n"); + send_to_char(ch, "гзаявка создать Верий - отправляет заявку в группу лидера Верий\r\n"); + send_to_char(ch, "гзаявка отменить Верий - отменяет заявку в группу лидера Верий\r\n"); + return; + } + else if (isname(subcmd, "список list")) { + rm.printRequestList(ch); + return; + } + /*создание заявки*/ + else if (isname(subcmd, "создать отправить make send")) { + rm.makeRequest(ch, target); + return; + } + /*отзыв заявки*/ + else if (isname(subcmd, "отменить отозвать cancel revoke")){ + rm.revokeRequest(ch, target); + return; + } + else { + send_to_char("Уточните команду.\r\n", ch); + return; + } + +} diff --git a/src/grp/grp.cmdprocessor.h b/src/grp/grp.cmdprocessor.h new file mode 100644 index 000000000..37fe4cfb7 --- /dev/null +++ b/src/grp/grp.cmdprocessor.h @@ -0,0 +1,11 @@ +/* + * Функции, подключаемые в движок процессора команд игрока + */ +#ifndef BYLINS_GRP_CMDPROCESSOR_H +#define BYLINS_GRP_CMDPROCESSOR_H + +#include "chars/char.hpp" + +void do_grequest(CHAR_DATA *ch, char *argument, int, int); + +#endif //BYLINS_GRP_CMDPROCESSOR_H diff --git a/src/grp/grp.group.cpp b/src/grp/grp.group.cpp index d02ce2b29..4f61a8539 100644 --- a/src/grp/grp.group.cpp +++ b/src/grp/grp.group.cpp @@ -724,27 +724,12 @@ Group::Group(CHAR_DATA *leader, u_long uid){ this->_memberCap = IS_NPC(leader)? 255 : max_group_size(leader); this->_uid = uid; // заодно иничим остальные поля - this->addMember(leader); + GroupManager mgr = this->getMemberManager(); + mgr->addMember(leader); this->setLeader(leader); } -void Group::addMember(CHAR_DATA *member) { - if (IS_NPC(member)) - return; - if (member->personGroup == this) - return; - auto it = this->_memberList.find(member->get_uid()); - if (it == this->_memberList.end()) - this->_memberList.emplace(member->get_uid(), new char_info(member->get_uid(), - member, - member->get_pc_name()) ); - else { - sprintf(buf, "ROSTER: группа id=%lu, добавление персонажа с тем же uid", this->_uid); - mudlog(buf, BRF, LVL_IMMORT, SYSLOG, TRUE); - return; - } - member->personGroup = this; -} + void Group::removeMember(CHAR_DATA *member) { if (this->_currentMemberCount == 0) { @@ -776,6 +761,8 @@ bool Group::checkMember(int uid) { return false; } +MemberManager::MemberManager(const std::map &memberList) : _memberList(memberList) {} + u_long Group::getUid() const { return _uid; } @@ -894,7 +881,11 @@ void Group::processGroupCommands(CHAR_DATA *ch, char *argument) { //grp->listMembers(ch); break; case INVITE: - groupRoster.makeRequest(target); + if (this->isFull()){ + send_to_char(ch, "Командир, но в твоей группе больше нет места!\r\n"); + return; + } + groupRoster.makeInvite(target); break; case TAKE: //grp->approveRequest(target); diff --git a/src/grp/grp.group.h b/src/grp/grp.group.h index 76bc2d5b0..7536d852a 100644 --- a/src/grp/grp.group.h +++ b/src/grp/grp.group.h @@ -2,7 +2,7 @@ #define BYLINS_GRP_GROUP_H #include "chars/char.hpp" -#include "grp.roster.h" +#include "grp.membermanager.h" void change_leader(CHAR_DATA *ch, CHAR_DATA *vict); void do_group(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/); @@ -11,17 +11,11 @@ void do_ungroup(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/); void do_report(CHAR_DATA *ch, char* /*argument*/, int/* cmd*/, int/* subcmd*/); int max_group_size(CHAR_DATA *ch); +enum GRP_COMM {GRP_COMM_LEADER, GRP_COMM_ALL}; + class Group { private: - struct char_info { - int memberUID; - char_info(int memberUid, CHAR_DATA *member, const std::string &memberName) : memberUID(memberUid), - member(member), - memberName(memberName) {} - CHAR_DATA * member; - std::string memberName; - }; // ид группы в ростере u_long _uid = 0; // текущее количество игроков @@ -35,7 +29,9 @@ class Group { // ссылка на персонажа, АХТУНГ! Может меняться и быть невалидным CHAR_DATA* _leader; // состав группы, ключом UID персонажа. Дохлые/вышедшие проверяем отдельным методом - std::map _memberList; + MemberManager * _memberManager; +public: + MemberManager *getMemberManager() const {return _memberManager;} public: u_long getUid() const; u_short getCurrentMemberCount() const; @@ -43,29 +39,19 @@ class Group { CHAR_DATA *getLeader() const; void setLeader(CHAR_DATA *leader); int getMemberCap() const; - virtual ~Group(); - bool isFull(); - Group(CHAR_DATA *leader, u_long uid); - void addMember(CHAR_DATA *member); - void removeMember(CHAR_DATA *member); - bool checkMember(int uid); // проверки валидности персонажа - void printGroup(CHAR_DATA *requestor); void listMembers(CHAR_DATA *requestor); bool isActive(); // проверка, что в группе все персонажи онлайн // обработчик команд группы void processGroupCommands(CHAR_DATA *ch, char *argument); - // методы работы с контентом группы - bool approveRequest(char *applicant); - bool denyRequest(char *applicant); - bool expellMember(char *applicant); - bool promote(char *applicant); - bool leave(CHAR_DATA *ch); + bool promote(char *applicant); + void sendToGroup(GRP_COMM mode, const char *msg, ...); }; + #endif //BYLINS_GRP_GROUP_H diff --git a/src/grp/grp.groupmanager.cpp b/src/grp/grp.groupmanager.cpp new file mode 100644 index 000000000..31da892d5 --- /dev/null +++ b/src/grp/grp.groupmanager.cpp @@ -0,0 +1,48 @@ +// +// Created by ubuntu on 23.12.2020. +// + +#include "grp.groupmanager.h" + +Group* GroupManager::addGroup(CHAR_DATA * leader) { + ++this->_currentGroupIndex; + auto *group = new Group(leader, _currentGroupIndex); + this->_groupList.emplace(_currentGroupIndex, new GroupRosterRecord(group)); + return group; +} + +void GroupManager::removeGroup(u_long uid) { + auto grp = _groupList.find(uid); + if (grp != _groupList.end()) + { + delete grp->second->member; + _groupList.erase(uid); + } +} + +Group *GroupManager::findGroup(CHAR_DATA *ch) { + if (ch == nullptr || ch->purged()){ + sprintf(buf, "GroupManager::findGroup: вызов для пустого или пурженого персонажа\r\n"); + mudlog(buf, BRF, LVL_IMMORT, SYSLOG, TRUE); + return nullptr; + } + for (auto & it : this->_groupList){ + auto mgr = it.second->member->getMemberManager(); + if (mgr->findMember(ch->get_uid())) + return it.second->member; + } + return nullptr; +} + +Group *GroupManager::findGroup(char *leaderName) { + if (leaderName == nullptr){ + sprintf(buf, "GroupManager::findGroup: leaderName is null\r\n"); + mudlog(buf, BRF, LVL_IMMORT, SYSLOG, TRUE); + return nullptr; + } + for (auto & it : this->_groupList) { + if (str_cmp(it.second->member->getLeaderName().c_str(), leaderName)) + return it.second->member; + } + return nullptr; +} diff --git a/src/grp/grp.groupmanager.h b/src/grp/grp.groupmanager.h new file mode 100644 index 000000000..ad36ae17d --- /dev/null +++ b/src/grp/grp.groupmanager.h @@ -0,0 +1,26 @@ +// +// Created by ubuntu on 23.12.2020. +// + +#ifndef BYLINS_GRP_GROUPMANAGER_H +#define BYLINS_GRP_GROUPMANAGER_H + +class GroupRosterRecord { +public: + explicit GroupRosterRecord(Group * group); + Group *member; +}; + + +class GroupManager{ +private: + u_long _currentGroupIndex = 0; + std::map _groupList; +public: + Group* addGroup(CHAR_DATA* leader); + void removeGroup(u_long uid); + Group* findGroup(CHAR_DATA* ch); + Group* findGroup(char* leaderName); +}; + +#endif //BYLINS_GRP_GROUPMANAGER_H diff --git a/src/grp/grp.membermanager.cpp b/src/grp/grp.membermanager.cpp new file mode 100644 index 000000000..64278cb5c --- /dev/null +++ b/src/grp/grp.membermanager.cpp @@ -0,0 +1,27 @@ +// +// Created by ubuntu on 23.12.2020. +// + +#include "grp.membermanager.h" + +void MemberManager::addMember(CHAR_DATA *member) { + if (IS_NPC(member)) + return; + if (member->personGroup == this->_group) + return; + auto it = this->_memberList.find(member->get_uid()); + if (it == this->_memberList.end()) + this->_memberList.emplace(member->get_uid(), new char_info(member->get_uid(), + member, + member->get_pc_name()) ); + else { + sprintf(buf, "ROSTER: группа id=%lu, попытка повторного добавления персонажа с тем же uid", this->_group->getUid()); + mudlog(buf, BRF, LVL_IMMORT, SYSLOG, TRUE); + return; + } + member->personGroup = this->_group; +} + +bool MemberManager::restoreMember(CHAR_DATA *member) { + return false; +} diff --git a/src/grp/grp.membermanager.h b/src/grp/grp.membermanager.h new file mode 100644 index 000000000..e85074a67 --- /dev/null +++ b/src/grp/grp.membermanager.h @@ -0,0 +1,31 @@ +// +// Created by ubuntu on 23.12.2020. +// + +#ifndef BYLINS_GRP_MEMBERMANAGER_H +#define BYLINS_GRP_MEMBERMANAGER_H + +#include "chars/char.hpp" +#include "grp.roster.h" + +class MemberManager { +private: + struct char_info { + int memberUID; + char_info(int memberUid, CHAR_DATA *member, const std::string &memberName) : memberUID(memberUid), + member(member), + memberName(memberName) {} + CHAR_DATA * member; + std::string memberName; + }; + std::map _memberList; + Group * _group; +public: + MemberManager(const std::map &memberList); + void addMember(CHAR_DATA *member); + void removeMember(CHAR_DATA *member); + bool findMember(int uid); + bool restoreMember(CHAR_DATA *member); +}; + +#endif //BYLINS_GRP_MEMBERMANAGER_H diff --git a/src/grp/grp.request.cpp b/src/grp/grp.request.cpp new file mode 100644 index 000000000..29be4726f --- /dev/null +++ b/src/grp/grp.request.cpp @@ -0,0 +1,18 @@ +// +// Created by ubuntu on 23.12.2020. +// + +#include "grp.request.h" +#include "handler.h" + +Request::Request(CHAR_DATA *subject, Group *group, RQ_TYPE type) { + if (!subject || !group) + return; + _applicant = subject; + _applicantName = subject->get_pc_name(); + _applicantUID = subject->get_uid(); + _group = group; + _type = type; +} + + diff --git a/src/grp/grp.request.h b/src/grp/grp.request.h new file mode 100644 index 000000000..3f4d1f536 --- /dev/null +++ b/src/grp/grp.request.h @@ -0,0 +1,20 @@ +#ifndef BYLINS_GRP_REQUEST_H +#define BYLINS_GRP_REQUEST_H + +#include "chars/char.hpp" + +enum RQ_TYPE {RQ_GROUP, RQ_PERSON}; + +class Request { +public: + std::chrono::time_point _time; + CHAR_DATA *_applicant; + Group *_group; + std::string _applicantName; + int _applicantUID; + RQ_TYPE _type; + Request(CHAR_DATA* subject, Group* group, RQ_TYPE type); +}; + + +#endif //BYLINS_GRP_REQUEST_H diff --git a/src/grp/grp.requestmanager.cpp b/src/grp/grp.requestmanager.cpp new file mode 100644 index 000000000..bab2b38e4 --- /dev/null +++ b/src/grp/grp.requestmanager.cpp @@ -0,0 +1,151 @@ +// +// Created by ubuntu on 23.12.2020. +// + +#include "grp.requestmanager.h" +#include "handler.h" +#include "global.objects.hpp" + +extern GroupRoster& groupRoster; + +void RequestManager::makeInvite(CHAR_DATA *leader, char *targetPerson) { + if (AFF_FLAGGED(leader, EAffectFlag::AFF_CHARM)) + return; + if (AFF_FLAGGED(leader, EAffectFlag::AFF_SILENCE) || AFF_FLAGGED(leader, EAffectFlag::AFF_STRANGLED)) { + send_to_char(leader, "Вы немы, как рыба об лед.\r\n"); + return; + } + if (leader->personGroup->getLeader() != leader){ + send_to_char(leader, "Чтобы пригласить в группу, надо быть лидером.\r\n"); + return; + } + auto [res, vict] = this->tryMakeInvite(leader->personGroup, targetPerson); + switch (res) { + case INV_R_BUSY: + send_to_char(leader, "Извините, но указанный персонаж уже в группе.\r\n"); + return; + case INV_R_NO_PERSON: + send_to_char(leader, "Протри глаза, нет такого игрока!\r\n"); + return; + case INV_R_REFRESH: + send_to_char(leader, "Вы продлили приглашение.\r\n"); + return; + case INV_R_OK: + send_to_char(leader, "Вы направили приглашение на вступление в группу.\r\n"); + send_to_char(vict, "Лидер группы %s направил вам приглашение в группу.\r\n", leader->get_pc_name().c_str()); + return; + } +} + +std::tuple RequestManager::tryMakeInvite(Group *grp, char *member) { + CHAR_DATA *vict = get_player_vis(grp->getLeader(), member, FIND_CHAR_WORLD); + if (!vict) return std::make_tuple(INV_R_NO_PERSON, nullptr); + if (vict->personGroup != nullptr ) return std::make_tuple(INV_R::INV_R_BUSY, nullptr); + for (auto & it : this->_requestList){ + if (vict == it._applicant && grp == it._group){ + it._time = std::chrono::system_clock::now(); + return std::make_tuple(INV_R::INV_R_REFRESH, vict); + } + } + // не нашли, создаём + this->_requestList.emplace(_requestList.end(), Request(vict, grp, RQ_GROUP)); + return std::make_tuple(INV_R::INV_R_OK, vict); +} + +void RequestManager::printRequestList(CHAR_DATA *ch) { + bool notFound = true; + if (!ch || ch->purged()) + return; + send_to_char(ch, "Активные заявки и приглашения:\r\n"); + for (const auto & it : this->_requestList){ + if (ch->get_uid() == it._applicantUID){ + sprintf(smallBuf, "%s%s\r\n", + it._type== RQ_TYPE::RQ_PERSON? "Заявка в группу лидера ": "Приглашение от лидера группы ", + it._group->getLeaderName().c_str()); + } + if (ch->personGroup != nullptr && ch->personGroup->getUid() == it._group->getUid()) { + if (it._type== RQ_TYPE::RQ_GROUP) { + sprintf(smallBuf, "Приглашение игроку: %s\r\n", it._applicantName.c_str() ); + } + else { + sprintf(smallBuf, "Заявка от игрока: %s\r\n", it._applicantName.c_str() ); + } + } + send_to_char(ch, "%s", smallBuf); + notFound = false; + } + if (notFound) + send_to_char(ch, "Заявок нет.\r\n"); +} + +void RequestManager::makeRequest(CHAR_DATA *author, char* target) { + if (AFF_FLAGGED(author, EAffectFlag::AFF_CHARM)) + return; + + if (AFF_FLAGGED(author, EAffectFlag::AFF_SILENCE) || AFF_FLAGGED(author, EAffectFlag::AFF_STRANGLED)) { + send_to_char(author, "Вы немы, как рыба об лед.\r\n"); + return; + } + if (author->personGroup && author->personGroup->getLeader() == author) { + send_to_char(author, "Негоже лидеру группы напрашиваться в чужую!\r\n"); + return; + } + + if (!*target) { + send_to_char("И кому же вы хотите напроситься в группу?.\r\n", author); + return; + } + RequestManager* mgr = groupRoster.getRequestManager(); + auto [res, grp] = mgr->tryAddRequest(author, target); + switch (res) { + case RQ_R::RQ_R_OVERFLOW: + send_to_char("В группе нет свободных мест, свяжитесь с лидером.\r\n", author); + return; + case RQ_R::RQ_R_NO_GROUP: + send_to_char("Протри глаза, не такой группы!\r\n", author); + return; + case RQ_R::RQ_R_OK: + send_to_char("Заявка на вступление в группу отправлена.\r\n", author); + break; + case RQ_R::RQ_REFRESH: + send_to_char("Заявка успешно продлена.\r\n", author); + break; + } + grp->sendToGroup(GRP_COMM_LEADER, "Поступила заявка на вступление в группу. \r\n"); +} + +void RequestManager::revokeRequest(CHAR_DATA *ch, char* target) { + for (auto it = this->_requestList.begin(); it != this->_requestList.end(); ++it) + if (it->_applicant == ch && str_cmp(target, it->_group->getLeaderName().c_str())){ + send_to_char(ch, "заявка найдена и удалена\r\n"); + _requestList.erase(it); + return; + } + send_to_char(ch, "заявка не найдена\r\n"); +} + +std::tuple RequestManager::tryAddRequest(CHAR_DATA *author, char *targetGroup) { + if (!author || author->purged()){ + sprintf(buf, "RequestManager::tryAddRequest: author is null or purged\r\n"); + mudlog(buf, BRF, LVL_IMMORT, SYSLOG, TRUE); + return std::make_tuple(RQ_R::RQ_R_NO_GROUP, nullptr); + } + if (targetGroup == nullptr){ + return std::make_tuple(RQ_R::RQ_R_NO_GROUP, nullptr); + } + Group* grp = groupRoster.getGroupManager()->findGroup(targetGroup) ; + if (grp == nullptr) + return std::make_tuple(RQ_R::RQ_R_NO_GROUP, nullptr); + if (grp->isFull()) + return std::make_tuple(RQ_R::RQ_R_OVERFLOW, nullptr); + + for (auto & it : this->_requestList){ + if (author == it._applicant && grp == it._group){ + it._time = std::chrono::system_clock::now(); + return std::make_tuple(RQ_REFRESH, grp); + } + } + // не нашли, создаём + this->_requestList.emplace(_requestList.end(), Request(author, grp, RQ_PERSON)); + return std::make_tuple(RQ_R::RQ_R_OK, grp); +} diff --git a/src/grp/grp.requestmanager.h b/src/grp/grp.requestmanager.h new file mode 100644 index 000000000..d9b5e0d83 --- /dev/null +++ b/src/grp/grp.requestmanager.h @@ -0,0 +1,28 @@ +// +// Created by ubuntu on 23.12.2020. +// + +#ifndef BYLINS_GRP_REQUESTMANAGER_H +#define BYLINS_GRP_REQUESTMANAGER_H + +#include "grp.request.h" +#include "chars/char.hpp" + +enum RQ_R {RQ_R_OK, RQ_R_NO_GROUP, RQ_R_OVERFLOW, RQ_REFRESH}; +enum INV_R {INV_R_OK, INV_R_NO_PERSON, INV_R_BUSY, INV_R_REFRESH}; + +class RequestManager{ +private: + std::list _requestList; + std::tuple tryMakeInvite(Group* grp, char* member); + std::tuple tryAddRequest(CHAR_DATA* author, char* targetGroup); +public: + RequestManager(); +// методы работы с заявками + void printRequestList(CHAR_DATA* ch); + void makeInvite(CHAR_DATA* leader, char* targetPerson); + void makeRequest(CHAR_DATA* author, char* targetGroup); + void revokeRequest(CHAR_DATA* author, char* targetGroup); +}; + +#endif //BYLINS_GRP_REQUESTMANAGER_H diff --git a/src/grp/grp.roster.cpp b/src/grp/grp.roster.cpp index 045024c2b..959c2cc91 100644 --- a/src/grp/grp.roster.cpp +++ b/src/grp/grp.roster.cpp @@ -7,178 +7,24 @@ #include "handler.h" GroupRoster& groupRoster = GlobalObjects::groupRoster(); -GroupRoster::GroupRoster() { - this->_currentGroupIndex = 0; -} - -Request::Request(CHAR_DATA *author, Group *group) { - if (!author || !group) - return; - _applicant = author; - _applicantName = author->get_pc_name(); - _applicantUID = author->get_uid(); - _group = group; - if (author == group->getLeader()) - _type = RQ_GROUP; - else - _type = RQ_PERSON; -} - -Group* GroupRoster::addGroup(CHAR_DATA * leader) { - ++this->_currentGroupIndex; - auto *group = new Group(leader, _currentGroupIndex); - this->_groupList.emplace(_currentGroupIndex, new GroupRosterRecord(group)); - return group; -} - -void GroupRoster::removeGroup(u_long uid) { - auto grp = _groupList.find(uid); - if (grp != _groupList.end()) - { - delete grp->second->member; - _groupList.erase(uid); - } -} - -void GroupRoster::recalculateRoster() { +GroupRoster::GroupRoster() { + this->_requestManager = new RequestManager(); + this->_groupManager = new GroupManager(); } void GroupRoster::restorePlayerGroup(CHAR_DATA *ch) { - for (auto & it : this->_groupList){ - if(it.second->member->checkMember(ch->get_uid())){ - ch->personGroup = it.second->member; - ch->send_to_TC(true, false, true, "Restore group: %d", ch->personGroup->getUid()); - break; - } - } -} - - -GroupRosterRecord::GroupRosterRecord(Group *group) { - this->member = group; -} - -void GroupRoster::printRequestList(CHAR_DATA *ch) { - bool notFound = true; - if (!ch || ch->purged()) - return; - send_to_char(ch, "Активные заявки и приглашения:\r\n"); - for (const auto & it : this->_requestList){ - if (ch->get_uid() == it._applicantUID) - sprintf(smallBuf, "%s%s\r\n", - it._type== RQ_TYPE::RQ_PERSON? "Заявка в группу лидера ": "Приглашение от лидера группы ", - it._group->getLeaderName().c_str()); - if (ch->personGroup != nullptr && ch->personGroup->getUid() == it._group->getUid()) - sprintf(smallBuf, "Приглашение игроку: %s\r\n", it._applicantName.c_str() ); - send_to_char(ch, "%s", smallBuf); - notFound = false; - } - if (notFound) - send_to_char(ch, "Заявок нет.\r\n"); -} - -void GroupRoster::makeRequest(CHAR_DATA *author, char* target) { - if (AFF_FLAGGED(author, EAffectFlag::AFF_CHARM)) - return; - - if (AFF_FLAGGED(author, EAffectFlag::AFF_SILENCE) || AFF_FLAGGED(author, EAffectFlag::AFF_STRANGLED)) { - send_to_char(author, "Вы немы, как рыба об лед.\r\n"); - return; - } - if (author->personGroup && author->personGroup->getLeader() == author) { - send_to_char(author, "Негоже лидеру группы напрашиваться в чужую!\r\n"); + Group* grp = this->_groupManager->findGroup(ch); + if (grp == nullptr) return; - } - - if (!*target) { - send_to_char("И кому же вы хотите напроситься в группу?.\r\n", author); + if (!grp->getMemberManager()->restoreMember(ch)) return; - } - - switch (groupRoster.tryAddRequest(author, target)) { - case OK_SUCCESS: - send_to_char("Заявка на вступление в группу отправлена.\r\n", author); - return; - case GROUP_OVERFLOW: - send_to_char("В группе нет свободных мест, свяжитесь с лидером.\r\n", author); - return; - case NO_GROUP: - send_to_char("Протри глаза, не такой группы!\r\n", author); - return; - case RQ_REFRESH: - send_to_char("Заявка успешно продлена.\r\n", author); - } + grp->sendToGroup(GRP_COMM_ALL, "Игрок %s заново присоединился к группе.\r\n", ch->get_pc_name().c_str()); + ch->send_to_TC(true, false, true, "Membership in group: %d restored\r\n", ch->personGroup->getUid()); } -void GroupRoster::revokeRequest(CHAR_DATA *ch, char* target) { - if (ch->personGroup != nullptr && ch->personGroup->getLeader() == ch) { - // вызов от имени лидера, отменяем заявку лидера на присоединение к группе - send_to_char(ch, "вызов от имени лидера, отменяем заявку лидера на присоединение к группе\r\n"); - return; - } - for (auto it = this->_requestList.begin(); it != this->_requestList.end(); ++it) - if (it->_applicant == ch && str_cmp(target, it->_group->getLeaderName().c_str())){ - send_to_char(ch, "заявка найдена и удалена\r\n"); - _requestList.erase(it); - return; - } - send_to_char(ch, "заявка не найдена\r\n"); -} - -void GroupRoster::makeRequest(char *targetPerson) { - -} - -RQ_RESULT GroupRoster::tryAddRequest(CHAR_DATA *author, char *targetGroup) { - Group* grp = nullptr; - for (auto & it : this->_groupList) { - if (str_cmp(it.second->member->getLeaderName().c_str(), targetGroup)) - grp = it.second->member; - } - if (grp == nullptr) - return RQ_RESULT::NO_GROUP; - if (grp->isFull()) - return RQ_RESULT::GROUP_OVERFLOW; - for (auto & it : this->_requestList){ - if (author == it._applicant && grp == it._group){ - it.time = std::chrono::system_clock::now(); - return RQ_REFRESH; - } - } - // не нашли, создаём - this->_requestList.emplace(_requestList.end(), Request(author, grp)); - return RQ_RESULT::OK_SUCCESS; +GroupRosterRecord::GroupRosterRecord(Group *group) { + this->member = group; } - -void do_grequest(CHAR_DATA *ch, char *argument, int, int){ - char subcmd[MAX_INPUT_LENGTH], target[MAX_INPUT_LENGTH]; - enum SUBCMD {LIST, MAKE, CANCEL}; - RQ_RESULT res; - // гзаявка - список - // гзаявка создать Верий - отправляет заявку в группу - // гзаявка отменить Верий - отменяет заявку в группу - - two_arguments(argument, subcmd, target); - - if (!*subcmd || isname(subcmd, "список list")) - { - groupRoster.printRequestList(ch); - return; - } - else if (isname(subcmd, "создать отправить make send")) { - groupRoster.makeRequest(ch, target); - return; - } - else if (isname(subcmd, "отменить отозвать cancel revoke")){ - groupRoster.revokeRequest(ch, target); - return; - } - else { - send_to_char("Уточните команду.\r\n", ch); - return; - } - -} diff --git a/src/grp/grp.roster.h b/src/grp/grp.roster.h index f4fb2292c..9cf70378f 100644 --- a/src/grp/grp.roster.h +++ b/src/grp/grp.roster.h @@ -7,50 +7,27 @@ #include "chars/char.hpp" #include "grp.group.h" +#include "grp.requestmanager.h" +#include "grp.groupmanager.h" -enum RQ_RESULT {OK_SUCCESS, NO_GROUP, GROUP_OVERFLOW, RQ_REFRESH}; -enum RQ_TYPE {RQ_GROUP, RQ_PERSON}; void do_grequest(CHAR_DATA *ch, char *argument, int, int); -class GroupRosterRecord { -public: - explicit GroupRosterRecord(Group * group); - Group *member; -}; +class GroupRoster { +private: // fields -class Request { -public: - CHAR_DATA *_applicant; - std::string _applicantName; - int _applicantUID; - Group *_group; - RQ_TYPE _type; - Request(CHAR_DATA* author, Group* group); - std::chrono::time_point time; -}; +private: // methods -class GroupRoster { +// properties private: - u_long _currentGroupIndex = 0; - std::map _groupList; - std::list _requestList; - RQ_RESULT tryAddRequest(CHAR_DATA* author, char* targetGroup); + RequestManager* _requestManager; + GroupManager* _groupManager; +public: + GroupManager* getGroupManager() const {return _groupManager;} + RequestManager* getRequestManager() const {return _requestManager;} public: GroupRoster(); - u_long getSize() {return _groupList.size();} - void restorePlayerGroup(CHAR_DATA* ch); - void recalculateRoster(); - - // манипуляция группой - Group* addGroup(CHAR_DATA* leader); - void removeGroup(u_long uid); - - // методы работы с заявками - void printRequestList(CHAR_DATA* ch); - void makeRequest(char* targetPerson); - void makeRequest(CHAR_DATA* author, char* targetGroup); - void revokeRequest(CHAR_DATA* author, char* targetGroup); + void restorePlayerGroup(CHAR_DATA* ch); // возвращает игрока в группу после смерти }; #endif //BYLINS_GRP_ROSTER_H From 45e7eac28b85e9f6383c240dc841070a69dc25b8 Mon Sep 17 00:00:00 2001 From: bikbai Date: Fri, 25 Dec 2020 17:26:47 +0300 Subject: [PATCH 05/40] =?UTF-8?q?=D0=BF=D1=80=D0=BE=D0=B4=D0=BE=D0=BB?= =?UTF-8?q?=D0=B6=D0=B0=D0=B5=D0=BC=20=D0=BC=D1=83=D1=87=D0=B0=D1=82=D1=8C?= =?UTF-8?q?=D1=81=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 169 +++---- src/act.informative.cpp | 50 +- src/act.item.cpp | 2 +- src/chars/char.hpp | 3 +- src/chars/char_player.hpp | 2 +- src/chars/player_i.hpp | 2 +- src/cmd/cmd.generic.h | 62 +++ src/cmd/hire.cpp | 2 +- src/cmd/hire.h | 15 - src/cmd/mercenary.cpp | 2 +- src/cmd/mercenary.h | 18 - src/cmd/order.cpp | 2 +- src/cmd/order.h | 8 - src/cmd/quit.cpp | 12 +- src/cmd/quit.h | 12 - src/cmd/retreat.cpp | 2 +- src/cmd/retreat.h | 8 - src/cmd/telegram.cpp | 2 +- src/cmd/telegram.h | 30 -- src/cmd/track.cpp | 2 +- src/cmd/track.h | 12 - src/cmd/whoami.cpp | 46 ++ src/fightsystem/mobact.cpp | 2 +- src/global.objects.cpp | 1 - src/global.objects.hpp | 2 +- src/grp/grp.cmdprocessor.cpp | 720 ++++++++++++++++++++++++++- src/grp/grp.cmdprocessor.h | 11 - src/grp/grp.group.cpp | 885 +-------------------------------- src/grp/grp.group.h | 57 --- src/grp/grp.groupmanager.cpp | 39 +- src/grp/grp.groupmanager.h | 26 - src/grp/grp.main.h | 145 ++++++ src/grp/grp.membermanager.cpp | 75 ++- src/grp/grp.membermanager.h | 31 -- src/grp/grp.request.cpp | 5 +- src/grp/grp.request.h | 20 - src/grp/grp.requestmanager.cpp | 12 +- src/grp/grp.requestmanager.h | 28 -- src/grp/grp.roster.cpp | 127 ++++- src/grp/grp.roster.h | 33 -- src/interpreter.cpp | 13 +- src/magic.cpp | 2 +- src/obj.hpp | 3 +- src/spec_assign.cpp | 2 +- src/spec_procs.cpp | 2 +- src/spells.cpp | 2 +- 46 files changed, 1321 insertions(+), 1385 deletions(-) create mode 100644 src/cmd/cmd.generic.h delete mode 100644 src/cmd/hire.h delete mode 100644 src/cmd/mercenary.h delete mode 100644 src/cmd/order.h delete mode 100644 src/cmd/quit.h delete mode 100644 src/cmd/retreat.h delete mode 100644 src/cmd/telegram.h delete mode 100644 src/cmd/track.h create mode 100644 src/cmd/whoami.cpp delete mode 100644 src/grp/grp.cmdprocessor.h delete mode 100644 src/grp/grp.group.h delete mode 100644 src/grp/grp.groupmanager.h create mode 100644 src/grp/grp.main.h delete mode 100644 src/grp/grp.membermanager.h delete mode 100644 src/grp/grp.request.h delete mode 100644 src/grp/grp.requestmanager.h delete mode 100644 src/grp/grp.roster.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 07bab450f..154fbcc94 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,51 +9,62 @@ endif () set_property(GLOBAL PROPERTY USE_FOLDERS ON) set(SOURCES - src/blocking.queue.cpp - src/influxdb.cpp - src/heartbeat.cpp - src/heartbeat.commands.cpp src/ability.rollsystem.cpp - src/chars/accounts.cpp - src/action.targeting.cpp src/act.comm.cpp src/act.informative.cpp src/act.item.cpp src/act.movement.cpp src/act.other.cpp src/act.social.cpp - src/cmd.imm/act.wizard.cpp + src/action.targeting.cpp src/AffectHandler.cpp src/alias.cpp src/auction.cpp src/backtrace.cpp src/ban.cpp src/birth_places.cpp + src/blocking.queue.cpp src/boards.changelog.loaders.cpp src/boards.constants.cpp src/boards.cpp src/boards.formatters.cpp src/boards.types.cpp - src/bonus.cpp src/bonus.command.parser.cpp + src/bonus.cpp src/boot.constants.cpp src/boot.data.files.cpp src/boot.index.cpp src/cache.cpp src/celebrates.cpp + src/chars/accounts.cpp src/chars/char.cpp src/chars/char_player.cpp + src/chars/mount.cpp + src/chars/player_races.cpp + src/chars/world.characters.cpp src/class.cpp + src/cmd.imm/act.wizard.cpp + src/cmd/hire.cpp + src/cmd/mercenary.cpp + src/cmd/order.cpp + src/cmd/quit.cpp + src/cmd/retreat.cpp + src/cmd/telegram.cpp + src/cmd/track.cpp + src/cmd/whoami.cpp src/color.cpp src/comm.cpp src/command.shutdown.cpp src/commands.cpp + src/compact.trie.cpp src/config.cpp src/constants.cpp + src/core/affect_data.cpp src/corpse.cpp src/daily_quest.cpp src/db.cpp src/deathtrap.cpp + src/debug.utils.cpp src/depot.cpp src/description.cpp src/dg_comm.cpp @@ -74,27 +85,47 @@ set(SOURCES src/ext_money.cpp src/external.trigger.cpp src/features.cpp - src/fightsystem/fight.cpp - src/fightsystem/fight_hit.cpp - src/fightsystem/fight.penalties.cpp - src/fightsystem/fight_stuff.cpp + src/fightsystem/assist.cpp + src/fightsystem/common.cpp + src/fightsystem/fight.cpp + src/fightsystem/fight.penalties.cpp + src/fightsystem/fight_hit.cpp + src/fightsystem/fight_stuff.cpp + src/fightsystem/mobact.cpp + src/fightsystem/pk.cpp + src/fightsystem/start.fight.cpp src/file_crc.cpp + src/find.obj.id.by.vnum.cpp src/genchar.cpp - src/glory_const.cpp + src/global.objects.cpp src/glory.cpp + src/glory_const.cpp src/glory_misc.cpp src/graph.cpp + src/grp/follow.cpp + src/grp/grp.cmdprocessor.cpp + src/grp/grp.group.cpp + src/grp/grp.groupmanager.cpp + src/grp/grp.membermanager.cpp + src/grp/grp.request.cpp + src/grp/grp.requestmanager.cpp + src/grp/grp.roster.cpp src/handler.cpp + src/heartbeat.commands.cpp + src/heartbeat.cpp src/help.cpp src/house.cpp src/house_exp.cpp + src/id.cpp + src/id.cpp src/ignores.cpp src/ignores.loader.cpp src/im.cpp + src/influxdb.cpp src/interpreter.cpp src/item.creation.cpp - src/limits.cpp src/levenshtein.cpp + src/limits.cpp src/liquid.cpp src/logger.cpp src/magic.cpp @@ -102,34 +133,33 @@ set(SOURCES src/map.cpp src/meat.maker.cpp src/medit.cpp - src/fightsystem/mobact.cpp - src/mobmax.cpp src/mob_stat.cpp + src/mobmax.cpp src/modify.cpp src/morph.cpp - src/named_stuff.cpp src/name_list.cpp + src/named_stuff.cpp src/names.cpp src/noob.cpp src/obj.cpp + src/obj.spell.cpp src/obj_enchant.cpp - src/objsave.cpp - src/objsave_ext.cpp src/obj_sets.cpp src/obj_sets_olc.cpp - src/obj.spell.cpp + src/object.prototypes.cpp + src/objsave.cpp + src/objsave_ext.cpp src/oedit.cpp src/olc.cpp src/parcel.cpp src/parse.cpp src/password.cpp - src/fightsystem/pk.cpp - src/chars/player_races.cpp src/poison.cpp src/privilege.cpp src/pugixml.cpp src/quest.cpp src/quested.cpp + src/radix.trie.cpp src/random.cpp src/redit.cpp src/remember.cpp @@ -137,13 +167,39 @@ set(SOURCES src/room.cpp src/sets_drop.cpp src/shop_ext.cpp + src/shops.implementation.cpp + src/shops.implementation.cpp src/shutdown.parameters.cpp src/skills.cpp + src/skills/backstab.cpp + src/skills/bash.cpp + src/skills/block.cpp + src/skills/chopoff.cpp + src/skills/disarm.cpp + src/skills/expendientcut.cpp + src/skills/flee.cpp + src/skills/ironwind.cpp + src/skills/kick.cpp + src/skills/manadrain.cpp + src/skills/mighthit.cpp + src/skills/parry.cpp + src/skills/protect.cpp + src/skills/resque.cpp + src/skills/strangle.cpp + src/skills/stun.cpp + src/skills/stupor.cpp + src/skills/styles.cpp + src/skills/throw.cpp + src/skills/townportal.cpp + src/skills/turnundead.cpp src/spam.cpp src/spec_assign.cpp src/spec_procs.cpp + src/speedwalks.cpp src/spell_parser.cpp src/spells.cpp + src/stigmas.cpp + src/strengthening.cpp src/structs.cpp src/structs.cpp src/stuff.cpp @@ -152,74 +208,21 @@ set(SOURCES src/title.cpp src/top.cpp src/utils.cpp + src/utils.string.cpp src/version.cpp src/weather.cpp - src/zedit.cpp - src/utils.string.cpp src/world.objects.cpp - src/object.prototypes.cpp - src/id.cpp - src/find.obj.id.by.vnum.cpp - src/global.objects.cpp - src/shops.implementation.cpp - src/chars/world.characters.cpp - src/debug.utils.cpp - src/speedwalks.cpp - src/shops.implementation.cpp - src/radix.trie.cpp - src/compact.trie.cpp - src/id.cpp - src/stigmas.cpp + src/zedit.cpp src/zone.table.cpp - src/strengthening.cpp - src/skills/backstab.cpp - src/skills/flee.cpp - src/skills/bash.cpp - src/skills/stun.cpp - src/skills/resque.cpp - src/skills/kick.cpp - src/skills/strangle.cpp - src/skills/chopoff.cpp - src/skills/disarm.cpp - src/skills/stupor.cpp - src/skills/ironwind.cpp - src/skills/throw.cpp - src/skills/mighthit.cpp - src/skills/block.cpp - src/skills/parry.cpp - src/skills/protect.cpp - src/skills/expendientcut.cpp - src/skills/turnundead.cpp - src/skills/townportal.cpp - src/skills/manadrain.cpp - src/chars/mount.cpp - src/fightsystem/assist.cpp - src/fightsystem/start.fight.cpp - src/skills/styles.cpp - src/fightsystem/common.cpp - src/cmd/retreat.cpp - src/cmd/order.cpp - src/cmd/mercenary.cpp - src/core/affect_data.cpp - src/cmd/track.cpp - src/cmd/telegram.cpp - src/cmd/hire.cpp - src/grp/grp.group.cpp - src/grp/grp.roster.cpp ) set(HEADERS - src/cmd/telegram.h - src/cmd/hire.h - src/cmd/telegram.h + src/grp/follow.h + src/grp/grp.main.h src/core/affect_data.h - src/cmd/track.h src/fightsystem/common.h src/fightsystem/assist.h src/fightsystem/start.fight.h - src/cmd/retreat.h - src/cmd/order.h - src/cmd/mercenary.h src/skills/styles.h src/skills/manadrain.h src/skills/townportal.h @@ -394,9 +397,7 @@ set(HEADERS src/skills/chopoff.h src/skills/stupor.h src/graph.h - src/grp/grp.group.h - src/grp/grp.roster.h - ) + src/cmd/cmd.generic.h) set(CONFIGURATION_FILES lib.template/misc/configuration.xml) # Build types @@ -573,7 +574,7 @@ set(MISC_FILES source_group("Misc" FILES ${MISC_FILES}) -set(CIRCLE_FILES ${SOURCES} ${HEADERS} readme.markdown CONTRIBUTING.md ${CRAFT_FILES} ${MISC_FILES} src/grp/follow.cpp src/grp/follow.h src/cmd/quit.cpp src/cmd/quit.h src/grp/grp.request.cpp src/grp/grp.request.h src/grp/grp.cmdprocessor.cpp src/grp/grp.cmdprocessor.h src/grp/grp.requestmanager.cpp src/grp/grp.requestmanager.h src/grp/grp.groupmanager.cpp src/grp/grp.groupmanager.h src/grp/grp.membermanager.cpp src/grp/grp.membermanager.h) +set(CIRCLE_FILES ${SOURCES} ${HEADERS} readme.markdown CONTRIBUTING.md ${CRAFT_FILES} ${MISC_FILES} ) # Sort source and header files. Just to convenience. list(SORT CIRCLE_FILES) @@ -754,7 +755,7 @@ endif () if (CMAKE_HOST_UNIX) if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang") set(CMAKE_CXX_FLAGS_RELEASE "-ggdb3 -O0 -Wall -Wextra") - set(CMAKE_CXX_FLAGS_DEBUG "-ggdb3 -O0 -Wall -Wextra -D_GLIBCXX_DEBUG -D_GLIBXX_DEBUG_PEDANTIC ${ASAN_FLAGS} ${DEBUG_CRYPT}") + set(CMAKE_CXX_FLAGS_DEBUG "-ggdb3 -O0 -Wall -Wextra -D_GLIBCXX_DEBUG -D_GLIBXX_DEBUG_PEDANTIC ${ASAN_FLAGS} ${DEBUG_CRYPT} ${TESTBUILD_DEFINITIONS} -DLOG_AUTOFLUSH") set(CMAKE_CXX_FLAGS_TEST " -Og -Wall -Wextra ${TESTBUILD_DEFINITIONS} -DLOG_AUTOFLUSH") set(CMAKE_CXX_FLAGS_FASTTEST " -Ofast -Wall -Wextra ${TESTBUILD_DEFINITIONS}") diff --git a/src/act.informative.cpp b/src/act.informative.cpp index 7c4b0de77..4534d3177 100644 --- a/src/act.informative.cpp +++ b/src/act.informative.cpp @@ -61,7 +61,7 @@ #include "sysdep.h" #include "bonus.h" #include "conf.h" -#include "grp/grp.group.h" +#include "grp/grp.main.h" #include #include @@ -5467,49 +5467,6 @@ void do_users(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) page_string(ch->desc, line, TRUE); } -void sendWhoami(CHAR_DATA *ch) { - sprintf(buf, "Персонаж : %s\r\n", GET_NAME(ch)); - sprintf(buf + strlen(buf), - "Падежи : &W%s&n/&W%s&n/&W%s&n/&W%s&n/&W%s&n/&W%s&n\r\n", - ch->get_name().c_str(), GET_PAD(ch, 1), GET_PAD(ch, 2), - GET_PAD(ch, 3), GET_PAD(ch, 4), GET_PAD(ch, 5)); - - sprintf(buf + strlen(buf), "Ваш e-mail : &S%s&s\r\n", GET_EMAIL(ch)); - time_t birt = ch->player_data.time.birth; - sprintf(buf + strlen(buf), "Дата вашего рождения : %s\r\n", rustime(localtime(&birt))); - sprintf(buf + strlen(buf), "Ваш IP-адрес : %s\r\n", ch->desc ? ch->desc->host : "Unknown"); - send_to_char(buf, ch); - if (!NAME_GOD(ch)) - { - sprintf(buf, "Имя никем не одобрено!\r\n"); - send_to_char(buf, ch); - } - else - { - const int god_level = NAME_GOD(ch) > 1000 ? NAME_GOD(ch) - 1000 : NAME_GOD(ch); - sprintf(buf1, "%s", get_name_by_id(NAME_ID_GOD(ch))); - *buf1 = UPPER(*buf1); - - static const char *by_rank_god = "Богом"; - static const char *by_rank_privileged = "привилегированным игроком"; - const char * by_rank = god_level < LVL_IMMORT ? by_rank_privileged : by_rank_god; - - if (NAME_GOD(ch) < 1000) - sprintf(buf, "&RИмя запрещено %s %s&n\r\n", by_rank, buf1); - else - sprintf(buf, "&WИмя одобрено %s %s&n\r\n", by_rank, buf1); - send_to_char(buf, ch); - } - sprintf(buf, "Перевоплощений: %d\r\n", GET_REMORT(ch)); - send_to_char(buf, ch); - Clan::CheckPkList(ch); - if (ch->player_specials->saved.telegram_id != 0) { //тут прямое обращение, ибо базовый класс, а не наследник - send_to_char(ch, "Подключен Телеграм, chat_id: %lu\r\n", ch->player_specials->saved.telegram_id); - } - if (ch->personGroup) - send_to_char(ch, "Стоит в группе #%d лидера %s\r\n", ch->personGroup->getUid(), ch->personGroup->getLeaderName().c_str()); -} - // Generic page_string function for displaying text void do_gen_ps(CHAR_DATA *ch, char* /*argument*/, int/* cmd*/, int subcmd) { @@ -5543,11 +5500,6 @@ void do_gen_ps(CHAR_DATA *ch, char* /*argument*/, int/* cmd*/, int subcmd) case SCMD_VERSION: show_code_date(ch); break; - case SCMD_WHOAMI: - { - sendWhoami(ch); - break; - } default: log("SYSERR: Unhandled case in do_gen_ps. (%d)", subcmd); return; diff --git a/src/act.item.cpp b/src/act.item.cpp index 149b6d38b..b6def264a 100644 --- a/src/act.item.cpp +++ b/src/act.item.cpp @@ -12,7 +12,7 @@ * $Revision$ * ************************************************************************ */ -#include "cmd/hire.h" +#include "cmd/cmd.generic.h" #include "world.objects.hpp" #include "object.prototypes.hpp" #include "logger.hpp" diff --git a/src/chars/char.hpp b/src/chars/char.hpp index e5e0a3021..567313bb6 100644 --- a/src/chars/char.hpp +++ b/src/chars/char.hpp @@ -371,6 +371,7 @@ class CHAR_DATA : public ProtectedCharacterData // новое public: using ptr_t = CHAR_DATA*; + using grp_ptr = std::shared_ptr; using shared_ptr = std::shared_ptr; using char_affects_list_t = std::list::shared_ptr>; using morphs_list_t = std::list; @@ -836,7 +837,7 @@ class CHAR_DATA : public ProtectedCharacterData bool isHorsePrevents(); void dismount(); public: - Group* personGroup; + grp_ptr personGroup; }; inline const player_special_data::ignores_t& CHAR_DATA::get_ignores() const diff --git a/src/chars/char_player.hpp b/src/chars/char_player.hpp index 036b03874..e8c9d9c9f 100644 --- a/src/chars/char_player.hpp +++ b/src/chars/char_player.hpp @@ -18,7 +18,7 @@ #include "boards.types.hpp" #include "quest.hpp" #include "stigmas.hpp" -#include "cmd/mercenary.h" +#include "cmd/cmd.generic.h" #include #include diff --git a/src/chars/player_i.hpp b/src/chars/player_i.hpp index 41906670e..9b5821053 100644 --- a/src/chars/player_i.hpp +++ b/src/chars/player_i.hpp @@ -11,7 +11,7 @@ #include "sysdep.h" #include #include -#include +#include "cmd/cmd.generic.h" struct MERCDATA; diff --git a/src/cmd/cmd.generic.h b/src/cmd/cmd.generic.h new file mode 100644 index 000000000..a484a1df9 --- /dev/null +++ b/src/cmd/cmd.generic.h @@ -0,0 +1,62 @@ +#ifndef BYLINS_CMD_GENERIC_H +#define BYLINS_CMD_GENERIC_H + +#include "chars/char.hpp" +#include "skills.h" + +#include + +#define MAXPRICE 9999999 + +void do_findhelpee(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/); +void do_freehelpee(CHAR_DATA* ch, char* /* argument*/, int/* cmd*/, int/* subcmd*/); +int get_reformed_charmice_hp(CHAR_DATA * ch, CHAR_DATA * victim, int spellnum); + +int mercenary(CHAR_DATA *ch, void* /*me*/, int cmd, char* argument); + +struct MERCDATA +{ + int CharmedCount; // кол-во раз почармлено + int spentGold; // если купец - сколько потрачено кун + int deathCount; // кол-во раз, когда чармис сдох + int currRemortAvail; // доступен на текущем реморте + int isFavorite; // моб показывается в сокращенном списке +}; + +void do_order(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/); +void do_quit(CHAR_DATA *ch, char *argument, int/* cmd*/, int subcmd); +void do_retreat(CHAR_DATA *ch, char* /*argument*/, int/* cmd*/, int/* subcmd*/); + +#if defined(HAVE_TG) +#include +#include +#endif +class CHAR_DATA; + +void do_telegram(CHAR_DATA *ch, char *argument, int, int); + +class TelegramBot { +private: +#if defined(HAVE_TG) + CURL *curl; +#endif + const std::string msgStr = "chat_id=358708535&text="; + const std::string chatIdParam = "chat_id="; + const std::string textParam = "&text="; + const std::string uri = "https://api.telegram.org/bot1330963555:AAHvh-gXBRxJHVKOmjsl8E73TJr0cO2eC50/sendMessage"; + const unsigned long debugChatId = 358708535; +public: + TelegramBot(); + ~TelegramBot(); + bool sendMessage(unsigned long chatId, const std::string& msg); +}; + + +#define CALC_TRACK(ch,vict) (calculate_skill(ch,SKILL_TRACK, 0)) + +int go_track(CHAR_DATA * ch, CHAR_DATA * victim, const ESkill skill_no); +void do_track(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/); +void do_hidetrack(CHAR_DATA *ch, char* /*argument*/, int/* cmd*/, int/* subcmd*/); +void do_whoami(CHAR_DATA *ch, char *, int, int); + +#endif //BYLINS_CMD_GENERIC_H diff --git a/src/cmd/hire.cpp b/src/cmd/hire.cpp index 9caf65619..583834e34 100644 --- a/src/cmd/hire.cpp +++ b/src/cmd/hire.cpp @@ -1,4 +1,4 @@ -#include "hire.h" +#include "cmd.generic.h" #include "handler.h" #include diff --git a/src/cmd/hire.h b/src/cmd/hire.h deleted file mode 100644 index 663622d8c..000000000 --- a/src/cmd/hire.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef BYLINS_HIRE_H -#define BYLINS_HIRE_H - -#include "chars/char.hpp" - -#define MAXPRICE 9999999 - -void do_findhelpee(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/); -void do_freehelpee(CHAR_DATA* ch, char* /* argument*/, int/* cmd*/, int/* subcmd*/); -int get_reformed_charmice_hp(CHAR_DATA * ch, CHAR_DATA * victim, int spellnum); - -#endif //BYLINS_HIRE_H - -// vim: ts=4 sw=4 tw=0 noet syntax=cpp : - diff --git a/src/cmd/mercenary.cpp b/src/cmd/mercenary.cpp index 213ccecca..8ab2d181f 100644 --- a/src/cmd/mercenary.cpp +++ b/src/cmd/mercenary.cpp @@ -1,4 +1,4 @@ -#include +#include "cmd.generic.h" #include #include #include diff --git a/src/cmd/mercenary.h b/src/cmd/mercenary.h deleted file mode 100644 index 02887023f..000000000 --- a/src/cmd/mercenary.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef BYLINS_MERCENARY_H -#define BYLINS_MERCENARY_H - -#include "chars/char.hpp" -#include - -int mercenary(CHAR_DATA *ch, void* /*me*/, int cmd, char* argument); - -struct MERCDATA -{ - int CharmedCount; // кол-во раз почармлено - int spentGold; // если купец - сколько потрачено кун - int deathCount; // кол-во раз, когда чармис сдох - int currRemortAvail; // доступен на текущем реморте - int isFavorite; // моб показывается в сокращенном списке -}; - -#endif //BYLINS_MERCENARY_H diff --git a/src/cmd/order.cpp b/src/cmd/order.cpp index 37bb76f07..cb21e417e 100644 --- a/src/cmd/order.cpp +++ b/src/cmd/order.cpp @@ -1,4 +1,4 @@ -#include "order.h" +#include "cmd.generic.h" #include "handler.h" diff --git a/src/cmd/order.h b/src/cmd/order.h deleted file mode 100644 index 03c8fdfd5..000000000 --- a/src/cmd/order.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef BYLINS_ORDER_H -#define BYLINS_ORDER_H - -#include "chars/char.hpp" - -void do_order(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/); - -#endif //BYLINS_ORDER_H diff --git a/src/cmd/quit.cpp b/src/cmd/quit.cpp index dfca0c662..7e99e5e4e 100644 --- a/src/cmd/quit.cpp +++ b/src/cmd/quit.cpp @@ -1,17 +1,13 @@ -// -// Created by ubuntu on 20/12/20. -// - -#include "quit.h" +#include "cmd.generic.h" #include "fightsystem/fight_stuff.hpp" #include "depot.hpp" #include "handler.h" #include "objsave.h" #include "house.h" -#include "grp/grp.group.h" +#include "global.objects.hpp" extern int free_rent; - +extern GroupRoster& groupRoster; void do_quit(CHAR_DATA *ch, char *argument, int/* cmd*/, int subcmd) { @@ -61,7 +57,7 @@ void do_quit(CHAR_DATA *ch, char *argument, int/* cmd*/, int subcmd) Depot::exit_char(ch); Clan::clan_invoice(ch, false); if (ch->personGroup) - ch->personGroup->removeMember(ch); + ch->personGroup->getMemberManager()->removeMember(ch); /* * kill off all sockets connected to the same player as the one who is diff --git a/src/cmd/quit.h b/src/cmd/quit.h deleted file mode 100644 index cee5a1240..000000000 --- a/src/cmd/quit.h +++ /dev/null @@ -1,12 +0,0 @@ -// -// Created by ubuntu on 20/12/20. -// - -#ifndef BYLINS_QUIT_H -#define BYLINS_QUIT_H - -#include "chars/char.hpp" - -void do_quit(CHAR_DATA *ch, char *argument, int/* cmd*/, int subcmd); - -#endif //BYLINS_QUIT_H diff --git a/src/cmd/retreat.cpp b/src/cmd/retreat.cpp index b782e7068..52e4b7271 100644 --- a/src/cmd/retreat.cpp +++ b/src/cmd/retreat.cpp @@ -1,4 +1,4 @@ -#include "retreat.h" +#include "cmd.generic.h" #include "fightsystem/fight.h" // ***************** STOPFIGHT diff --git a/src/cmd/retreat.h b/src/cmd/retreat.h deleted file mode 100644 index 55049341c..000000000 --- a/src/cmd/retreat.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef BYLINS_RETREAT_H -#define BYLINS_RETREAT_H - -#include "chars/char.hpp" - -void do_retreat(CHAR_DATA *ch, char* /*argument*/, int/* cmd*/, int/* subcmd*/); - -#endif //BYLINS_RETREAT_H diff --git a/src/cmd/telegram.cpp b/src/cmd/telegram.cpp index d3e49f3a3..5ee201f62 100644 --- a/src/cmd/telegram.cpp +++ b/src/cmd/telegram.cpp @@ -1,4 +1,4 @@ -#include "telegram.h" +#include "cmd.generic.h" #include "chars/char_player.hpp" #include "chars/world.characters.hpp" diff --git a/src/cmd/telegram.h b/src/cmd/telegram.h deleted file mode 100644 index 15f609caf..000000000 --- a/src/cmd/telegram.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef BYLINS_TELEGRAM_H -#define BYLINS_TELEGRAM_H - -#if defined(HAVE_TG) -#include -#include -#endif -class CHAR_DATA; - -void do_telegram(CHAR_DATA *ch, char *argument, int, int); - -class TelegramBot { -private: -#if defined(HAVE_TG) - CURL *curl; -#endif - const std::string msgStr = "chat_id=358708535&text="; - const std::string chatIdParam = "chat_id="; - const std::string textParam = "&text="; - const std::string uri = "https://api.telegram.org/bot1330963555:AAHvh-gXBRxJHVKOmjsl8E73TJr0cO2eC50/sendMessage"; - const unsigned long debugChatId = 358708535; -public: - TelegramBot(); - ~TelegramBot(); - bool sendMessage(unsigned long chatId, const std::string& msg); -}; - -#endif //BYLINS_TELEGRAM_H - -// vim: ts=4 sw=4 tw=0 noet syntax=cpp : diff --git a/src/cmd/track.cpp b/src/cmd/track.cpp index 56801154c..2b224d079 100644 --- a/src/cmd/track.cpp +++ b/src/cmd/track.cpp @@ -2,7 +2,7 @@ * File: track.cpp Part of Bylins * * ************************************************************************/ -#include "track.h" +#include "cmd.generic.h" #include "graph.h" #include "handler.h" diff --git a/src/cmd/track.h b/src/cmd/track.h deleted file mode 100644 index 21de7b046..000000000 --- a/src/cmd/track.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef BYLINS_TRACK_H -#define BYLINS_TRACK_H - -#include "chars/char.hpp" - -#define CALC_TRACK(ch,vict) (calculate_skill(ch,SKILL_TRACK, 0)) - -int go_track(CHAR_DATA * ch, CHAR_DATA * victim, const ESkill skill_no); -void do_track(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/); -void do_hidetrack(CHAR_DATA *ch, char* /*argument*/, int/* cmd*/, int/* subcmd*/); - -#endif //BYLINS_TRACK_H diff --git a/src/cmd/whoami.cpp b/src/cmd/whoami.cpp new file mode 100644 index 000000000..68875fe7c --- /dev/null +++ b/src/cmd/whoami.cpp @@ -0,0 +1,46 @@ +#include "cmd.generic.h" +#include "house.h" +#include "grp/grp.main.h" + +void do_whoami(CHAR_DATA *ch, char *, int, int) { + sprintf(buf, "Персонаж : %s\r\n", GET_NAME(ch)); + sprintf(buf + strlen(buf), + "Падежи : &W%s&n/&W%s&n/&W%s&n/&W%s&n/&W%s&n/&W%s&n\r\n", + ch->get_name().c_str(), GET_PAD(ch, 1), GET_PAD(ch, 2), + GET_PAD(ch, 3), GET_PAD(ch, 4), GET_PAD(ch, 5)); + + sprintf(buf + strlen(buf), "Ваш e-mail : &S%s&s\r\n", GET_EMAIL(ch)); + time_t birt = ch->player_data.time.birth; + sprintf(buf + strlen(buf), "Дата вашего рождения : %s\r\n", rustime(localtime(&birt))); + sprintf(buf + strlen(buf), "Ваш IP-адрес : %s\r\n", ch->desc ? ch->desc->host : "Unknown"); + send_to_char(buf, ch); + if (!NAME_GOD(ch)) + { + sprintf(buf, "Имя никем не одобрено!\r\n"); + send_to_char(buf, ch); + } + else + { + const int god_level = NAME_GOD(ch) > 1000 ? NAME_GOD(ch) - 1000 : NAME_GOD(ch); + sprintf(buf1, "%s", get_name_by_id(NAME_ID_GOD(ch))); + *buf1 = UPPER(*buf1); + + static const char *by_rank_god = "Богом"; + static const char *by_rank_privileged = "привилегированным игроком"; + const char * by_rank = god_level < LVL_IMMORT ? by_rank_privileged : by_rank_god; + + if (NAME_GOD(ch) < 1000) + sprintf(buf, "&RИмя запрещено %s %s&n\r\n", by_rank, buf1); + else + sprintf(buf, "&WИмя одобрено %s %s&n\r\n", by_rank, buf1); + send_to_char(buf, ch); + } + sprintf(buf, "Перевоплощений: %d\r\n", GET_REMORT(ch)); + send_to_char(buf, ch); + Clan::CheckPkList(ch); + if (ch->player_specials->saved.telegram_id != 0) { //тут прямое обращение, ибо базовый класс, а не наследник + send_to_char(ch, "Подключен Телеграм, chat_id: %lu\r\n", ch->player_specials->saved.telegram_id); + } + if (ch->personGroup) + send_to_char(ch, "Стоит в группе #%d лидера %s\r\n", ch->personGroup->getUid(), ch->personGroup->getLeaderName().c_str()); +} diff --git a/src/fightsystem/mobact.cpp b/src/fightsystem/mobact.cpp index 9ed15551a..b99c9f8d0 100644 --- a/src/fightsystem/mobact.cpp +++ b/src/fightsystem/mobact.cpp @@ -23,7 +23,7 @@ #include "skills/mighthit.h" #include "skills/protect.h" -#include "cmd/track.h" +#include "cmd/cmd.generic.h" #include "core/affect_data.h" #include "ability.rollsystem.hpp" diff --git a/src/global.objects.cpp b/src/global.objects.cpp index 6d97e2348..2382b909a 100644 --- a/src/global.objects.cpp +++ b/src/global.objects.cpp @@ -1,4 +1,3 @@ -#include #include "global.objects.hpp" #include "ban.hpp" diff --git a/src/global.objects.hpp b/src/global.objects.hpp index 56f5a21b6..59f4d2498 100644 --- a/src/global.objects.hpp +++ b/src/global.objects.hpp @@ -15,7 +15,7 @@ #include "zone.table.hpp" #include "daily_quest.hpp" #include "strengthening.hpp" -#include "grp/grp.roster.h" +#include class BanList; // to avoid inclusion of ban.hpp diff --git a/src/grp/grp.cmdprocessor.cpp b/src/grp/grp.cmdprocessor.cpp index 708472bb2..96e4089f8 100644 --- a/src/grp/grp.cmdprocessor.cpp +++ b/src/grp/grp.cmdprocessor.cpp @@ -2,8 +2,11 @@ * Функции, подключаемые в движок процессора команд игрока */ -#include "grp.cmdprocessor.h" -#include "grp.requestmanager.h" +#include "global.objects.hpp" +#include "handler.h" +#include "screen.h" +#include "msdp.constants.hpp" +#include "magic.h" extern GroupRoster& groupRoster; @@ -13,7 +16,7 @@ void do_grequest(CHAR_DATA *ch, char *argument, int, int){ // гзаявка создать Верий - отправляет заявку в группу // гзаявка отменить Верий - отменяет заявку в группу - RequestManager rm = groupRoster.getRequestManager(); + auto rm = groupRoster.getRequestManager(); two_arguments(argument, subcmd, target); /*печать перечня заявок*/ if (!*subcmd) { @@ -24,17 +27,17 @@ void do_grequest(CHAR_DATA *ch, char *argument, int, int){ return; } else if (isname(subcmd, "список list")) { - rm.printRequestList(ch); + rm->printRequestList(ch); return; } /*создание заявки*/ else if (isname(subcmd, "создать отправить make send")) { - rm.makeRequest(ch, target); + rm->makeRequest(ch, target); return; } /*отзыв заявки*/ else if (isname(subcmd, "отменить отозвать cancel revoke")){ - rm.revokeRequest(ch, target); + rm->revokeRequest(ch, target); return; } else { @@ -43,3 +46,708 @@ void do_grequest(CHAR_DATA *ch, char *argument, int, int){ } } + +bool is_group_member(CHAR_DATA *ch, CHAR_DATA *vict) +{ + if (IS_NPC(vict) || !AFF_FLAGGED(vict, EAffectFlag::AFF_GROUP) || vict->get_master() != ch) + { + return false; + } + else + { + return true; + } +} + +void print_one_line(CHAR_DATA * ch, CHAR_DATA * k, int leader, int header) +{ + int ok, ok2, div; + const char *WORD_STATE[] = { "При смерти", + "Оч.тяж.ран", + "Оч.тяж.ран", + " Тяж.ранен", + " Тяж.ранен", + " Ранен ", + " Ранен ", + " Ранен ", + "Лег.ранен ", + "Лег.ранен ", + "Слег.ранен", + " Невредим " + }; + const char *MOVE_STATE[] = { "Истощен", + "Истощен", + "О.устал", + " Устал ", + " Устал ", + "Л.устал", + "Л.устал", + "Хорошо ", + "Хорошо ", + "Хорошо ", + "Отдохн.", + " Полон " + }; + const char *POS_STATE[] = { "Умер", + "Истекает кровью", + "При смерти", + "Без сознания", + "Спит", + "Отдыхает", + "Сидит", + "Сражается", + "Стоит" + }; + + if (IS_NPC(k)) + { + if (!header) +// send_to_char("Персонаж | Здоровье |Рядом| Доп | Положение | Лояльн.\r\n",ch); + send_to_char("Персонаж | Здоровье |Рядом| Аффект | Положение\r\n", ch); + std::string name = GET_NAME(k); + name[0] = UPPER(name[0]); + sprintf(buf, "%s%-20s%s|", CCIBLU(ch, C_NRM), + name.substr(0, 20).c_str(), CCNRM(ch, C_NRM)); + sprintf(buf + strlen(buf), "%s%10s%s|", + color_value(ch, GET_HIT(k), GET_REAL_MAX_HIT(k)), + WORD_STATE[posi_value(GET_HIT(k), GET_REAL_MAX_HIT(k)) + 1], CCNRM(ch, C_NRM)); + + ok = ch->in_room == IN_ROOM(k); + sprintf(buf + strlen(buf), "%s%5s%s|", + ok ? CCGRN(ch, C_NRM) : CCRED(ch, C_NRM), ok ? " Да " : " Нет ", CCNRM(ch, C_NRM)); + + sprintf(buf + strlen(buf), " %s%s%s%s%s%s%s%s%s%s%s%s%s |", + CCIRED(ch, C_NRM), + AFF_FLAGGED(k, EAffectFlag::AFF_SANCTUARY) ? "О" : (AFF_FLAGGED(k, EAffectFlag::AFF_PRISMATICAURA) ? "П" : " "), + CCGRN(ch, C_NRM), + AFF_FLAGGED(k, EAffectFlag::AFF_WATERBREATH) ? "Д" : " ", CCICYN(ch, C_NRM), + AFF_FLAGGED(k, EAffectFlag::AFF_INVISIBLE) ? "Н" : " ", CCIYEL(ch, C_NRM), + (AFF_FLAGGED(k, EAffectFlag::AFF_SINGLELIGHT) + || AFF_FLAGGED(k, EAffectFlag::AFF_HOLYLIGHT) + || (GET_EQ(k, WEAR_LIGHT) + && GET_OBJ_VAL(GET_EQ(k, WEAR_LIGHT), 2))) ? "С" : " ", + CCIBLU(ch, C_NRM), AFF_FLAGGED(k, EAffectFlag::AFF_FLY) ? "Л" : " ", CCYEL(ch, C_NRM), + k->low_charm() ? "Т" : " ", CCNRM(ch, C_NRM)); + + sprintf(buf + strlen(buf), "%-15s", POS_STATE[(int) GET_POS(k)]); + + act(buf, FALSE, ch, 0, k, TO_CHAR); + } + else + { + if (!header) + send_to_char + ("Персонаж | Здоровье |Энергия|Рядом|Учить| Аффект | Кто | Держит строй | Положение \r\n", ch); + + std::string name = GET_NAME(k); + name[0] = UPPER(name[0]); + sprintf(buf, "%s%-20s%s|", CCIBLU(ch, C_NRM), name.c_str(), CCNRM(ch, C_NRM)); + sprintf(buf + strlen(buf), "%s%10s%s|", + color_value(ch, GET_HIT(k), GET_REAL_MAX_HIT(k)), + WORD_STATE[posi_value(GET_HIT(k), GET_REAL_MAX_HIT(k)) + 1], CCNRM(ch, C_NRM)); + + sprintf(buf + strlen(buf), "%s%7s%s|", + color_value(ch, GET_MOVE(k), GET_REAL_MAX_MOVE(k)), + MOVE_STATE[posi_value(GET_MOVE(k), GET_REAL_MAX_MOVE(k)) + 1], CCNRM(ch, C_NRM)); + + ok = ch->in_room == IN_ROOM(k); + sprintf(buf + strlen(buf), "%s%5s%s|", + ok ? CCGRN(ch, C_NRM) : CCRED(ch, C_NRM), ok ? " Да " : " Нет ", CCNRM(ch, C_NRM)); + + if ((!IS_MANA_CASTER(k) && !MEMQUEUE_EMPTY(k)) || + (IS_MANA_CASTER(k) && GET_MANA_STORED(k) < GET_MAX_MANA(k))) + { + div = mana_gain(k); + if (div > 0) + { + if (!IS_MANA_CASTER(k)) + { + ok2 = MAX(0, 1 + GET_MEM_TOTAL(k) - GET_MEM_COMPLETED(k)); + ok2 = ok2 * 60 / div; // время мема в сек + } + else + { + ok2 = MAX(0, 1 + GET_MAX_MANA(k) - GET_MANA_STORED(k)); + ok2 = ok2 / div; // время восстановления в секундах + } + ok = ok2 / 60; + ok2 %= 60; + if (ok > 99) + sprintf(buf + strlen(buf), "&g%5d&n|", ok); + else + sprintf(buf + strlen(buf), "&g%2d:%02d&n|", ok, ok2); + } + else + { + sprintf(buf + strlen(buf), "&r -&n|"); + } + } + else + sprintf(buf + strlen(buf), " |"); + + sprintf(buf + strlen(buf), " %s%s%s%s%s%s%s%s%s%s%s%s%s |", + CCIRED(ch, C_NRM), AFF_FLAGGED(k, EAffectFlag::AFF_SANCTUARY) ? "О" : (AFF_FLAGGED(k, EAffectFlag::AFF_PRISMATICAURA) + ? "П" : " "), CCGRN(ch, + C_NRM), + AFF_FLAGGED(k, EAffectFlag::AFF_WATERBREATH) ? "Д" : " ", CCICYN(ch, + C_NRM), + AFF_FLAGGED(k, EAffectFlag::AFF_INVISIBLE) ? "Н" : " ", CCIYEL(ch, C_NRM), (AFF_FLAGGED(k, EAffectFlag::AFF_SINGLELIGHT) + || AFF_FLAGGED(k, EAffectFlag::AFF_HOLYLIGHT) + || (GET_EQ(k, WEAR_LIGHT) + && + GET_OBJ_VAL(GET_EQ + (k, WEAR_LIGHT), + 2))) ? "С" : " ", + CCIBLU(ch, C_NRM), AFF_FLAGGED(k, EAffectFlag::AFF_FLY) ? "Л" : " ", CCYEL(ch, C_NRM), + k->ahorse() ? "В" : " ", CCNRM(ch, C_NRM)); + + sprintf(buf + strlen(buf), "%5s|", leader ? "Лидер" : ""); + ok = PRF_FLAGGED(k, PRF_SKIRMISHER); + sprintf(buf + strlen(buf), "%s%-14s%s|", ok ? CCGRN(ch, C_NRM) : CCNRM(ch, C_NRM), ok ? " Да " : " Нет ", CCNRM(ch, C_NRM)); + sprintf(buf + strlen(buf), " %s", POS_STATE[(int) GET_POS(k)]); + act(buf, FALSE, ch, 0, k, TO_CHAR); + } +} + +void print_group(CHAR_DATA * ch) +{ + int gfound = 0, cfound = 0; + CHAR_DATA *k; + struct follow_type *f, *g; + + k = ch->has_master() ? ch->get_master() : ch; + if (!IS_NPC(ch)) + ch->desc->msdp_report(msdp::constants::GROUP); + + if (AFF_FLAGGED(ch, EAffectFlag::AFF_GROUP)) + { + send_to_char("Ваша группа состоит из:\r\n", ch); + if (AFF_FLAGGED(k, EAffectFlag::AFF_GROUP)) + { + print_one_line(ch, k, TRUE, gfound++); + } + + for (f = k->followers; f; f = f->next) + { + if (!AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP)) + { + continue; + } + print_one_line(ch, f->follower, FALSE, gfound++); + } + } + + for (f = ch->followers; f; f = f->next) + { + if (!(AFF_FLAGGED(f->follower, EAffectFlag::AFF_CHARM) + || MOB_FLAGGED(f->follower, MOB_ANGEL)|| MOB_FLAGGED(f->follower, MOB_GHOST))) + { + continue; + } + if (!cfound) + send_to_char("Ваши последователи:\r\n", ch); + print_one_line(ch, f->follower, FALSE, cfound++); + } + if (!gfound && !cfound) + { + send_to_char("Но вы же не член (в лучшем смысле этого слова) группы!\r\n", ch); + return; + } + if (PRF_FLAGGED(ch, PRF_SHOWGROUP)) + { + for (g = k->followers, cfound = 0; g; g = g->next) + { + for (f = g->follower->followers; f; f = f->next) + { + if (!(AFF_FLAGGED(f->follower, EAffectFlag::AFF_CHARM) + || MOB_FLAGGED(f->follower, MOB_ANGEL) || MOB_FLAGGED(f->follower, MOB_GHOST)) + || !AFF_FLAGGED(ch, EAffectFlag::AFF_GROUP)) + { + continue; + } + + if (f->follower->get_master() == ch + || !AFF_FLAGGED(f->follower->get_master(), EAffectFlag::AFF_GROUP)) + { + continue; + } + + // shapirus: при включенном режиме не показываем клонов и хранителей + if (PRF_FLAGGED(ch, PRF_NOCLONES) + && IS_NPC(f->follower) + && (MOB_FLAGGED(f->follower, MOB_CLONE) + || GET_MOB_VNUM(f->follower) == MOB_KEEPER)) + { + continue; + } + + if (!cfound) + { + send_to_char("Последователи членов вашей группы:\r\n", ch); + } + print_one_line(ch, f->follower, FALSE, cfound++); + } + + if (ch->has_master()) + { + if (!(AFF_FLAGGED(g->follower, EAffectFlag::AFF_CHARM) + || MOB_FLAGGED(g->follower, MOB_ANGEL) || MOB_FLAGGED(g->follower, MOB_GHOST)) + || !AFF_FLAGGED(ch, EAffectFlag::AFF_GROUP)) + { + continue; + } + + // shapirus: при включенном режиме не показываем клонов и хранителей + if (PRF_FLAGGED(ch, PRF_NOCLONES) + && IS_NPC(g->follower) + && (MOB_FLAGGED(g->follower, MOB_CLONE) + || GET_MOB_VNUM(g->follower) == MOB_KEEPER)) + { + continue; + } + + if (!cfound) + { + send_to_char("Последователи членов вашей группы:\r\n", ch); + } + print_one_line(ch, g->follower, FALSE, cfound++); + } + } + } +} + +void print_list_group(CHAR_DATA *ch) +{ + CHAR_DATA *k; + struct follow_type *f; + int count = 1; + k = (ch->has_master() ? ch->get_master() : ch); + if (AFF_FLAGGED(ch, EAffectFlag::AFF_GROUP)) + { + send_to_char("Ваша группа состоит из:\r\n", ch); + if (AFF_FLAGGED(k, EAffectFlag::AFF_GROUP)) + { + sprintf(buf1, "Лидер: %s\r\n", GET_NAME(k)); + send_to_char(buf1, ch); + } + + for (f = k->followers; f; f = f->next) + { + if (!AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP)) + { + continue; + } + sprintf(buf1, "%d. Согруппник: %s\r\n", count, GET_NAME(f->follower)); + send_to_char(buf1, ch); + count++; + } + } + else + { + send_to_char("Но вы же не член (в лучшем смысле этого слова) группы!\r\n", ch); + } +} + +int perform_group(CHAR_DATA * ch, CHAR_DATA * vict) +{ + if (AFF_FLAGGED(vict, EAffectFlag::AFF_GROUP) + || AFF_FLAGGED(vict, EAffectFlag::AFF_CHARM) + || MOB_FLAGGED(vict, MOB_ANGEL) + || MOB_FLAGGED(vict, MOB_GHOST) + || IS_HORSE(vict)) + { + return (FALSE); + } + + AFF_FLAGS(vict).set(EAffectFlag::AFF_GROUP); + if (ch != vict) + { + act("$N принят$A в члены вашего кружка (тьфу-ты, группы :).", FALSE, ch, 0, vict, TO_CHAR); + act("Вы приняты в группу $n1.", FALSE, ch, 0, vict, TO_VICT); + act("$N принят$A в группу $n1.", FALSE, ch, 0, vict, TO_NOTVICT | TO_ARENA_LISTEN); + } + return (TRUE); +} + +int max_group_size(CHAR_DATA *ch) +{ + int bonus_commander = 0; + if (AFF_FLAGGED(ch, EAffectFlag::AFF_COMMANDER)) bonus_commander = VPOSI((ch->get_skill(SKILL_LEADERSHIP) - 120) / 10, 0 , 8); + + return MAX_GROUPED_FOLLOWERS + (int) VPOSI((ch->get_skill(SKILL_LEADERSHIP) - 80) / 5, 0, 4) + bonus_commander; +} + +/** +* Смена лидера группы на персонажа с макс лидеркой. +* Сам лидер при этом остается в группе, если он живой. +* \param vict - новый лидер, если смена происходит по команде 'гр лидер имя', +* старый лидер соответственно группится обратно, если нулевой, то считаем, что +* произошла смерть старого лидера и новый выбирается по наибольшей лидерке. +*/ +void change_leader(CHAR_DATA *ch, CHAR_DATA *vict) +{ + if (IS_NPC(ch) + || ch->has_master() + || !ch->followers) + { + return; + } + + CHAR_DATA *leader = vict; + if (!leader) + { + // лидер умер, ищем согрупника с максимальным скиллом лидерки + for (struct follow_type *l = ch->followers; l; l = l->next) + { + if (!is_group_member(ch, l->follower)) + continue; + if (!leader) + leader = l->follower; + else if (l->follower->get_skill(SKILL_LEADERSHIP) > leader->get_skill(SKILL_LEADERSHIP)) + leader = l->follower; + } + } + + if (!leader) + { + return; + } + + // для реследования используем стандартные функции + std::vector temp_list; + for (struct follow_type *n = 0, *l = ch->followers; l; l = n) + { + n = l->next; + if (!is_group_member(ch, l->follower)) + { + continue; + } + else + { + CHAR_DATA *temp_vict = l->follower; + if (temp_vict->has_master() + && stop_follower(temp_vict, SF_SILENCE)) + { + continue; + } + + if (temp_vict != leader) + { + temp_list.push_back(temp_vict); + } + } + } + + // вся эта фигня только для того, чтобы при реследовании пройтись по списку в обратном + // направлении и сохранить относительный порядок следования в группе + if (!temp_list.empty()) + { + for (std::vector::reverse_iterator it = temp_list.rbegin(); it != temp_list.rend(); ++it) + { + leader->add_follower_silently(*it); + } + } + + // бывшего лидера последним закидываем обратно в группу, если он живой + if (vict) + { + // флаг группы надо снять, иначе при регрупе не будет писаться о старом лидере + //AFF_FLAGS(ch).unset(EAffectFlag::AFF_GROUP); + ch->removeGroupFlags(); + leader->add_follower_silently(ch); + } + + if (!leader->followers) + { + return; + } + + ch->dps_copy(leader); + perform_group(leader, leader); + int followers = 0; + for (struct follow_type *f = leader->followers; f; f = f->next) + { + if (followers < max_group_size(leader)) + { + if (perform_group(leader, f->follower)) + ++followers; + } + else + { + send_to_char("Вы больше никого не можете принять в группу.\r\n", ch); + return; + } + } +} + +void do_group(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) +{ + CHAR_DATA *vict; + struct follow_type *f; + int found, f_number; + + argument = one_argument(argument, buf); + + if (!*buf) + { + print_group(ch); + return; + } + + if (!str_cmp(buf, "список")) + { + print_list_group(ch); + return; + } + + if (GET_POS(ch) < POS_RESTING) + { + send_to_char("Трудно управлять группой в таком состоянии.\r\n", ch); + return; + } + + if (ch->has_master()) + { + act("Вы не можете управлять группой. Вы еще не ведущий.", FALSE, ch, 0, 0, TO_CHAR); + return; + } + + if (!ch->followers) + { + send_to_char("За вами никто не следует.\r\n", ch); + return; + } + + + +// вычисляем количество последователей + for (f_number = 0, f = ch->followers; f; f = f->next) + { + if (AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP)) + { + f_number++; + } + } + + if (!str_cmp(buf, "all") + || !str_cmp(buf, "все")) + { + perform_group(ch, ch); + for (found = 0, f = ch->followers; f; f = f->next) + { + if ((f_number + found) >= max_group_size(ch)) + { + send_to_char("Вы больше никого не можете принять в группу.\r\n", ch); + return; + } + found += perform_group(ch, f->follower); + } + + if (!found) + { + send_to_char("Все, кто за вами следуют, уже включены в вашу группу.\r\n", ch); + } + + return; + } + else if (!str_cmp(buf, "leader") || !str_cmp(buf, "лидер")) + { + vict = get_player_vis(ch, argument, FIND_CHAR_WORLD); + // added by WorM (Видолюб) Если найден клон и его хозяин персонаж + // а то чото как-то глючно Двойник %1 не является членом вашей группы. + if (vict + && IS_NPC(vict) + && MOB_FLAGGED(vict, MOB_CLONE) + && AFF_FLAGGED(vict, EAffectFlag::AFF_CHARM) + && vict->has_master() + && !IS_NPC(vict->get_master())) + { + if (CAN_SEE(ch, vict->get_master())) + { + vict = vict->get_master(); + } + else + { + vict = NULL; + } + } + + // end by WorM + if (!vict) + { + send_to_char("Нет такого персонажа.\r\n", ch); + return; + } + else if (vict == ch) + { + send_to_char("Вы и так лидер группы...\r\n", ch); + return; + } + else if (!AFF_FLAGGED(vict, EAffectFlag::AFF_GROUP) + || vict->get_master() != ch) + { + send_to_char(ch, "%s не является членом вашей группы.\r\n", GET_NAME(vict)); + return; + } + change_leader(ch, vict); + return; + } + + if (!(vict = get_char_vis(ch, buf, FIND_CHAR_ROOM))) + { + send_to_char(NOPERSON, ch); + } + else if ((vict->get_master() != ch) && (vict != ch)) + { + act("$N2 нужно следовать за вами, чтобы стать членом вашей группы.", FALSE, ch, 0, vict, TO_CHAR); + } + else + { + if (!AFF_FLAGGED(vict, EAffectFlag::AFF_GROUP)) + { + if (AFF_FLAGGED(vict, EAffectFlag::AFF_CHARM) || MOB_FLAGGED(vict, MOB_ANGEL) || MOB_FLAGGED(vict, MOB_GHOST) || IS_HORSE(vict)) + { + send_to_char("Только равноправные персонажи могут быть включены в группу.\r\n", ch); + send_to_char("Только равноправные персонажи могут быть включены в группу.\r\n", vict); + }; + if (f_number >= max_group_size(ch)) + { + send_to_char("Вы больше никого не можете принять в группу.\r\n", ch); + return; + } + perform_group(ch, ch); + perform_group(ch, vict); + } + else if (ch != vict) + { + act("$N исключен$A из состава вашей группы.", FALSE, ch, 0, vict, TO_CHAR); + act("Вы исключены из группы $n1!", FALSE, ch, 0, vict, TO_VICT); + act("$N был$G исключен$A из группы $n1!", FALSE, ch, 0, vict, TO_NOTVICT | TO_ARENA_LISTEN); + //AFF_FLAGS(vict).unset(EAffectFlag::AFF_GROUP); + vict->removeGroupFlags(); + } + } +} + +void do_group2(CHAR_DATA *ch, char *argument, int, int){ + groupRoster.processGroupCommands(ch, argument); +} + +void do_ungroup(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) { + struct follow_type *f, *next_fol; + CHAR_DATA *tch; + + one_argument(argument, buf); + + if (ch->has_master() + || !(AFF_FLAGGED(ch, EAffectFlag::AFF_GROUP))) + { + send_to_char("Вы же не лидер группы!\r\n", ch); + return; + } + + if (!*buf) + { + sprintf(buf2, "Вы исключены из группы %s.\r\n", GET_PAD(ch, 1)); + for (f = ch->followers; f; f = next_fol) + { + next_fol = f->next; + if (AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP)) + { + //AFF_FLAGS(f->follower).unset(EAffectFlag::AFF_GROUP); + f->follower->removeGroupFlags(); + send_to_char(buf2, f->follower); + if (!AFF_FLAGGED(f->follower, EAffectFlag::AFF_CHARM) + && !(IS_NPC(f->follower) + && AFF_FLAGGED(f->follower, EAffectFlag::AFF_HORSE))) + { + stop_follower(f->follower, SF_EMPTY); + } + } + } + AFF_FLAGS(ch).unset(EAffectFlag::AFF_GROUP); + ch->removeGroupFlags(); + send_to_char("Вы распустили группу.\r\n", ch); + return; + } + for (f = ch->followers; f; f = next_fol) + { + next_fol = f->next; + tch = f->follower; + if (isname(buf, tch->get_pc_name()) + && !AFF_FLAGGED(tch, EAffectFlag::AFF_CHARM) + && !IS_HORSE(tch)) + { + //AFF_FLAGS(tch).unset(EAffectFlag::AFF_GROUP); + tch->removeGroupFlags(); + act("$N более не член вашей группы.", FALSE, ch, 0, tch, TO_CHAR); + act("Вы исключены из группы $n1!", FALSE, ch, 0, tch, TO_VICT); + act("$N был$G изгнан$A из группы $n1!", FALSE, ch, 0, tch, TO_NOTVICT | TO_ARENA_LISTEN); + stop_follower(tch, SF_EMPTY); + return; + } + } + send_to_char("Этот игрок не входит в состав вашей группы.\r\n", ch); + return; +} + +void do_report(CHAR_DATA *ch, char* /*argument*/, int/* cmd*/, int/* subcmd*/) +{ + CHAR_DATA *k; + struct follow_type *f; + + if (!AFF_FLAGGED(ch, EAffectFlag::AFF_GROUP) && !AFF_FLAGGED(ch, EAffectFlag::AFF_CHARM)) + { + send_to_char("И перед кем вы отчитываетесь?\r\n", ch); + return; + } + if (ch->is_druid()) + { + sprintf(buf, "%s доложил%s : %d(%d)H, %d(%d)V, %d(%d)M\r\n", + GET_NAME(ch), GET_CH_SUF_1(ch), + GET_HIT(ch), GET_REAL_MAX_HIT(ch), + GET_MOVE(ch), GET_REAL_MAX_MOVE(ch), + GET_MANA_STORED(ch), GET_MAX_MANA(ch)); + } + else if (AFF_FLAGGED(ch, EAffectFlag::AFF_CHARM)) + { + int loyalty = 0; + for (const auto& aff : ch->affected) + { + if (aff->type == SPELL_CHARM) + { + loyalty = aff->duration / 2; + break; + } + } + sprintf(buf, "%s доложил%s : %d(%d)H, %d(%d)V, %dL\r\n", + GET_NAME(ch), GET_CH_SUF_1(ch), + GET_HIT(ch), GET_REAL_MAX_HIT(ch), + GET_MOVE(ch), GET_REAL_MAX_MOVE(ch), + loyalty); + } + else + { + sprintf(buf, "%s доложил%s : %d(%d)H, %d(%d)V\r\n", + GET_NAME(ch), GET_CH_SUF_1(ch), + GET_HIT(ch), GET_REAL_MAX_HIT(ch), + GET_MOVE(ch), GET_REAL_MAX_MOVE(ch)); + } + CAP(buf); + k = ch->has_master() ? ch->get_master() : ch; + for (f = k->followers; f; f = f->next) + { + if (AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP) + && f->follower != ch + && !AFF_FLAGGED(f->follower, EAffectFlag::AFF_DEAFNESS)) + { + send_to_char(buf, f->follower); + } + } + + if (k != ch && !AFF_FLAGGED(k, EAffectFlag::AFF_DEAFNESS)) + { + send_to_char(buf, k); + } + send_to_char("Вы доложили о состоянии всем членам вашей группы.\r\n", ch); +} \ No newline at end of file diff --git a/src/grp/grp.cmdprocessor.h b/src/grp/grp.cmdprocessor.h deleted file mode 100644 index 37fe4cfb7..000000000 --- a/src/grp/grp.cmdprocessor.h +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Функции, подключаемые в движок процессора команд игрока - */ -#ifndef BYLINS_GRP_CMDPROCESSOR_H -#define BYLINS_GRP_CMDPROCESSOR_H - -#include "chars/char.hpp" - -void do_grequest(CHAR_DATA *ch, char *argument, int, int); - -#endif //BYLINS_GRP_CMDPROCESSOR_H diff --git a/src/grp/grp.group.cpp b/src/grp/grp.group.cpp index 4f61a8539..771387e63 100644 --- a/src/grp/grp.group.cpp +++ b/src/grp/grp.group.cpp @@ -1,749 +1,25 @@ -#include "grp.group.h" #include "comm.h" #include "handler.h" #include "magic.h" #include "msdp.constants.hpp" #include "screen.h" +#include "global.objects.hpp" +#include "grp.main.h" -extern GroupRoster& groupRoster; - -bool is_group_member(CHAR_DATA *ch, CHAR_DATA *vict) -{ - if (IS_NPC(vict) || !AFF_FLAGGED(vict, EAffectFlag::AFF_GROUP) || vict->get_master() != ch) - { - return false; - } - else - { - return true; - } -} - -void print_one_line(CHAR_DATA * ch, CHAR_DATA * k, int leader, int header) -{ - int ok, ok2, div; - const char *WORD_STATE[] = { "При смерти", - "Оч.тяж.ран", - "Оч.тяж.ран", - " Тяж.ранен", - " Тяж.ранен", - " Ранен ", - " Ранен ", - " Ранен ", - "Лег.ранен ", - "Лег.ранен ", - "Слег.ранен", - " Невредим " - }; - const char *MOVE_STATE[] = { "Истощен", - "Истощен", - "О.устал", - " Устал ", - " Устал ", - "Л.устал", - "Л.устал", - "Хорошо ", - "Хорошо ", - "Хорошо ", - "Отдохн.", - " Полон " - }; - const char *POS_STATE[] = { "Умер", - "Истекает кровью", - "При смерти", - "Без сознания", - "Спит", - "Отдыхает", - "Сидит", - "Сражается", - "Стоит" - }; - - if (IS_NPC(k)) - { - if (!header) -// send_to_char("Персонаж | Здоровье |Рядом| Доп | Положение | Лояльн.\r\n",ch); - send_to_char("Персонаж | Здоровье |Рядом| Аффект | Положение\r\n", ch); - std::string name = GET_NAME(k); - name[0] = UPPER(name[0]); - sprintf(buf, "%s%-20s%s|", CCIBLU(ch, C_NRM), - name.substr(0, 20).c_str(), CCNRM(ch, C_NRM)); - sprintf(buf + strlen(buf), "%s%10s%s|", - color_value(ch, GET_HIT(k), GET_REAL_MAX_HIT(k)), - WORD_STATE[posi_value(GET_HIT(k), GET_REAL_MAX_HIT(k)) + 1], CCNRM(ch, C_NRM)); - - ok = ch->in_room == IN_ROOM(k); - sprintf(buf + strlen(buf), "%s%5s%s|", - ok ? CCGRN(ch, C_NRM) : CCRED(ch, C_NRM), ok ? " Да " : " Нет ", CCNRM(ch, C_NRM)); - - sprintf(buf + strlen(buf), " %s%s%s%s%s%s%s%s%s%s%s%s%s |", - CCIRED(ch, C_NRM), - AFF_FLAGGED(k, EAffectFlag::AFF_SANCTUARY) ? "О" : (AFF_FLAGGED(k, EAffectFlag::AFF_PRISMATICAURA) ? "П" : " "), - CCGRN(ch, C_NRM), - AFF_FLAGGED(k, EAffectFlag::AFF_WATERBREATH) ? "Д" : " ", CCICYN(ch, C_NRM), - AFF_FLAGGED(k, EAffectFlag::AFF_INVISIBLE) ? "Н" : " ", CCIYEL(ch, C_NRM), - (AFF_FLAGGED(k, EAffectFlag::AFF_SINGLELIGHT) - || AFF_FLAGGED(k, EAffectFlag::AFF_HOLYLIGHT) - || (GET_EQ(k, WEAR_LIGHT) - && GET_OBJ_VAL(GET_EQ(k, WEAR_LIGHT), 2))) ? "С" : " ", - CCIBLU(ch, C_NRM), AFF_FLAGGED(k, EAffectFlag::AFF_FLY) ? "Л" : " ", CCYEL(ch, C_NRM), - k->low_charm() ? "Т" : " ", CCNRM(ch, C_NRM)); - - sprintf(buf + strlen(buf), "%-15s", POS_STATE[(int) GET_POS(k)]); - - act(buf, FALSE, ch, 0, k, TO_CHAR); - } - else - { - if (!header) - send_to_char - ("Персонаж | Здоровье |Энергия|Рядом|Учить| Аффект | Кто | Держит строй | Положение \r\n", ch); - - std::string name = GET_NAME(k); - name[0] = UPPER(name[0]); - sprintf(buf, "%s%-20s%s|", CCIBLU(ch, C_NRM), name.c_str(), CCNRM(ch, C_NRM)); - sprintf(buf + strlen(buf), "%s%10s%s|", - color_value(ch, GET_HIT(k), GET_REAL_MAX_HIT(k)), - WORD_STATE[posi_value(GET_HIT(k), GET_REAL_MAX_HIT(k)) + 1], CCNRM(ch, C_NRM)); - - sprintf(buf + strlen(buf), "%s%7s%s|", - color_value(ch, GET_MOVE(k), GET_REAL_MAX_MOVE(k)), - MOVE_STATE[posi_value(GET_MOVE(k), GET_REAL_MAX_MOVE(k)) + 1], CCNRM(ch, C_NRM)); - - ok = ch->in_room == IN_ROOM(k); - sprintf(buf + strlen(buf), "%s%5s%s|", - ok ? CCGRN(ch, C_NRM) : CCRED(ch, C_NRM), ok ? " Да " : " Нет ", CCNRM(ch, C_NRM)); - - if ((!IS_MANA_CASTER(k) && !MEMQUEUE_EMPTY(k)) || - (IS_MANA_CASTER(k) && GET_MANA_STORED(k) < GET_MAX_MANA(k))) - { - div = mana_gain(k); - if (div > 0) - { - if (!IS_MANA_CASTER(k)) - { - ok2 = MAX(0, 1 + GET_MEM_TOTAL(k) - GET_MEM_COMPLETED(k)); - ok2 = ok2 * 60 / div; // время мема в сек - } - else - { - ok2 = MAX(0, 1 + GET_MAX_MANA(k) - GET_MANA_STORED(k)); - ok2 = ok2 / div; // время восстановления в секундах - } - ok = ok2 / 60; - ok2 %= 60; - if (ok > 99) - sprintf(buf + strlen(buf), "&g%5d&n|", ok); - else - sprintf(buf + strlen(buf), "&g%2d:%02d&n|", ok, ok2); - } - else - { - sprintf(buf + strlen(buf), "&r -&n|"); - } - } - else - sprintf(buf + strlen(buf), " |"); - - sprintf(buf + strlen(buf), " %s%s%s%s%s%s%s%s%s%s%s%s%s |", - CCIRED(ch, C_NRM), AFF_FLAGGED(k, EAffectFlag::AFF_SANCTUARY) ? "О" : (AFF_FLAGGED(k, EAffectFlag::AFF_PRISMATICAURA) - ? "П" : " "), CCGRN(ch, - C_NRM), - AFF_FLAGGED(k, EAffectFlag::AFF_WATERBREATH) ? "Д" : " ", CCICYN(ch, - C_NRM), - AFF_FLAGGED(k, EAffectFlag::AFF_INVISIBLE) ? "Н" : " ", CCIYEL(ch, C_NRM), (AFF_FLAGGED(k, EAffectFlag::AFF_SINGLELIGHT) - || AFF_FLAGGED(k, EAffectFlag::AFF_HOLYLIGHT) - || (GET_EQ(k, WEAR_LIGHT) - && - GET_OBJ_VAL(GET_EQ - (k, WEAR_LIGHT), - 2))) ? "С" : " ", - CCIBLU(ch, C_NRM), AFF_FLAGGED(k, EAffectFlag::AFF_FLY) ? "Л" : " ", CCYEL(ch, C_NRM), - k->ahorse() ? "В" : " ", CCNRM(ch, C_NRM)); - - sprintf(buf + strlen(buf), "%5s|", leader ? "Лидер" : ""); - ok = PRF_FLAGGED(k, PRF_SKIRMISHER); - sprintf(buf + strlen(buf), "%s%-14s%s|", ok ? CCGRN(ch, C_NRM) : CCNRM(ch, C_NRM), ok ? " Да " : " Нет ", CCNRM(ch, C_NRM)); - sprintf(buf + strlen(buf), " %s", POS_STATE[(int) GET_POS(k)]); - act(buf, FALSE, ch, 0, k, TO_CHAR); - } -} - -void print_group(CHAR_DATA * ch) -{ - int gfound = 0, cfound = 0; - CHAR_DATA *k; - struct follow_type *f, *g; - - k = ch->has_master() ? ch->get_master() : ch; - if (!IS_NPC(ch)) - ch->desc->msdp_report(msdp::constants::GROUP); - - if (AFF_FLAGGED(ch, EAffectFlag::AFF_GROUP)) - { - send_to_char("Ваша группа состоит из:\r\n", ch); - if (AFF_FLAGGED(k, EAffectFlag::AFF_GROUP)) - { - print_one_line(ch, k, TRUE, gfound++); - } - - for (f = k->followers; f; f = f->next) - { - if (!AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP)) - { - continue; - } - print_one_line(ch, f->follower, FALSE, gfound++); - } - } - - for (f = ch->followers; f; f = f->next) - { - if (!(AFF_FLAGGED(f->follower, EAffectFlag::AFF_CHARM) - || MOB_FLAGGED(f->follower, MOB_ANGEL)|| MOB_FLAGGED(f->follower, MOB_GHOST))) - { - continue; - } - if (!cfound) - send_to_char("Ваши последователи:\r\n", ch); - print_one_line(ch, f->follower, FALSE, cfound++); - } - if (!gfound && !cfound) - { - send_to_char("Но вы же не член (в лучшем смысле этого слова) группы!\r\n", ch); - return; - } - if (PRF_FLAGGED(ch, PRF_SHOWGROUP)) - { - for (g = k->followers, cfound = 0; g; g = g->next) - { - for (f = g->follower->followers; f; f = f->next) - { - if (!(AFF_FLAGGED(f->follower, EAffectFlag::AFF_CHARM) - || MOB_FLAGGED(f->follower, MOB_ANGEL) || MOB_FLAGGED(f->follower, MOB_GHOST)) - || !AFF_FLAGGED(ch, EAffectFlag::AFF_GROUP)) - { - continue; - } - - if (f->follower->get_master() == ch - || !AFF_FLAGGED(f->follower->get_master(), EAffectFlag::AFF_GROUP)) - { - continue; - } - - // shapirus: при включенном режиме не показываем клонов и хранителей - if (PRF_FLAGGED(ch, PRF_NOCLONES) - && IS_NPC(f->follower) - && (MOB_FLAGGED(f->follower, MOB_CLONE) - || GET_MOB_VNUM(f->follower) == MOB_KEEPER)) - { - continue; - } - - if (!cfound) - { - send_to_char("Последователи членов вашей группы:\r\n", ch); - } - print_one_line(ch, f->follower, FALSE, cfound++); - } - - if (ch->has_master()) - { - if (!(AFF_FLAGGED(g->follower, EAffectFlag::AFF_CHARM) - || MOB_FLAGGED(g->follower, MOB_ANGEL) || MOB_FLAGGED(g->follower, MOB_GHOST)) - || !AFF_FLAGGED(ch, EAffectFlag::AFF_GROUP)) - { - continue; - } - - // shapirus: при включенном режиме не показываем клонов и хранителей - if (PRF_FLAGGED(ch, PRF_NOCLONES) - && IS_NPC(g->follower) - && (MOB_FLAGGED(g->follower, MOB_CLONE) - || GET_MOB_VNUM(g->follower) == MOB_KEEPER)) - { - continue; - } - - if (!cfound) - { - send_to_char("Последователи членов вашей группы:\r\n", ch); - } - print_one_line(ch, g->follower, FALSE, cfound++); - } - } - } -} - -void print_list_group(CHAR_DATA *ch) -{ - CHAR_DATA *k; - struct follow_type *f; - int count = 1; - k = (ch->has_master() ? ch->get_master() : ch); - if (AFF_FLAGGED(ch, EAffectFlag::AFF_GROUP)) - { - send_to_char("Ваша группа состоит из:\r\n", ch); - if (AFF_FLAGGED(k, EAffectFlag::AFF_GROUP)) - { - sprintf(buf1, "Лидер: %s\r\n", GET_NAME(k)); - send_to_char(buf1, ch); - } - - for (f = k->followers; f; f = f->next) - { - if (!AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP)) - { - continue; - } - sprintf(buf1, "%d. Согруппник: %s\r\n", count, GET_NAME(f->follower)); - send_to_char(buf1, ch); - count++; - } - } - else - { - send_to_char("Но вы же не член (в лучшем смысле этого слова) группы!\r\n", ch); - } -} - -int perform_group(CHAR_DATA * ch, CHAR_DATA * vict) -{ - if (AFF_FLAGGED(vict, EAffectFlag::AFF_GROUP) - || AFF_FLAGGED(vict, EAffectFlag::AFF_CHARM) - || MOB_FLAGGED(vict, MOB_ANGEL) - || MOB_FLAGGED(vict, MOB_GHOST) - || IS_HORSE(vict)) - { - return (FALSE); - } - - AFF_FLAGS(vict).set(EAffectFlag::AFF_GROUP); - if (ch != vict) - { - act("$N принят$A в члены вашего кружка (тьфу-ты, группы :).", FALSE, ch, 0, vict, TO_CHAR); - act("Вы приняты в группу $n1.", FALSE, ch, 0, vict, TO_VICT); - act("$N принят$A в группу $n1.", FALSE, ch, 0, vict, TO_NOTVICT | TO_ARENA_LISTEN); - } - if (ch->personGroup == nullptr) - ch->personGroup = groupRoster.addGroup(ch); - if (vict->personGroup == nullptr) - { - vict->personGroup = ch->personGroup; - vict->personGroup->addMember(ch); - } - return (TRUE); -} - -int max_group_size(CHAR_DATA *ch) -{ - int bonus_commander = 0; - if (AFF_FLAGGED(ch, EAffectFlag::AFF_COMMANDER)) bonus_commander = VPOSI((ch->get_skill(SKILL_LEADERSHIP) - 120) / 10, 0 , 8); - - return MAX_GROUPED_FOLLOWERS + (int) VPOSI((ch->get_skill(SKILL_LEADERSHIP) - 80) / 5, 0, 4) + bonus_commander; -} - -/** -* Смена лидера группы на персонажа с макс лидеркой. -* Сам лидер при этом остается в группе, если он живой. -* \param vict - новый лидер, если смена происходит по команде 'гр лидер имя', -* старый лидер соответственно группится обратно, если нулевой, то считаем, что -* произошла смерть старого лидера и новый выбирается по наибольшей лидерке. -*/ -void change_leader(CHAR_DATA *ch, CHAR_DATA *vict) -{ - if (IS_NPC(ch) - || ch->has_master() - || !ch->followers) - { - return; - } - - CHAR_DATA *leader = vict; - if (!leader) - { - // лидер умер, ищем согрупника с максимальным скиллом лидерки - for (struct follow_type *l = ch->followers; l; l = l->next) - { - if (!is_group_member(ch, l->follower)) - continue; - if (!leader) - leader = l->follower; - else if (l->follower->get_skill(SKILL_LEADERSHIP) > leader->get_skill(SKILL_LEADERSHIP)) - leader = l->follower; - } - } - - if (!leader) - { - return; - } - - // для реследования используем стандартные функции - std::vector temp_list; - for (struct follow_type *n = 0, *l = ch->followers; l; l = n) - { - n = l->next; - if (!is_group_member(ch, l->follower)) - { - continue; - } - else - { - CHAR_DATA *temp_vict = l->follower; - if (temp_vict->has_master() - && stop_follower(temp_vict, SF_SILENCE)) - { - continue; - } - - if (temp_vict != leader) - { - temp_list.push_back(temp_vict); - } - } - } - - // вся эта фигня только для того, чтобы при реследовании пройтись по списку в обратном - // направлении и сохранить относительный порядок следования в группе - if (!temp_list.empty()) - { - for (std::vector::reverse_iterator it = temp_list.rbegin(); it != temp_list.rend(); ++it) - { - leader->add_follower_silently(*it); - } - } - - // бывшего лидера последним закидываем обратно в группу, если он живой - if (vict) - { - // флаг группы надо снять, иначе при регрупе не будет писаться о старом лидере - //AFF_FLAGS(ch).unset(EAffectFlag::AFF_GROUP); - ch->removeGroupFlags(); - leader->add_follower_silently(ch); - } - - if (!leader->followers) - { - return; - } - - ch->dps_copy(leader); - perform_group(leader, leader); - int followers = 0; - for (struct follow_type *f = leader->followers; f; f = f->next) - { - if (followers < max_group_size(leader)) - { - if (perform_group(leader, f->follower)) - ++followers; - } - else - { - send_to_char("Вы больше никого не можете принять в группу.\r\n", ch); - return; - } - } -} - -void do_group(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) -{ - CHAR_DATA *vict; - struct follow_type *f; - int found, f_number; - - argument = one_argument(argument, buf); - - if (!*buf) - { - print_group(ch); - return; - } - if (!str_cmp(buf, "список")) - { - print_list_group(ch); - return; - } - - if (GET_POS(ch) < POS_RESTING) - { - send_to_char("Трудно управлять группой в таком состоянии.\r\n", ch); - return; - } - - if (ch->has_master()) - { - act("Вы не можете управлять группой. Вы еще не ведущий.", FALSE, ch, 0, 0, TO_CHAR); - return; - } - - if (!ch->followers) - { - send_to_char("За вами никто не следует.\r\n", ch); - return; - } - - - -// вычисляем количество последователей - for (f_number = 0, f = ch->followers; f; f = f->next) - { - if (AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP)) - { - f_number++; - } - } - - if (!str_cmp(buf, "all") - || !str_cmp(buf, "все")) - { - perform_group(ch, ch); - for (found = 0, f = ch->followers; f; f = f->next) - { - if ((f_number + found) >= max_group_size(ch)) - { - send_to_char("Вы больше никого не можете принять в группу.\r\n", ch); - return; - } - found += perform_group(ch, f->follower); - } - - if (!found) - { - send_to_char("Все, кто за вами следуют, уже включены в вашу группу.\r\n", ch); - } - - return; - } - else if (!str_cmp(buf, "leader") || !str_cmp(buf, "лидер")) - { - vict = get_player_vis(ch, argument, FIND_CHAR_WORLD); - // added by WorM (Видолюб) Если найден клон и его хозяин персонаж - // а то чото как-то глючно Двойник %1 не является членом вашей группы. - if (vict - && IS_NPC(vict) - && MOB_FLAGGED(vict, MOB_CLONE) - && AFF_FLAGGED(vict, EAffectFlag::AFF_CHARM) - && vict->has_master() - && !IS_NPC(vict->get_master())) - { - if (CAN_SEE(ch, vict->get_master())) - { - vict = vict->get_master(); - } - else - { - vict = NULL; - } - } - - // end by WorM - if (!vict) - { - send_to_char("Нет такого персонажа.\r\n", ch); - return; - } - else if (vict == ch) - { - send_to_char("Вы и так лидер группы...\r\n", ch); - return; - } - else if (!AFF_FLAGGED(vict, EAffectFlag::AFF_GROUP) - || vict->get_master() != ch) - { - send_to_char(ch, "%s не является членом вашей группы.\r\n", GET_NAME(vict)); - return; - } - change_leader(ch, vict); - return; - } - - if (!(vict = get_char_vis(ch, buf, FIND_CHAR_ROOM))) - { - send_to_char(NOPERSON, ch); - } - else if ((vict->get_master() != ch) && (vict != ch)) - { - act("$N2 нужно следовать за вами, чтобы стать членом вашей группы.", FALSE, ch, 0, vict, TO_CHAR); - } - else - { - if (!AFF_FLAGGED(vict, EAffectFlag::AFF_GROUP)) - { - if (AFF_FLAGGED(vict, EAffectFlag::AFF_CHARM) || MOB_FLAGGED(vict, MOB_ANGEL) || MOB_FLAGGED(vict, MOB_GHOST) || IS_HORSE(vict)) - { - send_to_char("Только равноправные персонажи могут быть включены в группу.\r\n", ch); - send_to_char("Только равноправные персонажи могут быть включены в группу.\r\n", vict); - }; - if (f_number >= max_group_size(ch)) - { - send_to_char("Вы больше никого не можете принять в группу.\r\n", ch); - return; - } - perform_group(ch, ch); - perform_group(ch, vict); - } - else if (ch != vict) - { - act("$N исключен$A из состава вашей группы.", FALSE, ch, 0, vict, TO_CHAR); - act("Вы исключены из группы $n1!", FALSE, ch, 0, vict, TO_VICT); - act("$N был$G исключен$A из группы $n1!", FALSE, ch, 0, vict, TO_NOTVICT | TO_ARENA_LISTEN); - //AFF_FLAGS(vict).unset(EAffectFlag::AFF_GROUP); - vict->removeGroupFlags(); - } - } -} - -void do_group2(CHAR_DATA *ch, char *argument, int, int){ - ch->personGroup->processGroupCommands(ch, argument); -} - -void do_ungroup(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) { - struct follow_type *f, *next_fol; - CHAR_DATA *tch; - - one_argument(argument, buf); - - if (ch->has_master() - || !(AFF_FLAGGED(ch, EAffectFlag::AFF_GROUP))) - { - send_to_char("Вы же не лидер группы!\r\n", ch); - return; - } - - if (!*buf) - { - sprintf(buf2, "Вы исключены из группы %s.\r\n", GET_PAD(ch, 1)); - for (f = ch->followers; f; f = next_fol) - { - next_fol = f->next; - if (AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP)) - { - //AFF_FLAGS(f->follower).unset(EAffectFlag::AFF_GROUP); - f->follower->removeGroupFlags(); - send_to_char(buf2, f->follower); - if (!AFF_FLAGGED(f->follower, EAffectFlag::AFF_CHARM) - && !(IS_NPC(f->follower) - && AFF_FLAGGED(f->follower, EAffectFlag::AFF_HORSE))) - { - stop_follower(f->follower, SF_EMPTY); - } - } - } - AFF_FLAGS(ch).unset(EAffectFlag::AFF_GROUP); - ch->removeGroupFlags(); - send_to_char("Вы распустили группу.\r\n", ch); - return; - } - for (f = ch->followers; f; f = next_fol) - { - next_fol = f->next; - tch = f->follower; - if (isname(buf, tch->get_pc_name()) - && !AFF_FLAGGED(tch, EAffectFlag::AFF_CHARM) - && !IS_HORSE(tch)) - { - //AFF_FLAGS(tch).unset(EAffectFlag::AFF_GROUP); - tch->removeGroupFlags(); - act("$N более не член вашей группы.", FALSE, ch, 0, tch, TO_CHAR); - act("Вы исключены из группы $n1!", FALSE, ch, 0, tch, TO_VICT); - act("$N был$G изгнан$A из группы $n1!", FALSE, ch, 0, tch, TO_NOTVICT | TO_ARENA_LISTEN); - stop_follower(tch, SF_EMPTY); - return; - } - } - send_to_char("Этот игрок не входит в состав вашей группы.\r\n", ch); - return; -} - -void do_report(CHAR_DATA *ch, char* /*argument*/, int/* cmd*/, int/* subcmd*/) -{ - CHAR_DATA *k; - struct follow_type *f; - - if (!AFF_FLAGGED(ch, EAffectFlag::AFF_GROUP) && !AFF_FLAGGED(ch, EAffectFlag::AFF_CHARM)) - { - send_to_char("И перед кем вы отчитываетесь?\r\n", ch); - return; - } - if (ch->is_druid()) - { - sprintf(buf, "%s доложил%s : %d(%d)H, %d(%d)V, %d(%d)M\r\n", - GET_NAME(ch), GET_CH_SUF_1(ch), - GET_HIT(ch), GET_REAL_MAX_HIT(ch), - GET_MOVE(ch), GET_REAL_MAX_MOVE(ch), - GET_MANA_STORED(ch), GET_MAX_MANA(ch)); - } - else if (AFF_FLAGGED(ch, EAffectFlag::AFF_CHARM)) - { - int loyalty = 0; - for (const auto& aff : ch->affected) - { - if (aff->type == SPELL_CHARM) - { - loyalty = aff->duration / 2; - break; - } - } - sprintf(buf, "%s доложил%s : %d(%d)H, %d(%d)V, %dL\r\n", - GET_NAME(ch), GET_CH_SUF_1(ch), - GET_HIT(ch), GET_REAL_MAX_HIT(ch), - GET_MOVE(ch), GET_REAL_MAX_MOVE(ch), - loyalty); - } - else - { - sprintf(buf, "%s доложил%s : %d(%d)H, %d(%d)V\r\n", - GET_NAME(ch), GET_CH_SUF_1(ch), - GET_HIT(ch), GET_REAL_MAX_HIT(ch), - GET_MOVE(ch), GET_REAL_MAX_MOVE(ch)); - } - CAP(buf); - k = ch->has_master() ? ch->get_master() : ch; - for (f = k->followers; f; f = f->next) - { - if (AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP) - && f->follower != ch - && !AFF_FLAGGED(f->follower, EAffectFlag::AFF_DEAFNESS)) - { - send_to_char(buf, f->follower); - } - } - - if (k != ch && !AFF_FLAGGED(k, EAffectFlag::AFF_DEAFNESS)) - { - send_to_char(buf, k); - } - send_to_char("Вы доложили о состоянии всем членам вашей группы.\r\n", ch); -} +extern GroupRoster& groupRoster; Group::Group(CHAR_DATA *leader, u_long uid){ this->_memberCap = IS_NPC(leader)? 255 : max_group_size(leader); this->_uid = uid; - // заодно иничим остальные поля - GroupManager mgr = this->getMemberManager(); - mgr->addMember(leader); this->setLeader(leader); -} - + this->_memberManager = mm_ptr (new MemberManager(leader, grp_ptr(this))); + _memberManager->addMember(leader); - -void Group::removeMember(CHAR_DATA *member) { - if (this->_currentMemberCount == 0) { - sprintf(buf, "ROSTER: попытка удалить из группы при текущем индексе 0, id=%lu", this->_uid); - mudlog(buf, BRF, LVL_IMMORT, SYSLOG, TRUE); - return; - } - auto it = this->_memberList.find(member->get_uid()); - if (it != this->_memberList.end()) - this->_memberList.erase(it); - member->personGroup = nullptr; } -CHAR_DATA *Group::getLeader() const { +CHAR_DATA *Group::getLeader() { return _leader; } @@ -757,12 +33,6 @@ bool Group::isActive() { return false; } -bool Group::checkMember(int uid) { - return false; -} - -MemberManager::MemberManager(const std::map &memberList) : _memberList(memberList) {} - u_long Group::getUid() const { return _uid; } @@ -775,145 +45,30 @@ int Group::getMemberCap() const { return _memberCap; } -Group::~Group() { - // чистим ссылки - for (auto & it : this->_memberList){ - if (!it.second->member || it.second->member->purged()) - continue; - if (it.second->member != this->_leader) - send_to_char(it.second->member, "Ваша группа была распущена.\r\n"); - it.second->member->personGroup = nullptr; - } -} - -bool Group::approveRequest(char *applicant) { - if ((int)this->_memberList.size() == this->_memberCap){ - send_to_char(this->_leader, "Чтобы принять кого нужного, надо сперва исключить кого ненужного!\r\n"); - return false; - } - return true; -} - void Group::printGroup(CHAR_DATA *requestor) { } -void Group::processGroupCommands(CHAR_DATA *ch, char *argument) { - enum SubCmd { PRINT, HELP, LIST, MAKE, INVITE, TAKE, REJECT, EXPELL, LEADER, LEAVE, DISBAND}; - SubCmd mode; - Group * grp; - - const std::string strHELP = "справка помощь"; - const std::string strMAKE = "создать make"; - const std::string strLIST = "список list"; - const std::string strINVITE = "пригласить invite"; - const std::string strTAKE = "принять approve"; - const std::string strREJECT = "отклонить reject"; - const std::string strEXPELL = "выгнать expell"; - const std::string strLEADER = "лидер leader"; - const std::string strLEAVE = "покинуть leave"; - const std::string strDISBAND = "распустить disband"; - char subcmd[MAX_INPUT_LENGTH], target[MAX_INPUT_LENGTH]; - - if (!ch || IS_NPC(ch)) - return; - - two_arguments(argument, subcmd, target); - - // выбираем режим - if (!*subcmd) - mode = PRINT; - else if (isname(subcmd, strHELP.c_str())) - mode = HELP; - else if (isname(subcmd, strMAKE.c_str())) - mode = MAKE; - else if (isname(subcmd, strLIST.c_str())) - mode = LIST; - else if (isname(subcmd, strINVITE.c_str())) - mode = INVITE; - else if (isname(subcmd, strTAKE.c_str())) - mode = TAKE; - else if (isname(subcmd, strREJECT.c_str())) - mode = REJECT; - else if (isname(subcmd, strEXPELL.c_str())) - mode = EXPELL; - else if (isname(subcmd, strLEADER.c_str())) - mode = LEADER; - else if (isname(subcmd, strLEAVE.c_str())) - mode = LEAVE; - else if (isname(subcmd, strDISBAND.c_str())) - mode = DISBAND; - else { - send_to_char("Уточните команду.\r\n", ch); - return; - } - grp = ch->personGroup; - - if (mode == MAKE) { - if (grp && grp->getLeader() == ch){ - send_to_char(ch, "Только древний правитель Цесарь Иулий мог водить много легионов!\r\n"); - return; - } - if (grp) // в группе - покидаем - grp->removeMember(ch); - groupRoster.addGroup(ch); - send_to_char(ch, "Вы создали группу id:%lu, максимальное число последователей: %d\r\n",ch->personGroup->getUid(), ch->personGroup->getMemberCap()); - return; - } - - if (!grp || grp->getLeader() != ch ) { - send_to_char(ch, "Необходимо быть лидером группы для этого.\r\n"); - return; - } - if (GET_POS(ch) < POS_RESTING) { - send_to_char("Трудно управлять группой в таком состоянии.\r\n", ch); - return; - } - - // MAKE был раньше, т.к. он создает группу - switch (mode) { - case PRINT: - grp->printGroup(ch); - break; - case HELP: - break; - case LIST: - //grp->listMembers(ch); - break; - case INVITE: - if (this->isFull()){ - send_to_char(ch, "Командир, но в твоей группе больше нет места!\r\n"); - return; - } - groupRoster.makeInvite(target); - break; - case TAKE: - //grp->approveRequest(target); - break; - case REJECT: - //grp->denyRequest(target); - break; - case EXPELL: - //grp->expellMember(target); - break; - case LEADER: - //grp->promote(target); - break; - case LEAVE: - //grp->leave(ch); - break; - case DISBAND: - groupRoster.removeGroup(grp->getUid()); - break; - } -} const std::string &Group::getLeaderName() const { return _leaderName; } bool Group::isFull() { - if (this->_currentMemberCount == this->_memberCap) + if (this->_memberManager->getMemberCount() == this->_memberCap) return true; return false; } + +void Group::sendToGroup(GRP_COMM mode, const char *msg, ...) { + + +} + +void Group::disband(bool silentMode) { + _memberManager->clear(silentMode); +}; + +Group::~Group() { + mudlog("[~Group]", BRF, LVL_IMMORT, SYSLOG, TRUE); +} \ No newline at end of file diff --git a/src/grp/grp.group.h b/src/grp/grp.group.h deleted file mode 100644 index 7536d852a..000000000 --- a/src/grp/grp.group.h +++ /dev/null @@ -1,57 +0,0 @@ -#ifndef BYLINS_GRP_GROUP_H -#define BYLINS_GRP_GROUP_H - -#include "chars/char.hpp" -#include "grp.membermanager.h" - -void change_leader(CHAR_DATA *ch, CHAR_DATA *vict); -void do_group(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/); -void do_group2(CHAR_DATA *ch, char *argument, int, int); -void do_ungroup(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/); -void do_report(CHAR_DATA *ch, char* /*argument*/, int/* cmd*/, int/* subcmd*/); -int max_group_size(CHAR_DATA *ch); - -enum GRP_COMM {GRP_COMM_LEADER, GRP_COMM_ALL}; - - -class Group { -private: - // ид группы в ростере - u_long _uid = 0; - // текущее количество игроков - u_short _currentMemberCount = 0; - //макс.количество игроков - int _memberCap = 0; - // ид лидера - int _leaderUID; - // имя лидера - std::string _leaderName; - // ссылка на персонажа, АХТУНГ! Может меняться и быть невалидным - CHAR_DATA* _leader; - // состав группы, ключом UID персонажа. Дохлые/вышедшие проверяем отдельным методом - MemberManager * _memberManager; -public: - MemberManager *getMemberManager() const {return _memberManager;} -public: - u_long getUid() const; - u_short getCurrentMemberCount() const; - const std::string &getLeaderName() const; - CHAR_DATA *getLeader() const; - void setLeader(CHAR_DATA *leader); - int getMemberCap() const; - virtual ~Group(); - bool isFull(); - Group(CHAR_DATA *leader, u_long uid); - void printGroup(CHAR_DATA *requestor); - void listMembers(CHAR_DATA *requestor); - bool isActive(); // проверка, что в группе все персонажи онлайн - - // обработчик команд группы - void processGroupCommands(CHAR_DATA *ch, char *argument); - - bool promote(char *applicant); - void sendToGroup(GRP_COMM mode, const char *msg, ...); -}; - - -#endif //BYLINS_GRP_GROUP_H diff --git a/src/grp/grp.groupmanager.cpp b/src/grp/grp.groupmanager.cpp index 31da892d5..65fd7673c 100644 --- a/src/grp/grp.groupmanager.cpp +++ b/src/grp/grp.groupmanager.cpp @@ -1,48 +1,59 @@ // // Created by ubuntu on 23.12.2020. // +#include "grp.main.h" -#include "grp.groupmanager.h" - -Group* GroupManager::addGroup(CHAR_DATA * leader) { +grp_ptr GroupManager::addGroup(CHAR_DATA * leader) { ++this->_currentGroupIndex; - auto *group = new Group(leader, _currentGroupIndex); - this->_groupList.emplace(_currentGroupIndex, new GroupRosterRecord(group)); + auto group = grp_ptr (new Group(leader, _currentGroupIndex)) ; + this->_groupList.emplace(_currentGroupIndex, group); + leader->send_to_TC(true, false, false, "addGroup: Размер списка: %lu\r\n", this->_groupList.size()); return group; } void GroupManager::removeGroup(u_long uid) { auto grp = _groupList.find(uid); - if (grp != _groupList.end()) - { - delete grp->second->member; + if (grp != _groupList.end()) { + grp->second->disband(false); _groupList.erase(uid); + return; } } -Group *GroupManager::findGroup(CHAR_DATA *ch) { +grp_ptr GroupManager::findGroup(CHAR_DATA *ch) { if (ch == nullptr || ch->purged()){ sprintf(buf, "GroupManager::findGroup: вызов для пустого или пурженого персонажа\r\n"); mudlog(buf, BRF, LVL_IMMORT, SYSLOG, TRUE); return nullptr; } for (auto & it : this->_groupList){ - auto mgr = it.second->member->getMemberManager(); + auto mgr = it.second->getMemberManager(); if (mgr->findMember(ch->get_uid())) - return it.second->member; + return it.second; } return nullptr; } -Group *GroupManager::findGroup(char *leaderName) { +grp_ptr GroupManager::findGroup(char *leaderName) { if (leaderName == nullptr){ sprintf(buf, "GroupManager::findGroup: leaderName is null\r\n"); mudlog(buf, BRF, LVL_IMMORT, SYSLOG, TRUE); return nullptr; } for (auto & it : this->_groupList) { - if (str_cmp(it.second->member->getLeaderName().c_str(), leaderName)) - return it.second->member; + if (str_cmp(it.second->getLeaderName().c_str(), leaderName)) + return it.second; } return nullptr; } + + +void GroupManager::printList(CHAR_DATA *ch) { + size_t cnt = this->_groupList.size(); + send_to_char(ch, "Текущее количество групп в мире: %lu\r\n", cnt); + for (auto & it : this->_groupList) { + send_to_char(ch, "Группа лидера %s, кол-во участников: %hu\r\n", + it.second->getLeaderName().c_str(), + it.second->getCurrentMemberCount()); + } +} diff --git a/src/grp/grp.groupmanager.h b/src/grp/grp.groupmanager.h deleted file mode 100644 index ad36ae17d..000000000 --- a/src/grp/grp.groupmanager.h +++ /dev/null @@ -1,26 +0,0 @@ -// -// Created by ubuntu on 23.12.2020. -// - -#ifndef BYLINS_GRP_GROUPMANAGER_H -#define BYLINS_GRP_GROUPMANAGER_H - -class GroupRosterRecord { -public: - explicit GroupRosterRecord(Group * group); - Group *member; -}; - - -class GroupManager{ -private: - u_long _currentGroupIndex = 0; - std::map _groupList; -public: - Group* addGroup(CHAR_DATA* leader); - void removeGroup(u_long uid); - Group* findGroup(CHAR_DATA* ch); - Group* findGroup(char* leaderName); -}; - -#endif //BYLINS_GRP_GROUPMANAGER_H diff --git a/src/grp/grp.main.h b/src/grp/grp.main.h new file mode 100644 index 000000000..70f2f8350 --- /dev/null +++ b/src/grp/grp.main.h @@ -0,0 +1,145 @@ +#ifndef BYLINS_GRP_MAIN_H +#define BYLINS_GRP_MAIN_H + +#include +#include "chars/char.hpp" + +enum RQ_TYPE {RQ_GROUP, RQ_PERSON}; +enum GRP_COMM {GRP_COMM_LEADER, GRP_COMM_ALL}; +enum RQ_R {RQ_R_OK, RQ_R_NO_GROUP, RQ_R_OVERFLOW, RQ_REFRESH}; +enum INV_R {INV_R_OK, INV_R_NO_PERSON, INV_R_BUSY, INV_R_REFRESH}; + +void do_grequest(CHAR_DATA *ch, char *argument, int, int); +void change_leader(CHAR_DATA *ch, CHAR_DATA *vict); +void do_group(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/); +void do_group2(CHAR_DATA *ch, char *argument, int, int); +void do_ungroup(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/); +void do_report(CHAR_DATA *ch, char* /*argument*/, int/* cmd*/, int/* subcmd*/); +int max_group_size(CHAR_DATA *ch); +void do_grequest(CHAR_DATA *ch, char *argument, int, int); + + +class Group; +class MemberManager; +class RequestManager; +class GroupManager; + +using mm_ptr = std::shared_ptr; +using rm_ptr = std::shared_ptr; +using gm_ptr = std::shared_ptr; +using grp_ptr = std::shared_ptr; + +struct char_info { + char_info(int memberUid, CHAR_DATA *member, std::string memberName); + virtual ~char_info(); + int memberUID; + CHAR_DATA * member; + std::string memberName; +}; + +class MemberManager { +private: + std::map> _memberList; + grp_ptr _group; +public: + MemberManager(CHAR_DATA* leader, grp_ptr group); + ~MemberManager(); + void addMember(CHAR_DATA *member); + bool removeMember(CHAR_DATA *member); + bool findMember(int uid); + bool restoreMember(CHAR_DATA *member); + u_short getMemberCount() {return (u_short)this->_memberList.size();} + void clear(bool silentMode); // вызывается при расформировании группы +}; + +class Group { +private: + // ид группы в ростере + u_long _uid = 0; + // текущее количество игроков + u_short _currentMemberCount = 0; + //макс.количество игроков + int _memberCap = 0; + // ид лидера + int _leaderUID; + // имя лидера + std::string _leaderName; + // ссылка на персонажа, АХТУНГ! Может меняться и быть невалидным + CHAR_DATA* _leader; + // состав группы, ключом UID персонажа. Дохлые/вышедшие проверяем отдельным методом + mm_ptr _memberManager; + void clear(); +public: + mm_ptr getMemberManager() {return _memberManager;} + u_long getUid() const; + u_short getCurrentMemberCount() const; + const std::string &getLeaderName() const; + CHAR_DATA *getLeader(); + void disband(bool silent); + + ~Group(); + + void setLeader(CHAR_DATA *leader); + int getMemberCap() const; + bool isFull(); + Group(CHAR_DATA *leader, u_long uid); + void printGroup(CHAR_DATA *requestor); + void listMembers(CHAR_DATA *requestor); + bool isActive(); // проверка, что в группе все персонажи онлайн + + bool promote(char *applicant); + void sendToGroup(GRP_COMM mode, const char *msg, ...); +}; + +class Request { +public: + std::chrono::time_point _time; + CHAR_DATA *_applicant; + grp_ptr _group; + std::string _applicantName; + int _applicantUID; + RQ_TYPE _type; + Request(CHAR_DATA* subject, grp_ptr group, RQ_TYPE type); +}; + +class GroupManager{ +private: + u_long _currentGroupIndex = 0; + std::map _groupList; +public: + grp_ptr addGroup(CHAR_DATA* leader); + void removeGroup(u_long uid); + grp_ptr findGroup(CHAR_DATA* ch); + grp_ptr findGroup(char* leaderName); + void printList(CHAR_DATA *ch); +}; + +class RequestManager{ +private: + std::list _requestList; + std::tuple tryMakeInvite(grp_ptr grp, char* member); + std::tuple tryAddRequest(CHAR_DATA* author, char* targetGroup); +public: + RequestManager(); +// методы работы с заявками + void printRequestList(CHAR_DATA* ch); + void makeInvite(CHAR_DATA* leader, char* targetPerson); + void makeRequest(CHAR_DATA* author, char* targetGroup); + void revokeRequest(CHAR_DATA* author, char* targetGroup); +}; + +class GroupRoster { +// properties +private: + rm_ptr _requestManager; + gm_ptr _groupManager; +public: + gm_ptr getGroupManager() const {return _groupManager;} + rm_ptr getRequestManager() const {return _requestManager;} + GroupRoster(); + void restorePlayerGroup(CHAR_DATA* ch); // возвращает игрока в группу после смерти + void processGroupCommands(CHAR_DATA *ch, char *argument); + void printList(CHAR_DATA *ch); +}; + +#endif //BYLINS_GRP_MAIN_H diff --git a/src/grp/grp.membermanager.cpp b/src/grp/grp.membermanager.cpp index 64278cb5c..de57c10ac 100644 --- a/src/grp/grp.membermanager.cpp +++ b/src/grp/grp.membermanager.cpp @@ -1,8 +1,27 @@ // // Created by ubuntu on 23.12.2020. // +#include + +#include "grp.main.h" + +char_info::char_info(int memberUid, CHAR_DATA *member, std::string memberName) : memberUID( + memberUid), member(member), memberName(std::move(memberName)) { +} + +char_info::~char_info() { + mudlog("[~char_info]", BRF, LVL_IMMORT, SYSLOG, TRUE); +} + +MemberManager::MemberManager(CHAR_DATA* leader, grp_ptr group) { + this->_group = std::move(group); + this->addMember(leader); +} + +MemberManager::~MemberManager() { + mudlog("~MemberManager", BRF, LVL_IMMORT, SYSLOG, TRUE); +} -#include "grp.membermanager.h" void MemberManager::addMember(CHAR_DATA *member) { if (IS_NPC(member)) @@ -10,18 +29,60 @@ void MemberManager::addMember(CHAR_DATA *member) { if (member->personGroup == this->_group) return; auto it = this->_memberList.find(member->get_uid()); - if (it == this->_memberList.end()) - this->_memberList.emplace(member->get_uid(), new char_info(member->get_uid(), - member, - member->get_pc_name()) ); + if (it == this->_memberList.end()) { + auto ci = std::shared_ptr(new char_info(member->get_uid(), member, member->get_pc_name())); + this->_memberList.emplace(member->get_uid(), ci); + } else { - sprintf(buf, "ROSTER: группа id=%lu, попытка повторного добавления персонажа с тем же uid", this->_group->getUid()); + sprintf(buf, "MemberManager::addMember: группа id=%lu, попытка повторного добавления персонажа с тем же uid", this->_group->getUid()); mudlog(buf, BRF, LVL_IMMORT, SYSLOG, TRUE); return; } member->personGroup = this->_group; + this->_group->getLeader()->send_to_TC(true, false, false, "Размер списка: %lu\r\n", this->_memberList.size()); } bool MemberManager::restoreMember(CHAR_DATA *member) { - return false; + return false;r } + +bool MemberManager::removeMember(CHAR_DATA *member) { + if (member == nullptr || member->purged()) + return false; + if (this->_memberList.empty()) { + sprintf(buf, "MemberManager::removeMember: попытка удалить из группы при текущем индексе 0, id=%lu", this->_group->getUid()); + mudlog(buf, BRF, LVL_IMMORT, SYSLOG, TRUE); + return false; + } + auto it = this->_memberList.find(member->get_uid()); + if (it == this->_memberList.end()){ + sprintf(buf, "MemberManager::removeMember: персонаж не найден, id=%lu", this->_group->getUid()); + mudlog(buf, BRF, LVL_IMMORT, SYSLOG, TRUE); + return false; + } + this->_memberList.erase(it); + member->personGroup = nullptr; + return true; +} + +bool MemberManager::findMember(int uid) { + auto it = this->_memberList.find(uid); + if (it == this->_memberList.end() ) + return false; + return true; +} + +void MemberManager::clear(bool silentMode) { + sprintf(buf, "[MemberManager::clear()]: _memberList: %lu", _memberList.size()); + mudlog(buf, BRF, LVL_IMMORT, SYSLOG, TRUE); + CHAR_DATA* ch; + for (auto & it : _memberList) { + ch = it.second->member; + if (ch == nullptr || ch->purged()) + continue; + if (!silentMode) + send_to_char(ch, "Ваша группа распущена.\r\n"); + ch->personGroup = nullptr; + } + _memberList.clear(); +} \ No newline at end of file diff --git a/src/grp/grp.membermanager.h b/src/grp/grp.membermanager.h deleted file mode 100644 index e85074a67..000000000 --- a/src/grp/grp.membermanager.h +++ /dev/null @@ -1,31 +0,0 @@ -// -// Created by ubuntu on 23.12.2020. -// - -#ifndef BYLINS_GRP_MEMBERMANAGER_H -#define BYLINS_GRP_MEMBERMANAGER_H - -#include "chars/char.hpp" -#include "grp.roster.h" - -class MemberManager { -private: - struct char_info { - int memberUID; - char_info(int memberUid, CHAR_DATA *member, const std::string &memberName) : memberUID(memberUid), - member(member), - memberName(memberName) {} - CHAR_DATA * member; - std::string memberName; - }; - std::map _memberList; - Group * _group; -public: - MemberManager(const std::map &memberList); - void addMember(CHAR_DATA *member); - void removeMember(CHAR_DATA *member); - bool findMember(int uid); - bool restoreMember(CHAR_DATA *member); -}; - -#endif //BYLINS_GRP_MEMBERMANAGER_H diff --git a/src/grp/grp.request.cpp b/src/grp/grp.request.cpp index 29be4726f..40d641913 100644 --- a/src/grp/grp.request.cpp +++ b/src/grp/grp.request.cpp @@ -2,10 +2,9 @@ // Created by ubuntu on 23.12.2020. // -#include "grp.request.h" -#include "handler.h" +#include "grp.main.h" -Request::Request(CHAR_DATA *subject, Group *group, RQ_TYPE type) { +Request::Request(CHAR_DATA *subject, grp_ptr group, RQ_TYPE type) { if (!subject || !group) return; _applicant = subject; diff --git a/src/grp/grp.request.h b/src/grp/grp.request.h deleted file mode 100644 index 3f4d1f536..000000000 --- a/src/grp/grp.request.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef BYLINS_GRP_REQUEST_H -#define BYLINS_GRP_REQUEST_H - -#include "chars/char.hpp" - -enum RQ_TYPE {RQ_GROUP, RQ_PERSON}; - -class Request { -public: - std::chrono::time_point _time; - CHAR_DATA *_applicant; - Group *_group; - std::string _applicantName; - int _applicantUID; - RQ_TYPE _type; - Request(CHAR_DATA* subject, Group* group, RQ_TYPE type); -}; - - -#endif //BYLINS_GRP_REQUEST_H diff --git a/src/grp/grp.requestmanager.cpp b/src/grp/grp.requestmanager.cpp index bab2b38e4..94e2a26bb 100644 --- a/src/grp/grp.requestmanager.cpp +++ b/src/grp/grp.requestmanager.cpp @@ -2,9 +2,9 @@ // Created by ubuntu on 23.12.2020. // -#include "grp.requestmanager.h" #include "handler.h" #include "global.objects.hpp" +#include "grp/grp.main.h" extern GroupRoster& groupRoster; @@ -37,7 +37,7 @@ void RequestManager::makeInvite(CHAR_DATA *leader, char *targetPerson) { } } -std::tuple RequestManager::tryMakeInvite(Group *grp, char *member) { +std::tuple RequestManager::tryMakeInvite(grp_ptr grp, char *member) { CHAR_DATA *vict = get_player_vis(grp->getLeader(), member, FIND_CHAR_WORLD); if (!vict) return std::make_tuple(INV_R_NO_PERSON, nullptr); if (vict->personGroup != nullptr ) return std::make_tuple(INV_R::INV_R_BUSY, nullptr); @@ -95,7 +95,7 @@ void RequestManager::makeRequest(CHAR_DATA *author, char* target) { send_to_char("И кому же вы хотите напроситься в группу?.\r\n", author); return; } - RequestManager* mgr = groupRoster.getRequestManager(); + std::shared_ptr mgr = groupRoster.getRequestManager(); auto [res, grp] = mgr->tryAddRequest(author, target); switch (res) { case RQ_R::RQ_R_OVERFLOW: @@ -124,7 +124,7 @@ void RequestManager::revokeRequest(CHAR_DATA *ch, char* target) { send_to_char(ch, "заявка не найдена\r\n"); } -std::tuple RequestManager::tryAddRequest(CHAR_DATA *author, char *targetGroup) { +std::tuple RequestManager::tryAddRequest(CHAR_DATA *author, char *targetGroup) { if (!author || author->purged()){ sprintf(buf, "RequestManager::tryAddRequest: author is null or purged\r\n"); mudlog(buf, BRF, LVL_IMMORT, SYSLOG, TRUE); @@ -133,7 +133,7 @@ std::tuple RequestManager::tryAddRequest(CHAR_DATA *author, char if (targetGroup == nullptr){ return std::make_tuple(RQ_R::RQ_R_NO_GROUP, nullptr); } - Group* grp = groupRoster.getGroupManager()->findGroup(targetGroup) ; + grp_ptr grp = groupRoster.getGroupManager()->findGroup(targetGroup) ; if (grp == nullptr) return std::make_tuple(RQ_R::RQ_R_NO_GROUP, nullptr); if (grp->isFull()) @@ -149,3 +149,5 @@ std::tuple RequestManager::tryAddRequest(CHAR_DATA *author, char this->_requestList.emplace(_requestList.end(), Request(author, grp, RQ_PERSON)); return std::make_tuple(RQ_R::RQ_R_OK, grp); } + +RequestManager::RequestManager() = default; diff --git a/src/grp/grp.requestmanager.h b/src/grp/grp.requestmanager.h deleted file mode 100644 index d9b5e0d83..000000000 --- a/src/grp/grp.requestmanager.h +++ /dev/null @@ -1,28 +0,0 @@ -// -// Created by ubuntu on 23.12.2020. -// - -#ifndef BYLINS_GRP_REQUESTMANAGER_H -#define BYLINS_GRP_REQUESTMANAGER_H - -#include "grp.request.h" -#include "chars/char.hpp" - -enum RQ_R {RQ_R_OK, RQ_R_NO_GROUP, RQ_R_OVERFLOW, RQ_REFRESH}; -enum INV_R {INV_R_OK, INV_R_NO_PERSON, INV_R_BUSY, INV_R_REFRESH}; - -class RequestManager{ -private: - std::list _requestList; - std::tuple tryMakeInvite(Group* grp, char* member); - std::tuple tryAddRequest(CHAR_DATA* author, char* targetGroup); -public: - RequestManager(); -// методы работы с заявками - void printRequestList(CHAR_DATA* ch); - void makeInvite(CHAR_DATA* leader, char* targetPerson); - void makeRequest(CHAR_DATA* author, char* targetGroup); - void revokeRequest(CHAR_DATA* author, char* targetGroup); -}; - -#endif //BYLINS_GRP_REQUESTMANAGER_H diff --git a/src/grp/grp.roster.cpp b/src/grp/grp.roster.cpp index 959c2cc91..68f2f2d3d 100644 --- a/src/grp/grp.roster.cpp +++ b/src/grp/grp.roster.cpp @@ -2,19 +2,18 @@ // Created by ubuntu on 17/12/20. // -#include "grp.roster.h" #include "global.objects.hpp" #include "handler.h" GroupRoster& groupRoster = GlobalObjects::groupRoster(); GroupRoster::GroupRoster() { - this->_requestManager = new RequestManager(); - this->_groupManager = new GroupManager(); + this->_requestManager = std::make_shared(RequestManager()); + this->_groupManager = std::make_shared(GroupManager()); } void GroupRoster::restorePlayerGroup(CHAR_DATA *ch) { - Group* grp = this->_groupManager->findGroup(ch); + auto grp = this->_groupManager->findGroup(ch); if (grp == nullptr) return; if (!grp->getMemberManager()->restoreMember(ch)) @@ -23,8 +22,122 @@ void GroupRoster::restorePlayerGroup(CHAR_DATA *ch) { ch->send_to_TC(true, false, true, "Membership in group: %d restored\r\n", ch->personGroup->getUid()); } +void GroupRoster::processGroupCommands(CHAR_DATA *ch, char *argument) { + enum SubCmd { PRINT, ALL, HELP, LIST, MAKE, INVITE, TAKE, REJECT, EXPELL, LEADER, LEAVE, DISBAND}; + SubCmd mode; + + const std::string strHELP = "справка помощь"; + const std::string strMAKE = "создать make"; + const std::string strLIST = "список list"; + const std::string strINVITE = "пригласить invite"; + const std::string strTAKE = "принять approve"; + const std::string strREJECT = "отклонить reject"; + const std::string strEXPELL = "выгнать expell"; + const std::string strLEADER = "лидер leader"; + const std::string strLEAVE = "покинуть leave"; + const std::string strDISBAND = "распустить disband"; + const std::string strALL = "все all"; + char subcmd[MAX_INPUT_LENGTH], target[MAX_INPUT_LENGTH]; + + if (!ch || IS_NPC(ch)) + return; + + two_arguments(argument, subcmd, target); + + // выбираем режим + if (!*subcmd) + mode = PRINT; + else if (isname(subcmd, strALL.c_str())) + mode = ALL; + else if (isname(subcmd, strHELP.c_str())) + mode = HELP; + else if (isname(subcmd, strMAKE.c_str())) + mode = MAKE; + else if (isname(subcmd, strLIST.c_str())) + mode = LIST; + else if (isname(subcmd, strINVITE.c_str())) + mode = INVITE; + else if (isname(subcmd, strTAKE.c_str())) + mode = TAKE; + else if (isname(subcmd, strREJECT.c_str())) + mode = REJECT; + else if (isname(subcmd, strEXPELL.c_str())) + mode = EXPELL; + else if (isname(subcmd, strLEADER.c_str())) + mode = LEADER; + else if (isname(subcmd, strLEAVE.c_str())) + mode = LEAVE; + else if (isname(subcmd, strDISBAND.c_str())) + mode = DISBAND; + else { + send_to_char("Уточните команду.\r\n", ch); + return; + } + if (GET_POS(ch) < POS_RESTING) { + send_to_char("Трудно управлять группой в таком состоянии.\r\n", ch); + return; + } + auto grp = ch->personGroup; + + if (mode == MAKE) { + if (grp != nullptr && grp->getLeader() == ch){ + send_to_char(ch, "Только великим правителям, навроде Цесаря Иулия, было дозволено водить много легионов!\r\n"); + return; + } + if (grp != nullptr) // в группе - покидаем + grp->getMemberManager()->removeMember(ch); + groupRoster.getGroupManager()->addGroup(ch); + send_to_char(ch, "Вы создали группу c максимальным числом последователей %d.\r\n", ch->personGroup->getMemberCap()); + return; + } + + if (grp != nullptr && grp->getLeader() != ch ) { + send_to_char(ch, "Необходимо быть лидером группы для этого.\r\n"); + return; + } -GroupRosterRecord::GroupRosterRecord(Group *group) { - this->member = group; -} + // MAKE был раньше, т.к. он создает группу + switch (mode) { + case MAKE: + break; + case PRINT: + grp->printGroup(ch); + break; + case ALL: + if (!IS_IMMORTAL(ch)) + return; + groupRoster.getGroupManager()->printList(ch); + break; + case HELP: + break; + case LIST: + //grp->listMembers(ch); + break; + case INVITE: + if (grp->isFull()){ + send_to_char(ch, "Командир, но в твоей группе больше нет места!\r\n"); + return; + } + groupRoster.getRequestManager()->makeInvite(ch, target); + break; + case TAKE: + //grp->approveRequest(target); + break; + case REJECT: + //grp->denyRequest(target); + break; + case EXPELL: + //grp->expellMember(target); + break; + case LEADER: + //grp->promote(target); + break; + case LEAVE: + //grp->leave(ch); + break; + case DISBAND: + groupRoster.getGroupManager()->removeGroup(grp->getUid()); + break; + } +} \ No newline at end of file diff --git a/src/grp/grp.roster.h b/src/grp/grp.roster.h deleted file mode 100644 index 9cf70378f..000000000 --- a/src/grp/grp.roster.h +++ /dev/null @@ -1,33 +0,0 @@ -// -// Created by ubuntu on 17/12/20. -// - -#ifndef BYLINS_GRP_ROSTER_H -#define BYLINS_GRP_ROSTER_H - -#include "chars/char.hpp" -#include "grp.group.h" -#include "grp.requestmanager.h" -#include "grp.groupmanager.h" - - -void do_grequest(CHAR_DATA *ch, char *argument, int, int); - -class GroupRoster { -private: // fields - -private: // methods - -// properties -private: - RequestManager* _requestManager; - GroupManager* _groupManager; -public: - GroupManager* getGroupManager() const {return _groupManager;} - RequestManager* getRequestManager() const {return _requestManager;} -public: - GroupRoster(); - void restorePlayerGroup(CHAR_DATA* ch); // возвращает игрока в группу после смерти -}; - -#endif //BYLINS_GRP_ROSTER_H diff --git a/src/interpreter.cpp b/src/interpreter.cpp index 6a57cb093..567e2c649 100644 --- a/src/interpreter.cpp +++ b/src/interpreter.cpp @@ -21,13 +21,7 @@ #include "chars/char.hpp" #include "chars/char_player.hpp" #include "chars/world.characters.hpp" -#include "cmd/hire.h" -#include "cmd/mercenary.h" -#include "cmd/order.h" -#include "cmd/quit.h" -#include "cmd/retreat.h" -#include "cmd/telegram.h" -#include "cmd/track.h" +#include "cmd/cmd.generic.h" #include "comm.h" #include "constants.h" #include "craft.commands.hpp" @@ -43,7 +37,6 @@ #include "glory.hpp" #include "glory_const.hpp" #include "glory_misc.hpp" -#include "grp/grp.group.h" #include "handler.h" #include "heartbeat.commands.hpp" #include "house.h" @@ -594,7 +587,7 @@ cpp_extern const struct command_info cmd_info[] = {"кричать", POS_RESTING, do_gen_comm, 0, SCMD_SHOUT, -1}, {"кто", POS_RESTING, do_who, 0, 0, 0}, {"ктодружина", POS_RESTING, DoWhoClan, 0, 0, 0}, - {"ктоя", POS_DEAD, do_gen_ps, 0, SCMD_WHOAMI, 0}, + {"ктоя", POS_DEAD, do_whoami, 0, 0, 0}, {"купить", POS_STANDING, do_not_here, 0, 0, -1}, {"леваярука", POS_RESTING, do_grab, 1, 0, 300}, @@ -1042,7 +1035,7 @@ cpp_extern const struct command_info cmd_info[] = {"whirl", POS_FIGHTING, do_iron_wind, 0, 0, -1}, {"whisper", POS_RESTING, do_spec_comm, 0, SCMD_WHISPER, -1}, {"who", POS_RESTING, do_who, 0, 0, 0}, - {"whoami", POS_DEAD, do_gen_ps, 0, SCMD_WHOAMI, 0}, + {"whoami", POS_DEAD, do_whoami, 0, 0, 0}, {"wield", POS_RESTING, do_wield, 0, 0, 500}, {"wimpy", POS_DEAD, do_wimpy, 0, 0, 0}, {"withdraw", POS_STANDING, do_not_here, 1, 0, -1}, diff --git a/src/magic.cpp b/src/magic.cpp index e88552887..a18f919ca 100644 --- a/src/magic.cpp +++ b/src/magic.cpp @@ -14,7 +14,7 @@ #include "magic.h" -#include "cmd/hire.h" +#include "cmd/cmd.generic.h" #include "core/affect_data.h" #include "action.targeting.hpp" #include "chars/world.characters.hpp" diff --git a/src/obj.hpp b/src/obj.hpp index ca6265169..96feb4237 100644 --- a/src/obj.hpp +++ b/src/obj.hpp @@ -12,13 +12,14 @@ #include "structs.h" #include "sysdep.h" #include "conf.h" -#include "cmd/telegram.h" #include #include #include #include +class TelegramBot; + std::string print_obj_affects(const obj_affected_type &affect); void print_obj_affects(CHAR_DATA *ch, const obj_affected_type &affect); diff --git a/src/spec_assign.cpp b/src/spec_assign.cpp index 098dd2b2f..a186babe4 100644 --- a/src/spec_assign.cpp +++ b/src/spec_assign.cpp @@ -13,7 +13,7 @@ ************************************************************************ */ #include "object.prototypes.hpp" -#include "cmd/mercenary.h" +#include "cmd/cmd.generic.h" #include "conf.h" #include "sysdep.h" #include "structs.h" diff --git a/src/spec_procs.cpp b/src/spec_procs.cpp index 0105df0d0..958d75ebd 100644 --- a/src/spec_procs.cpp +++ b/src/spec_procs.cpp @@ -13,7 +13,7 @@ ************************************************************************ */ #include "act.movement.hpp" -#include "cmd/mercenary.h" +#include "cmd/cmd.generic.h" #include "obj.hpp" #include "comm.h" #include "interpreter.h" diff --git a/src/spells.cpp b/src/spells.cpp index a07e5ed6d..75ea63091 100644 --- a/src/spells.cpp +++ b/src/spells.cpp @@ -15,7 +15,7 @@ #include "spells.h" #include "skills/townportal.h" -#include "cmd/hire.h" +#include "cmd/cmd.generic.h" #include "coredump.hpp" #include "world.objects.hpp" From 2142bdc9b30a1061dd6ae8148112ced36a58a6ea Mon Sep 17 00:00:00 2001 From: bikbai Date: Sun, 27 Dec 2020 01:29:06 +0300 Subject: [PATCH 06/40] =?UTF-8?q?=D1=87=D0=B0=D1=81=D1=82=D1=8C=20=D1=82?= =?UTF-8?q?=D1=80=D0=B5=D1=82=D1=8C=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 5 +- src/act.informative.cpp | 2 +- src/chars/char.hpp | 2 +- src/cmd/quit.cpp | 2 +- src/cmd/whoami.cpp | 2 +- src/grp/grp.cmdprocessor.cpp | 7 +- src/grp/grp.group.cpp | 153 ++++++++++++++++++---- src/grp/grp.groupmanager.cpp | 59 --------- src/grp/grp.main.h | 96 +++++--------- src/grp/grp.membermanager.cpp | 88 ------------- src/grp/grp.request.cpp | 2 +- src/grp/grp.requestmanager.cpp | 153 ---------------------- src/grp/grp.roster.cpp | 231 ++++++++++++++++++++++++++++++--- 13 files changed, 387 insertions(+), 415 deletions(-) delete mode 100644 src/grp/grp.groupmanager.cpp delete mode 100644 src/grp/grp.membermanager.cpp delete mode 100644 src/grp/grp.requestmanager.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 154fbcc94..9b5224f4e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -105,10 +105,7 @@ set(SOURCES src/grp/follow.cpp src/grp/grp.cmdprocessor.cpp src/grp/grp.group.cpp - src/grp/grp.groupmanager.cpp - src/grp/grp.membermanager.cpp src/grp/grp.request.cpp - src/grp/grp.requestmanager.cpp src/grp/grp.roster.cpp src/handler.cpp src/heartbeat.commands.cpp @@ -214,7 +211,7 @@ set(SOURCES src/world.objects.cpp src/zedit.cpp src/zone.table.cpp - ) + ) set(HEADERS src/grp/follow.h diff --git a/src/act.informative.cpp b/src/act.informative.cpp index 4534d3177..ba506f0ab 100644 --- a/src/act.informative.cpp +++ b/src/act.informative.cpp @@ -61,7 +61,7 @@ #include "sysdep.h" #include "bonus.h" #include "conf.h" -#include "grp/grp.main.h" + #include #include diff --git a/src/chars/char.hpp b/src/chars/char.hpp index 567313bb6..bb426d5a7 100644 --- a/src/chars/char.hpp +++ b/src/chars/char.hpp @@ -837,7 +837,7 @@ class CHAR_DATA : public ProtectedCharacterData bool isHorsePrevents(); void dismount(); public: - grp_ptr personGroup; + Group* personGroup; }; inline const player_special_data::ignores_t& CHAR_DATA::get_ignores() const diff --git a/src/cmd/quit.cpp b/src/cmd/quit.cpp index 7e99e5e4e..2b40262ac 100644 --- a/src/cmd/quit.cpp +++ b/src/cmd/quit.cpp @@ -57,7 +57,7 @@ void do_quit(CHAR_DATA *ch, char *argument, int/* cmd*/, int subcmd) Depot::exit_char(ch); Clan::clan_invoice(ch, false); if (ch->personGroup) - ch->personGroup->getMemberManager()->removeMember(ch); + ch->personGroup->removeMember(ch); /* * kill off all sockets connected to the same player as the one who is diff --git a/src/cmd/whoami.cpp b/src/cmd/whoami.cpp index 68875fe7c..3c009fe59 100644 --- a/src/cmd/whoami.cpp +++ b/src/cmd/whoami.cpp @@ -42,5 +42,5 @@ void do_whoami(CHAR_DATA *ch, char *, int, int) { send_to_char(ch, "Подключен Телеграм, chat_id: %lu\r\n", ch->player_specials->saved.telegram_id); } if (ch->personGroup) - send_to_char(ch, "Стоит в группе #%d лидера %s\r\n", ch->personGroup->getUid(), ch->personGroup->getLeaderName().c_str()); + send_to_char(ch, "Состоит в группе #%d лидера %s\r\n", ch->personGroup->getUid(), ch->personGroup->getLeaderName().c_str()); } diff --git a/src/grp/grp.cmdprocessor.cpp b/src/grp/grp.cmdprocessor.cpp index 96e4089f8..4893e814c 100644 --- a/src/grp/grp.cmdprocessor.cpp +++ b/src/grp/grp.cmdprocessor.cpp @@ -16,7 +16,6 @@ void do_grequest(CHAR_DATA *ch, char *argument, int, int){ // гзаявка создать Верий - отправляет заявку в группу // гзаявка отменить Верий - отменяет заявку в группу - auto rm = groupRoster.getRequestManager(); two_arguments(argument, subcmd, target); /*печать перечня заявок*/ if (!*subcmd) { @@ -27,17 +26,17 @@ void do_grequest(CHAR_DATA *ch, char *argument, int, int){ return; } else if (isname(subcmd, "список list")) { - rm->printRequestList(ch); + groupRoster.printRequestList(ch); return; } /*создание заявки*/ else if (isname(subcmd, "создать отправить make send")) { - rm->makeRequest(ch, target); + groupRoster.makeRequest(ch, target); return; } /*отзыв заявки*/ else if (isname(subcmd, "отменить отозвать cancel revoke")){ - rm->revokeRequest(ch, target); + groupRoster.revokeRequest(ch, target); return; } else { diff --git a/src/grp/grp.group.cpp b/src/grp/grp.group.cpp index 771387e63..5a52de5ac 100644 --- a/src/grp/grp.group.cpp +++ b/src/grp/grp.group.cpp @@ -1,35 +1,35 @@ +#include + #include "comm.h" #include "handler.h" -#include "magic.h" #include "msdp.constants.hpp" -#include "screen.h" -#include "global.objects.hpp" +//#include "global.objects.hpp" #include "grp.main.h" extern GroupRoster& groupRoster; Group::Group(CHAR_DATA *leader, u_long uid){ - this->_memberCap = IS_NPC(leader)? 255 : max_group_size(leader); - this->_uid = uid; - this->setLeader(leader); - this->_memberManager = mm_ptr (new MemberManager(leader, grp_ptr(this))); - _memberManager->addMember(leader); - + _leaderUID = 0; // чтобы не ругался clion =) + _memberCap = 0; + _uid = uid; + addMember(leader); + _setLeader(leader); } CHAR_DATA *Group::getLeader() { return _leader; } -void Group::setLeader(CHAR_DATA *leader) { - this->_leaderUID = leader->get_uid(); +void Group::_setLeader(CHAR_DATA *leader) { + _leaderUID = leader->get_uid(); _leader = leader; - _leaderName = leader->get_pc_name(); + _leaderName.assign(leader->get_pc_name()); + _memberCap = IS_NPC(leader)? 255 : max_group_size(leader); } -bool Group::isActive() { +bool Group::_isActive() { return false; } @@ -38,10 +38,10 @@ u_long Group::getUid() const { } u_short Group::getCurrentMemberCount() const { - return _currentMemberCount; + return (u_short)_memberList.size(); } -int Group::getMemberCap() const { +int Group::_getMemberCap() const { return _memberCap; } @@ -49,13 +49,12 @@ void Group::printGroup(CHAR_DATA *requestor) { } - const std::string &Group::getLeaderName() const { return _leaderName; } -bool Group::isFull() { - if (this->_memberManager->getMemberCount() == this->_memberCap) +bool Group::_isFull() { + if (_memberList.size() >= _memberCap) return true; return false; } @@ -65,10 +64,116 @@ void Group::sendToGroup(GRP_COMM mode, const char *msg, ...) { } -void Group::disband(bool silentMode) { - _memberManager->clear(silentMode); -}; - Group::~Group() { - mudlog("[~Group]", BRF, LVL_IMMORT, SYSLOG, TRUE); -} \ No newline at end of file + mudlog("~Group", BRF, LVL_IMMORT, SYSLOG, TRUE); +} + +char_info::char_info(int uid, CHAR_DATA *m, std::string name) { + memberUID = uid; + member = m; + memberName.assign(name); +} + +char_info::~char_info() { + mudlog("[~char_info]", BRF, LVL_IMMORT, SYSLOG, TRUE); +} + +void Group::addMember(CHAR_DATA *member) { + if (IS_NPC(member)) + return; + if (member->personGroup == this) + return; + auto it = _memberList.find(member->get_uid()); + if (it == _memberList.end()) { + auto ci = std::make_shared(member->get_uid(), member, member->get_pc_name()); + _memberList.emplace(member->get_uid(), ci); + } + else { + sprintf(buf, "Group::addMember: группа id=%lu, попытка повторного добавления персонажа с тем же uid", getUid()); + mudlog(buf, BRF, LVL_IMMORT, SYSLOG, TRUE); + return; + } + member->personGroup = this; +// getLeader()->send_to_TC(true, false, false, "Размер списка: %lu\r\n", _memberList.size()); + +} + +bool Group::restoreMember(CHAR_DATA *member) { + return false; +} + +bool Group::removeMember(CHAR_DATA *member) { + if (member == nullptr || member->purged()) + return false; + if (_memberList.empty()) { + sprintf(buf, "Group::removeMember: попытка удалить из группы при текущем индексе 0, id=%lu", getUid()); + mudlog(buf, BRF, LVL_IMMORT, SYSLOG, TRUE); + return false; + } + auto it = _memberList.find(member->get_uid()); + if (it == _memberList.end()){ + sprintf(buf, "Group::removeMember: персонаж не найден, id=%lu", getUid()); + mudlog(buf, BRF, LVL_IMMORT, SYSLOG, TRUE); + return false; + } + _memberList.erase(it); + member->personGroup = nullptr; + return true; +} + +bool Group::_isMember(int uid) { + auto it = _memberList.find(uid); + if (it == _memberList.end() ) + return false; + return true; +} + +void Group::clear(bool silentMode) { + sprintf(buf, "[Group::clear()]: _memberList: %lu", _memberList.size()); + mudlog(buf, BRF, LVL_IMMORT, SYSLOG, TRUE); + CHAR_DATA* ch; + for (auto & it : _memberList) { + ch = it.second->member; + if (ch == nullptr || ch->purged()) + continue; + if (!silentMode) + send_to_char(ch, "Ваша группа распущена.\r\n"); + ch->personGroup = nullptr; + } + _memberList.clear(); +} + +void Group::promote(char *applicant) { + int memberId = _findMember(applicant); + if (memberId == 0) { + sendToGroup(GRP_COMM_LEADER, "Нет такого персонажа."); + return; + } + _setLeader(_memberList.at(memberId)->member); + sendToGroup(GRP_COMM_ALL, "Изменился лидер группы на %.", _memberList.at(memberId)->memberName.c_str()); + if (u_short diff = _memberCap - (int)_memberList.size() >0){ + u_short list[diff]; u_short i = 0; + for (auto it = _memberList.rend(); it != _memberList.rbegin(); --it){ + ++i; + if (it->second->member == _leader ) + continue; + list[i-1] = it->second->memberUID; + if (diff == i) + break; + } + for (i = 0; i <= diff; i++) + removeMember(_memberList.find(list[i])->second->member); + } +} + +int Group::_findMember(char *memberName) { + for (auto & it : _memberList) { + if (!str_cmp(it.second->memberName, memberName)) + return it.first; + } + return 0; +} + +void Group::approveRequest(char *applicant) { + +} diff --git a/src/grp/grp.groupmanager.cpp b/src/grp/grp.groupmanager.cpp deleted file mode 100644 index 65fd7673c..000000000 --- a/src/grp/grp.groupmanager.cpp +++ /dev/null @@ -1,59 +0,0 @@ -// -// Created by ubuntu on 23.12.2020. -// -#include "grp.main.h" - -grp_ptr GroupManager::addGroup(CHAR_DATA * leader) { - ++this->_currentGroupIndex; - auto group = grp_ptr (new Group(leader, _currentGroupIndex)) ; - this->_groupList.emplace(_currentGroupIndex, group); - leader->send_to_TC(true, false, false, "addGroup: Размер списка: %lu\r\n", this->_groupList.size()); - return group; -} - -void GroupManager::removeGroup(u_long uid) { - auto grp = _groupList.find(uid); - if (grp != _groupList.end()) { - grp->second->disband(false); - _groupList.erase(uid); - return; - } -} - -grp_ptr GroupManager::findGroup(CHAR_DATA *ch) { - if (ch == nullptr || ch->purged()){ - sprintf(buf, "GroupManager::findGroup: вызов для пустого или пурженого персонажа\r\n"); - mudlog(buf, BRF, LVL_IMMORT, SYSLOG, TRUE); - return nullptr; - } - for (auto & it : this->_groupList){ - auto mgr = it.second->getMemberManager(); - if (mgr->findMember(ch->get_uid())) - return it.second; - } - return nullptr; -} - -grp_ptr GroupManager::findGroup(char *leaderName) { - if (leaderName == nullptr){ - sprintf(buf, "GroupManager::findGroup: leaderName is null\r\n"); - mudlog(buf, BRF, LVL_IMMORT, SYSLOG, TRUE); - return nullptr; - } - for (auto & it : this->_groupList) { - if (str_cmp(it.second->getLeaderName().c_str(), leaderName)) - return it.second; - } - return nullptr; -} - - -void GroupManager::printList(CHAR_DATA *ch) { - size_t cnt = this->_groupList.size(); - send_to_char(ch, "Текущее количество групп в мире: %lu\r\n", cnt); - for (auto & it : this->_groupList) { - send_to_char(ch, "Группа лидера %s, кол-во участников: %hu\r\n", - it.second->getLeaderName().c_str(), - it.second->getCurrentMemberCount()); - } -} diff --git a/src/grp/grp.main.h b/src/grp/grp.main.h index 70f2f8350..a87160fff 100644 --- a/src/grp/grp.main.h +++ b/src/grp/grp.main.h @@ -5,7 +5,7 @@ #include "chars/char.hpp" enum RQ_TYPE {RQ_GROUP, RQ_PERSON}; -enum GRP_COMM {GRP_COMM_LEADER, GRP_COMM_ALL}; +enum GRP_COMM {GRP_COMM_LEADER, GRP_COMM_ALL, GRP_COMM_ACT}; enum RQ_R {RQ_R_OK, RQ_R_NO_GROUP, RQ_R_OVERFLOW, RQ_REFRESH}; enum INV_R {INV_R_OK, INV_R_NO_PERSON, INV_R_BUSY, INV_R_REFRESH}; @@ -16,18 +16,11 @@ void do_group2(CHAR_DATA *ch, char *argument, int, int); void do_ungroup(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/); void do_report(CHAR_DATA *ch, char* /*argument*/, int/* cmd*/, int/* subcmd*/); int max_group_size(CHAR_DATA *ch); -void do_grequest(CHAR_DATA *ch, char *argument, int, int); - class Group; -class MemberManager; -class RequestManager; -class GroupManager; - -using mm_ptr = std::shared_ptr; -using rm_ptr = std::shared_ptr; -using gm_ptr = std::shared_ptr; +class Request; using grp_ptr = std::shared_ptr; +using rq_ptr = std::shared_ptr; struct char_info { char_info(int memberUid, CHAR_DATA *member, std::string memberName); @@ -37,27 +30,11 @@ struct char_info { std::string memberName; }; -class MemberManager { -private: - std::map> _memberList; - grp_ptr _group; -public: - MemberManager(CHAR_DATA* leader, grp_ptr group); - ~MemberManager(); - void addMember(CHAR_DATA *member); - bool removeMember(CHAR_DATA *member); - bool findMember(int uid); - bool restoreMember(CHAR_DATA *member); - u_short getMemberCount() {return (u_short)this->_memberList.size();} - void clear(bool silentMode); // вызывается при расформировании группы -}; class Group { private: // ид группы в ростере u_long _uid = 0; - // текущее количество игроков - u_short _currentMemberCount = 0; //макс.количество игроков int _memberCap = 0; // ид лидера @@ -65,62 +42,69 @@ class Group { // имя лидера std::string _leaderName; // ссылка на персонажа, АХТУНГ! Может меняться и быть невалидным - CHAR_DATA* _leader; - // состав группы, ключом UID персонажа. Дохлые/вышедшие проверяем отдельным методом - mm_ptr _memberManager; + CHAR_DATA* _leader = nullptr; void clear(); public: - mm_ptr getMemberManager() {return _memberManager;} u_long getUid() const; u_short getCurrentMemberCount() const; const std::string &getLeaderName() const; CHAR_DATA *getLeader(); - void disband(bool silent); - + void clear(bool silent); + Group(CHAR_DATA *leader, u_long uid); ~Group(); + void _setLeader(CHAR_DATA *leader); + int _getMemberCap() const; + bool _isFull(); + bool _isActive(); // проверка, что в группе все персонажи онлайн + bool _isMember(int uid); + int _findMember(char* memberName); +private: + std::map> _memberList; - void setLeader(CHAR_DATA *leader); - int getMemberCap() const; - bool isFull(); - Group(CHAR_DATA *leader, u_long uid); +public: + void addMember(CHAR_DATA *member); + bool removeMember(CHAR_DATA *member); + bool restoreMember(CHAR_DATA *member); void printGroup(CHAR_DATA *requestor); void listMembers(CHAR_DATA *requestor); - bool isActive(); // проверка, что в группе все персонажи онлайн - - bool promote(char *applicant); void sendToGroup(GRP_COMM mode, const char *msg, ...); + void promote(char *applicant); + void approveRequest(char *applicant); }; class Request { public: std::chrono::time_point _time; CHAR_DATA *_applicant; - grp_ptr _group; + Group* _group; std::string _applicantName; int _applicantUID; RQ_TYPE _type; - Request(CHAR_DATA* subject, grp_ptr group, RQ_TYPE type); + Request(CHAR_DATA* subject, Group* group, RQ_TYPE type); }; -class GroupManager{ + +class GroupRoster { +// properties +public: + GroupRoster(); + void restorePlayerGroup(CHAR_DATA* ch); // возвращает игрока в группу после смерти + void processGroupCommands(CHAR_DATA *ch, char *argument); + void printList(CHAR_DATA *ch); private: u_long _currentGroupIndex = 0; std::map _groupList; +// методы работы с группами public: grp_ptr addGroup(CHAR_DATA* leader); void removeGroup(u_long uid); grp_ptr findGroup(CHAR_DATA* ch); grp_ptr findGroup(char* leaderName); - void printList(CHAR_DATA *ch); -}; - -class RequestManager{ private: - std::list _requestList; - std::tuple tryMakeInvite(grp_ptr grp, char* member); + std::list _requestList; + std::tuple tryMakeInvite(Group* grp, char* member); std::tuple tryAddRequest(CHAR_DATA* author, char* targetGroup); public: - RequestManager(); // методы работы с заявками void printRequestList(CHAR_DATA* ch); void makeInvite(CHAR_DATA* leader, char* targetPerson); @@ -128,18 +112,4 @@ class RequestManager{ void revokeRequest(CHAR_DATA* author, char* targetGroup); }; -class GroupRoster { -// properties -private: - rm_ptr _requestManager; - gm_ptr _groupManager; -public: - gm_ptr getGroupManager() const {return _groupManager;} - rm_ptr getRequestManager() const {return _requestManager;} - GroupRoster(); - void restorePlayerGroup(CHAR_DATA* ch); // возвращает игрока в группу после смерти - void processGroupCommands(CHAR_DATA *ch, char *argument); - void printList(CHAR_DATA *ch); -}; - #endif //BYLINS_GRP_MAIN_H diff --git a/src/grp/grp.membermanager.cpp b/src/grp/grp.membermanager.cpp deleted file mode 100644 index de57c10ac..000000000 --- a/src/grp/grp.membermanager.cpp +++ /dev/null @@ -1,88 +0,0 @@ -// -// Created by ubuntu on 23.12.2020. -// -#include - -#include "grp.main.h" - -char_info::char_info(int memberUid, CHAR_DATA *member, std::string memberName) : memberUID( - memberUid), member(member), memberName(std::move(memberName)) { -} - -char_info::~char_info() { - mudlog("[~char_info]", BRF, LVL_IMMORT, SYSLOG, TRUE); -} - -MemberManager::MemberManager(CHAR_DATA* leader, grp_ptr group) { - this->_group = std::move(group); - this->addMember(leader); -} - -MemberManager::~MemberManager() { - mudlog("~MemberManager", BRF, LVL_IMMORT, SYSLOG, TRUE); -} - - -void MemberManager::addMember(CHAR_DATA *member) { - if (IS_NPC(member)) - return; - if (member->personGroup == this->_group) - return; - auto it = this->_memberList.find(member->get_uid()); - if (it == this->_memberList.end()) { - auto ci = std::shared_ptr(new char_info(member->get_uid(), member, member->get_pc_name())); - this->_memberList.emplace(member->get_uid(), ci); - } - else { - sprintf(buf, "MemberManager::addMember: группа id=%lu, попытка повторного добавления персонажа с тем же uid", this->_group->getUid()); - mudlog(buf, BRF, LVL_IMMORT, SYSLOG, TRUE); - return; - } - member->personGroup = this->_group; - this->_group->getLeader()->send_to_TC(true, false, false, "Размер списка: %lu\r\n", this->_memberList.size()); -} - -bool MemberManager::restoreMember(CHAR_DATA *member) { - return false;r -} - -bool MemberManager::removeMember(CHAR_DATA *member) { - if (member == nullptr || member->purged()) - return false; - if (this->_memberList.empty()) { - sprintf(buf, "MemberManager::removeMember: попытка удалить из группы при текущем индексе 0, id=%lu", this->_group->getUid()); - mudlog(buf, BRF, LVL_IMMORT, SYSLOG, TRUE); - return false; - } - auto it = this->_memberList.find(member->get_uid()); - if (it == this->_memberList.end()){ - sprintf(buf, "MemberManager::removeMember: персонаж не найден, id=%lu", this->_group->getUid()); - mudlog(buf, BRF, LVL_IMMORT, SYSLOG, TRUE); - return false; - } - this->_memberList.erase(it); - member->personGroup = nullptr; - return true; -} - -bool MemberManager::findMember(int uid) { - auto it = this->_memberList.find(uid); - if (it == this->_memberList.end() ) - return false; - return true; -} - -void MemberManager::clear(bool silentMode) { - sprintf(buf, "[MemberManager::clear()]: _memberList: %lu", _memberList.size()); - mudlog(buf, BRF, LVL_IMMORT, SYSLOG, TRUE); - CHAR_DATA* ch; - for (auto & it : _memberList) { - ch = it.second->member; - if (ch == nullptr || ch->purged()) - continue; - if (!silentMode) - send_to_char(ch, "Ваша группа распущена.\r\n"); - ch->personGroup = nullptr; - } - _memberList.clear(); -} \ No newline at end of file diff --git a/src/grp/grp.request.cpp b/src/grp/grp.request.cpp index 40d641913..a05c151c7 100644 --- a/src/grp/grp.request.cpp +++ b/src/grp/grp.request.cpp @@ -4,7 +4,7 @@ #include "grp.main.h" -Request::Request(CHAR_DATA *subject, grp_ptr group, RQ_TYPE type) { +Request::Request(CHAR_DATA *subject, Group* group, RQ_TYPE type) { if (!subject || !group) return; _applicant = subject; diff --git a/src/grp/grp.requestmanager.cpp b/src/grp/grp.requestmanager.cpp deleted file mode 100644 index 94e2a26bb..000000000 --- a/src/grp/grp.requestmanager.cpp +++ /dev/null @@ -1,153 +0,0 @@ -// -// Created by ubuntu on 23.12.2020. -// - -#include "handler.h" -#include "global.objects.hpp" -#include "grp/grp.main.h" - -extern GroupRoster& groupRoster; - -void RequestManager::makeInvite(CHAR_DATA *leader, char *targetPerson) { - if (AFF_FLAGGED(leader, EAffectFlag::AFF_CHARM)) - return; - if (AFF_FLAGGED(leader, EAffectFlag::AFF_SILENCE) || AFF_FLAGGED(leader, EAffectFlag::AFF_STRANGLED)) { - send_to_char(leader, "Вы немы, как рыба об лед.\r\n"); - return; - } - if (leader->personGroup->getLeader() != leader){ - send_to_char(leader, "Чтобы пригласить в группу, надо быть лидером.\r\n"); - return; - } - auto [res, vict] = this->tryMakeInvite(leader->personGroup, targetPerson); - switch (res) { - case INV_R_BUSY: - send_to_char(leader, "Извините, но указанный персонаж уже в группе.\r\n"); - return; - case INV_R_NO_PERSON: - send_to_char(leader, "Протри глаза, нет такого игрока!\r\n"); - return; - case INV_R_REFRESH: - send_to_char(leader, "Вы продлили приглашение.\r\n"); - return; - case INV_R_OK: - send_to_char(leader, "Вы направили приглашение на вступление в группу.\r\n"); - send_to_char(vict, "Лидер группы %s направил вам приглашение в группу.\r\n", leader->get_pc_name().c_str()); - return; - } -} - -std::tuple RequestManager::tryMakeInvite(grp_ptr grp, char *member) { - CHAR_DATA *vict = get_player_vis(grp->getLeader(), member, FIND_CHAR_WORLD); - if (!vict) return std::make_tuple(INV_R_NO_PERSON, nullptr); - if (vict->personGroup != nullptr ) return std::make_tuple(INV_R::INV_R_BUSY, nullptr); - for (auto & it : this->_requestList){ - if (vict == it._applicant && grp == it._group){ - it._time = std::chrono::system_clock::now(); - return std::make_tuple(INV_R::INV_R_REFRESH, vict); - } - } - // не нашли, создаём - this->_requestList.emplace(_requestList.end(), Request(vict, grp, RQ_GROUP)); - return std::make_tuple(INV_R::INV_R_OK, vict); -} - -void RequestManager::printRequestList(CHAR_DATA *ch) { - bool notFound = true; - if (!ch || ch->purged()) - return; - send_to_char(ch, "Активные заявки и приглашения:\r\n"); - for (const auto & it : this->_requestList){ - if (ch->get_uid() == it._applicantUID){ - sprintf(smallBuf, "%s%s\r\n", - it._type== RQ_TYPE::RQ_PERSON? "Заявка в группу лидера ": "Приглашение от лидера группы ", - it._group->getLeaderName().c_str()); - } - if (ch->personGroup != nullptr && ch->personGroup->getUid() == it._group->getUid()) { - if (it._type== RQ_TYPE::RQ_GROUP) { - sprintf(smallBuf, "Приглашение игроку: %s\r\n", it._applicantName.c_str() ); - } - else { - sprintf(smallBuf, "Заявка от игрока: %s\r\n", it._applicantName.c_str() ); - } - } - send_to_char(ch, "%s", smallBuf); - notFound = false; - } - if (notFound) - send_to_char(ch, "Заявок нет.\r\n"); -} - -void RequestManager::makeRequest(CHAR_DATA *author, char* target) { - if (AFF_FLAGGED(author, EAffectFlag::AFF_CHARM)) - return; - - if (AFF_FLAGGED(author, EAffectFlag::AFF_SILENCE) || AFF_FLAGGED(author, EAffectFlag::AFF_STRANGLED)) { - send_to_char(author, "Вы немы, как рыба об лед.\r\n"); - return; - } - if (author->personGroup && author->personGroup->getLeader() == author) { - send_to_char(author, "Негоже лидеру группы напрашиваться в чужую!\r\n"); - return; - } - - if (!*target) { - send_to_char("И кому же вы хотите напроситься в группу?.\r\n", author); - return; - } - std::shared_ptr mgr = groupRoster.getRequestManager(); - auto [res, grp] = mgr->tryAddRequest(author, target); - switch (res) { - case RQ_R::RQ_R_OVERFLOW: - send_to_char("В группе нет свободных мест, свяжитесь с лидером.\r\n", author); - return; - case RQ_R::RQ_R_NO_GROUP: - send_to_char("Протри глаза, не такой группы!\r\n", author); - return; - case RQ_R::RQ_R_OK: - send_to_char("Заявка на вступление в группу отправлена.\r\n", author); - break; - case RQ_R::RQ_REFRESH: - send_to_char("Заявка успешно продлена.\r\n", author); - break; - } - grp->sendToGroup(GRP_COMM_LEADER, "Поступила заявка на вступление в группу. \r\n"); -} - -void RequestManager::revokeRequest(CHAR_DATA *ch, char* target) { - for (auto it = this->_requestList.begin(); it != this->_requestList.end(); ++it) - if (it->_applicant == ch && str_cmp(target, it->_group->getLeaderName().c_str())){ - send_to_char(ch, "заявка найдена и удалена\r\n"); - _requestList.erase(it); - return; - } - send_to_char(ch, "заявка не найдена\r\n"); -} - -std::tuple RequestManager::tryAddRequest(CHAR_DATA *author, char *targetGroup) { - if (!author || author->purged()){ - sprintf(buf, "RequestManager::tryAddRequest: author is null or purged\r\n"); - mudlog(buf, BRF, LVL_IMMORT, SYSLOG, TRUE); - return std::make_tuple(RQ_R::RQ_R_NO_GROUP, nullptr); - } - if (targetGroup == nullptr){ - return std::make_tuple(RQ_R::RQ_R_NO_GROUP, nullptr); - } - grp_ptr grp = groupRoster.getGroupManager()->findGroup(targetGroup) ; - if (grp == nullptr) - return std::make_tuple(RQ_R::RQ_R_NO_GROUP, nullptr); - if (grp->isFull()) - return std::make_tuple(RQ_R::RQ_R_OVERFLOW, nullptr); - - for (auto & it : this->_requestList){ - if (author == it._applicant && grp == it._group){ - it._time = std::chrono::system_clock::now(); - return std::make_tuple(RQ_REFRESH, grp); - } - } - // не нашли, создаём - this->_requestList.emplace(_requestList.end(), Request(author, grp, RQ_PERSON)); - return std::make_tuple(RQ_R::RQ_R_OK, grp); -} - -RequestManager::RequestManager() = default; diff --git a/src/grp/grp.roster.cpp b/src/grp/grp.roster.cpp index 68f2f2d3d..535045e60 100644 --- a/src/grp/grp.roster.cpp +++ b/src/grp/grp.roster.cpp @@ -8,15 +8,14 @@ GroupRoster& groupRoster = GlobalObjects::groupRoster(); GroupRoster::GroupRoster() { - this->_requestManager = std::make_shared(RequestManager()); - this->_groupManager = std::make_shared(GroupManager()); + } void GroupRoster::restorePlayerGroup(CHAR_DATA *ch) { - auto grp = this->_groupManager->findGroup(ch); + auto grp = this->findGroup(ch); if (grp == nullptr) return; - if (!grp->getMemberManager()->restoreMember(ch)) + if (!grp->restoreMember(ch)) return; grp->sendToGroup(GRP_COMM_ALL, "Игрок %s заново присоединился к группе.\r\n", ch->get_pc_name().c_str()); ch->send_to_TC(true, false, true, "Membership in group: %d restored\r\n", ch->personGroup->getUid()); @@ -35,7 +34,7 @@ void GroupRoster::processGroupCommands(CHAR_DATA *ch, char *argument) { const std::string strEXPELL = "выгнать expell"; const std::string strLEADER = "лидер leader"; const std::string strLEAVE = "покинуть leave"; - const std::string strDISBAND = "распустить disband"; + const std::string strDISBAND = "распустить clear"; const std::string strALL = "все all"; char subcmd[MAX_INPUT_LENGTH], target[MAX_INPUT_LENGTH]; @@ -85,9 +84,9 @@ void GroupRoster::processGroupCommands(CHAR_DATA *ch, char *argument) { return; } if (grp != nullptr) // в группе - покидаем - grp->getMemberManager()->removeMember(ch); - groupRoster.getGroupManager()->addGroup(ch); - send_to_char(ch, "Вы создали группу c максимальным числом последователей %d.\r\n", ch->personGroup->getMemberCap()); + grp->removeMember(ch); + groupRoster.addGroup(ch); + send_to_char(ch, "Вы создали группу c максимальным числом последователей %d.\r\n", ch->personGroup ? ch->personGroup->_getMemberCap() : 0); return; } @@ -107,7 +106,7 @@ void GroupRoster::processGroupCommands(CHAR_DATA *ch, char *argument) { case ALL: if (!IS_IMMORTAL(ch)) return; - groupRoster.getGroupManager()->printList(ch); + groupRoster.printList(ch); break; case HELP: break; @@ -115,14 +114,14 @@ void GroupRoster::processGroupCommands(CHAR_DATA *ch, char *argument) { //grp->listMembers(ch); break; case INVITE: - if (grp->isFull()){ + if (grp->_isFull()){ send_to_char(ch, "Командир, но в твоей группе больше нет места!\r\n"); return; } - groupRoster.getRequestManager()->makeInvite(ch, target); + groupRoster.makeInvite(ch, target); break; case TAKE: - //grp->approveRequest(target); + grp->approveRequest(target); break; case REJECT: //grp->denyRequest(target); @@ -131,13 +130,215 @@ void GroupRoster::processGroupCommands(CHAR_DATA *ch, char *argument) { //grp->expellMember(target); break; case LEADER: - //grp->promote(target); + grp->promote(target); break; case LEAVE: //grp->leave(ch); break; case DISBAND: - groupRoster.getGroupManager()->removeGroup(grp->getUid()); + groupRoster.removeGroup(grp->getUid()); + grp = nullptr; + break; + } +} + + +grp_ptr GroupRoster::addGroup(CHAR_DATA * leader) { + if (leader == nullptr || leader->purged()) + return nullptr; + ++this->_currentGroupIndex; + auto group = std::make_shared(leader, _currentGroupIndex) ; + this->_groupList.emplace(_currentGroupIndex, group); + leader->send_to_TC(true, false, false, "addGroup: Размер списка: %lu\r\n", this->_groupList.size()); + return group; +} + +void GroupRoster::removeGroup(u_long uid) { + auto grp = _groupList.find(uid); + if (grp != _groupList.end()) { + grp->second->clear(false); + _groupList.erase(uid); + return; + } +} + +grp_ptr GroupRoster::findGroup(CHAR_DATA *ch) { + if (ch == nullptr || ch->purged()){ + sprintf(buf, "GroupRoster::findGroup: вызов для пустого или пурженого персонажа\r\n"); + mudlog(buf, BRF, LVL_IMMORT, SYSLOG, TRUE); + return nullptr; + } + for (auto & it : this->_groupList){ + if (it.second->_isMember(ch->get_uid())) + return it.second; + } + return nullptr; +} + +grp_ptr GroupRoster::findGroup(char *leaderName) { + if (leaderName == nullptr){ + sprintf(buf, "GroupRoster::findGroup: leaderName is null\r\n"); + mudlog(buf, BRF, LVL_IMMORT, SYSLOG, TRUE); + return nullptr; + } + for (auto & it : this->_groupList) { + if (str_cmp(it.second->getLeaderName().c_str(), leaderName)) + return it.second; + } + return nullptr; +} + + +void GroupRoster::printList(CHAR_DATA *ch) { + size_t cnt = this->_groupList.size(); + send_to_char(ch, "Текущее количество групп в мире: %lu\r\n", cnt); + for (auto & it : this->_groupList) { + send_to_char(ch, "Группа лидера %s, кол-во участников: %hu\r\n", + it.second->getLeaderName().c_str(), + it.second->getCurrentMemberCount()); + } +} + + +void GroupRoster::makeInvite(CHAR_DATA *leader, char *targetPerson) { + if (AFF_FLAGGED(leader, EAffectFlag::AFF_CHARM)) + return; + if (AFF_FLAGGED(leader, EAffectFlag::AFF_SILENCE) || AFF_FLAGGED(leader, EAffectFlag::AFF_STRANGLED)) { + send_to_char(leader, "Вы немы, как рыба об лед.\r\n"); + return; + } + if (leader->personGroup->getLeader() != leader){ + send_to_char(leader, "Чтобы пригласить в группу, надо быть лидером.\r\n"); + return; + } + auto [res, vict] = this->tryMakeInvite(leader->personGroup, targetPerson); + switch (res) { + case INV_R_BUSY: + send_to_char(leader, "Извините, но указанный персонаж уже в группе.\r\n"); + return; + case INV_R_NO_PERSON: + send_to_char(leader, "Протри глаза, нет такого игрока!\r\n"); + return; + case INV_R_REFRESH: + send_to_char(leader, "Вы продлили приглашение.\r\n"); + return; + case INV_R_OK: + send_to_char(leader, "Вы направили приглашение на вступление в группу.\r\n"); + send_to_char(vict, "Лидер группы %s направил вам приглашение в группу.\r\n", leader->get_pc_name().c_str()); + return; + } +} + +std::tuple GroupRoster::tryMakeInvite(Group* grp, char *member) { + CHAR_DATA *vict = get_player_vis(grp->getLeader(), member, FIND_CHAR_WORLD); + if (!vict) return std::make_tuple(INV_R_NO_PERSON, nullptr); + if (vict->personGroup != nullptr ) return std::make_tuple(INV_R::INV_R_BUSY, nullptr); + for (auto & it : this->_requestList){ + if (vict == it->_applicant && grp == it->_group){ + it->_time = std::chrono::system_clock::now(); + return std::make_tuple(INV_R::INV_R_REFRESH, vict); + } + } + // не нашли, создаём + rq_ptr inv = std::make_shared(vict, grp, RQ_GROUP); + this->_requestList.emplace(_requestList.end(), inv); + return std::make_tuple(INV_R::INV_R_OK, vict); +} + +void GroupRoster::printRequestList(CHAR_DATA *ch) { + bool notFound = true; + if (!ch || ch->purged()) + return; + send_to_char(ch, "Активные заявки и приглашения:\r\n"); + for (const auto & it : this->_requestList){ + if (ch->get_uid() == it->_applicantUID){ + sprintf(smallBuf, "%s%s\r\n", + it->_type== RQ_TYPE::RQ_PERSON? "Заявка в группу лидера ": "Приглашение от лидера группы ", + it->_group->getLeaderName().c_str()); + } + if (ch->personGroup != nullptr && ch->personGroup->getUid() == it->_group->getUid()) { + if (it->_type== RQ_TYPE::RQ_GROUP) { + sprintf(smallBuf, "Приглашение игроку: %s\r\n", it->_applicantName.c_str() ); + } + else { + sprintf(smallBuf, "Заявка от игрока: %s\r\n", it->_applicantName.c_str() ); + } + } + send_to_char(ch, "%s", smallBuf); + notFound = false; + } + if (notFound) + send_to_char(ch, "Заявок нет.\r\n"); +} + +void GroupRoster::makeRequest(CHAR_DATA *author, char* target) { + if (AFF_FLAGGED(author, EAffectFlag::AFF_CHARM)) + return; + + if (AFF_FLAGGED(author, EAffectFlag::AFF_SILENCE) || AFF_FLAGGED(author, EAffectFlag::AFF_STRANGLED)) { + send_to_char(author, "Вы немы, как рыба об лед.\r\n"); + return; + } + if (author->personGroup && author->personGroup->getLeader() == author) { + send_to_char(author, "Негоже лидеру группы напрашиваться в чужую!\r\n"); + return; + } + + if (!*target) { + send_to_char("И кому же вы хотите напроситься в группу?.\r\n", author); + return; + } + auto [res, grp] = groupRoster.tryAddRequest(author, target); + switch (res) { + case RQ_R::RQ_R_OVERFLOW: + send_to_char("В группе нет свободных мест, свяжитесь с лидером.\r\n", author); + return; + case RQ_R::RQ_R_NO_GROUP: + send_to_char("Протри глаза, не такой группы!\r\n", author); + return; + case RQ_R::RQ_R_OK: + send_to_char("Заявка на вступление в группу отправлена.\r\n", author); + break; + case RQ_R::RQ_REFRESH: + send_to_char("Заявка успешно продлена.\r\n", author); break; } -} \ No newline at end of file + grp->sendToGroup(GRP_COMM_LEADER, "Поступила заявка на вступление в группу. \r\n"); +} + +void GroupRoster::revokeRequest(CHAR_DATA *ch, char* target) { + for (auto it = this->_requestList.begin(); it != this->_requestList.end(); ++it) + if ( it->get()->_applicant == ch && str_cmp(target, it->get()->_group->getLeaderName().c_str())){ + send_to_char(ch, "заявка найдена и удалена\r\n"); + this->_requestList.erase(it); + return; + } + send_to_char(ch, "заявка не найдена\r\n"); +} + +std::tuple GroupRoster::tryAddRequest(CHAR_DATA *author, char *targetGroup) { + if (!author || author->purged()){ + sprintf(buf, "GroupRoster::tryAddRequest: author is null or purged\r\n"); + mudlog(buf, BRF, LVL_IMMORT, SYSLOG, TRUE); + return std::make_tuple(RQ_R::RQ_R_NO_GROUP, nullptr); + } + if (targetGroup == nullptr){ + return std::make_tuple(RQ_R::RQ_R_NO_GROUP, nullptr); + } + grp_ptr grp = groupRoster.findGroup(targetGroup) ; + if (grp == nullptr) + return std::make_tuple(RQ_R::RQ_R_NO_GROUP, nullptr); + if (grp->_isFull()) + return std::make_tuple(RQ_R::RQ_R_OVERFLOW, nullptr); + + for (auto & it : this->_requestList){ + if (author == it->_applicant && grp.get() == it->_group){ + it->_time = std::chrono::system_clock::now(); + return std::make_tuple(RQ_REFRESH, grp); + } + } + // не нашли, создаём + rq_ptr rq = std::make_shared(author, grp.get(), RQ_PERSON); + this->_requestList.emplace(_requestList.end(), rq); + return std::make_tuple(RQ_R::RQ_R_OK, grp); +} From 4559c53c90843b0c761497a7fee8e9c2b980a4a7 Mon Sep 17 00:00:00 2001 From: bikbai Date: Mon, 28 Dec 2020 21:39:34 +0300 Subject: [PATCH 07/40] =?UTF-8?q?=D1=87=D1=91=D1=82=20=D0=BF=D1=80=D0=B8?= =?UTF-8?q?=D1=82=D0=BE=D0=BC=D0=B8=D0=BB=D1=81=D1=8F=20=D1=8D=D1=82=D0=BE?= =?UTF-8?q?=D1=82=20=D0=BA=D0=BE=D0=BF=D0=B8=D0=BF=D0=B0=D1=81=D1=82=20?= =?UTF-8?q?=D0=BA=D0=BE=D0=B4=20=D0=BA=D0=BE=D0=B2=D1=8B=D1=80=D1=8F=D1=82?= =?UTF-8?q?=D1=8C=20=3D)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chars/char.cpp | 3 + src/cmd/quit.cpp | 2 +- src/grp/grp.cmdprocessor.cpp | 265 +--------------------- src/grp/grp.group.cpp | 413 +++++++++++++++++++++++++++++++---- src/grp/grp.main.h | 33 ++- src/grp/grp.roster.cpp | 189 ++++++++-------- 6 files changed, 504 insertions(+), 401 deletions(-) diff --git a/src/chars/char.cpp b/src/chars/char.cpp index 6c17feddd..a5c2dfe9c 100644 --- a/src/chars/char.cpp +++ b/src/chars/char.cpp @@ -609,6 +609,9 @@ void CHAR_DATA::purge() free(follower); follower = next_one; } + + // чистим указатель в групе + this->personGroup->charDataPurged(this); } // * Скилл с учетом всех плюсов и минусов от шмоток/яда. diff --git a/src/cmd/quit.cpp b/src/cmd/quit.cpp index 2b40262ac..7fa0ca172 100644 --- a/src/cmd/quit.cpp +++ b/src/cmd/quit.cpp @@ -57,7 +57,7 @@ void do_quit(CHAR_DATA *ch, char *argument, int/* cmd*/, int subcmd) Depot::exit_char(ch); Clan::clan_invoice(ch, false); if (ch->personGroup) - ch->personGroup->removeMember(ch); + ch->personGroup->_removeMember(ch); /* * kill off all sockets connected to the same player as the one who is diff --git a/src/grp/grp.cmdprocessor.cpp b/src/grp/grp.cmdprocessor.cpp index 4893e814c..e85b2fb43 100644 --- a/src/grp/grp.cmdprocessor.cpp +++ b/src/grp/grp.cmdprocessor.cpp @@ -23,6 +23,7 @@ void do_grequest(CHAR_DATA *ch, char *argument, int, int){ send_to_char(ch, "гзаявка список - для получения списка заявок\r\n"); send_to_char(ch, "гзаявка создать Верий - отправляет заявку в группу лидера Верий\r\n"); send_to_char(ch, "гзаявка отменить Верий - отменяет заявку в группу лидера Верий\r\n"); + send_to_char(ch, "гзаявка принять Верий - принимает заявку от лидера группы Верий\r\n"); return; } else if (isname(subcmd, "список list")) { @@ -39,7 +40,10 @@ void do_grequest(CHAR_DATA *ch, char *argument, int, int){ groupRoster.revokeRequest(ch, target); return; } - else { + else if (isname(subcmd, "принять accept")){ + groupRoster.acceptInvite(ch, target); + return; + }else { send_to_char("Уточните команду.\r\n", ch); return; } @@ -58,263 +62,6 @@ bool is_group_member(CHAR_DATA *ch, CHAR_DATA *vict) } } -void print_one_line(CHAR_DATA * ch, CHAR_DATA * k, int leader, int header) -{ - int ok, ok2, div; - const char *WORD_STATE[] = { "При смерти", - "Оч.тяж.ран", - "Оч.тяж.ран", - " Тяж.ранен", - " Тяж.ранен", - " Ранен ", - " Ранен ", - " Ранен ", - "Лег.ранен ", - "Лег.ранен ", - "Слег.ранен", - " Невредим " - }; - const char *MOVE_STATE[] = { "Истощен", - "Истощен", - "О.устал", - " Устал ", - " Устал ", - "Л.устал", - "Л.устал", - "Хорошо ", - "Хорошо ", - "Хорошо ", - "Отдохн.", - " Полон " - }; - const char *POS_STATE[] = { "Умер", - "Истекает кровью", - "При смерти", - "Без сознания", - "Спит", - "Отдыхает", - "Сидит", - "Сражается", - "Стоит" - }; - - if (IS_NPC(k)) - { - if (!header) -// send_to_char("Персонаж | Здоровье |Рядом| Доп | Положение | Лояльн.\r\n",ch); - send_to_char("Персонаж | Здоровье |Рядом| Аффект | Положение\r\n", ch); - std::string name = GET_NAME(k); - name[0] = UPPER(name[0]); - sprintf(buf, "%s%-20s%s|", CCIBLU(ch, C_NRM), - name.substr(0, 20).c_str(), CCNRM(ch, C_NRM)); - sprintf(buf + strlen(buf), "%s%10s%s|", - color_value(ch, GET_HIT(k), GET_REAL_MAX_HIT(k)), - WORD_STATE[posi_value(GET_HIT(k), GET_REAL_MAX_HIT(k)) + 1], CCNRM(ch, C_NRM)); - - ok = ch->in_room == IN_ROOM(k); - sprintf(buf + strlen(buf), "%s%5s%s|", - ok ? CCGRN(ch, C_NRM) : CCRED(ch, C_NRM), ok ? " Да " : " Нет ", CCNRM(ch, C_NRM)); - - sprintf(buf + strlen(buf), " %s%s%s%s%s%s%s%s%s%s%s%s%s |", - CCIRED(ch, C_NRM), - AFF_FLAGGED(k, EAffectFlag::AFF_SANCTUARY) ? "О" : (AFF_FLAGGED(k, EAffectFlag::AFF_PRISMATICAURA) ? "П" : " "), - CCGRN(ch, C_NRM), - AFF_FLAGGED(k, EAffectFlag::AFF_WATERBREATH) ? "Д" : " ", CCICYN(ch, C_NRM), - AFF_FLAGGED(k, EAffectFlag::AFF_INVISIBLE) ? "Н" : " ", CCIYEL(ch, C_NRM), - (AFF_FLAGGED(k, EAffectFlag::AFF_SINGLELIGHT) - || AFF_FLAGGED(k, EAffectFlag::AFF_HOLYLIGHT) - || (GET_EQ(k, WEAR_LIGHT) - && GET_OBJ_VAL(GET_EQ(k, WEAR_LIGHT), 2))) ? "С" : " ", - CCIBLU(ch, C_NRM), AFF_FLAGGED(k, EAffectFlag::AFF_FLY) ? "Л" : " ", CCYEL(ch, C_NRM), - k->low_charm() ? "Т" : " ", CCNRM(ch, C_NRM)); - - sprintf(buf + strlen(buf), "%-15s", POS_STATE[(int) GET_POS(k)]); - - act(buf, FALSE, ch, 0, k, TO_CHAR); - } - else - { - if (!header) - send_to_char - ("Персонаж | Здоровье |Энергия|Рядом|Учить| Аффект | Кто | Держит строй | Положение \r\n", ch); - - std::string name = GET_NAME(k); - name[0] = UPPER(name[0]); - sprintf(buf, "%s%-20s%s|", CCIBLU(ch, C_NRM), name.c_str(), CCNRM(ch, C_NRM)); - sprintf(buf + strlen(buf), "%s%10s%s|", - color_value(ch, GET_HIT(k), GET_REAL_MAX_HIT(k)), - WORD_STATE[posi_value(GET_HIT(k), GET_REAL_MAX_HIT(k)) + 1], CCNRM(ch, C_NRM)); - - sprintf(buf + strlen(buf), "%s%7s%s|", - color_value(ch, GET_MOVE(k), GET_REAL_MAX_MOVE(k)), - MOVE_STATE[posi_value(GET_MOVE(k), GET_REAL_MAX_MOVE(k)) + 1], CCNRM(ch, C_NRM)); - - ok = ch->in_room == IN_ROOM(k); - sprintf(buf + strlen(buf), "%s%5s%s|", - ok ? CCGRN(ch, C_NRM) : CCRED(ch, C_NRM), ok ? " Да " : " Нет ", CCNRM(ch, C_NRM)); - - if ((!IS_MANA_CASTER(k) && !MEMQUEUE_EMPTY(k)) || - (IS_MANA_CASTER(k) && GET_MANA_STORED(k) < GET_MAX_MANA(k))) - { - div = mana_gain(k); - if (div > 0) - { - if (!IS_MANA_CASTER(k)) - { - ok2 = MAX(0, 1 + GET_MEM_TOTAL(k) - GET_MEM_COMPLETED(k)); - ok2 = ok2 * 60 / div; // время мема в сек - } - else - { - ok2 = MAX(0, 1 + GET_MAX_MANA(k) - GET_MANA_STORED(k)); - ok2 = ok2 / div; // время восстановления в секундах - } - ok = ok2 / 60; - ok2 %= 60; - if (ok > 99) - sprintf(buf + strlen(buf), "&g%5d&n|", ok); - else - sprintf(buf + strlen(buf), "&g%2d:%02d&n|", ok, ok2); - } - else - { - sprintf(buf + strlen(buf), "&r -&n|"); - } - } - else - sprintf(buf + strlen(buf), " |"); - - sprintf(buf + strlen(buf), " %s%s%s%s%s%s%s%s%s%s%s%s%s |", - CCIRED(ch, C_NRM), AFF_FLAGGED(k, EAffectFlag::AFF_SANCTUARY) ? "О" : (AFF_FLAGGED(k, EAffectFlag::AFF_PRISMATICAURA) - ? "П" : " "), CCGRN(ch, - C_NRM), - AFF_FLAGGED(k, EAffectFlag::AFF_WATERBREATH) ? "Д" : " ", CCICYN(ch, - C_NRM), - AFF_FLAGGED(k, EAffectFlag::AFF_INVISIBLE) ? "Н" : " ", CCIYEL(ch, C_NRM), (AFF_FLAGGED(k, EAffectFlag::AFF_SINGLELIGHT) - || AFF_FLAGGED(k, EAffectFlag::AFF_HOLYLIGHT) - || (GET_EQ(k, WEAR_LIGHT) - && - GET_OBJ_VAL(GET_EQ - (k, WEAR_LIGHT), - 2))) ? "С" : " ", - CCIBLU(ch, C_NRM), AFF_FLAGGED(k, EAffectFlag::AFF_FLY) ? "Л" : " ", CCYEL(ch, C_NRM), - k->ahorse() ? "В" : " ", CCNRM(ch, C_NRM)); - - sprintf(buf + strlen(buf), "%5s|", leader ? "Лидер" : ""); - ok = PRF_FLAGGED(k, PRF_SKIRMISHER); - sprintf(buf + strlen(buf), "%s%-14s%s|", ok ? CCGRN(ch, C_NRM) : CCNRM(ch, C_NRM), ok ? " Да " : " Нет ", CCNRM(ch, C_NRM)); - sprintf(buf + strlen(buf), " %s", POS_STATE[(int) GET_POS(k)]); - act(buf, FALSE, ch, 0, k, TO_CHAR); - } -} - -void print_group(CHAR_DATA * ch) -{ - int gfound = 0, cfound = 0; - CHAR_DATA *k; - struct follow_type *f, *g; - - k = ch->has_master() ? ch->get_master() : ch; - if (!IS_NPC(ch)) - ch->desc->msdp_report(msdp::constants::GROUP); - - if (AFF_FLAGGED(ch, EAffectFlag::AFF_GROUP)) - { - send_to_char("Ваша группа состоит из:\r\n", ch); - if (AFF_FLAGGED(k, EAffectFlag::AFF_GROUP)) - { - print_one_line(ch, k, TRUE, gfound++); - } - - for (f = k->followers; f; f = f->next) - { - if (!AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP)) - { - continue; - } - print_one_line(ch, f->follower, FALSE, gfound++); - } - } - - for (f = ch->followers; f; f = f->next) - { - if (!(AFF_FLAGGED(f->follower, EAffectFlag::AFF_CHARM) - || MOB_FLAGGED(f->follower, MOB_ANGEL)|| MOB_FLAGGED(f->follower, MOB_GHOST))) - { - continue; - } - if (!cfound) - send_to_char("Ваши последователи:\r\n", ch); - print_one_line(ch, f->follower, FALSE, cfound++); - } - if (!gfound && !cfound) - { - send_to_char("Но вы же не член (в лучшем смысле этого слова) группы!\r\n", ch); - return; - } - if (PRF_FLAGGED(ch, PRF_SHOWGROUP)) - { - for (g = k->followers, cfound = 0; g; g = g->next) - { - for (f = g->follower->followers; f; f = f->next) - { - if (!(AFF_FLAGGED(f->follower, EAffectFlag::AFF_CHARM) - || MOB_FLAGGED(f->follower, MOB_ANGEL) || MOB_FLAGGED(f->follower, MOB_GHOST)) - || !AFF_FLAGGED(ch, EAffectFlag::AFF_GROUP)) - { - continue; - } - - if (f->follower->get_master() == ch - || !AFF_FLAGGED(f->follower->get_master(), EAffectFlag::AFF_GROUP)) - { - continue; - } - - // shapirus: при включенном режиме не показываем клонов и хранителей - if (PRF_FLAGGED(ch, PRF_NOCLONES) - && IS_NPC(f->follower) - && (MOB_FLAGGED(f->follower, MOB_CLONE) - || GET_MOB_VNUM(f->follower) == MOB_KEEPER)) - { - continue; - } - - if (!cfound) - { - send_to_char("Последователи членов вашей группы:\r\n", ch); - } - print_one_line(ch, f->follower, FALSE, cfound++); - } - - if (ch->has_master()) - { - if (!(AFF_FLAGGED(g->follower, EAffectFlag::AFF_CHARM) - || MOB_FLAGGED(g->follower, MOB_ANGEL) || MOB_FLAGGED(g->follower, MOB_GHOST)) - || !AFF_FLAGGED(ch, EAffectFlag::AFF_GROUP)) - { - continue; - } - - // shapirus: при включенном режиме не показываем клонов и хранителей - if (PRF_FLAGGED(ch, PRF_NOCLONES) - && IS_NPC(g->follower) - && (MOB_FLAGGED(g->follower, MOB_CLONE) - || GET_MOB_VNUM(g->follower) == MOB_KEEPER)) - { - continue; - } - - if (!cfound) - { - send_to_char("Последователи членов вашей группы:\r\n", ch); - } - print_one_line(ch, g->follower, FALSE, cfound++); - } - } - } -} - void print_list_group(CHAR_DATA *ch) { CHAR_DATA *k; @@ -489,7 +236,7 @@ void do_group(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) if (!*buf) { - print_group(ch); +// print_group(ch); return; } diff --git a/src/grp/grp.group.cpp b/src/grp/grp.group.cpp index 5a52de5ac..568719e7f 100644 --- a/src/grp/grp.group.cpp +++ b/src/grp/grp.group.cpp @@ -4,24 +4,80 @@ #include "comm.h" #include "handler.h" #include "msdp.constants.hpp" -//#include "global.objects.hpp" #include "grp.main.h" - +#include "screen.h" +#include "magic.h" extern GroupRoster& groupRoster; +const char *WORD_STATE[] = { "При смерти", + "Оч.тяж.ран", + "Оч.тяж.ран", + " Тяж.ранен", + " Тяж.ранен", + " Ранен ", + " Ранен ", + " Ранен ", + "Лег.ранен ", + "Лег.ранен ", + "Слег.ранен", + " Невредим " +}; +const char *MOVE_STATE[] = { "Истощен", + "Истощен", + "О.устал", + " Устал ", + " Устал ", + "Л.устал", + "Л.устал", + "Хорошо ", + "Хорошо ", + "Хорошо ", + "Отдохн.", + " Полон " +}; +const char *POS_STATE[] = { "Умер", + "Истекает кровью", + "При смерти", + "Без сознания", + "Спит", + "Отдыхает", + "Сидит", + "Сражается", + "Стоит" +}; + + Group::Group(CHAR_DATA *leader, u_long uid){ + mudlog("Group", BRF, LVL_IMMORT, SYSLOG, TRUE); _leaderUID = 0; // чтобы не ругался clion =) _memberCap = 0; _uid = uid; + _memberList = new std::map>; addMember(leader); _setLeader(leader); } -CHAR_DATA *Group::getLeader() { +CHAR_DATA *Group::getLeader() const{ return _leader; } +int Group::_findMember(char *memberName) { + for (auto & it : *_memberList) { + if (!str_cmp((*it.second)->memberName, memberName)) + return it.first; + } + return 0; +} + +CHAR_DATA* Group::_findMember(int uid) { + for (auto & it : *_memberList) { + if ((*it.second)->memberUID == uid) + return (*it.second)->member; + } + return nullptr; +} + void Group::_setLeader(CHAR_DATA *leader) { _leaderUID = leader->get_uid(); _leader = leader; @@ -38,15 +94,72 @@ u_long Group::getUid() const { } u_short Group::getCurrentMemberCount() const { - return (u_short)_memberList.size(); + return (u_short)_memberList->size(); } int Group::_getMemberCap() const { return _memberCap; } -void Group::printGroup(CHAR_DATA *requestor) { +void Group::printGroup(CHAR_DATA *ch) { + int gfound = 0, cfound = 0; + CHAR_DATA *leader; + struct follow_type *f, *g; + + if (ch->personGroup) + leader = ch->personGroup->getLeader(); + else + leader = ch; + if (!IS_NPC(ch)) + ch->desc->msdp_report(msdp::constants::GROUP); + // печатаем группу + if (ch->personGroup != nullptr ) + { + send_to_char("Ваша группа состоит из:\r\n", ch); + for (auto &it : *_memberList ){ + _printLine(ch, it.first, gfound++); + } + } + // допечатываем чармисов + for (f = ch->followers; f; f = f->next) + { + if (!(AFF_FLAGGED(f->follower, EAffectFlag::AFF_CHARM) + || MOB_FLAGGED(f->follower, MOB_ANGEL)|| MOB_FLAGGED(f->follower, MOB_GHOST))) + { + continue; + } + if (!cfound) + send_to_char("Ваши последователи:\r\n", ch); + _printLine(ch, f->follower, cfound++); + } + if (!gfound && !cfound) { + send_to_char("Но вы же не член (в лучшем смысле этого слова) группы!\r\n", ch); + return; + } + // печатаем чармисов членов группы + if (PRF_FLAGGED(ch, PRF_SHOWGROUP)) + { + for (auto &it : *_memberList ) { + for (f = (*it.second)->member->followers; f; f = f->next) { + if (!(AFF_FLAGGED(f->follower, EAffectFlag::AFF_CHARM) + || MOB_FLAGGED(f->follower, MOB_ANGEL) || MOB_FLAGGED(f->follower, MOB_GHOST))){ + continue; + } + // клоны отключены + if (PRF_FLAGGED(ch, PRF_NOCLONES) && IS_NPC(f->follower) && + (MOB_FLAGGED(f->follower, MOB_CLONE) || GET_MOB_VNUM(f->follower) == MOB_KEEPER)) { + continue; + } + + if (!cfound) { + send_to_char("Последователи членов вашей группы:\r\n", ch); + } + _printLine(ch, f->follower, cfound++); + } + + } + } } const std::string &Group::getLeaderName() const { @@ -54,17 +167,26 @@ const std::string &Group::getLeaderName() const { } bool Group::_isFull() { - if (_memberList.size() >= _memberCap) + if ((u_short)_memberList->size() >= (u_short)_memberCap) return true; return false; } void Group::sendToGroup(GRP_COMM mode, const char *msg, ...) { - - + va_list args; + va_start(args, msg); + vsnprintf(smallBuf, sizeof(smallBuf), msg, args); + va_end(args); + if (mode == GRP_COMM::GRP_COMM_ALL) + for (auto & it : *_memberList ) { + send_to_char((*it.second)->member, "%s%s", smallBuf, "\r\n"); + } + if (mode == GRP_COMM_LEADER) + send_to_char(_leader, "%s%s", smallBuf, "\r\n"); } Group::~Group() { + this->_clear(true); mudlog("~Group", BRF, LVL_IMMORT, SYSLOG, TRUE); } @@ -83,10 +205,10 @@ void Group::addMember(CHAR_DATA *member) { return; if (member->personGroup == this) return; - auto it = _memberList.find(member->get_uid()); - if (it == _memberList.end()) { - auto ci = std::make_shared(member->get_uid(), member, member->get_pc_name()); - _memberList.emplace(member->get_uid(), ci); + auto it = _memberList->find(member->get_uid()); + if (it == _memberList->end()) { + auto ci = new char_info(member->get_uid(), member, member->get_pc_name()); + _memberList->emplace(member->get_uid(), std::make_shared(ci)); } else { sprintf(buf, "Group::addMember: группа id=%lu, попытка повторного добавления персонажа с тем же uid", getUid()); @@ -94,7 +216,7 @@ void Group::addMember(CHAR_DATA *member) { return; } member->personGroup = this; -// getLeader()->send_to_TC(true, false, false, "Размер списка: %lu\r\n", _memberList.size()); +// getLeader()->send_to_TC(true, false, false, "Размер списка: %lu\r\n", _memberList->size()); } @@ -102,45 +224,45 @@ bool Group::restoreMember(CHAR_DATA *member) { return false; } -bool Group::removeMember(CHAR_DATA *member) { +bool Group::_removeMember(CHAR_DATA *member) { if (member == nullptr || member->purged()) return false; - if (_memberList.empty()) { - sprintf(buf, "Group::removeMember: попытка удалить из группы при текущем индексе 0, id=%lu", getUid()); + if (_memberList->empty()) { + sprintf(buf, "Group::_removeMember: попытка удалить из группы при текущем индексе 0, id=%lu", getUid()); mudlog(buf, BRF, LVL_IMMORT, SYSLOG, TRUE); return false; } - auto it = _memberList.find(member->get_uid()); - if (it == _memberList.end()){ - sprintf(buf, "Group::removeMember: персонаж не найден, id=%lu", getUid()); + auto it = _memberList->find(member->get_uid()); + if (it == _memberList->end()){ + sprintf(buf, "Group::_removeMember: персонаж не найден, id=%lu", getUid()); mudlog(buf, BRF, LVL_IMMORT, SYSLOG, TRUE); return false; } - _memberList.erase(it); + _memberList->erase(it); member->personGroup = nullptr; return true; } bool Group::_isMember(int uid) { - auto it = _memberList.find(uid); - if (it == _memberList.end() ) + auto it = _memberList->find(uid); + if (it == _memberList->end() ) return false; return true; } -void Group::clear(bool silentMode) { - sprintf(buf, "[Group::clear()]: _memberList: %lu", _memberList.size()); +void Group::_clear(bool silentMode) { + sprintf(buf, "[Group::clear()]: _memberList: %lu", _memberList->size()); mudlog(buf, BRF, LVL_IMMORT, SYSLOG, TRUE); CHAR_DATA* ch; - for (auto & it : _memberList) { - ch = it.second->member; + for (auto & it : *_memberList) { + ch = (*it.second)->member; if (ch == nullptr || ch->purged()) continue; if (!silentMode) send_to_char(ch, "Ваша группа распущена.\r\n"); ch->personGroup = nullptr; } - _memberList.clear(); + _memberList->clear(); } void Group::promote(char *applicant) { @@ -149,31 +271,232 @@ void Group::promote(char *applicant) { sendToGroup(GRP_COMM_LEADER, "Нет такого персонажа."); return; } - _setLeader(_memberList.at(memberId)->member); - sendToGroup(GRP_COMM_ALL, "Изменился лидер группы на %.", _memberList.at(memberId)->memberName.c_str()); - if (u_short diff = _memberCap - (int)_memberList.size() >0){ - u_short list[diff]; u_short i = 0; - for (auto it = _memberList.rend(); it != _memberList.rbegin(); --it){ - ++i; - if (it->second->member == _leader ) + _setLeader((*_memberList->at(memberId))->member); + sendToGroup(GRP_COMM_ALL, "Изменился лидер группы на %.", (*_memberList->at(memberId))->memberName.c_str()); + _memberCap = 1; + u_short diff = (u_short)(_memberList->size() - _memberCap); + if (diff > 0){ + u_short i = 0; + CHAR_DATA* expellList[diff]; + for (auto it = _memberList->begin(); it!= _memberList->end() || diff > i; it++){ + if ((*it->second)->member != _leader ) { + expellList[i] = (*it->second)->member; + ++i; + } + } + for (i=0; i < diff; i++){ + _removeMember(expellList[i]); + } + } +} + +void Group::approveRequest(const char *applicant) { + auto r = groupRoster.findRequest(applicant, this->getLeaderName().c_str(), RQ_PERSON); + if (r == nullptr || r->_group != this){ + send_to_char(_leader, "Заявка не найдена, уточните имя.\r\n"); + return; + } + addMember(r->_applicant); + send_to_char(r->_applicant, "Вас приняли в группу.\r\n"); + groupRoster.deleteRequest(r); +} + +void Group::expellMember(char *memberName) { + auto mId = _findMember(memberName); + auto vict = (*_memberList->at(mId))->member; + if (mId != 0 && vict != nullptr) { + _removeMember(vict); + act("$N исключен$A из состава вашей группы.", FALSE, _leader, 0, vict, TO_CHAR); + act("Вы исключены из группы $n1!", FALSE, _leader, 0, vict, TO_VICT); + act("$N был$G исключен$A из группы $n1!", FALSE, _leader, 0, vict, TO_NOTVICT | TO_ARENA_LISTEN); + } +} + +void Group::listMembers(CHAR_DATA *ch) { + CHAR_DATA *leader; + struct follow_type *f; + int count = 1; + + if (ch->personGroup) + { + leader = ch->personGroup->getLeader(); + for (auto & it : *_memberList ) + { + if ((*it.second)->member == leader) continue; - list[i-1] = it->second->memberUID; - if (diff == i) - break; + count++; + if (count == 2){ + send_to_char("Ваша группа состоит из:\r\n", ch); + sprintf(smallBuf, "Лидер: %s\r\n", ch->personGroup->getLeaderName().c_str() ); + send_to_char(smallBuf, ch); + } + sprintf(smallBuf, "%d. Согруппник: %s\r\n", count, (*it.second)->memberName.c_str()); + send_to_char(smallBuf, ch); } - for (i = 0; i <= diff; i++) - removeMember(_memberList.find(list[i])->second->member); + if (count == 1) + send_to_char(ch, "Увы, но вы один, как перст!\r\n"); + } + else + { + send_to_char("Но вы же не член (в лучшем смысле этого слова) группы!\r\n", ch); } } -int Group::_findMember(char *memberName) { - for (auto & it : _memberList) { - if (!str_cmp(it.second->memberName, memberName)) - return it.first; +void Group::rejectRequest(char *applicant) { + +} + +void Group::leaveGroup() { + +} + +void Group::_printDeadLine(CHAR_DATA* ch, char* playerName, int header) { + if (ch == nullptr) + return; + if (header) + send_to_char("Персонаж | Здоровье |Рядом| Аффект | Положение\r\n", ch); + send_to_char(ch, "%s47 Отсутсвует в игре\r\n", playerName); +} + + +void Group::_printLine(CHAR_DATA* ch, int memberUID, int header) +{ + CHAR_DATA* member = this->_findMember(memberUID); + if (ch == nullptr) + return; + int ok, ok2, div; + + bool leader = false; + if (member!= nullptr && member->personGroup) + if (member->personGroup->getLeader() == member) + leader = true; + if (member == nullptr) { + _printDeadLine(); + } + + if (IS_NPC(member)) + { + if (!header) +// send_to_char("Персонаж | Здоровье |Рядом| Доп | Положение | Лояльн.\r\n",ch); + send_to_char("Персонаж | Здоровье |Рядом| Аффект | Положение\r\n", ch); + std::string name = GET_NAME(member); + name[0] = UPPER(name[0]); + sprintf(buf, "%s%-20s%s|", CCIBLU(ch, C_NRM), + name.substr(0, 20).c_str(), CCNRM(ch, C_NRM)); + sprintf(buf + strlen(buf), "%s%10s%s|", + color_value(ch, GET_HIT(member), GET_REAL_MAX_HIT(member)), + WORD_STATE[posi_value(GET_HIT(member), GET_REAL_MAX_HIT(member)) + 1], CCNRM(ch, C_NRM)); + + ok = ch->in_room == IN_ROOM(member); + sprintf(buf + strlen(buf), "%s%5s%s|", + ok ? CCGRN(ch, C_NRM) : CCRED(ch, C_NRM), ok ? " Да " : " Нет ", CCNRM(ch, C_NRM)); + + sprintf(buf + strlen(buf), " %s%s%s%s%s%s%s%s%s%s%s%s%s |", + CCIRED(ch, C_NRM), + AFF_FLAGGED(member, EAffectFlag::AFF_SANCTUARY) ? "О" : (AFF_FLAGGED(member, EAffectFlag::AFF_PRISMATICAURA) ? "П" : " "), + CCGRN(ch, C_NRM), + AFF_FLAGGED(member, EAffectFlag::AFF_WATERBREATH) ? "Д" : " ", CCICYN(ch, C_NRM), + AFF_FLAGGED(member, EAffectFlag::AFF_INVISIBLE) ? "Н" : " ", CCIYEL(ch, C_NRM), + (AFF_FLAGGED(member, EAffectFlag::AFF_SINGLELIGHT) + || AFF_FLAGGED(member, EAffectFlag::AFF_HOLYLIGHT) + || (GET_EQ(member, WEAR_LIGHT) + && GET_OBJ_VAL(GET_EQ(member, WEAR_LIGHT), 2))) ? "С" : " ", + CCIBLU(ch, C_NRM), AFF_FLAGGED(member, EAffectFlag::AFF_FLY) ? "Л" : " ", CCYEL(ch, C_NRM), + member->low_charm() ? "Т" : " ", CCNRM(ch, C_NRM)); + + sprintf(buf + strlen(buf), "%-15s", POS_STATE[(int) GET_POS(member)]); + + act(buf, FALSE, ch, 0, member, TO_CHAR); + } + else + { + if (!header) + send_to_char + ("Персонаж | Здоровье |Энергия|Рядом|Учить| Аффект | Кто | Держит строй | Положение \r\n", ch); + + std::string name = member->get_pc_name(); + name[0] = UPPER(name[0]); + sprintf(buf, "%s%-20s%s|", CCIBLU(ch, C_NRM), name.c_str(), CCNRM(ch, C_NRM)); + sprintf(buf + strlen(buf), "%s%10s%s|", + color_value(ch, GET_HIT(member), GET_REAL_MAX_HIT(member)), + WORD_STATE[posi_value(GET_HIT(member), GET_REAL_MAX_HIT(member)) + 1], CCNRM(ch, C_NRM)); + + sprintf(buf + strlen(buf), "%s%7s%s|", + color_value(ch, GET_MOVE(member), GET_REAL_MAX_MOVE(member)), + MOVE_STATE[posi_value(GET_MOVE(member), GET_REAL_MAX_MOVE(member)) + 1], CCNRM(ch, C_NRM)); + + ok = ch->in_room == IN_ROOM(member); + sprintf(buf + strlen(buf), "%s%5s%s|", + ok ? CCGRN(ch, C_NRM) : CCRED(ch, C_NRM), ok ? " Да " : " Нет ", CCNRM(ch, C_NRM)); + + if ((!IS_MANA_CASTER(member) && !MEMQUEUE_EMPTY(member)) || + (IS_MANA_CASTER(member) && GET_MANA_STORED(member) < GET_MAX_MANA(member))) + { + div = mana_gain(member); + if (div > 0) + { + if (!IS_MANA_CASTER(member)) + { + ok2 = MAX(0, 1 + GET_MEM_TOTAL(member) - GET_MEM_COMPLETED(member)); + ok2 = ok2 * 60 / div; // время мема в сек + } + else + { + ok2 = MAX(0, 1 + GET_MAX_MANA(member) - GET_MANA_STORED(member)); + ok2 = ok2 / div; // время восстановления в секундах + } + ok = ok2 / 60; + ok2 %= 60; + if (ok > 99) + sprintf(buf + strlen(buf), "&g%5d&n|", ok); + else + sprintf(buf + strlen(buf), "&g%2d:%02d&n|", ok, ok2); + } + else + { + sprintf(buf + strlen(buf), "&r -&n|"); + } + } + else + sprintf(buf + strlen(buf), " |"); + + sprintf(buf + strlen(buf), " %s%s%s%s%s%s%s%s%s%s%s%s%s |", + CCIRED(ch, C_NRM), AFF_FLAGGED(member, EAffectFlag::AFF_SANCTUARY) ? "О" : (AFF_FLAGGED(member, EAffectFlag::AFF_PRISMATICAURA) + ? "П" : " "), CCGRN(ch, + C_NRM), + AFF_FLAGGED(member, EAffectFlag::AFF_WATERBREATH) ? "Д" : " ", CCICYN(ch, + C_NRM), + AFF_FLAGGED(member, EAffectFlag::AFF_INVISIBLE) ? "Н" : " ", CCIYEL(ch, C_NRM), (AFF_FLAGGED(member, EAffectFlag::AFF_SINGLELIGHT) + || AFF_FLAGGED(member, EAffectFlag::AFF_HOLYLIGHT) + || (GET_EQ(member, WEAR_LIGHT) + && + GET_OBJ_VAL(GET_EQ + (member, WEAR_LIGHT), + 2))) ? "С" : " ", + CCIBLU(ch, C_NRM), AFF_FLAGGED(member, EAffectFlag::AFF_FLY) ? "Л" : " ", CCYEL(ch, C_NRM), + member->ahorse() ? "В" : " ", CCNRM(ch, C_NRM)); + + sprintf(buf + strlen(buf), "%5s|", leader ? "Лидер" : ""); + ok = PRF_FLAGGED(member, PRF_SKIRMISHER); + sprintf(buf + strlen(buf), "%s%-14s%s|", ok ? CCGRN(ch, C_NRM) : CCNRM(ch, C_NRM), ok ? " Да " : " Нет ", CCNRM(ch, C_NRM)); + sprintf(buf + strlen(buf), " %s", POS_STATE[(int) GET_POS(member)]); + act(buf, FALSE, ch, 0, member, TO_CHAR); } - return 0; } -void Group::approveRequest(char *applicant) { +bool Group::_sameGroup(CHAR_DATA *ch, CHAR_DATA *vict) { + if (ch == nullptr || vict == nullptr) + return false; + if (ch->personGroup == vict->personGroup) + return true; + return false; +} +void Group::charDataPurged(CHAR_DATA *ch) { + for (auto &it : *_memberList) { + if (ch == (*(it.second))->member) { + (*(it.second))->member = nullptr; + return; + } + } } diff --git a/src/grp/grp.main.h b/src/grp/grp.main.h index a87160fff..4e20b60f2 100644 --- a/src/grp/grp.main.h +++ b/src/grp/grp.main.h @@ -19,8 +19,6 @@ int max_group_size(CHAR_DATA *ch); class Group; class Request; -using grp_ptr = std::shared_ptr; -using rq_ptr = std::shared_ptr; struct char_info { char_info(int memberUid, CHAR_DATA *member, std::string memberName); @@ -30,6 +28,9 @@ struct char_info { std::string memberName; }; +using grp_ptr = std::shared_ptr; +using rq_ptr = std::shared_ptr; +using mm_ptr = char_info *; class Group { private: @@ -43,13 +44,13 @@ class Group { std::string _leaderName; // ссылка на персонажа, АХТУНГ! Может меняться и быть невалидным CHAR_DATA* _leader = nullptr; - void clear(); + void _printDeadLine(CHAR_DATA* ch, char* playerName, int header); public: u_long getUid() const; u_short getCurrentMemberCount() const; const std::string &getLeaderName() const; - CHAR_DATA *getLeader(); - void clear(bool silent); + CHAR_DATA *getLeader() const; + void _clear(bool silent); Group(CHAR_DATA *leader, u_long uid); ~Group(); void _setLeader(CHAR_DATA *leader); @@ -58,18 +59,27 @@ class Group { bool _isActive(); // проверка, что в группе все персонажи онлайн bool _isMember(int uid); int _findMember(char* memberName); -private: - std::map> _memberList; + CHAR_DATA* _findMember(int UID); + bool _removeMember(CHAR_DATA *member); +private: + std::map> * _memberList; + void _printLine(CHAR_DATA * ch, int memberUID, int header); + bool _sameGroup(CHAR_DATA * ch, CHAR_DATA * vict); public: void addMember(CHAR_DATA *member); - bool removeMember(CHAR_DATA *member); + void expellMember(char* memberName); bool restoreMember(CHAR_DATA *member); void printGroup(CHAR_DATA *requestor); void listMembers(CHAR_DATA *requestor); + void sendToGroup(GRP_COMM mode, const char *msg, ...); void promote(char *applicant); - void approveRequest(char *applicant); + void approveRequest(const char *applicant); + void rejectRequest(char *applicant); + void leaveGroup(); + + void charDataPurged(CHAR_DATA* ch); }; class Request { @@ -105,11 +115,14 @@ class GroupRoster { std::tuple tryMakeInvite(Group* grp, char* member); std::tuple tryAddRequest(CHAR_DATA* author, char* targetGroup); public: -// методы работы с заявками + // методы работы с заявками void printRequestList(CHAR_DATA* ch); void makeInvite(CHAR_DATA* leader, char* targetPerson); void makeRequest(CHAR_DATA* author, char* targetGroup); void revokeRequest(CHAR_DATA* author, char* targetGroup); + void acceptInvite(CHAR_DATA* who, char* author); + void deleteRequest(Request * r); + Request* findRequest(const char* targetPerson, const char* group, RQ_TYPE type); }; #endif //BYLINS_GRP_MAIN_H diff --git a/src/grp/grp.roster.cpp b/src/grp/grp.roster.cpp index 535045e60..74e9731fb 100644 --- a/src/grp/grp.roster.cpp +++ b/src/grp/grp.roster.cpp @@ -4,6 +4,8 @@ #include "global.objects.hpp" #include "handler.h" +#include "grp.main.h" + GroupRoster& groupRoster = GlobalObjects::groupRoster(); @@ -22,8 +24,6 @@ void GroupRoster::restorePlayerGroup(CHAR_DATA *ch) { } void GroupRoster::processGroupCommands(CHAR_DATA *ch, char *argument) { - enum SubCmd { PRINT, ALL, HELP, LIST, MAKE, INVITE, TAKE, REJECT, EXPELL, LEADER, LEAVE, DISBAND}; - SubCmd mode; const std::string strHELP = "справка помощь"; const std::string strMAKE = "создать make"; @@ -36,110 +36,92 @@ void GroupRoster::processGroupCommands(CHAR_DATA *ch, char *argument) { const std::string strLEAVE = "покинуть leave"; const std::string strDISBAND = "распустить clear"; const std::string strALL = "все all"; + const std::string strTEST = "тест test"; char subcmd[MAX_INPUT_LENGTH], target[MAX_INPUT_LENGTH]; if (!ch || IS_NPC(ch)) return; two_arguments(argument, subcmd, target); - - // выбираем режим - if (!*subcmd) - mode = PRINT; - else if (isname(subcmd, strALL.c_str())) - mode = ALL; - else if (isname(subcmd, strHELP.c_str())) - mode = HELP; - else if (isname(subcmd, strMAKE.c_str())) - mode = MAKE; - else if (isname(subcmd, strLIST.c_str())) - mode = LIST; - else if (isname(subcmd, strINVITE.c_str())) - mode = INVITE; - else if (isname(subcmd, strTAKE.c_str())) - mode = TAKE; - else if (isname(subcmd, strREJECT.c_str())) - mode = REJECT; - else if (isname(subcmd, strEXPELL.c_str())) - mode = EXPELL; - else if (isname(subcmd, strLEADER.c_str())) - mode = LEADER; - else if (isname(subcmd, strLEAVE.c_str())) - mode = LEAVE; - else if (isname(subcmd, strDISBAND.c_str())) - mode = DISBAND; - else { - send_to_char("Уточните команду.\r\n", ch); - return; - } if (GET_POS(ch) < POS_RESTING) { send_to_char("Трудно управлять группой в таком состоянии.\r\n", ch); return; } auto grp = ch->personGroup; - if (mode == MAKE) { + // выбираем режим + if (!*subcmd) { + grp->printGroup(ch); return;} + else if (isname(subcmd, strALL.c_str())) { + if (IS_IMMORTAL(ch)) + groupRoster.printList(ch); + return; + } + else if (isname(subcmd, strHELP.c_str())) { + send_to_char(ch, "Команды: %s\r\n%s\r\n%s\r\n%s\r\n%s\r\n%s\r\n%s\r\n%s\r\n%s\r\n%s\r\n", + strHELP.c_str(), strMAKE.c_str(), strLIST.c_str(),strINVITE.c_str(), strTAKE.c_str(), + strREJECT.c_str(), strEXPELL.c_str(), strLEADER.c_str(), strLEAVE.c_str(), strDISBAND.c_str()); + return; + } + else if (isname(subcmd, strMAKE.c_str())) { if (grp != nullptr && grp->getLeader() == ch){ send_to_char(ch, "Только великим правителям, навроде Цесаря Иулия, было дозволено водить много легионов!\r\n"); return; } if (grp != nullptr) // в группе - покидаем - grp->removeMember(ch); + grp->_removeMember(ch); groupRoster.addGroup(ch); send_to_char(ch, "Вы создали группу c максимальным числом последователей %d.\r\n", ch->personGroup ? ch->personGroup->_getMemberCap() : 0); + return; + } + else if (isname(subcmd, strLIST.c_str())) { + grp->listMembers(ch); + return; + } + else if (isname(subcmd, strLEAVE.c_str())){ + grp->leaveGroup(); return; } + if (grp != nullptr && grp->getLeader() != ch ) { send_to_char(ch, "Необходимо быть лидером группы для этого.\r\n"); return; } - - // MAKE был раньше, т.к. он создает группу - switch (mode) { - case MAKE: - break; - case PRINT: - grp->printGroup(ch); - break; - case ALL: - if (!IS_IMMORTAL(ch)) - return; - groupRoster.printList(ch); - break; - case HELP: - break; - case LIST: - //grp->listMembers(ch); - break; - case INVITE: - if (grp->_isFull()){ - send_to_char(ch, "Командир, но в твоей группе больше нет места!\r\n"); - return; - } - groupRoster.makeInvite(ch, target); - break; - case TAKE: - grp->approveRequest(target); - break; - case REJECT: - //grp->denyRequest(target); - break; - case EXPELL: - //grp->expellMember(target); - break; - case LEADER: - grp->promote(target); - break; - case LEAVE: - //grp->leave(ch); - break; - case DISBAND: - groupRoster.removeGroup(grp->getUid()); - grp = nullptr; - break; + if (isname(subcmd, strINVITE.c_str())) { + if (grp->_isFull()){ + send_to_char(ch, "Командир, но в твоей группе больше нет места!\r\n"); + return; + } + groupRoster.makeInvite(ch, target); + return; + } + else if (isname(subcmd, strTAKE.c_str())) { + grp->approveRequest(target); } + else if (isname(subcmd, strREJECT.c_str())){ + grp->rejectRequest(target); + return; + } + else if (isname(subcmd, strEXPELL.c_str())){ + grp->expellMember(target); + return; + } + else if (isname(subcmd, strLEADER.c_str())) { + grp->promote(target); + return; + } + else if (isname(subcmd, strDISBAND.c_str())) { + groupRoster.removeGroup(grp->getUid()); + grp = nullptr; + return; + } + else { + send_to_char("Уточните команду.\r\n", ch); + return; + } + } @@ -156,7 +138,6 @@ grp_ptr GroupRoster::addGroup(CHAR_DATA * leader) { void GroupRoster::removeGroup(u_long uid) { auto grp = _groupList.find(uid); if (grp != _groupList.end()) { - grp->second->clear(false); _groupList.erase(uid); return; } @@ -190,6 +171,8 @@ grp_ptr GroupRoster::findGroup(char *leaderName) { void GroupRoster::printList(CHAR_DATA *ch) { + if (ch == nullptr) + return; size_t cnt = this->_groupList.size(); send_to_char(ch, "Текущее количество групп в мире: %lu\r\n", cnt); for (auto & it : this->_groupList) { @@ -199,9 +182,8 @@ void GroupRoster::printList(CHAR_DATA *ch) { } } - void GroupRoster::makeInvite(CHAR_DATA *leader, char *targetPerson) { - if (AFF_FLAGGED(leader, EAffectFlag::AFF_CHARM)) + if (!leader || AFF_FLAGGED(leader, EAffectFlag::AFF_CHARM)) return; if (AFF_FLAGGED(leader, EAffectFlag::AFF_SILENCE) || AFF_FLAGGED(leader, EAffectFlag::AFF_STRANGLED)) { send_to_char(leader, "Вы немы, как рыба об лед.\r\n"); @@ -230,9 +212,11 @@ void GroupRoster::makeInvite(CHAR_DATA *leader, char *targetPerson) { } std::tuple GroupRoster::tryMakeInvite(Group* grp, char *member) { + // проверки CHAR_DATA *vict = get_player_vis(grp->getLeader(), member, FIND_CHAR_WORLD); if (!vict) return std::make_tuple(INV_R_NO_PERSON, nullptr); if (vict->personGroup != nullptr ) return std::make_tuple(INV_R::INV_R_BUSY, nullptr); + // ищем и продлеваем for (auto & it : this->_requestList){ if (vict == it->_applicant && grp == it->_group){ it->_time = std::chrono::system_clock::now(); @@ -247,7 +231,7 @@ std::tuple GroupRoster::tryMakeInvite(Group* grp, char *memb void GroupRoster::printRequestList(CHAR_DATA *ch) { bool notFound = true; - if (!ch || ch->purged()) + if (!ch) return; send_to_char(ch, "Активные заявки и приглашения:\r\n"); for (const auto & it : this->_requestList){ @@ -272,7 +256,7 @@ void GroupRoster::printRequestList(CHAR_DATA *ch) { } void GroupRoster::makeRequest(CHAR_DATA *author, char* target) { - if (AFF_FLAGGED(author, EAffectFlag::AFF_CHARM)) + if (!author || AFF_FLAGGED(author, EAffectFlag::AFF_CHARM)) return; if (AFF_FLAGGED(author, EAffectFlag::AFF_SILENCE) || AFF_FLAGGED(author, EAffectFlag::AFF_STRANGLED)) { @@ -307,16 +291,19 @@ void GroupRoster::makeRequest(CHAR_DATA *author, char* target) { } void GroupRoster::revokeRequest(CHAR_DATA *ch, char* target) { + if (!ch) + return; for (auto it = this->_requestList.begin(); it != this->_requestList.end(); ++it) if ( it->get()->_applicant == ch && str_cmp(target, it->get()->_group->getLeaderName().c_str())){ - send_to_char(ch, "заявка найдена и удалена\r\n"); + send_to_char(ch, "Вы отменили заявку на вступление в группу.\r\n"); this->_requestList.erase(it); return; } - send_to_char(ch, "заявка не найдена\r\n"); + send_to_char(ch, "Заявка не найдена.\r\n"); } std::tuple GroupRoster::tryAddRequest(CHAR_DATA *author, char *targetGroup) { + // проверки if (!author || author->purged()){ sprintf(buf, "GroupRoster::tryAddRequest: author is null or purged\r\n"); mudlog(buf, BRF, LVL_IMMORT, SYSLOG, TRUE); @@ -325,12 +312,13 @@ std::tuple GroupRoster::tryAddRequest(CHAR_DATA *author, char *ta if (targetGroup == nullptr){ return std::make_tuple(RQ_R::RQ_R_NO_GROUP, nullptr); } + // ищем группу и проверяем на всякое grp_ptr grp = groupRoster.findGroup(targetGroup) ; if (grp == nullptr) return std::make_tuple(RQ_R::RQ_R_NO_GROUP, nullptr); if (grp->_isFull()) return std::make_tuple(RQ_R::RQ_R_OVERFLOW, nullptr); - + // продлеваем for (auto & it : this->_requestList){ if (author == it->_applicant && grp.get() == it->_group){ it->_time = std::chrono::system_clock::now(); @@ -342,3 +330,32 @@ std::tuple GroupRoster::tryAddRequest(CHAR_DATA *author, char *ta this->_requestList.emplace(_requestList.end(), rq); return std::make_tuple(RQ_R::RQ_R_OK, grp); } + +Request *GroupRoster::findRequest(const char* targetPerson, const char* group, RQ_TYPE type) { + for (auto & it : this->_requestList){ + if (type == it->_type && + !str_cmp(group, it->_group->getLeaderName().c_str()) && + !str_cmp(targetPerson, it->_applicantName)) + return it.get(); + } + return nullptr; +} + +void GroupRoster::deleteRequest(Request *r) { + for (auto it = this->_requestList.begin(); it != this->_requestList.end(); ++it) + if ( it->get() == r){ + this->_requestList.erase(it); + return; + } + +} +// who - игрок, +// author - имя лидера (может быть офлайн) +void GroupRoster::acceptInvite(CHAR_DATA* who, char* author) { + if (!who) + return; + auto r = findRequest(who->get_pc_name().c_str(), author, RQ_TYPE::RQ_GROUP); + r->_group->addMember(r->_applicant); + send_to_char(r->_applicant, "Вы одобрили заявку от лидера %s\r\n", r->_group->getLeaderName().c_str()); + deleteRequest(r); +} From 36b76c2fb21f260f73428f9ef4afcef92bcf8c65 Mon Sep 17 00:00:00 2001 From: bikbai Date: Tue, 29 Dec 2020 21:57:04 +0300 Subject: [PATCH 08/40] =?UTF-8?q?=D0=B5=D1=89=D0=B5=20=D0=BE=D0=B4=D0=B8?= =?UTF-8?q?=D0=BD=20=D0=B4=D0=B5=D0=BD=D1=8C,=20=D0=B5=D1=89=D0=B5=20?= =?UTF-8?q?=D0=BD=D0=B5=D0=BC=D0=BD=D0=BE=D0=B3=D0=BE=20=D0=B8=D1=81=D0=BF?= =?UTF-8?q?=D1=80=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chars/char.cpp | 3 +- src/comm.h | 1 - src/grp/grp.cmdprocessor.cpp | 2 +- src/grp/grp.group.cpp | 493 ++++++++++++++++++++--------------- src/grp/grp.main.h | 19 +- src/grp/grp.roster.cpp | 65 +++-- src/handler.cpp | 96 +++---- 7 files changed, 371 insertions(+), 308 deletions(-) diff --git a/src/chars/char.cpp b/src/chars/char.cpp index a5c2dfe9c..112e846db 100644 --- a/src/chars/char.cpp +++ b/src/chars/char.cpp @@ -611,7 +611,8 @@ void CHAR_DATA::purge() } // чистим указатель в групе - this->personGroup->charDataPurged(this); + if (this->personGroup != nullptr) + this->personGroup->charDataPurged(this); } // * Скилл с учетом всех плюсов и минусов от шмоток/яда. diff --git a/src/comm.h b/src/comm.h index 4d4453592..112a97809 100644 --- a/src/comm.h +++ b/src/comm.h @@ -72,7 +72,6 @@ unsigned long get_ip(const char *addr); #define TO_NOTVICT 3 #define TO_CHAR 4 #define TO_ROOM_HIDE 5 // В комнату, но только тем, кто чувствует жизнь -#define TO_GROUP 6 // сообщения игрокам в группе #define CHECK_NODEAF 32 // посылать только глухим #define CHECK_DEAF 64 // не посылать глухим #define TO_SLEEP 128 // to char, even if sleeping diff --git a/src/grp/grp.cmdprocessor.cpp b/src/grp/grp.cmdprocessor.cpp index e85b2fb43..62ce07c47 100644 --- a/src/grp/grp.cmdprocessor.cpp +++ b/src/grp/grp.cmdprocessor.cpp @@ -240,7 +240,7 @@ void do_group(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) return; } - if (!str_cmp(buf, "список")) + if (isname(buf, "список")) { print_list_group(ch); return; diff --git a/src/grp/grp.group.cpp b/src/grp/grp.group.cpp index 568719e7f..53a60efb7 100644 --- a/src/grp/grp.group.cpp +++ b/src/grp/grp.group.cpp @@ -64,7 +64,7 @@ CHAR_DATA *Group::getLeader() const{ int Group::_findMember(char *memberName) { for (auto & it : *_memberList) { - if (!str_cmp((*it.second)->memberName, memberName)) + if (isname((*it.second)->memberName, memberName)) return it.first; } return 0; @@ -78,6 +78,14 @@ CHAR_DATA* Group::_findMember(int uid) { return nullptr; } +const char* Group::_getMemberName(int uid) { + for (auto & it : *_memberList) { + if ((*it.second)->memberUID == uid) + return (*it.second)->memberName.c_str(); + } + return nullptr; +} + void Group::_setLeader(CHAR_DATA *leader) { _leaderUID = leader->get_uid(); _leader = leader; @@ -101,67 +109,6 @@ int Group::_getMemberCap() const { return _memberCap; } -void Group::printGroup(CHAR_DATA *ch) { - int gfound = 0, cfound = 0; - CHAR_DATA *leader; - struct follow_type *f, *g; - - if (ch->personGroup) - leader = ch->personGroup->getLeader(); - else - leader = ch; - - if (!IS_NPC(ch)) - ch->desc->msdp_report(msdp::constants::GROUP); - // печатаем группу - if (ch->personGroup != nullptr ) - { - send_to_char("Ваша группа состоит из:\r\n", ch); - for (auto &it : *_memberList ){ - _printLine(ch, it.first, gfound++); - } - } - // допечатываем чармисов - for (f = ch->followers; f; f = f->next) - { - if (!(AFF_FLAGGED(f->follower, EAffectFlag::AFF_CHARM) - || MOB_FLAGGED(f->follower, MOB_ANGEL)|| MOB_FLAGGED(f->follower, MOB_GHOST))) - { - continue; - } - if (!cfound) - send_to_char("Ваши последователи:\r\n", ch); - _printLine(ch, f->follower, cfound++); - } - if (!gfound && !cfound) { - send_to_char("Но вы же не член (в лучшем смысле этого слова) группы!\r\n", ch); - return; - } - // печатаем чармисов членов группы - if (PRF_FLAGGED(ch, PRF_SHOWGROUP)) - { - for (auto &it : *_memberList ) { - for (f = (*it.second)->member->followers; f; f = f->next) { - if (!(AFF_FLAGGED(f->follower, EAffectFlag::AFF_CHARM) - || MOB_FLAGGED(f->follower, MOB_ANGEL) || MOB_FLAGGED(f->follower, MOB_GHOST))){ - continue; - } - // клоны отключены - if (PRF_FLAGGED(ch, PRF_NOCLONES) && IS_NPC(f->follower) && - (MOB_FLAGGED(f->follower, MOB_CLONE) || GET_MOB_VNUM(f->follower) == MOB_KEEPER)) { - continue; - } - - if (!cfound) { - send_to_char("Последователи членов вашей группы:\r\n", ch); - } - _printLine(ch, f->follower, cfound++); - } - - } - } -} - const std::string &Group::getLeaderName() const { return _leaderName; } @@ -172,21 +119,17 @@ bool Group::_isFull() { return false; } -void Group::sendToGroup(GRP_COMM mode, const char *msg, ...) { - va_list args; - va_start(args, msg); - vsnprintf(smallBuf, sizeof(smallBuf), msg, args); - va_end(args); - if (mode == GRP_COMM::GRP_COMM_ALL) - for (auto & it : *_memberList ) { - send_to_char((*it.second)->member, "%s%s", smallBuf, "\r\n"); - } - if (mode == GRP_COMM_LEADER) - send_to_char(_leader, "%s%s", smallBuf, "\r\n"); +bool Group::_sameGroup(CHAR_DATA *ch, CHAR_DATA *vict) { + if (ch == nullptr || vict == nullptr) + return false; + if (ch->personGroup == vict->personGroup) + return true; + return false; } + Group::~Group() { - this->_clear(true); + this->_clear(false); mudlog("~Group", BRF, LVL_IMMORT, SYSLOG, TRUE); } @@ -216,11 +159,17 @@ void Group::addMember(CHAR_DATA *member) { return; } member->personGroup = this; + actToGroup(member, "$N принят$A в группу."); // getLeader()->send_to_TC(true, false, false, "Размер списка: %lu\r\n", _memberList->size()); - } bool Group::restoreMember(CHAR_DATA *member) { + for (auto &it: *_memberList){ + if (it.first == member->get_uid()) { + (*it.second)->member = member; + return true; + } + } return false; } @@ -232,14 +181,33 @@ bool Group::_removeMember(CHAR_DATA *member) { mudlog(buf, BRF, LVL_IMMORT, SYSLOG, TRUE); return false; } - auto it = _memberList->find(member->get_uid()); - if (it == _memberList->end()){ + // удаляем персонажа из группы и ссылку на группу + auto num = _memberList->erase(member->get_uid()); + member->personGroup = nullptr; + if (num == 0){ sprintf(buf, "Group::_removeMember: персонаж не найден, id=%lu", getUid()); mudlog(buf, BRF, LVL_IMMORT, SYSLOG, TRUE); return false; } - _memberList->erase(it); - member->personGroup = nullptr; + + CHAR_DATA* nxtLdr = _leader; + int nxtLdrGrpSize = 0; + // если ушел лидер - ищем нового, но после ухода предыдущего + if ( member == _leader) { + for (const auto & mit : *_memberList){ + auto m = (*mit.second)->member; + if ( m != nullptr && !m->purged() && max_group_size(m) > nxtLdrGrpSize ) { + nxtLdr = m; + nxtLdrGrpSize = max_group_size(m); + } + } + } + // никого не осталось, распускаем группу + if (nxtLdr == nullptr) { + groupRoster.removeGroup(_uid); + return false; + } + _setLeader(nxtLdr); return true; } @@ -274,7 +242,7 @@ void Group::promote(char *applicant) { _setLeader((*_memberList->at(memberId))->member); sendToGroup(GRP_COMM_ALL, "Изменился лидер группы на %.", (*_memberList->at(memberId))->memberName.c_str()); _memberCap = 1; - u_short diff = (u_short)(_memberList->size() - _memberCap); + auto diff = (u_short)(_memberList->size() - _memberCap); if (diff > 0){ u_short i = 0; CHAR_DATA* expellList[diff]; @@ -290,12 +258,33 @@ void Group::promote(char *applicant) { } } +void Group::rejectRequest(char *applicant) { + if (!*applicant) { + send_to_char(_leader, "Заявка не найдена, уточните имя.\r\n"); + return; + } + auto r = groupRoster.findRequest(applicant, this->getLeaderName().c_str(), RQ_PERSON); + if (r == nullptr || r->_group != this){ + send_to_char(_leader, "Заявка не найдена, уточните имя.\r\n"); + return; + } + send_to_char(r->_applicant, "Ваша заявка в группу лидера %s отклонена.\r\n", r->_group->getLeaderName().c_str()); + groupRoster.deleteRequest(r); + send_to_char(_leader, "Вы отклонили заявку.\r\n"); +} + + void Group::approveRequest(const char *applicant) { + if (!*applicant) { + send_to_char(_leader, "Заявка не найдена, уточните имя.\r\n"); + return; + } auto r = groupRoster.findRequest(applicant, this->getLeaderName().c_str(), RQ_PERSON); if (r == nullptr || r->_group != this){ send_to_char(_leader, "Заявка не найдена, уточните имя.\r\n"); return; } + actToGroup(r->_applicant, "$N принят$A в группу."); addMember(r->_applicant); send_to_char(r->_applicant, "Вас приняли в группу.\r\n"); groupRoster.deleteRequest(r); @@ -306,9 +295,9 @@ void Group::expellMember(char *memberName) { auto vict = (*_memberList->at(mId))->member; if (mId != 0 && vict != nullptr) { _removeMember(vict); - act("$N исключен$A из состава вашей группы.", FALSE, _leader, 0, vict, TO_CHAR); - act("Вы исключены из группы $n1!", FALSE, _leader, 0, vict, TO_VICT); - act("$N был$G исключен$A из группы $n1!", FALSE, _leader, 0, vict, TO_NOTVICT | TO_ARENA_LISTEN); + act("$N исключен$A из состава вашей группы.", FALSE, _leader, nullptr, vict, TO_CHAR); + act("Вы исключены из группы $n1!", FALSE, _leader, nullptr, vict, TO_VICT); + act("$N был$G исключен$A из группы $n1!", FALSE, _leader, nullptr, vict, TO_NOTVICT | TO_ARENA_LISTEN); } } @@ -342,155 +331,206 @@ void Group::listMembers(CHAR_DATA *ch) { } } -void Group::rejectRequest(char *applicant) { - +void Group::leaveGroup(CHAR_DATA* ch) { + if (ch->personGroup == nullptr) { + send_to_char(ch, "Нельзя стать еще более одиноким, чем сейчас.\r\n"); + return; + } + _removeMember(ch); } -void Group::leaveGroup() { +void Group::_printHeader(CHAR_DATA* ch, bool npc) { + if (ch == nullptr) + return; + if (npc) + send_to_char("Персонаж | Здоровье |Рядом| Аффект | Положение\r\n", ch); + else + send_to_char ("Персонаж | Здоровье |Энергия|Рядом|Учить| Аффект | Кто | Держит строй | Положение \r\n", ch); } -void Group::_printDeadLine(CHAR_DATA* ch, char* playerName, int header) { +void Group::_printDeadLine(CHAR_DATA* ch, const char* playerName, int header) { if (ch == nullptr) return; - if (header) - send_to_char("Персонаж | Здоровье |Рядом| Аффект | Положение\r\n", ch); - send_to_char(ch, "%s47 Отсутсвует в игре\r\n", playerName); + if (header == 0) + _printHeader(ch, false); + send_to_char(ch, "%s%-20s| Пока нет в игре.%s\r\n", CCINRM(ch, C_NRM), playerName, CCNRM(ch, C_NRM)); } - -void Group::_printLine(CHAR_DATA* ch, int memberUID, int header) -{ - CHAR_DATA* member = this->_findMember(memberUID); - if (ch == nullptr) +void Group::_printNPCLine(CHAR_DATA* ch, CHAR_DATA* npc, int header) { + if (ch == nullptr || npc == nullptr) return; + if (header == 0) + _printHeader(ch, true); + std::string name = GET_NAME(npc); + name[0] = UPPER(name[0]); + sprintf(buf, "%s%-20s%s|", CCIBLU(ch, C_NRM), + name.substr(0, 20).c_str(), CCNRM(ch, C_NRM)); + sprintf(buf + strlen(buf), "%s%10s%s|", + color_value(ch, GET_HIT(npc), GET_REAL_MAX_HIT(npc)), + WORD_STATE[posi_value(GET_HIT(npc), GET_REAL_MAX_HIT(npc)) + 1], CCNRM(ch, C_NRM)); + + auto ok = ch->in_room == IN_ROOM(npc); + sprintf(buf + strlen(buf), "%s%5s%s|", + ok ? CCGRN(ch, C_NRM) : CCRED(ch, C_NRM), ok ? " Да " : " Нет ", CCNRM(ch, C_NRM)); + + sprintf(buf + strlen(buf), " %s%s%s%s%s%s%s%s%s%s%s%s%s |", + CCIRED(ch, C_NRM), + AFF_FLAGGED(npc, EAffectFlag::AFF_SANCTUARY) ? "О" : (AFF_FLAGGED(npc, EAffectFlag::AFF_PRISMATICAURA) ? "П" : " "), + CCGRN(ch, C_NRM), + AFF_FLAGGED(npc, EAffectFlag::AFF_WATERBREATH) ? "Д" : " ", CCICYN(ch, C_NRM), + AFF_FLAGGED(npc, EAffectFlag::AFF_INVISIBLE) ? "Н" : " ", CCIYEL(ch, C_NRM), + (AFF_FLAGGED(npc, EAffectFlag::AFF_SINGLELIGHT) + || AFF_FLAGGED(npc, EAffectFlag::AFF_HOLYLIGHT) + || (GET_EQ(npc, WEAR_LIGHT) + && GET_OBJ_VAL(GET_EQ(npc, WEAR_LIGHT), 2))) ? "С" : " ", + CCIBLU(ch, C_NRM), AFF_FLAGGED(npc, EAffectFlag::AFF_FLY) ? "Л" : " ", CCYEL(ch, C_NRM), + npc->low_charm() ? "Т" : " ", CCNRM(ch, C_NRM)); + + sprintf(buf + strlen(buf), "%-15s", POS_STATE[(int) GET_POS(npc)]); + + act(buf, FALSE, ch, nullptr, npc, TO_CHAR); +} + +void Group::_printPCLine(CHAR_DATA* ch, CHAR_DATA* pc, int header) { int ok, ok2, div; bool leader = false; - if (member!= nullptr && member->personGroup) - if (member->personGroup->getLeader() == member) + if (pc!= nullptr && pc->personGroup) + if (pc->personGroup->getLeader() == pc) leader = true; - if (member == nullptr) { - _printDeadLine(); + if (header == 0) + _printHeader(ch, false); + std::string name = pc->get_pc_name(); + name[0] = UPPER(name[0]); + sprintf(buf, "%s%-20s%s|", CCIBLU(ch, C_NRM), name.c_str(), CCNRM(ch, C_NRM)); + sprintf(buf + strlen(buf), "%s%10s%s|", + color_value(ch, GET_HIT(pc), GET_REAL_MAX_HIT(pc)), + WORD_STATE[posi_value(GET_HIT(pc), GET_REAL_MAX_HIT(pc)) + 1], CCNRM(ch, C_NRM)); + + sprintf(buf + strlen(buf), "%s%7s%s|", + color_value(ch, GET_MOVE(pc), GET_REAL_MAX_MOVE(pc)), + MOVE_STATE[posi_value(GET_MOVE(pc), GET_REAL_MAX_MOVE(pc)) + 1], CCNRM(ch, C_NRM)); + + ok = ch->in_room == IN_ROOM(pc); + sprintf(buf + strlen(buf), "%s%5s%s|", + ok ? CCGRN(ch, C_NRM) : CCRED(ch, C_NRM), ok ? " Да " : " Нет ", CCNRM(ch, C_NRM)); + + if ((!IS_MANA_CASTER(pc) && !MEMQUEUE_EMPTY(pc)) || + (IS_MANA_CASTER(pc) && GET_MANA_STORED(pc) < GET_MAX_MANA(pc))) + { + div = mana_gain(pc); + if (div > 0) { + if (!IS_MANA_CASTER(pc)) { + ok2 = MAX(0, 1 + GET_MEM_TOTAL(pc) - GET_MEM_COMPLETED(pc)); + ok2 = ok2 * 60 / div; // время мема в сек + } else { + ok2 = MAX(0, 1 + GET_MAX_MANA(pc) - GET_MANA_STORED(pc)); + ok2 = ok2 / div; // время восстановления в секундах + } + ok = ok2 / 60; + ok2 %= 60; + if (ok > 99) + sprintf(buf + strlen(buf), "&g%5d&n|", ok); + else + sprintf(buf + strlen(buf), "&g%2d:%02d&n|", ok, ok2); + } else { + sprintf(buf + strlen(buf), "&r -&n|"); + } + } else + sprintf(buf + strlen(buf), " |"); + + sprintf(buf + strlen(buf), " %s%s%s%s%s%s%s%s%s%s%s%s%s |", + CCIRED(ch, C_NRM), AFF_FLAGGED(pc, EAffectFlag::AFF_SANCTUARY) ? "О" : (AFF_FLAGGED(pc, EAffectFlag::AFF_PRISMATICAURA) + ? "П" : " "), CCGRN(ch, + C_NRM), + AFF_FLAGGED(pc, EAffectFlag::AFF_WATERBREATH) ? "Д" : " ", CCICYN(ch, + C_NRM), + AFF_FLAGGED(pc, EAffectFlag::AFF_INVISIBLE) ? "Н" : " ", CCIYEL(ch, C_NRM), (AFF_FLAGGED(pc, EAffectFlag::AFF_SINGLELIGHT) + || AFF_FLAGGED(pc, EAffectFlag::AFF_HOLYLIGHT) + || (GET_EQ(pc, WEAR_LIGHT) + && + GET_OBJ_VAL(GET_EQ + (pc, WEAR_LIGHT), + 2))) ? "С" : " ", + CCIBLU(ch, C_NRM), AFF_FLAGGED(pc, EAffectFlag::AFF_FLY) ? "Л" : " ", CCYEL(ch, C_NRM), + pc->ahorse() ? "В" : " ", CCNRM(ch, C_NRM)); + + sprintf(buf + strlen(buf), "%5s|", leader ? "Лидер" : ""); + ok = PRF_FLAGGED(pc, PRF_SKIRMISHER); + sprintf(buf + strlen(buf), "%s%-14s%s|", ok ? CCGRN(ch, C_NRM) : CCNRM(ch, C_NRM), ok ? " Да " : " Нет ", CCNRM(ch, C_NRM)); + sprintf(buf + strlen(buf), " %s", POS_STATE[(int) GET_POS(pc)]); + act(buf, FALSE, ch, nullptr, pc, TO_CHAR); +} + +void Group::printGroup(CHAR_DATA *ch) { + int gfound = 0, cfound = 0; + CHAR_DATA *leader; + struct follow_type *f, *g; + + if (ch->personGroup) + leader = ch->personGroup->getLeader(); + else + leader = ch; + + if (!IS_NPC(ch)) + ch->desc->msdp_report(msdp::constants::GROUP); + + // печатаем группу + if (ch->personGroup != nullptr ) + { + send_to_char("Ваша группа состоит из:\r\n", ch); + for (auto &it : *_memberList ){ + if ((*it.second)->member == nullptr || (*it.second)->member->purged()) + _printDeadLine(ch, (*it.second)->memberName.c_str(), gfound++); + else + _printPCLine(ch, (*it.second)->member, gfound++); + } } - if (IS_NPC(member)) + // допечатываем чармисов + for (f = ch->followers; f; f = f->next) { - if (!header) -// send_to_char("Персонаж | Здоровье |Рядом| Доп | Положение | Лояльн.\r\n",ch); - send_to_char("Персонаж | Здоровье |Рядом| Аффект | Положение\r\n", ch); - std::string name = GET_NAME(member); - name[0] = UPPER(name[0]); - sprintf(buf, "%s%-20s%s|", CCIBLU(ch, C_NRM), - name.substr(0, 20).c_str(), CCNRM(ch, C_NRM)); - sprintf(buf + strlen(buf), "%s%10s%s|", - color_value(ch, GET_HIT(member), GET_REAL_MAX_HIT(member)), - WORD_STATE[posi_value(GET_HIT(member), GET_REAL_MAX_HIT(member)) + 1], CCNRM(ch, C_NRM)); - - ok = ch->in_room == IN_ROOM(member); - sprintf(buf + strlen(buf), "%s%5s%s|", - ok ? CCGRN(ch, C_NRM) : CCRED(ch, C_NRM), ok ? " Да " : " Нет ", CCNRM(ch, C_NRM)); - - sprintf(buf + strlen(buf), " %s%s%s%s%s%s%s%s%s%s%s%s%s |", - CCIRED(ch, C_NRM), - AFF_FLAGGED(member, EAffectFlag::AFF_SANCTUARY) ? "О" : (AFF_FLAGGED(member, EAffectFlag::AFF_PRISMATICAURA) ? "П" : " "), - CCGRN(ch, C_NRM), - AFF_FLAGGED(member, EAffectFlag::AFF_WATERBREATH) ? "Д" : " ", CCICYN(ch, C_NRM), - AFF_FLAGGED(member, EAffectFlag::AFF_INVISIBLE) ? "Н" : " ", CCIYEL(ch, C_NRM), - (AFF_FLAGGED(member, EAffectFlag::AFF_SINGLELIGHT) - || AFF_FLAGGED(member, EAffectFlag::AFF_HOLYLIGHT) - || (GET_EQ(member, WEAR_LIGHT) - && GET_OBJ_VAL(GET_EQ(member, WEAR_LIGHT), 2))) ? "С" : " ", - CCIBLU(ch, C_NRM), AFF_FLAGGED(member, EAffectFlag::AFF_FLY) ? "Л" : " ", CCYEL(ch, C_NRM), - member->low_charm() ? "Т" : " ", CCNRM(ch, C_NRM)); - - sprintf(buf + strlen(buf), "%-15s", POS_STATE[(int) GET_POS(member)]); - - act(buf, FALSE, ch, 0, member, TO_CHAR); + if (!(AFF_FLAGGED(f->follower, EAffectFlag::AFF_CHARM) + || MOB_FLAGGED(f->follower, MOB_ANGEL)|| MOB_FLAGGED(f->follower, MOB_GHOST))) { + continue; + } + if (!cfound) + send_to_char("Ваши последователи:\r\n", ch); + _printNPCLine(ch, f->follower, cfound++); } - else + + if (!gfound && !cfound) { + send_to_char("Но вы же не член (в лучшем смысле этого слова) группы!\r\n", ch); + return; + } + + // печатаем чармисов членов группы + if (PRF_FLAGGED(ch, PRF_SHOWGROUP)) { - if (!header) - send_to_char - ("Персонаж | Здоровье |Энергия|Рядом|Учить| Аффект | Кто | Держит строй | Положение \r\n", ch); - - std::string name = member->get_pc_name(); - name[0] = UPPER(name[0]); - sprintf(buf, "%s%-20s%s|", CCIBLU(ch, C_NRM), name.c_str(), CCNRM(ch, C_NRM)); - sprintf(buf + strlen(buf), "%s%10s%s|", - color_value(ch, GET_HIT(member), GET_REAL_MAX_HIT(member)), - WORD_STATE[posi_value(GET_HIT(member), GET_REAL_MAX_HIT(member)) + 1], CCNRM(ch, C_NRM)); - - sprintf(buf + strlen(buf), "%s%7s%s|", - color_value(ch, GET_MOVE(member), GET_REAL_MAX_MOVE(member)), - MOVE_STATE[posi_value(GET_MOVE(member), GET_REAL_MAX_MOVE(member)) + 1], CCNRM(ch, C_NRM)); - - ok = ch->in_room == IN_ROOM(member); - sprintf(buf + strlen(buf), "%s%5s%s|", - ok ? CCGRN(ch, C_NRM) : CCRED(ch, C_NRM), ok ? " Да " : " Нет ", CCNRM(ch, C_NRM)); - - if ((!IS_MANA_CASTER(member) && !MEMQUEUE_EMPTY(member)) || - (IS_MANA_CASTER(member) && GET_MANA_STORED(member) < GET_MAX_MANA(member))) - { - div = mana_gain(member); - if (div > 0) - { - if (!IS_MANA_CASTER(member)) - { - ok2 = MAX(0, 1 + GET_MEM_TOTAL(member) - GET_MEM_COMPLETED(member)); - ok2 = ok2 * 60 / div; // время мема в сек + for (auto &it : *_memberList ) { + for (f = (*it.second)->member->followers; f; f = f->next) { + if (!(AFF_FLAGGED(f->follower, EAffectFlag::AFF_CHARM) + || MOB_FLAGGED(f->follower, MOB_ANGEL) || MOB_FLAGGED(f->follower, MOB_GHOST))){ + continue; } - else - { - ok2 = MAX(0, 1 + GET_MAX_MANA(member) - GET_MANA_STORED(member)); - ok2 = ok2 / div; // время восстановления в секундах + // клоны отключены + if (PRF_FLAGGED(ch, PRF_NOCLONES) && IS_NPC(f->follower) && + (MOB_FLAGGED(f->follower, MOB_CLONE) || GET_MOB_VNUM(f->follower) == MOB_KEEPER)) { + continue; } - ok = ok2 / 60; - ok2 %= 60; - if (ok > 99) - sprintf(buf + strlen(buf), "&g%5d&n|", ok); - else - sprintf(buf + strlen(buf), "&g%2d:%02d&n|", ok, ok2); - } - else - { - sprintf(buf + strlen(buf), "&r -&n|"); + + if (!cfound) { + send_to_char("Последователи членов вашей группы:\r\n", ch); + } + _printNPCLine(ch, f->follower, cfound++); } + } - else - sprintf(buf + strlen(buf), " |"); - - sprintf(buf + strlen(buf), " %s%s%s%s%s%s%s%s%s%s%s%s%s |", - CCIRED(ch, C_NRM), AFF_FLAGGED(member, EAffectFlag::AFF_SANCTUARY) ? "О" : (AFF_FLAGGED(member, EAffectFlag::AFF_PRISMATICAURA) - ? "П" : " "), CCGRN(ch, - C_NRM), - AFF_FLAGGED(member, EAffectFlag::AFF_WATERBREATH) ? "Д" : " ", CCICYN(ch, - C_NRM), - AFF_FLAGGED(member, EAffectFlag::AFF_INVISIBLE) ? "Н" : " ", CCIYEL(ch, C_NRM), (AFF_FLAGGED(member, EAffectFlag::AFF_SINGLELIGHT) - || AFF_FLAGGED(member, EAffectFlag::AFF_HOLYLIGHT) - || (GET_EQ(member, WEAR_LIGHT) - && - GET_OBJ_VAL(GET_EQ - (member, WEAR_LIGHT), - 2))) ? "С" : " ", - CCIBLU(ch, C_NRM), AFF_FLAGGED(member, EAffectFlag::AFF_FLY) ? "Л" : " ", CCYEL(ch, C_NRM), - member->ahorse() ? "В" : " ", CCNRM(ch, C_NRM)); - - sprintf(buf + strlen(buf), "%5s|", leader ? "Лидер" : ""); - ok = PRF_FLAGGED(member, PRF_SKIRMISHER); - sprintf(buf + strlen(buf), "%s%-14s%s|", ok ? CCGRN(ch, C_NRM) : CCNRM(ch, C_NRM), ok ? " Да " : " Нет ", CCNRM(ch, C_NRM)); - sprintf(buf + strlen(buf), " %s", POS_STATE[(int) GET_POS(member)]); - act(buf, FALSE, ch, 0, member, TO_CHAR); } } -bool Group::_sameGroup(CHAR_DATA *ch, CHAR_DATA *vict) { - if (ch == nullptr || vict == nullptr) - return false; - if (ch->personGroup == vict->personGroup) - return true; - return false; -} + void Group::charDataPurged(CHAR_DATA *ch) { for (auto &it : *_memberList) { @@ -500,3 +540,44 @@ void Group::charDataPurged(CHAR_DATA *ch) { } } } + + +void Group::sendToGroup(GRP_COMM mode, const char *msg, ...) { + va_list args; + va_start(args, msg); + vsnprintf(smallBuf, sizeof(smallBuf), msg, args); + va_end(args); + if (mode == GRP_COMM::GRP_COMM_ALL) + for (auto & it : *_memberList ) { + send_to_char((*it.second)->member, "%s%s", smallBuf, "\r\n"); + } + else if (mode == GRP_COMM_LEADER) { + send_to_char(_leader, "%s%s", smallBuf, "\r\n"); + } +} + +// надстройка над act, пока только про персонажей +void Group::actToGroup(CHAR_DATA* vict, const char *msg, ...) { + if (vict == nullptr) + return; + va_list args; + va_start(args, msg); + vsnprintf(smallBuf, sizeof(smallBuf), msg, args); + va_end(args); + for (auto &it : *_memberList) { + auto ch = (*(it.second))->member; + if ( ch == nullptr || ch->purged()) { + continue; + } + act(smallBuf, FALSE, ch, nullptr, vict, TO_CHAR); + } +} + +void Group::makeAddFollowers(CHAR_DATA *leader) { + for (auto f = leader->followers; f; f = f->next) { + if (IS_NPC(f->follower)) + continue; + if (_memberList->size() > _memberCap) + this->addMember(f->follower); + } +} diff --git a/src/grp/grp.main.h b/src/grp/grp.main.h index 4e20b60f2..2d817bf02 100644 --- a/src/grp/grp.main.h +++ b/src/grp/grp.main.h @@ -5,7 +5,7 @@ #include "chars/char.hpp" enum RQ_TYPE {RQ_GROUP, RQ_PERSON}; -enum GRP_COMM {GRP_COMM_LEADER, GRP_COMM_ALL, GRP_COMM_ACT}; +enum GRP_COMM {GRP_COMM_LEADER, GRP_COMM_ALL, GRP_COMM_OTHER}; enum RQ_R {RQ_R_OK, RQ_R_NO_GROUP, RQ_R_OVERFLOW, RQ_REFRESH}; enum INV_R {INV_R_OK, INV_R_NO_PERSON, INV_R_BUSY, INV_R_REFRESH}; @@ -44,7 +44,6 @@ class Group { std::string _leaderName; // ссылка на персонажа, АХТУНГ! Может меняться и быть невалидным CHAR_DATA* _leader = nullptr; - void _printDeadLine(CHAR_DATA* ch, char* playerName, int header); public: u_long getUid() const; u_short getCurrentMemberCount() const; @@ -58,28 +57,34 @@ class Group { bool _isFull(); bool _isActive(); // проверка, что в группе все персонажи онлайн bool _isMember(int uid); + const char* _getMemberName(int uid); int _findMember(char* memberName); CHAR_DATA* _findMember(int UID); bool _removeMember(CHAR_DATA *member); - + void charDataPurged(CHAR_DATA* ch); private: std::map> * _memberList; - void _printLine(CHAR_DATA * ch, int memberUID, int header); + void _printHeader(CHAR_DATA* ch, bool npc); + void _printDeadLine(CHAR_DATA* ch, const char* playerName, int header); + void _printNPCLine(CHAR_DATA* ch, CHAR_DATA* npc, int header); + void _printPCLine(CHAR_DATA* ch, CHAR_DATA* pc, int header); bool _sameGroup(CHAR_DATA * ch, CHAR_DATA * vict); public: + void makeAddFollowers(CHAR_DATA* leader); void addMember(CHAR_DATA *member); void expellMember(char* memberName); bool restoreMember(CHAR_DATA *member); + void printGroup(CHAR_DATA *requestor); void listMembers(CHAR_DATA *requestor); - void sendToGroup(GRP_COMM mode, const char *msg, ...); void promote(char *applicant); void approveRequest(const char *applicant); void rejectRequest(char *applicant); - void leaveGroup(); + void leaveGroup(CHAR_DATA* vict); - void charDataPurged(CHAR_DATA* ch); + void sendToGroup(GRP_COMM mode, const char *msg, ...); + void actToGroup(CHAR_DATA* vict, const char *msg, ...); }; class Request { diff --git a/src/grp/grp.roster.cpp b/src/grp/grp.roster.cpp index 74e9731fb..1475fc89f 100644 --- a/src/grp/grp.roster.cpp +++ b/src/grp/grp.roster.cpp @@ -19,15 +19,14 @@ void GroupRoster::restorePlayerGroup(CHAR_DATA *ch) { return; if (!grp->restoreMember(ch)) return; - grp->sendToGroup(GRP_COMM_ALL, "Игрок %s заново присоединился к группе.\r\n", ch->get_pc_name().c_str()); - ch->send_to_TC(true, false, true, "Membership in group: %d restored\r\n", ch->personGroup->getUid()); + grp->actToGroup(ch, "$N заново присоединил$A к вашей группе."); } void GroupRoster::processGroupCommands(CHAR_DATA *ch, char *argument) { const std::string strHELP = "справка помощь"; const std::string strMAKE = "создать make"; - const std::string strLIST = "список list"; + const std::string strLIST = "список list"; // краткий список членов группы const std::string strINVITE = "пригласить invite"; const std::string strTAKE = "принять approve"; const std::string strREJECT = "отклонить reject"; @@ -35,7 +34,8 @@ void GroupRoster::processGroupCommands(CHAR_DATA *ch, char *argument) { const std::string strLEADER = "лидер leader"; const std::string strLEAVE = "покинуть leave"; const std::string strDISBAND = "распустить clear"; - const std::string strALL = "все all"; + const std::string strWORLD = "мир world"; + const std::string strALL = "все all";// старый режим, создание группы и добавление всех последователей. const std::string strTEST = "тест test"; char subcmd[MAX_INPUT_LENGTH], target[MAX_INPUT_LENGTH]; @@ -51,19 +51,15 @@ void GroupRoster::processGroupCommands(CHAR_DATA *ch, char *argument) { // выбираем режим if (!*subcmd) { - grp->printGroup(ch); return;} - else if (isname(subcmd, strALL.c_str())) { - if (IS_IMMORTAL(ch)) - groupRoster.printList(ch); + grp->printGroup(ch); return; - } - else if (isname(subcmd, strHELP.c_str())) { - send_to_char(ch, "Команды: %s\r\n%s\r\n%s\r\n%s\r\n%s\r\n%s\r\n%s\r\n%s\r\n%s\r\n%s\r\n", + } else if (isname(subcmd, strHELP.c_str())) { + send_to_char(ch, "Команды: %s\r\n%s\r\n%s\r\n%s\r\n%s\r\n%s\r\n%s\r\n%s\r\n%s\r\n%s\r\n%s\r\n", + strALL.c_str(), strHELP.c_str(), strMAKE.c_str(), strLIST.c_str(),strINVITE.c_str(), strTAKE.c_str(), strREJECT.c_str(), strEXPELL.c_str(), strLEADER.c_str(), strLEAVE.c_str(), strDISBAND.c_str()); return; - } - else if (isname(subcmd, strMAKE.c_str())) { + } else if (isname(subcmd, strMAKE.c_str())) { if (grp != nullptr && grp->getLeader() == ch){ send_to_char(ch, "Только великим правителям, навроде Цесаря Иулия, было дозволено водить много легионов!\r\n"); return; @@ -73,18 +69,30 @@ void GroupRoster::processGroupCommands(CHAR_DATA *ch, char *argument) { groupRoster.addGroup(ch); send_to_char(ch, "Вы создали группу c максимальным числом последователей %d.\r\n", ch->personGroup ? ch->personGroup->_getMemberCap() : 0); return; - } - else if (isname(subcmd, strLIST.c_str())) { + } else if (isname(subcmd, strLIST.c_str())) { grp->listMembers(ch); return; - } - else if (isname(subcmd, strLEAVE.c_str())){ - grp->leaveGroup(); + } else if (isname(subcmd, strALL.c_str())) { + if (grp != nullptr && grp->getLeader() == ch){ + send_to_char(ch, "Только великим правителям, навроде Цесаря Иулия, было дозволено водить много легионов!\r\n"); + return; + } + if (grp != nullptr) // в группе - покидаем + grp->_removeMember(ch); + auto group = groupRoster.addGroup(ch); + group->makeAddFollowers(ch); + send_to_char(ch, "Вы создали группу c максимальным числом последователей %d.\r\n", ch->personGroup ? ch->personGroup->_getMemberCap() : 0); + return; + } else if (isname(subcmd, strLEAVE.c_str())){ + grp->leaveGroup(ch); + return; + } else if (isname(subcmd, strWORLD.c_str())) { + if (IS_IMMORTAL(ch)) + groupRoster.printList(ch); return; } - - if (grp != nullptr && grp->getLeader() != ch ) { + if (grp == nullptr || grp->getLeader() != ch ) { send_to_char(ch, "Необходимо быть лидером группы для этого.\r\n"); return; } @@ -121,10 +129,8 @@ void GroupRoster::processGroupCommands(CHAR_DATA *ch, char *argument) { send_to_char("Уточните команду.\r\n", ch); return; } - } - grp_ptr GroupRoster::addGroup(CHAR_DATA * leader) { if (leader == nullptr || leader->purged()) return nullptr; @@ -163,7 +169,7 @@ grp_ptr GroupRoster::findGroup(char *leaderName) { return nullptr; } for (auto & it : this->_groupList) { - if (str_cmp(it.second->getLeaderName().c_str(), leaderName)) + if (isname(leaderName, it.second->getLeaderName().c_str())) return it.second; } return nullptr; @@ -287,14 +293,14 @@ void GroupRoster::makeRequest(CHAR_DATA *author, char* target) { send_to_char("Заявка успешно продлена.\r\n", author); break; } - grp->sendToGroup(GRP_COMM_LEADER, "Поступила заявка на вступление в группу. \r\n"); + grp->sendToGroup(GRP_COMM_LEADER, "Поступила заявка на вступление в группу от персонажа %s.", author->get_pc_name().c_str()); } void GroupRoster::revokeRequest(CHAR_DATA *ch, char* target) { if (!ch) return; for (auto it = this->_requestList.begin(); it != this->_requestList.end(); ++it) - if ( it->get()->_applicant == ch && str_cmp(target, it->get()->_group->getLeaderName().c_str())){ + if ( it->get()->_applicant == ch && isname(target, it->get()->_group->getLeaderName().c_str())){ send_to_char(ch, "Вы отменили заявку на вступление в группу.\r\n"); this->_requestList.erase(it); return; @@ -334,8 +340,8 @@ std::tuple GroupRoster::tryAddRequest(CHAR_DATA *author, char *ta Request *GroupRoster::findRequest(const char* targetPerson, const char* group, RQ_TYPE type) { for (auto & it : this->_requestList){ if (type == it->_type && - !str_cmp(group, it->_group->getLeaderName().c_str()) && - !str_cmp(targetPerson, it->_applicantName)) + isname(group, it->_group->getLeaderName().c_str()) && + isname(targetPerson, it->_applicantName)) return it.get(); } return nullptr; @@ -354,6 +360,11 @@ void GroupRoster::deleteRequest(Request *r) { void GroupRoster::acceptInvite(CHAR_DATA* who, char* author) { if (!who) return; + if (!*author) { + send_to_char(who, "Соберись! Чье приглашение принимаем?!\r\n"); + return; + } + auto r = findRequest(who->get_pc_name().c_str(), author, RQ_TYPE::RQ_GROUP); r->_group->addMember(r->_applicant); send_to_char(r->_applicant, "Вы одобрили заявку от лидера %s\r\n", r->_group->getLeaderName().c_str()); diff --git a/src/handler.cpp b/src/handler.cpp index eb26a5434..434098230 100644 --- a/src/handler.cpp +++ b/src/handler.cpp @@ -2333,8 +2333,7 @@ void change_npc_leader(CHAR_DATA *ch) */ void extract_char(CHAR_DATA* ch, int clear_objs, bool zone_reset) { - if (ch->purged()) - { + if (ch->purged()) { log("SYSERROR: double extract_char (%s:%d)", __FILE__, __LINE__); return; } @@ -2342,41 +2341,33 @@ void extract_char(CHAR_DATA* ch, int clear_objs, bool zone_reset) DESCRIPTOR_DATA *t_desc; int i; - if (MOB_FLAGGED(ch, MOB_FREE) - || MOB_FLAGGED(ch, MOB_DELETE)) - { + if (MOB_FLAGGED(ch, MOB_FREE) || MOB_FLAGGED(ch, MOB_DELETE)) { return; } std::string name = GET_NAME(ch); + log("[Extract char] Start function for char %s VNUM: %d", name.c_str(), GET_MOB_VNUM(ch)); - if (!IS_NPC(ch) && !ch->desc) - { -// log("[Extract char] Extract descriptors"); - for (t_desc = descriptor_list; t_desc; t_desc = t_desc->next) - { - if (t_desc->original.get() == ch) - { - do_return(t_desc->character.get(), NULL, 0, 0); + if (!IS_NPC(ch) && !ch->desc) { + for (t_desc = descriptor_list; t_desc; t_desc = t_desc->next) { + if (t_desc->original.get() == ch) { + do_return(t_desc->character.get(), nullptr, 0, 0); } } } // Forget snooping, if applicable // log("[Extract char] Stop snooping"); - if (ch->desc) - { - if (ch->desc->snooping) - { - ch->desc->snooping->snoop_by = NULL; - ch->desc->snooping = NULL; + if (ch->desc) { + if (ch->desc->snooping) { + ch->desc->snooping->snoop_by = nullptr; + ch->desc->snooping = nullptr; } - if (ch->desc->snoop_by) - { + if (ch->desc->snoop_by) { SEND_TO_Q("Ваша жертва теперь недоступна.\r\n", ch->desc->snoop_by); - ch->desc->snoop_by->snooping = NULL; - ch->desc->snoop_by = NULL; + ch->desc->snoop_by->snooping = nullptr; + ch->desc->snoop_by = nullptr; } } @@ -2399,47 +2390,32 @@ void extract_char(CHAR_DATA* ch, int clear_objs, bool zone_reset) // transfer objects to room, if any // log("[Extract char] Drop objects"); - while (ch->carrying) - { + while (ch->carrying) { OBJ_DATA *obj = ch->carrying; obj_from_char(obj); drop_obj_on_zreset(ch, obj, 1, zone_reset); } - if(IS_NPC(ch)) - { + if(IS_NPC(ch)) { // дроп гривен до изменений последователей за мобом ExtMoney::drop_torc(ch); } + // лидер группы теперь не меняется после смерти, код удалил - if (!IS_NPC(ch) - && !ch->has_master() - && ch->followers - && AFF_FLAGGED(ch, EAffectFlag::AFF_GROUP)) - { -// log("[Extract char] Change group leader"); - change_leader(ch, 0); - } - else if (IS_NPC(ch) - && !IS_CHARMICE(ch) - && !ch->has_master() - && ch->followers) - { -// log("[Extract char] Changing NPC leader"); + // у НПЦ всё по-прежнему, хороводы + if (IS_NPC(ch) && !IS_CHARMICE(ch) && !ch->has_master() && ch->followers) { change_npc_leader(ch); } // log("[Extract char] Die followers"); if ((ch->followers || ch->has_master()) - && die_follower(ch)) - { + && die_follower(ch)) { // TODO: странно все это с пуржем в stop_follower return; } // log("[Extract char] Stop fighting self"); - if (ch->get_fighting()) - { + if (ch->get_fighting()) { stop_fighting(ch, TRUE); } @@ -2452,45 +2428,35 @@ void extract_char(CHAR_DATA* ch, int clear_objs, bool zone_reset) // pull the char from the list MOB_FLAGS(ch).set(MOB_DELETE); - if (ch->desc && ch->desc->original) - { + // TODO: совершенно не понял, причём тут вселение?!! + if (ch->desc && ch->desc->original) { do_return(ch, NULL, 0, 0); } const bool is_npc = IS_NPC(ch); - if (!is_npc) - { -// log("[Extract char] All save for PC"); + if (!is_npc) { check_auction(ch, NULL); ch->save_char(); //удаляются рент-файлы, если только персонаж не ушел в ренту Crash_delete_crashfile(ch); - } - else - { -// log("[Extract char] All clear for NPC"); - if ((GET_MOB_RNUM(ch) > -1) - && !MOB_FLAGGED(ch, MOB_PLAYER_SUMMON)) // if mobile и не умертвие - { + } else { + if ((GET_MOB_RNUM(ch) > -1) && !MOB_FLAGGED(ch, MOB_PLAYER_SUMMON)) { // if mobile и не умертвие mob_index[GET_MOB_RNUM(ch)].number--; } } + // выкидываем табличку входа в игру, если персонаж еще онлайн bool left_in_game = false; - if (!is_npc - && ch->desc != NULL) - { + if (!is_npc && ch->desc != nullptr) { STATE(ch->desc) = CON_MENU; SEND_TO_Q(MENU, ch->desc); - if (!IS_NPC(ch) && RENTABLE(ch) && clear_objs) - { + if (!IS_NPC(ch) && RENTABLE(ch) && clear_objs) { do_entergame(ch->desc); left_in_game = true; } } - - if (!left_in_game) - { + // если ушел или нпц - удаляем + if (!left_in_game) { character_list.remove(ch); } From 9b2f79e2c5febc2da43adf61fb5d4fde12b9e2c3 Mon Sep 17 00:00:00 2001 From: bikbai Date: Wed, 30 Dec 2020 22:51:22 +0300 Subject: [PATCH 09/40] =?UTF-8?q?=D0=9E=D0=B1=D0=B2=D1=8F=D0=B7=D0=BA?= =?UTF-8?q?=D0=B0=20=D0=B3=D1=80=D1=83=D0=BF=D0=BF=D1=8B,=20=D0=BD=D0=B0?= =?UTF-8?q?=D0=B2=D0=B5=D1=80=D0=BD=D0=BE=D0=B5,=20=D0=B3=D0=BE=D1=82?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/comm.cpp | 6 ++--- src/grp/grp.group.cpp | 45 +++++++++++++++++++++-------------- src/grp/grp.main.h | 41 ++++++++++++++++++-------------- src/grp/grp.roster.cpp | 53 ++++++++++++++++++++++++------------------ 4 files changed, 82 insertions(+), 63 deletions(-) diff --git a/src/comm.cpp b/src/comm.cpp index 1cf0bccb6..6275394f4 100644 --- a/src/comm.cpp +++ b/src/comm.cpp @@ -4135,13 +4135,11 @@ void act(const char *str, int hide_invisible, CHAR_DATA* ch, const OBJ_DATA* obj int to_sleeping, check_deaf, check_nodeaf, to_arena=0, arena_room_rnum; int to_brief_shields = 0, to_no_brief_shields = 0; - if (!str || !*str) - { + if (!str || !*str) { return; } - if (!(dg_act_check = !(type & DG_NO_TRIG))) - { + if (!(dg_act_check = !(type & DG_NO_TRIG))){ type &= ~DG_NO_TRIG; } diff --git a/src/grp/grp.group.cpp b/src/grp/grp.group.cpp index 53a60efb7..37be07453 100644 --- a/src/grp/grp.group.cpp +++ b/src/grp/grp.group.cpp @@ -133,10 +133,11 @@ Group::~Group() { mudlog("~Group", BRF, LVL_IMMORT, SYSLOG, TRUE); } -char_info::char_info(int uid, CHAR_DATA *m, std::string name) { +char_info::char_info(int uid, CHAR_DATA *m, const std::string& name) { memberUID = uid; member = m; memberName.assign(name); + expiryTime = steady_clock::now() + DEF_EXPIRY_TIME; } char_info::~char_info() { @@ -148,10 +149,16 @@ void Group::addMember(CHAR_DATA *member) { return; if (member->personGroup == this) return; + // в другой группе, вышвыриваем + if (member->personGroup != nullptr) + member->personGroup->_removeMember(member); auto it = _memberList->find(member->get_uid()); if (it == _memberList->end()) { + // рисуем сообщение до добавления + actToGroup(member, GRP_COMM_ALL, "$N принят$A в группу."); auto ci = new char_info(member->get_uid(), member, member->get_pc_name()); _memberList->emplace(member->get_uid(), std::make_shared(ci)); + send_to_char(member, "Вас приняли в группу.\r\n"); } else { sprintf(buf, "Group::addMember: группа id=%lu, попытка повторного добавления персонажа с тем же uid", getUid()); @@ -159,11 +166,14 @@ void Group::addMember(CHAR_DATA *member) { return; } member->personGroup = this; - actToGroup(member, "$N принят$A в группу."); -// getLeader()->send_to_TC(true, false, false, "Размер списка: %lu\r\n", _memberList->size()); + auto r = groupRoster.findRequest(member->get_pc_name().c_str(), getLeaderName().c_str(), RQ_ANY); + if (r) + groupRoster.deleteRequest(r); } -bool Group::restoreMember(CHAR_DATA *member) { +bool Group::_restoreMember(CHAR_DATA *member) { + if (!member) + return false; for (auto &it: *_memberList){ if (it.first == member->get_uid()) { (*it.second)->member = member; @@ -190,7 +200,7 @@ bool Group::_removeMember(CHAR_DATA *member) { return false; } - CHAR_DATA* nxtLdr = _leader; + CHAR_DATA* nxtLdr = nullptr; int nxtLdrGrpSize = 0; // если ушел лидер - ищем нового, но после ухода предыдущего if ( member == _leader) { @@ -284,10 +294,7 @@ void Group::approveRequest(const char *applicant) { send_to_char(_leader, "Заявка не найдена, уточните имя.\r\n"); return; } - actToGroup(r->_applicant, "$N принят$A в группу."); - addMember(r->_applicant); - send_to_char(r->_applicant, "Вас приняли в группу.\r\n"); - groupRoster.deleteRequest(r); + addMember(r->_applicant); //и удаление заявки } void Group::expellMember(char *memberName) { @@ -530,8 +537,6 @@ void Group::printGroup(CHAR_DATA *ch) { } } - - void Group::charDataPurged(CHAR_DATA *ch) { for (auto &it : *_memberList) { if (ch == (*(it.second))->member) { @@ -541,7 +546,6 @@ void Group::charDataPurged(CHAR_DATA *ch) { } } - void Group::sendToGroup(GRP_COMM mode, const char *msg, ...) { va_list args; va_start(args, msg); @@ -557,27 +561,32 @@ void Group::sendToGroup(GRP_COMM mode, const char *msg, ...) { } // надстройка над act, пока только про персонажей -void Group::actToGroup(CHAR_DATA* vict, const char *msg, ...) { +void Group::actToGroup(CHAR_DATA* vict, GRP_COMM mode, const char *msg, ...) { if (vict == nullptr) return; va_list args; va_start(args, msg); vsnprintf(smallBuf, sizeof(smallBuf), msg, args); va_end(args); + CHAR_DATA* to; + if (mode == GRP_COMM_LEADER) { + act(smallBuf, FALSE, _leader, nullptr, vict, TO_CHAR); + return; + } for (auto &it : *_memberList) { - auto ch = (*(it.second))->member; - if ( ch == nullptr || ch->purged()) { + to = (*(it.second))->member; + if ( to == nullptr || to->purged()) { continue; } - act(smallBuf, FALSE, ch, nullptr, vict, TO_CHAR); + act(smallBuf, FALSE, to, nullptr, vict, TO_CHAR); } } -void Group::makeAddFollowers(CHAR_DATA *leader) { +void Group::addFollowers(CHAR_DATA *leader) { for (auto f = leader->followers; f; f = f->next) { if (IS_NPC(f->follower)) continue; - if (_memberList->size() > _memberCap) + if ((u_short)_memberList->size() < (u_short)_memberCap) this->addMember(f->follower); } } diff --git a/src/grp/grp.main.h b/src/grp/grp.main.h index 2d817bf02..20aa445c8 100644 --- a/src/grp/grp.main.h +++ b/src/grp/grp.main.h @@ -4,7 +4,7 @@ #include #include "chars/char.hpp" -enum RQ_TYPE {RQ_GROUP, RQ_PERSON}; +enum RQ_TYPE {RQ_GROUP, RQ_PERSON, RQ_ANY}; enum GRP_COMM {GRP_COMM_LEADER, GRP_COMM_ALL, GRP_COMM_OTHER}; enum RQ_R {RQ_R_OK, RQ_R_NO_GROUP, RQ_R_OVERFLOW, RQ_REFRESH}; enum INV_R {INV_R_OK, INV_R_NO_PERSON, INV_R_BUSY, INV_R_REFRESH}; @@ -20,18 +20,22 @@ int max_group_size(CHAR_DATA *ch); class Group; class Request; +using namespace std::chrono; +using grp_ptr = std::shared_ptr; +using rq_ptr = std::shared_ptr; +using sclock_t = time_point; + +const duration DEF_EXPIRY_TIME = 600s; + struct char_info { - char_info(int memberUid, CHAR_DATA *member, std::string memberName); + char_info(int memberUid, CHAR_DATA *member, const std::string& memberName); virtual ~char_info(); - int memberUID; - CHAR_DATA * member; - std::string memberName; + int memberUID; // часть ключа + CHAR_DATA * member; // ссылка на персонажа, может быть невалидна! + std::string memberName; // бэкап имени, если персонаж оффлайн + sclock_t expiryTime; // время, когда запись автоматом удаляется после проверок. }; -using grp_ptr = std::shared_ptr; -using rq_ptr = std::shared_ptr; -using mm_ptr = char_info *; - class Group { private: // ид группы в ростере @@ -64,16 +68,16 @@ class Group { void charDataPurged(CHAR_DATA* ch); private: std::map> * _memberList; - void _printHeader(CHAR_DATA* ch, bool npc); - void _printDeadLine(CHAR_DATA* ch, const char* playerName, int header); - void _printNPCLine(CHAR_DATA* ch, CHAR_DATA* npc, int header); - void _printPCLine(CHAR_DATA* ch, CHAR_DATA* pc, int header); + static void _printHeader(CHAR_DATA* ch, bool npc); + static void _printDeadLine(CHAR_DATA* ch, const char* playerName, int header); + static void _printNPCLine(CHAR_DATA* ch, CHAR_DATA* npc, int header); + static void _printPCLine(CHAR_DATA* ch, CHAR_DATA* pc, int header); bool _sameGroup(CHAR_DATA * ch, CHAR_DATA * vict); public: - void makeAddFollowers(CHAR_DATA* leader); + void addFollowers(CHAR_DATA* leader); void addMember(CHAR_DATA *member); void expellMember(char* memberName); - bool restoreMember(CHAR_DATA *member); + bool _restoreMember(CHAR_DATA *member); void printGroup(CHAR_DATA *requestor); void listMembers(CHAR_DATA *requestor); @@ -84,12 +88,12 @@ class Group { void leaveGroup(CHAR_DATA* vict); void sendToGroup(GRP_COMM mode, const char *msg, ...); - void actToGroup(CHAR_DATA* vict, const char *msg, ...); + void actToGroup(CHAR_DATA* vict, GRP_COMM mode, const char *msg, ...); }; class Request { public: - std::chrono::time_point _time; + sclock_t _time; CHAR_DATA *_applicant; Group* _group; std::string _applicantName; @@ -113,8 +117,9 @@ class GroupRoster { public: grp_ptr addGroup(CHAR_DATA* leader); void removeGroup(u_long uid); - grp_ptr findGroup(CHAR_DATA* ch); + grp_ptr findGroup(int personUID); grp_ptr findGroup(char* leaderName); + void runTests(CHAR_DATA* leader); private: std::list _requestList; std::tuple tryMakeInvite(Group* grp, char* member); diff --git a/src/grp/grp.roster.cpp b/src/grp/grp.roster.cpp index 1475fc89f..80dd1c50f 100644 --- a/src/grp/grp.roster.cpp +++ b/src/grp/grp.roster.cpp @@ -14,12 +14,12 @@ GroupRoster::GroupRoster() { } void GroupRoster::restorePlayerGroup(CHAR_DATA *ch) { - auto grp = this->findGroup(ch); + auto grp = this->findGroup(ch->get_uid()); if (grp == nullptr) return; - if (!grp->restoreMember(ch)) + if (!grp->_restoreMember(ch)) return; - grp->actToGroup(ch, "$N заново присоединил$A к вашей группе."); + grp->actToGroup(ch, GRP_COMM_ALL, "$N заново присоединил$A к вашей группе."); } void GroupRoster::processGroupCommands(CHAR_DATA *ch, char *argument) { @@ -73,15 +73,17 @@ void GroupRoster::processGroupCommands(CHAR_DATA *ch, char *argument) { grp->listMembers(ch); return; } else if (isname(subcmd, strALL.c_str())) { - if (grp != nullptr && grp->getLeader() == ch){ - send_to_char(ch, "Только великим правителям, навроде Цесаря Иулия, было дозволено водить много легионов!\r\n"); - return; - } - if (grp != nullptr) // в группе - покидаем + // если в группе, но не лидером - покидаем + if (grp != nullptr && grp->getLeader() != ch){ grp->_removeMember(ch); - auto group = groupRoster.addGroup(ch); - group->makeAddFollowers(ch); - send_to_char(ch, "Вы создали группу c максимальным числом последователей %d.\r\n", ch->personGroup ? ch->personGroup->_getMemberCap() : 0); + grp = nullptr; + } + // если нет группы - создаем и становимся лидером + if (grp == nullptr) { + grp = groupRoster.addGroup(ch).get(); + } + // сюда приходим лидером + grp->addFollowers(ch); return; } else if (isname(subcmd, strLEAVE.c_str())){ grp->leaveGroup(ch); @@ -90,6 +92,9 @@ void GroupRoster::processGroupCommands(CHAR_DATA *ch, char *argument) { if (IS_IMMORTAL(ch)) groupRoster.printList(ch); return; + } else if (isname(subcmd, strTEST.c_str())) { + groupRoster.runTests(ch); + return; } if (grp == nullptr || grp->getLeader() != ch ) { @@ -137,7 +142,7 @@ grp_ptr GroupRoster::addGroup(CHAR_DATA * leader) { ++this->_currentGroupIndex; auto group = std::make_shared(leader, _currentGroupIndex) ; this->_groupList.emplace(_currentGroupIndex, group); - leader->send_to_TC(true, false, false, "addGroup: Размер списка: %lu\r\n", this->_groupList.size()); + send_to_char(leader, "Вы создали группу c максимальным числом последователей %d.\r\n", leader->personGroup ? leader->personGroup->_getMemberCap() : 0); return group; } @@ -149,14 +154,14 @@ void GroupRoster::removeGroup(u_long uid) { } } -grp_ptr GroupRoster::findGroup(CHAR_DATA *ch) { - if (ch == nullptr || ch->purged()){ +grp_ptr GroupRoster::findGroup(int personUID) { + if (personUID == 0){ sprintf(buf, "GroupRoster::findGroup: вызов для пустого или пурженого персонажа\r\n"); mudlog(buf, BRF, LVL_IMMORT, SYSLOG, TRUE); return nullptr; } for (auto & it : this->_groupList){ - if (it.second->_isMember(ch->get_uid())) + if (it.second->_isMember(personUID)) return it.second; } return nullptr; @@ -225,7 +230,7 @@ std::tuple GroupRoster::tryMakeInvite(Group* grp, char *memb // ищем и продлеваем for (auto & it : this->_requestList){ if (vict == it->_applicant && grp == it->_group){ - it->_time = std::chrono::system_clock::now(); + it->_time = steady_clock::now() + DEF_EXPIRY_TIME; return std::make_tuple(INV_R::INV_R_REFRESH, vict); } } @@ -288,12 +293,12 @@ void GroupRoster::makeRequest(CHAR_DATA *author, char* target) { return; case RQ_R::RQ_R_OK: send_to_char("Заявка на вступление в группу отправлена.\r\n", author); + grp->actToGroup(author, GRP_COMM_LEADER, "Получена заявка от $N1 на вступление в группу.\r\n"); break; case RQ_R::RQ_REFRESH: send_to_char("Заявка успешно продлена.\r\n", author); break; } - grp->sendToGroup(GRP_COMM_LEADER, "Поступила заявка на вступление в группу от персонажа %s.", author->get_pc_name().c_str()); } void GroupRoster::revokeRequest(CHAR_DATA *ch, char* target) { @@ -327,7 +332,7 @@ std::tuple GroupRoster::tryAddRequest(CHAR_DATA *author, char *ta // продлеваем for (auto & it : this->_requestList){ if (author == it->_applicant && grp.get() == it->_group){ - it->_time = std::chrono::system_clock::now(); + it->_time = std::chrono::steady_clock::now() + DEF_EXPIRY_TIME; return std::make_tuple(RQ_REFRESH, grp); } } @@ -339,7 +344,7 @@ std::tuple GroupRoster::tryAddRequest(CHAR_DATA *author, char *ta Request *GroupRoster::findRequest(const char* targetPerson, const char* group, RQ_TYPE type) { for (auto & it : this->_requestList){ - if (type == it->_type && + if (it->_type == (type == RQ_ANY ? it->_type : type) && // осподи, какой я пиздец пишу) isname(group, it->_group->getLeaderName().c_str()) && isname(targetPerson, it->_applicantName)) return it.get(); @@ -353,7 +358,6 @@ void GroupRoster::deleteRequest(Request *r) { this->_requestList.erase(it); return; } - } // who - игрок, // author - имя лидера (может быть офлайн) @@ -366,7 +370,10 @@ void GroupRoster::acceptInvite(CHAR_DATA* who, char* author) { } auto r = findRequest(who->get_pc_name().c_str(), author, RQ_TYPE::RQ_GROUP); - r->_group->addMember(r->_applicant); - send_to_char(r->_applicant, "Вы одобрили заявку от лидера %s\r\n", r->_group->getLeaderName().c_str()); - deleteRequest(r); + r->_group->addMember(r->_applicant); // и удалит заявку, если есть + send_to_char(r->_applicant, "Вы приняли приглашение.\r\n", r->_group->getLeaderName().c_str()); +} + +void GroupRoster::runTests(CHAR_DATA *leader) { + } From 10b9de9c5cfdf4bda9dd9e52bea17e5465f1a431 Mon Sep 17 00:00:00 2001 From: bikbai Date: Thu, 31 Dec 2020 17:59:57 +0300 Subject: [PATCH 10/40] =?UTF-8?q?=D0=A7=D0=B0=D1=80=D0=BC=D0=B8=D1=81?= =?UTF-8?q?=D1=8B=20-=20=D1=81=D0=B0=D0=BC=D0=BC=D0=BE=D0=BD=D1=8B=20?= =?UTF-8?q?=D0=B8=20=D0=BF=D1=80=D0=BE=D1=87=D0=B8=D0=B5=20=D0=B0=D0=BD?= =?UTF-8?q?=D0=B4=D0=B5=D0=B4=D1=8B.=20=D0=A3=D0=B6=D0=BE=D1=81.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/act.item.cpp | 10 +- src/chars/char.cpp | 9 ++ src/chars/char.hpp | 1 + src/corpse.cpp | 10 +- src/db.cpp | 9 +- src/fightsystem/fight.cpp | 166 +++----------------------------- src/fightsystem/fight_hit.cpp | 5 +- src/fightsystem/fight_hit.hpp | 2 +- src/fightsystem/fight_stuff.cpp | 4 +- src/grp/follow.cpp | 19 +++- src/grp/grp.group.cpp | 13 +++ src/grp/grp.main.h | 3 + src/handler.cpp | 5 +- src/magic.cpp | 2 +- src/spells.cpp | 6 +- src/structs.h | 7 +- src/utils.cpp | 25 +---- 17 files changed, 88 insertions(+), 208 deletions(-) diff --git a/src/act.item.cpp b/src/act.item.cpp index b6def264a..d9dd327b8 100644 --- a/src/act.item.cpp +++ b/src/act.item.cpp @@ -1321,7 +1321,7 @@ void perform_drop_gold(CHAR_DATA * ch, int amount) } // Если этот моб трупа не оставит, то не выводить сообщение иначе ужасно коряво смотрится в бою и в тригах - if (!IS_NPC(ch) || !MOB_FLAGGED(ch, MOB_CORPSE)) + if (!IS_NPC(ch) || !MOB_FLAGGED(ch, MOB_PLAYER_SUMMON)) { send_to_char(ch, "Вы бросили %d %s на землю.\r\n", amount, desc_count(amount, WHAT_MONEYu)); @@ -2368,9 +2368,9 @@ void do_wield(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) { } else if (IS_NPC(ch) && AFF_FLAGGED(ch, EAffectFlag::AFF_CHARM) - && MOB_FLAGGED(ch, MOB_CORPSE)) + && MOB_FLAGGED(ch, MOB_PLAYER_SUMMON)) { - send_to_char("Ожившие трупы не могут вооружаться.\r\n", ch); + send_to_char("Существа, созданные магией не могут вооружаться.\r\n", ch); } else { @@ -2486,9 +2486,9 @@ void do_grab(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) if (IS_NPC(ch) && AFF_FLAGGED(ch, EAffectFlag::AFF_CHARM) - && MOB_FLAGGED(ch, MOB_CORPSE)) + && MOB_FLAGGED(ch, MOB_PLAYER_SUMMON)) { - send_to_char("Ожившие трупы не могут вооружаться.\r\n", ch); + send_to_char("Существа, созданные магией, не могут вооружаться.\r\n", ch); return; } if (!IS_IMMORTAL(ch) diff --git a/src/chars/char.cpp b/src/chars/char.cpp index 112e846db..58fcc1e3d 100644 --- a/src/chars/char.cpp +++ b/src/chars/char.cpp @@ -928,6 +928,13 @@ bool IS_HORSE(const CHAR_DATA* ch) && AFF_FLAGGED(ch, EAffectFlag::AFF_HORSE); } +bool IS_HIRED(const CHAR_DATA* ch) +{ + return IS_NPC(ch) + && ch->has_master() + && AFF_FLAGGED(ch, EAffectFlag::AFF_HELPER); +} + bool IS_MORTIFIER(const CHAR_DATA* ch) { return IS_NPC(ch) @@ -935,6 +942,8 @@ bool IS_MORTIFIER(const CHAR_DATA* ch) && MOB_FLAGGED(ch, MOB_CORPSE); } + + bool MAY_ATTACK(const CHAR_DATA* sub) { return (!AFF_FLAGGED((sub), EAffectFlag::AFF_CHARM) diff --git a/src/chars/char.hpp b/src/chars/char.hpp index bb426d5a7..b3b7432aa 100644 --- a/src/chars/char.hpp +++ b/src/chars/char.hpp @@ -894,6 +894,7 @@ inline bool AFF_FLAGGED(const CHAR_DATA::shared_ptr& ch, const EAffectFlag flag) } bool IS_CHARMICE(const CHAR_DATA* ch); +bool IS_HIRED(const CHAR_DATA* ch) inline bool IS_CHARMICE(const CHAR_DATA::shared_ptr& ch) { return IS_CHARMICE(ch.get()); } inline bool IS_FLY(const CHAR_DATA* ch) diff --git a/src/corpse.cpp b/src/corpse.cpp index 8a3ee5d46..22600e72e 100644 --- a/src/corpse.cpp +++ b/src/corpse.cpp @@ -475,7 +475,7 @@ OBJ_DATA *make_corpse(CHAR_DATA * ch, CHAR_DATA * killer) OBJ_DATA *o; int i; - if (IS_NPC(ch) && MOB_FLAGGED(ch, MOB_CORPSE)) + if (IS_NPC(ch) && MOB_FLAGGED(ch, MOB_PLAYER_SUMMON)) return NULL; sprintf(buf2, "труп %s", GET_PAD(ch, 1)); @@ -595,12 +595,10 @@ OBJ_DATA *make_corpse(CHAR_DATA * ch, CHAR_DATA * killer) // если чармис убит палачом или на арене(и владелец не в бд) то труп попадает не в клетку а в инвентарь к владельцу чармиса if(IS_CHARMICE(ch) - && !MOB_FLAGGED(ch, MOB_CORPSE) + && !MOB_FLAGGED(ch, MOB_PLAYER_SUMMON) && ch->has_master() - && ((killer - && PRF_FLAGGED(killer, PRF_EXECUTOR)) - || (ROOM_FLAGGED(ch->in_room, ROOM_ARENA) - && !RENTABLE(ch->get_master())))) + && ((killer && PRF_FLAGGED(killer, PRF_EXECUTOR)) + || (ROOM_FLAGGED(ch->in_room, ROOM_ARENA) && !RENTABLE(ch->get_master())))) { obj_to_char(corpse.get(), ch->get_master()); return NULL; diff --git a/src/db.cpp b/src/db.cpp index 630704410..33ee1f61f 100644 --- a/src/db.cpp +++ b/src/db.cpp @@ -3384,8 +3384,8 @@ int dl_load_obj(OBJ_DATA * corpse, CHAR_DATA * ch, CHAR_DATA * chr, int DL_LOAD_ { trans_obj_name(tobj.get(), ch); } - // Добавлена проверка на отсутствие трупа - if (MOB_FLAGGED(ch, MOB_CORPSE)) + // Нежить и саммоны не оставляют трупов + if (MOB_FLAGGED(ch, MOB_PLAYER_SUMMON) || MOB_FLAGGED(ch, MOB_CORPSE)) { act("На земле остал$U лежать $o.", FALSE, ch, tobj.get(), 0, TO_ROOM); obj_to_room(tobj.get(), ch->in_room); @@ -3948,7 +3948,7 @@ CHAR_DATA *read_mobile(mob_vnum nr, int type) } else { - MOB_FLAGS(mob).set(MOB_PLAYER_SUMMON); + MOB_FLAGS(mob).set(MOB_CORPSE); } i = mob_index[i].zone; @@ -4167,8 +4167,7 @@ void paste_mob(CHAR_DATA *ch, room_rnum room) { return; } -// if (MOB_FLAGGED(ch, MOB_CORPSE)) -// return; + if (room == NOWHERE) return; diff --git a/src/fightsystem/fight.cpp b/src/fightsystem/fight.cpp index 3c41ec669..1cc617020 100644 --- a/src/fightsystem/fight.cpp +++ b/src/fightsystem/fight.cpp @@ -14,6 +14,7 @@ #include "fight.h" +#include "grp/grp.main.h" #include "skills/bash.h" #include "skills/kick.h" #include "skills/chopoff.h" @@ -2389,176 +2390,37 @@ void perform_violence() } } -// returns 1 if only ch was outcasted -// returns 2 if only victim was outcasted -// returns 4 if both were outcasted -// returns 0 if none was outcasted -int check_agro_follower(CHAR_DATA * ch, CHAR_DATA * victim) -{ - CHAR_DATA *cleader, *vleader; - int return_value = 0; - - if (ch == victim) - { - return return_value; - } - -// translating pointers from charimces to their leaders - if (IS_NPC(ch) - && ch->has_master() - && (AFF_FLAGGED(ch, EAffectFlag::AFF_CHARM) - || MOB_FLAGGED(ch, MOB_ANGEL) - || MOB_FLAGGED(ch, MOB_GHOST) - || IS_HORSE(ch))) - { - ch = ch->get_master(); - } - - if (IS_NPC(victim) - && victim->has_master() - && (AFF_FLAGGED(victim, EAffectFlag::AFF_CHARM) - || MOB_FLAGGED(victim, MOB_ANGEL) - || MOB_FLAGGED(victim, MOB_GHOST) - || IS_HORSE(victim))) - { - victim = victim->get_master(); - } - - cleader = ch; - vleader = victim; -// finding leaders - while (cleader->has_master()) - { - if (IS_NPC(cleader) - && !AFF_FLAGGED(cleader, EAffectFlag::AFF_CHARM) - && !(MOB_FLAGGED(cleader, MOB_ANGEL)||MOB_FLAGGED(cleader, MOB_GHOST)) - && !IS_HORSE(cleader)) - { - break; - } - cleader = cleader->get_master(); - } - - while (vleader->has_master()) - { - if (IS_NPC(vleader) - && !AFF_FLAGGED(vleader, EAffectFlag::AFF_CHARM) - && !(MOB_FLAGGED(vleader, MOB_ANGEL)||MOB_FLAGGED(vleader, MOB_GHOST)) - && !IS_HORSE(vleader)) - { - break; - } - vleader = vleader->get_master(); - } - - if (cleader != vleader) - { - return return_value; - } - -// finding closest to the leader nongrouped agressor -// it cannot be a charmice - while (ch->has_master() - && ch->get_master()->has_master()) - { - if (!AFF_FLAGGED(ch->get_master(), EAffectFlag::AFF_GROUP) - && !IS_NPC(ch->get_master())) - { - ch = ch->get_master(); - continue; - } - else if (IS_NPC(ch->get_master()) - && !AFF_FLAGGED(ch->get_master()->get_master(), EAffectFlag::AFF_GROUP) - && !IS_NPC(ch->get_master()->get_master()) - && ch->get_master()->get_master()->get_master()) - { - ch = ch->get_master()->get_master(); - continue; - } - else - { - break; - } - } -// finding closest to the leader nongrouped victim -// it cannot be a charmice - while (victim->has_master() - && victim->get_master()->has_master()) - { - if (!AFF_FLAGGED(victim->get_master(), EAffectFlag::AFF_GROUP) - && !IS_NPC(victim->get_master())) - { - victim = victim->get_master(); - continue; - } - else if (IS_NPC(victim->get_master()) - && !AFF_FLAGGED(victim->get_master()->get_master(), EAffectFlag::AFF_GROUP) - && !IS_NPC(victim->get_master()->get_master()) - && victim->get_master()->get_master()->has_master()) - { - victim = victim->get_master()->get_master(); - continue; - } - else - { - break; - } - } - if (!AFF_FLAGGED(ch, EAffectFlag::AFF_GROUP) - || cleader == victim) - { - stop_follower(ch, SF_EMPTY); - return_value |= 1; - } - if (!AFF_FLAGGED(victim, EAffectFlag::AFF_GROUP) - || vleader == ch) - { - stop_follower(victim, SF_EMPTY); - return_value |= 2; - } - return return_value; +int stopFollowWhenAggro(CHAR_DATA * ch, CHAR_DATA * victim) +{ + if (IS_CHARMICE(ch)) } int calc_leadership(CHAR_DATA * ch) { int prob, percent; - CHAR_DATA *leader = 0; - if (IS_NPC(ch) - || !AFF_FLAGGED(ch, EAffectFlag::AFF_GROUP) - || (!ch->has_master() - && !ch->followers)) - { + if (IS_NPC(ch) || ch->personGroup == nullptr) { return FALSE; } - if (ch->has_master()) - { - if (IN_ROOM(ch) != IN_ROOM(ch->get_master())) - { - return FALSE; - } - leader = ch->get_master(); - } - else - { - leader = ch; - } + CHAR_DATA* leader = ch->personGroup->getLeader(); - if (!leader->get_skill(SKILL_LEADERSHIP)) - { + // если лидер умер или нет в комнате - фиг вам, а не бонусы + if (leader == nullptr || IN_ROOM(ch) != IN_ROOM(leader)) { + return FALSE; + } + + if (!leader->get_skill(SKILL_LEADERSHIP)) { return (FALSE); } percent = number(1, 101); prob = calculate_skill(leader, SKILL_LEADERSHIP, 0); - if (percent > prob) - { + if (percent > prob) { return (FALSE); } - else - { + else { return (TRUE); } } diff --git a/src/fightsystem/fight_hit.cpp b/src/fightsystem/fight_hit.cpp index eca66e551..51cab0e72 100644 --- a/src/fightsystem/fight_hit.cpp +++ b/src/fightsystem/fight_hit.cpp @@ -14,6 +14,7 @@ #include "bonus.h" #include "mobact.hpp" #include "fightsystem/common.h" +#include "grp/grp.main.h" // extern int extra_aco(int class_num, int level); @@ -101,7 +102,7 @@ void aff_random_pc_inspiration(CHAR_DATA *ch, EApplyLocation num_apply, int time CHAR_DATA *target; AFFECT_DATA af; - target = get_random_pc_group(ch); + target = ch->personGroup? ch->personGroup->get_random_pc_group() : ch; af.location = num_apply; af.type = SPELL_PALADINE_INSPIRATION; af.modifier = GET_REMORT(ch) / 5 * 2 + modi; @@ -2640,7 +2641,7 @@ int Damage::process(CHAR_DATA *ch, CHAR_DATA *victim) // If you attack a pet, it hates your guts if (!same_group(ch, victim)) - check_agro_follower(ch, victim); + stopFollowWhenAggro(ch, victim); if (victim != ch) // Start the attacker fighting the victim { diff --git a/src/fightsystem/fight_hit.hpp b/src/fightsystem/fight_hit.hpp index 53c73a076..7838fde3b 100644 --- a/src/fightsystem/fight_hit.hpp +++ b/src/fightsystem/fight_hit.hpp @@ -89,7 +89,7 @@ struct HitData flags_t m_flags; }; -int check_agro_follower(CHAR_DATA * ch, CHAR_DATA * victim); +int stopFollowWhenAggro(CHAR_DATA * ch, CHAR_DATA * victim); void set_battle_pos(CHAR_DATA * ch); void gain_battle_exp(CHAR_DATA *ch, CHAR_DATA *victim, int dam); diff --git a/src/fightsystem/fight_stuff.cpp b/src/fightsystem/fight_stuff.cpp index 0ff38294e..b8c356c1d 100644 --- a/src/fightsystem/fight_stuff.cpp +++ b/src/fightsystem/fight_stuff.cpp @@ -1346,7 +1346,9 @@ void char_dam_message(int dam, CHAR_DATA * ch, CHAR_DATA * victim, bool noflee) send_to_char("Сознание покинуло вас. В битве от вас пока проку мало.\r\n", victim); break; case POS_DEAD: - if (IS_NPC(victim) && (MOB_FLAGGED(victim, MOB_CORPSE))) + // нежить и саммоны не оставляют трупов + if (IS_NPC(victim) && (MOB_FLAGGED(victim,MOB_CORPSE) + || MOB_FLAGGED(victim, MOB_PLAYER_SUMMON))) { act("$n вспыхнул$g и рассыпал$u в прах.", FALSE, victim, 0, 0, TO_ROOM | TO_ARENA_LISTEN); send_to_char("Похоже вас убили и даже тела не оставили!\r\n", victim); diff --git a/src/grp/follow.cpp b/src/grp/follow.cpp index 6d9295a29..8ac52ef97 100644 --- a/src/grp/follow.cpp +++ b/src/grp/follow.cpp @@ -98,7 +98,7 @@ bool stop_follower(CHAR_DATA * ch, int mode) if (IS_NPC(ch)) { - if (MOB_FLAGGED(ch, MOB_CORPSE)) + if (MOB_FLAGGED(ch, MOB_PLAYER_SUMMON)) { act("Налетевший ветер развеял $n3, не оставив и следа.", TRUE, ch, 0, 0, TO_ROOM | TO_ARENA_LISTEN); GET_LASTROOM(ch) = GET_ROOM_VNUM(ch->in_room); @@ -124,8 +124,6 @@ bool stop_follower(CHAR_DATA * ch, int mode) return (FALSE); } - - // * Called when a character that follows/is followed dies bool die_follower(CHAR_DATA * ch) { @@ -251,3 +249,18 @@ void do_follow(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) } } } + +// возвращает true, если последователь - чармис или ему похожая тварь +// при указании второго параметра - проверяет, что эта тварь персональная +bool isGroupedFollower(CHAR_DATA* master, CHAR_DATA* vict){ + if (master == nullptr || vict == nullptr) + return false; + if IS_NPC(master) + return false; + // проверяем флаги, нпц-проверки внутре + if ( IS_HORSE(vict) // конь + || IS_CHARMICE(vict) // почармлен + || IS_HIRED(vict) // нанят + ) + +} \ No newline at end of file diff --git a/src/grp/grp.group.cpp b/src/grp/grp.group.cpp index 37be07453..638b9d36e 100644 --- a/src/grp/grp.group.cpp +++ b/src/grp/grp.group.cpp @@ -590,3 +590,16 @@ void Group::addFollowers(CHAR_DATA *leader) { this->addMember(f->follower); } } + +// метод может вернуть мусор :( +CHAR_DATA* Group::get_random_pc_group() { + u_short rnd = number(0, (u_short)_memberList->size() - 1); + int i = 0; + for (auto it : *_memberList){ + if (i == rnd) { + return (*it.second)->member; // + } + i++; + } + return nullptr; +} \ No newline at end of file diff --git a/src/grp/grp.main.h b/src/grp/grp.main.h index 20aa445c8..de43a0af3 100644 --- a/src/grp/grp.main.h +++ b/src/grp/grp.main.h @@ -89,6 +89,9 @@ class Group { void sendToGroup(GRP_COMM mode, const char *msg, ...); void actToGroup(CHAR_DATA* vict, GRP_COMM mode, const char *msg, ...); +public: + // всякий унаследованный стафф + CHAR_DATA* get_random_pc_group(); }; class Request { diff --git a/src/handler.cpp b/src/handler.cpp index 434098230..9eee19268 100644 --- a/src/handler.cpp +++ b/src/handler.cpp @@ -2261,10 +2261,9 @@ void drop_obj_on_zreset(CHAR_DATA *ch, OBJ_DATA *obj, bool inv, bool zone_reset) act("Вы выбросили $o3 на землю.", FALSE, ch, obj, 0, TO_CHAR); else act("Вы сняли $o3 и выбросили на землю.", FALSE, ch, obj, 0, TO_CHAR); - // Если этот моб трупа не оставит, то не выводить сообщение - // иначе ужасно коряво смотрится в бою и в тригах + // Саммоны и андеды трупов не оставляют bool msgShown = false; - if (!IS_NPC(ch) || !MOB_FLAGGED(ch, MOB_CORPSE)) + if (!IS_NPC(ch) || !MOB_FLAGGED(ch, MOB_PLAYER_SUMMON) || !MOB_FLAGGED(ch, MOB_CORPSE)) { if (inv) act("$n бросил$g $o3 на землю.", FALSE, ch, obj, 0, TO_ROOM); diff --git a/src/magic.cpp b/src/magic.cpp index a18f919ca..f9997e57f 100644 --- a/src/magic.cpp +++ b/src/magic.cpp @@ -4324,7 +4324,7 @@ int mag_summons(int level, CHAR_DATA * ch, OBJ_DATA * obj, int spellnum, int sav mob->set_skill(SKILL_RESCUE, 100); } - MOB_FLAGS(mob).set(MOB_CORPSE); + MOB_FLAGS(mob).set(MOB_PLAYER_SUMMON); if (spellnum == SPELL_CLONE) { sprintf(buf2, "двойник %s %s", GET_PAD(ch, 1), GET_NAME(ch)); mob->set_pc_name(buf2); diff --git a/src/spells.cpp b/src/spells.cpp index 75ea63091..4a0467cb8 100644 --- a/src/spells.cpp +++ b/src/spells.cpp @@ -2617,11 +2617,12 @@ void spell_angel(int/* level*/, CHAR_DATA *ch, CHAR_DATA* /*victim*/, OBJ_DATA* IS_CARRYING_W(mob) = 0; IS_CARRYING_N(mob) = 0; - MOB_FLAGS(mob).set(MOB_CORPSE); + // ангел - не андед! MOB_FLAGS(mob).set(MOB_ANGEL); MOB_FLAGS(mob).set(MOB_LIGHTBREATH); + MOB_FLAGS(mob).set(MOB_PLAYER_SUMMON); - mob->set_level(ch->get_level()); + mob->set_level(ch->get_level()); char_to_room(mob, ch->in_room); @@ -2683,6 +2684,7 @@ void spell_mental_shadow(int/* level*/, CHAR_DATA* ch, CHAR_DATA* /*victim*/, OB mob->set_protecting(ch); MOB_FLAGS(mob).set(MOB_CORPSE); MOB_FLAGS(mob).set(MOB_GHOST); + MOB_FLAGS(mob).set(MOB_PLAYER_SUMMON); act("Мимолётное наваждение воплотилось в призрачную тень.", TRUE, mob, 0, 0, TO_ROOM | TO_ARENA_LISTEN); diff --git a/src/structs.h b/src/structs.h index 54e66d804..60b27f83c 100644 --- a/src/structs.h +++ b/src/structs.h @@ -448,7 +448,7 @@ extern const religion_names_t religion_name; #define MOB_AGGRPOLY (1 << 22) #define MOB_NOFEAR (1 << 23) #define MOB_NOGROUP (1 << 24) -#define MOB_CORPSE (1 << 25) +#define MOB_CORPSE (1 << 25) // нежить, если по-нашенски. #define MOB_LOOTER (1 << 26) #define MOB_PROTECT (1 << 27) #define MOB_DELETE (1 << 28) // RESERVED - ONLY INTERNALLY // @@ -471,10 +471,10 @@ extern const religion_names_t religion_name; #define MOB_NOFIGHT (INT_ONE | (1 << 14)) #define MOB_EADECREASE (INT_ONE | (1 << 15)) // понижает количество своих атак по мере убывания тек.хп #define MOB_HORDE (INT_ONE | (1 << 16)) -#define MOB_CLONE (INT_ONE | (1 << 17)) +#define MOB_CLONE (INT_ONE | (1 << 17)) // моб создан спеллом !клонирование! #define MOB_NOTKILLPUNCTUAL (INT_ONE | (1 << 18)) #define MOB_NOTRIP (INT_ONE | (1 << 19)) -#define MOB_ANGEL (INT_ONE | (1 << 20)) +#define MOB_ANGEL (INT_ONE | (1 << 20)) // моб создан спеллом !ангел! #define MOB_GUARDIAN (INT_ONE | (1 << 21)) //Polud моб-стражник, ставится программно, берется из файла guards.xml #define MOB_IGNORE_FORBIDDEN (INT_ONE | (1 << 22)) // игнорирует печать #define MOB_NO_BATTLE_EXP (INT_ONE | (1 << 23)) // не дает экспу за удары @@ -504,6 +504,7 @@ extern const religion_names_t religion_name; #define MOB_AGGR_STEPNYAKI (INT_TWO | (1 << 19)) #define MOB_NORESURRECTION (INT_TWO | (1 << 20)) #define MOB_AWAKE (INT_TWO | (1 << 21)) +#define MOB_NO_BODY (INT_TWO | (1 << 22)) // не оставляет труп после смерти #define NPC_NORTH (1 << 0) diff --git a/src/utils.cpp b/src/utils.cpp index dc74119a7..9d1a5a663 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -287,30 +287,7 @@ int get_virtual_race(CHAR_DATA *mob) } -CHAR_DATA *get_random_pc_group(CHAR_DATA *ch) { - std::vector tmp_list; - CHAR_DATA *victim; - CHAR_DATA *k; - if (!AFF_FLAGGED(ch, EAffectFlag::AFF_GROUP)) - return nullptr; - if (ch->has_master()) { - k = ch->get_master(); - } - else { - k = ch; - } - for (follow_type *i = k->followers; i; i = i->next) { - if (!IS_NPC(k) && !IS_CHARMICE(i->follower) && (k != i->follower) && (k->in_room == i->follower->in_room)) { - tmp_list.push_back(i->follower); - } - } - if (tmp_list.empty()) { - return nullptr; - } - tmp_list.push_back(k); // засунем в список лидера - victim = tmp_list.at(number(0, tmp_list.size() - 1)); - return victim; -} + /* * str_cmp: a case-insensitive version of strcmp(). From 46c39408ea26944214883113c9660737f03cfba1 Mon Sep 17 00:00:00 2001 From: bikbai Date: Fri, 1 Jan 2021 02:55:29 +0300 Subject: [PATCH 11/40] =?UTF-8?q?=D0=97=D0=B0=D0=BB=D0=B5=D0=B7=20=D0=B2?= =?UTF-8?q?=20=D1=81=D0=B0=D0=BC=D1=83=D1=8E=20=D0=B4=D0=B8=D1=87=D1=8C=20?= =?UTF-8?q?-=20=D0=BE=D0=B1=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=BA=D0=B0=20?= =?UTF-8?q?=D1=81=D0=BF=D0=B8=D1=81=D0=BA=D0=B0=20=D1=87=D1=83=D0=B2=D0=B0?= =?UTF-8?q?=D0=BA=D0=BE=D0=B2=20=D0=B3=D1=80=D1=83=D0=BF=D0=BF=D1=8B..=207?= =?UTF-8?q?5=20=D1=82=D0=BE=D1=87=D0=B5=D0=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/act.comm.cpp | 91 ------------- src/act.item.cpp | 27 +--- src/act.other.cpp | 143 -------------------- src/chars/char.cpp | 2 +- src/chars/char.hpp | 2 +- src/db.cpp | 43 +++---- src/features.cpp | 4 +- src/fightsystem/fight.cpp | 6 - src/fightsystem/fight_hit.cpp | 23 +++- src/fightsystem/fight_hit.hpp | 1 - src/fightsystem/mobact.cpp | 2 +- src/grp/follow.cpp | 8 +- src/grp/grp.cmdprocessor.cpp | 236 +++++++++++++++++++++++++++++++++- src/grp/grp.group.cpp | 60 ++++++++- src/grp/grp.main.h | 2 + src/interpreter.cpp | 1 - src/msdp.reporters.cpp | 7 +- src/utils.cpp | 52 -------- 18 files changed, 348 insertions(+), 362 deletions(-) diff --git a/src/act.comm.cpp b/src/act.comm.cpp index e8d0740f2..4717b2a89 100644 --- a/src/act.comm.cpp +++ b/src/act.comm.cpp @@ -120,97 +120,6 @@ void do_say(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/){ } } -void do_gsay(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) -{ - CHAR_DATA *k; - struct follow_type *f; - - if (AFF_FLAGGED(ch, EAffectFlag::AFF_SILENCE) - || AFF_FLAGGED(ch, EAffectFlag::AFF_STRANGLED)) - { - send_to_char(SIELENCE, ch); - return; - } - - if (!IS_NPC(ch) && PLR_FLAGGED(ch, PLR_DUMB)) - { - send_to_char("Вам запрещено обращаться к другим игрокам!\r\n", ch); - return; - } - - skip_spaces(&argument); - - if (!AFF_FLAGGED(ch, EAffectFlag::AFF_GROUP)) - { - send_to_char("Вы не являетесь членом группы!\r\n", ch); - return; - } - - if (!*argument) - { - send_to_char("О чем вы хотите сообщить своей группе?\r\n", ch); - } - else - { - if (ch->has_master()) - { - k = ch->get_master(); - } - else - { - k = ch; - } - - sprintf(buf, "$n сообщил$g группе : '%s'", argument); - - if (AFF_FLAGGED(k, EAffectFlag::AFF_GROUP) - && k != ch - && !ignores(k, ch, IGNORE_GROUP)) - { - act(buf, FALSE, ch, 0, k, TO_VICT | TO_SLEEP | CHECK_DEAF); - // added by WorM групптелы 2010.10.13 - if(!AFF_FLAGGED(k, EAffectFlag::AFF_DEAFNESS) - && GET_POS(k) > POS_DEAD) - { - sprintf(buf1, "%s сообщил%s группе : '%s'\r\n", tell_can_see(ch, k) ? GET_NAME(ch) : "Кто-то", GET_CH_VIS_SUF_1(ch, k), argument); - k->remember_add(buf1, Remember::ALL); - k->remember_add(buf1, Remember::GROUP); - } - //end by WorM - } - for (f = k->followers; f; f = f->next) - { - if (AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP) - && (f->follower != ch) - && !ignores(f->follower, ch, IGNORE_GROUP)) - { - act(buf, FALSE, ch, 0, f->follower, TO_VICT | TO_SLEEP | CHECK_DEAF); - // added by WorM групптелы 2010.10.13 - if (!AFF_FLAGGED(f->follower, EAffectFlag::AFF_DEAFNESS) - && GET_POS(f->follower) > POS_DEAD) - { - sprintf(buf1, "%s сообщил%s группе : '%s'\r\n", tell_can_see(ch, f->follower) ? GET_NAME(ch) : "Кто-то", GET_CH_VIS_SUF_1(ch, f->follower), argument); - f->follower->remember_add(buf1, Remember::ALL); - f->follower->remember_add(buf1, Remember::GROUP); - } - //end by WorM - } - } - - if (PRF_FLAGGED(ch, PRF_NOREPEAT)) - send_to_char(OK, ch); - else - { - sprintf(buf, "Вы сообщили группе : '%s'\r\n", argument); - send_to_char(buf, ch); - // added by WorM групптелы 2010.10.13 - ch->remember_add(buf, Remember::ALL); - ch->remember_add(buf, Remember::GROUP); - //end by WorM - } - } -} - bool tell_can_see(CHAR_DATA *ch, CHAR_DATA *vict) { if (CAN_SEE_CHAR(vict, ch) || IS_IMMORTAL(ch) || GET_INVIS_LEV(ch)) diff --git a/src/act.item.cpp b/src/act.item.cpp index d9dd327b8..f99fd9fef 100644 --- a/src/act.item.cpp +++ b/src/act.item.cpp @@ -731,29 +731,9 @@ int can_take_obj(CHAR_DATA * ch, OBJ_DATA * obj) return (1); } -/// считаем сколько у ch в группе еще игроков (не мобов) -int other_pc_in_group(CHAR_DATA *ch) -{ - int num = 0; - CHAR_DATA *k = ch->has_master() ? ch->get_master() : ch; - for (follow_type *f = k->followers; f; f = f->next) - { - if (AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP) - && !IS_NPC(f->follower) - && IN_ROOM(f->follower) == ch->in_room) - { - ++num; - } - } - return num; -} - void split_or_clan_tax(CHAR_DATA *ch, long amount) { - if (IS_AFFECTED(ch, AFF_GROUP) - && other_pc_in_group(ch) > 0 - && PRF_FLAGGED(ch, PRF_AUTOSPLIT)) - { + if (ch->personGroup != nullptr && ch->personGroup->size() > 1 && PRF_FLAGGED(ch, PRF_AUTOSPLIT)) { char buf_[MAX_INPUT_LENGTH]; snprintf(buf_, sizeof(buf_), "%ld", amount); do_split(ch, buf_, 0, 0); @@ -789,7 +769,8 @@ void get_check_money(CHAR_DATA *ch, OBJ_DATA *obj, OBJ_DATA *cont) send_to_char(buf, ch); ch->add_ice_currency(value); //Делить лед ВСЕГДА! - if (IS_AFFECTED(ch, AFF_GROUP) && other_pc_in_group(ch) > 0) { + if (ch->personGroup != nullptr && ch->personGroup->size() > 1) + { char local_buf[256]; sprintf(local_buf, "%d", value); do_split(ch, local_buf, 0, 0,curr_type); @@ -808,7 +789,7 @@ void get_check_money(CHAR_DATA *ch, OBJ_DATA *obj, OBJ_DATA *cont) send_to_char(buf, ch); // все, что делится на группу - идет через налог (из кошельков не делится) - if (IS_AFFECTED(ch, AFF_GROUP) && other_pc_in_group(ch) > 0 + if (ch->personGroup != nullptr && ch->personGroup->size() > 1 && PRF_FLAGGED(ch, PRF_AUTOSPLIT) && (!cont || !system_obj::is_purse(cont))) { diff --git a/src/act.other.cpp b/src/act.other.cpp index 86277eca6..e363f4d3f 100644 --- a/src/act.other.cpp +++ b/src/act.other.cpp @@ -848,149 +848,6 @@ void do_courage(CHAR_DATA *ch, char* /*argument*/, int/* cmd*/, int/* subcmd*/) } -void do_split(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) { - do_split(ch,argument,0,0,0); -} - -void do_split(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/,int currency) -{ - int amount, num, share, rest; - CHAR_DATA *k; - struct follow_type *f; - - if (IS_NPC(ch)) - return; - - one_argument(argument, buf); - - int what_currency; - - switch (currency) { - case currency::ICE : - what_currency = WHAT_ICEu; - break; - default : - what_currency = WHAT_MONEYu; - break; - } - - if (is_number(buf)) - { - amount = atoi(buf); - if (amount <= 0) - { - send_to_char("И как вы это планируете сделать?\r\n", ch); - return; - } - - if (amount > ch->get_gold() && currency == currency::GOLD) - { - send_to_char("И где бы взять вам столько денег?.\r\n", ch); - return; - } - k = ch->has_master() ? ch->get_master() : ch; - - if (AFF_FLAGGED(k, EAffectFlag::AFF_GROUP) - && (k->in_room == ch->in_room)) - { - num = 1; - } - else - { - num = 0; - } - - for (f = k->followers; f; f = f->next) - { - if (AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP) - && !IS_NPC(f->follower) - && IN_ROOM(f->follower) == ch->in_room) - { - num++; - } - } - - if (num && AFF_FLAGGED(ch, EAffectFlag::AFF_GROUP)) - { - share = amount / num; - rest = amount % num; - } - else - { - send_to_char("С кем вы хотите разделить это добро?\r\n", ch); - return; - } - //MONEY_HACK - - switch (currency) { - case currency::ICE : - ch->sub_ice_currency(share* (num - 1)); - break; - case currency::GOLD : - ch->remove_gold(share * (num - 1)); - break; - } - - sprintf(buf, "%s разделил%s %d %s; вам досталось %d.\r\n", - GET_NAME(ch), GET_CH_SUF_1(ch), amount, desc_count(amount, what_currency), share); - if (AFF_FLAGGED(k, EAffectFlag::AFF_GROUP) && IN_ROOM(k) == ch->in_room && !IS_NPC(k) && k != ch) - { - send_to_char(buf, k); - switch (currency) - { - case currency::ICE : - { - k->add_ice_currency(share); - break; - } - case currency::GOLD : - { - k->add_gold(share, true, true); - break; - } - } - } - for (f = k->followers; f; f = f->next) - { - if (AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP) - && !IS_NPC(f->follower) - && IN_ROOM(f->follower) == ch->in_room - && f->follower != ch) - { - send_to_char(buf, f->follower); - switch (currency) { - case currency::ICE : - f->follower->add_ice_currency(share); - break; - case currency::GOLD : - f->follower->add_gold(share, true, true); - break; - } - } - } - sprintf(buf, "Вы разделили %d %s на %d - по %d каждому.\r\n", - amount, desc_count(amount, what_currency), num, share); - if (rest) - { - sprintf(buf + strlen(buf), - "Как истинный еврей вы оставили %d %s (которые не смогли разделить нацело) себе.\r\n", - rest, desc_count(rest, what_currency)); - } - - send_to_char(buf, ch); - // клан-налог лутера с той части, которая пошла каждому в группе - if (currency == currency::GOLD) { - const long clan_tax = ClanSystem::do_gold_tax(ch, share); - ch->remove_gold(clan_tax); - } - } - else - { - send_to_char("Сколько и чего вы хотите разделить?\r\n", ch); - return; - } -} - OBJ_DATA * get_obj_equip_or_carry(CHAR_DATA *ch, const std::string &text) { int eq_num = 0; diff --git a/src/chars/char.cpp b/src/chars/char.cpp index 58fcc1e3d..0b7f341fa 100644 --- a/src/chars/char.cpp +++ b/src/chars/char.cpp @@ -39,7 +39,7 @@ #include #include -#include "grp/grp.group.cpp" +#include "grp/grp.main.h" std::string PlayerI::empty_const_str; MapSystem::Options PlayerI::empty_map_options; diff --git a/src/chars/char.hpp b/src/chars/char.hpp index b3b7432aa..772f58f22 100644 --- a/src/chars/char.hpp +++ b/src/chars/char.hpp @@ -894,7 +894,7 @@ inline bool AFF_FLAGGED(const CHAR_DATA::shared_ptr& ch, const EAffectFlag flag) } bool IS_CHARMICE(const CHAR_DATA* ch); -bool IS_HIRED(const CHAR_DATA* ch) +bool IS_HIRED(const CHAR_DATA* ch); inline bool IS_CHARMICE(const CHAR_DATA::shared_ptr& ch) { return IS_CHARMICE(ch.get()); } inline bool IS_FLY(const CHAR_DATA* ch) diff --git a/src/db.cpp b/src/db.cpp index 33ee1f61f..5ef161ee5 100644 --- a/src/db.cpp +++ b/src/db.cpp @@ -17,23 +17,25 @@ #include "db.h" -#include "skills/townportal.h" -#include "global.objects.hpp" -#include "speedwalks.hpp" -#include "chars/world.characters.hpp" -#include "object.prototypes.hpp" -#include "world.objects.hpp" -#include "logger.hpp" -#include "utils.h" -#include "shutdown.parameters.hpp" -#include "boards.h" +#include +#include +#include +#include +#include +#include +#include + #include "ban.hpp" #include "birth_places.hpp" +#include "boards.h" #include "bonus.h" #include "boot.data.files.hpp" #include "boot.index.hpp" #include "celebrates.hpp" #include "chars/char.hpp" +#include "chars/player_races.hpp" +#include "chars/world.characters.hpp" +#include "class.hpp" #include "corpse.hpp" #include "deathtrap.hpp" #include "depot.hpp" @@ -42,6 +44,7 @@ #include "ext_money.hpp" #include "fightsystem/fight.h" #include "file_crc.hpp" +#include "global.objects.hpp" #include "glory.hpp" #include "glory_const.hpp" #include "glory_misc.hpp" @@ -50,33 +53,29 @@ #include "house.h" #include "item.creation.hpp" #include "liquid.hpp" +#include "logger.hpp" #include "mail.h" #include "mob_stat.hpp" #include "modify.h" #include "named_stuff.hpp" +#include "names.hpp" #include "noob.hpp" +#include "object.prototypes.hpp" #include "olc.h" #include "parcel.hpp" #include "parse.hpp" -#include "chars/player_races.hpp" #include "privilege.hpp" #include "sets_drop.hpp" #include "shop_ext.hpp" +#include "shutdown.parameters.hpp" +#include "skills/townportal.h" +#include "speedwalks.hpp" #include "stuff.hpp" #include "time_utils.hpp" #include "title.hpp" -#include "names.hpp" #include "top.h" -#include "class.hpp" -#include -#include - -#include - -#include -#include -#include -#include +#include "utils.h" +#include "world.objects.hpp" #define CRITERION_FILE "criterion.xml" #define CASES_FILE "cases.xml" diff --git a/src/features.cpp b/src/features.cpp index eae730c40..ecd895679 100644 --- a/src/features.cpp +++ b/src/features.cpp @@ -1558,7 +1558,7 @@ void activateFeature(CHAR_DATA *ch, int featureNum) { PRF_FLAGS(ch).set(PRF_GREATAIMINGATTACK); break; case SKIRMISHER_FEAT: - if (!AFF_FLAGGED(ch, EAffectFlag::AFF_GROUP)) { + if (ch->personGroup == nullptr) { send_to_char(ch, "Голос десятника Никифора вдруг рявкнул: \"%s, тюрюхайло! 'В шеренгу по одному' иначе сполняется!\"\r\n", ch->get_name().c_str()); return; } @@ -1598,7 +1598,7 @@ void deactivateFeature(CHAR_DATA *ch, int featureNum) { break; case SKIRMISHER_FEAT: PRF_FLAGS(ch).unset(PRF_SKIRMISHER); - if (AFF_FLAGGED(ch, EAffectFlag::AFF_GROUP)) { + if (ch->personGroup != nullptr) { send_to_char("Вы решили, что в обозе вам будет спокойней.\r\n", ch); act("$n0 тактически отступил$g в тыл отряда.", FALSE, ch, 0, 0, TO_ROOM | TO_ARENA_LISTEN); } diff --git a/src/fightsystem/fight.cpp b/src/fightsystem/fight.cpp index 1cc617020..9265aa303 100644 --- a/src/fightsystem/fight.cpp +++ b/src/fightsystem/fight.cpp @@ -2390,12 +2390,6 @@ void perform_violence() } } - -int stopFollowWhenAggro(CHAR_DATA * ch, CHAR_DATA * victim) -{ - if (IS_CHARMICE(ch)) -} - int calc_leadership(CHAR_DATA * ch) { int prob, percent; diff --git a/src/fightsystem/fight_hit.cpp b/src/fightsystem/fight_hit.cpp index 51cab0e72..b72b921f7 100644 --- a/src/fightsystem/fight_hit.cpp +++ b/src/fightsystem/fight_hit.cpp @@ -2518,8 +2518,8 @@ void Damage::process_death(CHAR_DATA *ch, CHAR_DATA *victim) group_gain(killer, victim); } else if ((AFF_FLAGGED(killer, EAffectFlag::AFF_CHARM) - || MOB_FLAGGED(killer, MOB_ANGEL) - || MOB_FLAGGED(killer, MOB_GHOST)) + || AFF_FLAGGED(killer, EAffectFlag::AFF_HELPER) + || MOB_FLAGGED(killer, MOB_PLAYER_SUMMON)) && killer->has_master()) // killer - зачармленный NPC с хозяином { @@ -2582,6 +2582,23 @@ void Damage::process_death(CHAR_DATA *ch, CHAR_DATA *victim) die(victim, ch); } +void stopFollowOnAggro(CHAR_DATA* aggressor, CHAR_DATA* victim) +{ + if (aggressor == nullptr || victim == nullptr) + return; + if (!victim->has_master() || IS_CHARMICE(victim)) + return; + // оба в группе - всё нормально + if (victim->personGroup == aggressor->personGroup) + return; + // агрессор - чармис чувака в группе + if (aggressor->has_master() && aggressor->get_master()->personGroup == victim->personGroup) + return; + if (victim->get_master() == aggressor || + (aggressor->has_master() && victim->get_master() == aggressor->get_master())) + stop_follower(victim, SF_EMPTY); +} + // обработка щитов, зб, поглощения, сообщения для огн. щита НЕ ЗДЕСЬ // возвращает сделанный дамаг int Damage::process(CHAR_DATA *ch, CHAR_DATA *victim) @@ -2641,7 +2658,7 @@ int Damage::process(CHAR_DATA *ch, CHAR_DATA *victim) // If you attack a pet, it hates your guts if (!same_group(ch, victim)) - stopFollowWhenAggro(ch, victim); + stopFollowOnAggro(ch, victim); if (victim != ch) // Start the attacker fighting the victim { diff --git a/src/fightsystem/fight_hit.hpp b/src/fightsystem/fight_hit.hpp index 7838fde3b..307dc3b1f 100644 --- a/src/fightsystem/fight_hit.hpp +++ b/src/fightsystem/fight_hit.hpp @@ -89,7 +89,6 @@ struct HitData flags_t m_flags; }; -int stopFollowWhenAggro(CHAR_DATA * ch, CHAR_DATA * victim); void set_battle_pos(CHAR_DATA * ch); void gain_battle_exp(CHAR_DATA *ch, CHAR_DATA *victim, int dam); diff --git a/src/fightsystem/mobact.cpp b/src/fightsystem/mobact.cpp index b99c9f8d0..eb789c7e5 100644 --- a/src/fightsystem/mobact.cpp +++ b/src/fightsystem/mobact.cpp @@ -302,7 +302,7 @@ CHAR_DATA* selectRandomSkirmisherFromGroup(CHAR_DATA *leader) { } CHAR_DATA* selectVictimDependingOnGroupFormation(CHAR_DATA *assaulter, CHAR_DATA *initialVictim) { - if ((initialVictim == nullptr) || !AFF_FLAGGED(initialVictim, EAffectFlag::AFF_GROUP)) { + if ((initialVictim == nullptr) || initialVictim->personGroup== nullptr) { return initialVictim; } diff --git a/src/grp/follow.cpp b/src/grp/follow.cpp index 8ac52ef97..b890a439b 100644 --- a/src/grp/follow.cpp +++ b/src/grp/follow.cpp @@ -252,7 +252,7 @@ void do_follow(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) // возвращает true, если последователь - чармис или ему похожая тварь // при указании второго параметра - проверяет, что эта тварь персональная -bool isGroupedFollower(CHAR_DATA* master, CHAR_DATA* vict){ +bool isGroupedFollower(CHAR_DATA* master, CHAR_DATA* vict) { if (master == nullptr || vict == nullptr) return false; if IS_NPC(master) @@ -260,7 +260,7 @@ bool isGroupedFollower(CHAR_DATA* master, CHAR_DATA* vict){ // проверяем флаги, нпц-проверки внутре if ( IS_HORSE(vict) // конь || IS_CHARMICE(vict) // почармлен - || IS_HIRED(vict) // нанят - ) - + || IS_HIRED(vict)) // нанят + return true; + return false; } \ No newline at end of file diff --git a/src/grp/grp.cmdprocessor.cpp b/src/grp/grp.cmdprocessor.cpp index 62ce07c47..6464e4b7e 100644 --- a/src/grp/grp.cmdprocessor.cpp +++ b/src/grp/grp.cmdprocessor.cpp @@ -437,6 +437,98 @@ void do_ungroup(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) { return; } +void do_gsay(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) +{ + CHAR_DATA *k; + struct follow_type *f; + + if (AFF_FLAGGED(ch, EAffectFlag::AFF_SILENCE) + || AFF_FLAGGED(ch, EAffectFlag::AFF_STRANGLED)) + { + send_to_char("Вы немы, как рыба об лед.\r\n", ch); + return; + } + + if (!IS_NPC(ch) && PLR_FLAGGED(ch, PLR_DUMB)) + { + send_to_char("Вам запрещено обращаться к другим игрокам!\r\n", ch); + return; + } + + skip_spaces(&argument); + + if (!AFF_FLAGGED(ch, EAffectFlag::AFF_GROUP)) + { + send_to_char("Вы не являетесь членом группы!\r\n", ch); + return; + } + + if (!*argument) + { + send_to_char("О чем вы хотите сообщить своей группе?\r\n", ch); + } + else + { + if (ch->has_master()) + { + k = ch->get_master(); + } + else + { + k = ch; + } + + sprintf(buf, "$n сообщил$g группе : '%s'", argument); + + if (AFF_FLAGGED(k, EAffectFlag::AFF_GROUP) + && k != ch + && !ignores(k, ch, IGNORE_GROUP)) + { + act(buf, FALSE, ch, 0, k, TO_VICT | TO_SLEEP | CHECK_DEAF); + // added by WorM групптелы 2010.10.13 + if(!AFF_FLAGGED(k, EAffectFlag::AFF_DEAFNESS) + && GET_POS(k) > POS_DEAD) + { + sprintf(buf1, "%s сообщил%s группе : '%s'\r\n", tell_can_see(ch, k) ? GET_NAME(ch) : "Кто-то", GET_CH_VIS_SUF_1(ch, k), argument); + k->remember_add(buf1, Remember::ALL); + k->remember_add(buf1, Remember::GROUP); + } + //end by WorM + } + for (f = k->followers; f; f = f->next) + { + if (AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP) + && (f->follower != ch) + && !ignores(f->follower, ch, IGNORE_GROUP)) + { + act(buf, FALSE, ch, 0, f->follower, TO_VICT | TO_SLEEP | CHECK_DEAF); + // added by WorM групптелы 2010.10.13 + if (!AFF_FLAGGED(f->follower, EAffectFlag::AFF_DEAFNESS) + && GET_POS(f->follower) > POS_DEAD) + { + sprintf(buf1, "%s сообщил%s группе : '%s'\r\n", tell_can_see(ch, f->follower) ? GET_NAME(ch) : "Кто-то", GET_CH_VIS_SUF_1(ch, f->follower), argument); + f->follower->remember_add(buf1, Remember::ALL); + f->follower->remember_add(buf1, Remember::GROUP); + } + //end by WorM + } + } + + if (PRF_FLAGGED(ch, PRF_NOREPEAT)) + send_to_char(OK, ch); + else + { + sprintf(buf, "Вы сообщили группе : '%s'\r\n", argument); + send_to_char(buf, ch); + // added by WorM групптелы 2010.10.13 + ch->remember_add(buf, Remember::ALL); + ch->remember_add(buf, Remember::GROUP); + //end by WorM + } + } +} + + void do_report(CHAR_DATA *ch, char* /*argument*/, int/* cmd*/, int/* subcmd*/) { CHAR_DATA *k; @@ -496,4 +588,146 @@ void do_report(CHAR_DATA *ch, char* /*argument*/, int/* cmd*/, int/* subcmd*/) send_to_char(buf, k); } send_to_char("Вы доложили о состоянии всем членам вашей группы.\r\n", ch); -} \ No newline at end of file +} + +void do_split(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) { + do_split(ch,argument,0,0,0); +} + +void do_split(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/,int currency) +{ + int amount, num, share, rest; + CHAR_DATA *k; + struct follow_type *f; + + if (IS_NPC(ch)) + return; + + one_argument(argument, buf); + + int what_currency; + + switch (currency) { + case currency::ICE : + what_currency = WHAT_ICEu; + break; + default : + what_currency = WHAT_MONEYu; + break; + } + + if (is_number(buf)) { + amount = atoi(buf); + if (amount <= 0) + { + send_to_char("И как вы это планируете сделать?\r\n", ch); + return; + } + + if (amount > ch->get_gold() && currency == currency::GOLD) + { + send_to_char("И где бы взять вам столько денег?.\r\n", ch); + return; + } + k = ch->has_master() ? ch->get_master() : ch; + + if (AFF_FLAGGED(k, EAffectFlag::AFF_GROUP) + && (k->in_room == ch->in_room)) + { + num = 1; + } + else + { + num = 0; + } + + for (f = k->followers; f; f = f->next) + { + if (AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP) + && !IS_NPC(f->follower) + && IN_ROOM(f->follower) == ch->in_room) + { + num++; + } + } + + if (num && AFF_FLAGGED(ch, EAffectFlag::AFF_GROUP)) + { + share = amount / num; + rest = amount % num; + } + else + { + send_to_char("С кем вы хотите разделить это добро?\r\n", ch); + return; + } + //MONEY_HACK + + switch (currency) { + case currency::ICE : + ch->sub_ice_currency(share* (num - 1)); + break; + case currency::GOLD : + ch->remove_gold(share * (num - 1)); + break; + } + + sprintf(buf, "%s разделил%s %d %s; вам досталось %d.\r\n", + GET_NAME(ch), GET_CH_SUF_1(ch), amount, desc_count(amount, what_currency), share); + if (AFF_FLAGGED(k, EAffectFlag::AFF_GROUP) && IN_ROOM(k) == ch->in_room && !IS_NPC(k) && k != ch) + { + send_to_char(buf, k); + switch (currency) + { + case currency::ICE : + { + k->add_ice_currency(share); + break; + } + case currency::GOLD : + { + k->add_gold(share, true, true); + break; + } + } + } + for (f = k->followers; f; f = f->next) + { + if (AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP) + && !IS_NPC(f->follower) + && IN_ROOM(f->follower) == ch->in_room + && f->follower != ch) + { + send_to_char(buf, f->follower); + switch (currency) { + case currency::ICE : + f->follower->add_ice_currency(share); + break; + case currency::GOLD : + f->follower->add_gold(share, true, true); + break; + } + } + } + sprintf(buf, "Вы разделили %d %s на %d - по %d каждому.\r\n", + amount, desc_count(amount, what_currency), num, share); + if (rest) + { + sprintf(buf + strlen(buf), + "Как истинный еврей вы оставили %d %s (которые не смогли разделить нацело) себе.\r\n", + rest, desc_count(rest, what_currency)); + } + + send_to_char(buf, ch); + // клан-налог лутера с той части, которая пошла каждому в группе + if (currency == currency::GOLD) { + const long clan_tax = ClanSystem::do_gold_tax(ch, share); + ch->remove_gold(clan_tax); + } + } + else + { + send_to_char("Сколько и чего вы хотите разделить?\r\n", ch); + return; + } +} diff --git a/src/grp/grp.group.cpp b/src/grp/grp.group.cpp index 638b9d36e..ace37172e 100644 --- a/src/grp/grp.group.cpp +++ b/src/grp/grp.group.cpp @@ -1,10 +1,9 @@ - #include +#include "grp.main.h" + #include "comm.h" -#include "handler.h" #include "msdp.constants.hpp" -#include "grp.main.h" #include "screen.h" #include "magic.h" @@ -47,7 +46,6 @@ const char *POS_STATE[] = { "Умер", "Стоит" }; - Group::Group(CHAR_DATA *leader, u_long uid){ mudlog("Group", BRF, LVL_IMMORT, SYSLOG, TRUE); _leaderUID = 0; // чтобы не ругался clion =) @@ -602,4 +600,56 @@ CHAR_DATA* Group::get_random_pc_group() { i++; } return nullptr; -} \ No newline at end of file +} + +bool same_group(CHAR_DATA * ch, CHAR_DATA * tch) +{ + if (!ch || !tch) + return false; + + // Добавлены проверки чтобы не любой заследовавшийся моб считался согруппником (Купала) + if (IS_NPC(ch) + && ch->has_master() + && !IS_NPC(ch->get_master()) + && (IS_HORSE(ch) + || AFF_FLAGGED(ch, EAffectFlag::AFF_CHARM) + || MOB_FLAGGED(ch, MOB_ANGEL) + || MOB_FLAGGED(ch, MOB_GHOST))) + { + ch = ch->get_master(); + } + + if (IS_NPC(tch) + && tch->has_master() + && !IS_NPC(tch->get_master()) + && (IS_HORSE(tch) + || AFF_FLAGGED(tch, EAffectFlag::AFF_CHARM) + || MOB_FLAGGED(tch, MOB_ANGEL) + || MOB_FLAGGED(tch, MOB_GHOST))) + { + tch = tch->get_master(); + } + + // NPC's always in same group + if ((IS_NPC(ch) && IS_NPC(tch)) + || ch == tch) + { + return true; + } + + if (!AFF_FLAGGED(ch, EAffectFlag::AFF_GROUP) + || !AFF_FLAGGED(tch, EAffectFlag::AFF_GROUP)) + { + return false; + } + + if (ch->get_master() == tch + || tch->get_master() == ch + || (ch->has_master() + && ch->get_master() == tch->get_master())) + { + return true; + } + + return false; +} diff --git a/src/grp/grp.main.h b/src/grp/grp.main.h index de43a0af3..128ea01ed 100644 --- a/src/grp/grp.main.h +++ b/src/grp/grp.main.h @@ -16,6 +16,7 @@ void do_group2(CHAR_DATA *ch, char *argument, int, int); void do_ungroup(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/); void do_report(CHAR_DATA *ch, char* /*argument*/, int/* cmd*/, int/* subcmd*/); int max_group_size(CHAR_DATA *ch); +bool isGroupedFollower(CHAR_DATA* master, CHAR_DATA* vict); class Group; class Request; @@ -66,6 +67,7 @@ class Group { CHAR_DATA* _findMember(int UID); bool _removeMember(CHAR_DATA *member); void charDataPurged(CHAR_DATA* ch); + u_short size() { return (u_short)_memberList->size();} private: std::map> * _memberList; static void _printHeader(CHAR_DATA* ch, bool npc); diff --git a/src/interpreter.cpp b/src/interpreter.cpp index 567e2c649..49308b795 100644 --- a/src/interpreter.cpp +++ b/src/interpreter.cpp @@ -2509,7 +2509,6 @@ void do_entergame(DESCRIPTOR_DATA * d) Temporary_Spells::update_char_times(d->character.get(), time(0)); // Карачун. Редкая бага. Сбрасываем явно не нужные аффекты. - d->character->remove_affect(EAffectFlag::AFF_GROUP); d->character->remove_affect(EAffectFlag::AFF_HORSE); // изменяем порталы diff --git a/src/msdp.reporters.cpp b/src/msdp.reporters.cpp index 95973456f..860f4dff1 100644 --- a/src/msdp.reporters.cpp +++ b/src/msdp.reporters.cpp @@ -307,9 +307,7 @@ namespace msdp for (auto f = master->followers; f; f = f->next) { if (!AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP) - && !(AFF_FLAGGED(f->follower, EAffectFlag::AFF_CHARM) - || MOB_FLAGGED(f->follower, MOB_ANGEL) - || MOB_FLAGGED(f->follower, MOB_GHOST))) + && !(MOB_FLAGGED(f->follower, MOB_PLAYER_SUMMON))) { continue; } @@ -325,8 +323,7 @@ namespace msdp for (auto ff = f->follower->followers; ff; ff = ff->next) { if (!(AFF_FLAGGED(ff->follower, EAffectFlag::AFF_CHARM) - || MOB_FLAGGED(ff->follower, MOB_ANGEL) - || MOB_FLAGGED(ff->follower, MOB_GHOST))) + || MOB_FLAGGED(ff->follower, MOB_PLAYER_SUMMON))) { continue; } diff --git a/src/utils.cpp b/src/utils.cpp index 9d1a5a663..0ed91994b 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -1124,58 +1124,6 @@ int real_sector(int room) return SECT_INSIDE; } -bool same_group(CHAR_DATA * ch, CHAR_DATA * tch) -{ - if (!ch || !tch) - return false; - - // Добавлены проверки чтобы не любой заследовавшийся моб считался согруппником (Купала) - if (IS_NPC(ch) - && ch->has_master() - && !IS_NPC(ch->get_master()) - && (IS_HORSE(ch) - || AFF_FLAGGED(ch, EAffectFlag::AFF_CHARM) - || MOB_FLAGGED(ch, MOB_ANGEL) - || MOB_FLAGGED(ch, MOB_GHOST))) - { - ch = ch->get_master(); - } - - if (IS_NPC(tch) - && tch->has_master() - && !IS_NPC(tch->get_master()) - && (IS_HORSE(tch) - || AFF_FLAGGED(tch, EAffectFlag::AFF_CHARM) - || MOB_FLAGGED(tch, MOB_ANGEL) - || MOB_FLAGGED(tch, MOB_GHOST))) - { - tch = tch->get_master(); - } - - // NPC's always in same group - if ((IS_NPC(ch) && IS_NPC(tch)) - || ch == tch) - { - return true; - } - - if (!AFF_FLAGGED(ch, EAffectFlag::AFF_GROUP) - || !AFF_FLAGGED(tch, EAffectFlag::AFF_GROUP)) - { - return false; - } - - if (ch->get_master() == tch - || tch->get_master() == ch - || (ch->has_master() - && ch->get_master() == tch->get_master())) - { - return true; - } - - return false; -} - // Проверка является комната рентой. bool is_rent(room_rnum room) { From 75665a4a7fa33a6809c7e0fd46ff85aa373fa8bc Mon Sep 17 00:00:00 2001 From: bikbai Date: Fri, 1 Jan 2021 16:45:13 +0300 Subject: [PATCH 12/40] =?UTF-8?q?=D0=9F=D1=80=D0=B8=D0=BC=D0=B5=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=B3=D1=80=D1=83=D0=BF=D0=BF,=20?= =?UTF-8?q?=D0=BF=D1=80=D0=BE=D0=B4=D0=BE=D0=BB=D0=B6=D0=B0=D0=B5=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/dg_scripts.cpp | 24 ++------ src/ext_money.cpp | 45 +++++--------- src/grp/grp.group.cpp | 137 ++++++++++++++++++++++-------------------- src/grp/grp.main.h | 10 ++- 4 files changed, 101 insertions(+), 115 deletions(-) diff --git a/src/dg_scripts.cpp b/src/dg_scripts.cpp index a6b2daeec..f74773d45 100644 --- a/src/dg_scripts.cpp +++ b/src/dg_scripts.cpp @@ -2292,6 +2292,7 @@ void find_replacement(void* go, SCRIPT_DATA* sc, TRIG_DATA* trig, int type, char } else if (!str_cmp(field, "arenahp")) { + // TODO: переписать эту сраку, непонятно ни фига CHAR_DATA *k; struct follow_type *f; int arena_hp = GET_HIT(c); @@ -2308,7 +2309,7 @@ void find_replacement(void* go, SCRIPT_DATA* sc, TRIG_DATA* trig, int type, char { can_use = 1; } - else if (AFF_FLAGGED(k, EAffectFlag::AFF_GROUP)) + else if (IN_GROUP(k)) { if (!IS_NPC(k) && (GET_CLASS(k) == 8 || GET_CLASS(k) == 13) //чернок или волхв может использовать ужи на согруппов && world[IN_ROOM(k)]->zone == world[IN_ROOM(c)]->zone) //но только если находится в той же зоне @@ -3085,26 +3086,11 @@ void find_replacement(void* go, SCRIPT_DATA* sc, TRIG_DATA* trig, int type, char } else if (!str_cmp(field, "group")) { - CHAR_DATA *l; - struct follow_type *f; - if (!AFF_FLAGGED(c, EAffectFlag::AFF_GROUP)) - { + if (!IN_GROUP(c)) { return; } - l = c->get_master(); - if (!l) - { - l = c; - } - // l - лидер группы - sprintf(str + strlen(str), "%c%ld ", UID_CHAR, GET_ID(l)); - for (f = l->followers; f; f = f->next) - { - if (!AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP)) - { - continue; - } - sprintf(str + strlen(str), "%c%ld ", UID_CHAR, GET_ID(f->follower)); + for (auto it : *(c->personGroup->getMembers())) { + sprintf(str + strlen(str), "%c%ld ", UID_CHAR, GET_ID(it)); } } else if (!str_cmp(field, "attackers")) diff --git a/src/ext_money.cpp b/src/ext_money.cpp index 96a44531d..ad48f4e4f 100644 --- a/src/ext_money.cpp +++ b/src/ext_money.cpp @@ -14,6 +14,7 @@ #include "parse.hpp" #include "zone.table.hpp" #include "utils.h" +#include "grp/grp.main.h" #include #include @@ -697,30 +698,18 @@ void drop_torc(CHAR_DATA *mob) log("[Extract char] Checking %s for ExtMoney.", mob->get_name().c_str()); std::pair damager = mob->get_max_damager_in_room(); - DESCRIPTOR_DATA *d = 0; - if (damager.first > 0) - { + DESCRIPTOR_DATA *d = nullptr; + if (damager.first > 0) { d = DescByUID(damager.first); } - if (!d) - { + if (!d) { return; } - - CHAR_DATA *leader = (d->character->has_master() && AFF_FLAGGED(d->character, EAffectFlag::AFF_GROUP)) - ? d->character->get_master() - : d->character.get(); + auto grp = d->character->personGroup; + CHAR_DATA *leader = grp != nullptr? d->character->personGroup->getLeader() : d->character.get(); int members = 1; - for (follow_type *f = leader->followers; f; f = f->next) - { - if (AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP) - && f->follower->in_room == IN_ROOM(mob) - && !IS_NPC(f->follower)) - { - ++members; - } - } + members = leader->personGroup->size(mob->in_room); const int zone_lvl = zone_table[mob_index[GET_MOB_RNUM(mob)].zone].mob_level; const int drop = calc_drop_torc(zone_lvl, members); @@ -732,20 +721,16 @@ void drop_torc(CHAR_DATA *mob) if (IN_ROOM(leader) == IN_ROOM(mob) && GET_GOD_FLAG(leader, GF_REMORT) && (GET_UNIQUE(leader) == damager.first - || mob->get_attacker(leader, ATTACKER_ROUNDS) >= damager.second / 2)) - { + || mob->get_attacker(leader, ATTACKER_ROUNDS) >= damager.second / 2)) { gain_torc(leader, drop); } - - for (follow_type *f = leader->followers; f; f = f->next) - { - if (AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP) - && f->follower->in_room == IN_ROOM(mob) - && !IS_NPC(f->follower) - && GET_GOD_FLAG(f->follower, GF_REMORT) - && mob->get_attacker(f->follower, ATTACKER_ROUNDS) >= damager.second / 2) - { - gain_torc(f->follower, drop); + // он был один, сваливаем + if (grp == nullptr) + return; + auto m_list = grp->getMembers(mob->in_room); // список живых мемберов в комнате + for (auto &m : *m_list) { + if (GET_GOD_FLAG(m, GF_REMORT) && mob->get_attacker(m, ATTACKER_ROUNDS) >= damager.second / 2) { + gain_torc(m, drop); } } } diff --git a/src/grp/grp.group.cpp b/src/grp/grp.group.cpp index ace37172e..31552afc2 100644 --- a/src/grp/grp.group.cpp +++ b/src/grp/grp.group.cpp @@ -167,6 +167,14 @@ void Group::addMember(CHAR_DATA *member) { auto r = groupRoster.findRequest(member->get_pc_name().c_str(), getLeaderName().c_str(), RQ_ANY); if (r) groupRoster.deleteRequest(r); + // добавляем чармисов группу + for (auto f = member->followers; f; f = f->next) { + auto ff = f->follower; + if (IS_CHARMICE(ff)) { + _npcRoster->insert(ff); + ff->personGroup = this; + } + } } bool Group::_restoreMember(CHAR_DATA *member) { @@ -197,6 +205,20 @@ bool Group::_removeMember(CHAR_DATA *member) { mudlog(buf, BRF, LVL_IMMORT, SYSLOG, TRUE); return false; } + // а также удаляем чармисов персонажа из групповых + if (member->followers != nullptr) { + for (auto f = member->followers; f; f = f->next) { + auto ff = f->follower; + if (IS_CHARMICE(ff)) { + auto c = _npcRoster->find(ff); + if (c != _npcRoster->end()) { + _npcRoster->erase(c); + ff->personGroup = nullptr; + } + } + } + } + CHAR_DATA* nxtLdr = nullptr; int nxtLdrGrpSize = 0; @@ -230,6 +252,7 @@ void Group::_clear(bool silentMode) { sprintf(buf, "[Group::clear()]: _memberList: %lu", _memberList->size()); mudlog(buf, BRF, LVL_IMMORT, SYSLOG, TRUE); CHAR_DATA* ch; + // чистим игроков for (auto & it : *_memberList) { ch = (*it.second)->member; if (ch == nullptr || ch->purged()) @@ -238,6 +261,11 @@ void Group::_clear(bool silentMode) { send_to_char(ch, "Ваша группа распущена.\r\n"); ch->personGroup = nullptr; } + // чистим чармисов + for (auto & it : *_npcRoster) { + it->personGroup = nullptr; + } + _npcRoster->clear(); _memberList->clear(); } @@ -281,7 +309,6 @@ void Group::rejectRequest(char *applicant) { send_to_char(_leader, "Вы отклонили заявку.\r\n"); } - void Group::approveRequest(const char *applicant) { if (!*applicant) { send_to_char(_leader, "Заявка не найдена, уточните имя.\r\n"); @@ -482,8 +509,7 @@ void Group::printGroup(CHAR_DATA *ch) { ch->desc->msdp_report(msdp::constants::GROUP); // печатаем группу - if (ch->personGroup != nullptr ) - { + if (ch->personGroup != nullptr ) { send_to_char("Ваша группа состоит из:\r\n", ch); for (auto &it : *_memberList ){ if ((*it.second)->member == nullptr || (*it.second)->member->purged()) @@ -494,15 +520,11 @@ void Group::printGroup(CHAR_DATA *ch) { } // допечатываем чармисов - for (f = ch->followers; f; f = f->next) - { - if (!(AFF_FLAGGED(f->follower, EAffectFlag::AFF_CHARM) - || MOB_FLAGGED(f->follower, MOB_ANGEL)|| MOB_FLAGGED(f->follower, MOB_GHOST))) { - continue; - } + for (auto & it: *_npcRoster) { if (!cfound) send_to_char("Ваши последователи:\r\n", ch); - _printNPCLine(ch, f->follower, cfound++); + if (it->get_master() == ch) + _printNPCLine(ch, it, cfound++); } if (!gfound && !cfound) { @@ -511,24 +533,17 @@ void Group::printGroup(CHAR_DATA *ch) { } // печатаем чармисов членов группы - if (PRF_FLAGGED(ch, PRF_SHOWGROUP)) - { + if (PRF_FLAGGED(ch, PRF_SHOWGROUP)) { for (auto &it : *_memberList ) { - for (f = (*it.second)->member->followers; f; f = f->next) { - if (!(AFF_FLAGGED(f->follower, EAffectFlag::AFF_CHARM) - || MOB_FLAGGED(f->follower, MOB_ANGEL) || MOB_FLAGGED(f->follower, MOB_GHOST))){ - continue; - } + for (auto & npc: *_npcRoster) { // клоны отключены - if (PRF_FLAGGED(ch, PRF_NOCLONES) && IS_NPC(f->follower) && - (MOB_FLAGGED(f->follower, MOB_CLONE) || GET_MOB_VNUM(f->follower) == MOB_KEEPER)) { + if (PRF_FLAGGED(ch, PRF_NOCLONES) && + (MOB_FLAGGED(npc, MOB_CLONE) || GET_MOB_VNUM(npc) == MOB_KEEPER) && + npc->get_master() == ch /*этих раньше вывели*/) continue; - } - - if (!cfound) { + if (!cfound) send_to_char("Последователи членов вашей группы:\r\n", ch); - } - _printNPCLine(ch, f->follower, cfound++); + _printNPCLine(ch, npc, cfound++); } } @@ -602,54 +617,46 @@ CHAR_DATA* Group::get_random_pc_group() { return nullptr; } -bool same_group(CHAR_DATA * ch, CHAR_DATA * tch) -{ - if (!ch || !tch) - return false; - - // Добавлены проверки чтобы не любой заследовавшийся моб считался согруппником (Купала) - if (IS_NPC(ch) - && ch->has_master() - && !IS_NPC(ch->get_master()) - && (IS_HORSE(ch) - || AFF_FLAGGED(ch, EAffectFlag::AFF_CHARM) - || MOB_FLAGGED(ch, MOB_ANGEL) - || MOB_FLAGGED(ch, MOB_GHOST))) - { - ch = ch->get_master(); +// room_rnum = 0 - ignore +cd_v Group::getMembers(rnum_t room_rnum) { + cd_v retval = std::make_shared>(); + for (const auto& it : *_memberList) { + auto c = (*it.second)->member; + if (room_rnum == (room_rnum == 0 ? room_rnum : c->in_room) && !c->purged()) + retval->push_back((*it.second)->member); } + return cd_v(); +} - if (IS_NPC(tch) - && tch->has_master() - && !IS_NPC(tch->get_master()) - && (IS_HORSE(tch) - || AFF_FLAGGED(tch, EAffectFlag::AFF_CHARM) - || MOB_FLAGGED(tch, MOB_ANGEL) - || MOB_FLAGGED(tch, MOB_GHOST))) - { - tch = tch->get_master(); +npc_r Group::getCharmee(rnum_t room_rnum) { + npc_r retval; + if (room_rnum == 0) + return _npcRoster; + else { + retval = new std::unordered_set(); + for (auto &it: *_npcRoster) { + if (it->in_room == room_rnum) + retval->insert(it); + } + return retval; } +} - // NPC's always in same group - if ((IS_NPC(ch) && IS_NPC(tch)) - || ch == tch) - { - return true; +u_short Group::size(rnum_t room_rnum) { + u_short retval = 0; + for (const auto& it : *_memberList){ + auto c = (*it.second)->member; + if (room_rnum == (room_rnum == 0 ? room_rnum : c->in_room) && !c->purged() && retval < 255) + retval++; } + return retval; +} - if (!AFF_FLAGGED(ch, EAffectFlag::AFF_GROUP) - || !AFF_FLAGGED(tch, EAffectFlag::AFF_GROUP)) - { +bool same_group(CHAR_DATA * ch, CHAR_DATA * tch) +{ + if (!ch || !tch) return false; - } - - if (ch->get_master() == tch - || tch->get_master() == ch - || (ch->has_master() - && ch->get_master() == tch->get_master())) - { + if (ch->personGroup == tch->personGroup) return true; - } - return false; } diff --git a/src/grp/grp.main.h b/src/grp/grp.main.h index 128ea01ed..f31583856 100644 --- a/src/grp/grp.main.h +++ b/src/grp/grp.main.h @@ -3,6 +3,7 @@ #include #include "chars/char.hpp" +#include "structs.h" enum RQ_TYPE {RQ_GROUP, RQ_PERSON, RQ_ANY}; enum GRP_COMM {GRP_COMM_LEADER, GRP_COMM_ALL, GRP_COMM_OTHER}; @@ -25,9 +26,13 @@ using namespace std::chrono; using grp_ptr = std::shared_ptr; using rq_ptr = std::shared_ptr; using sclock_t = time_point; +using cd_v = std::shared_ptr>; +using npc_r = std::unordered_set *; const duration DEF_EXPIRY_TIME = 600s; +inline bool IN_GROUP(CHAR_DATA* ch) {return ch != nullptr && ch->personGroup != nullptr;}; + struct char_info { char_info(int memberUid, CHAR_DATA *member, const std::string& memberName); virtual ~char_info(); @@ -67,9 +72,10 @@ class Group { CHAR_DATA* _findMember(int UID); bool _removeMember(CHAR_DATA *member); void charDataPurged(CHAR_DATA* ch); - u_short size() { return (u_short)_memberList->size();} + u_short size(rnum_t room_rnum = 0); private: std::map> * _memberList; + npc_r _npcRoster; static void _printHeader(CHAR_DATA* ch, bool npc); static void _printDeadLine(CHAR_DATA* ch, const char* playerName, int header); static void _printNPCLine(CHAR_DATA* ch, CHAR_DATA* npc, int header); @@ -91,6 +97,8 @@ class Group { void sendToGroup(GRP_COMM mode, const char *msg, ...); void actToGroup(CHAR_DATA* vict, GRP_COMM mode, const char *msg, ...); + cd_v getMembers(rnum_t room_rnum = 0); + npc_r getCharmee(rnum_t room_rnum = 0); public: // всякий унаследованный стафф CHAR_DATA* get_random_pc_group(); From 1e5de234700247ee76e48d75b14ea035dc164584 Mon Sep 17 00:00:00 2001 From: bikbai Date: Sun, 3 Jan 2021 17:55:12 +0300 Subject: [PATCH 13/40] =?UTF-8?q?=D0=BF=D1=80=D0=BE=D0=B4=D0=BE=D0=BB?= =?UTF-8?q?=D0=B6=D0=B0=D0=B5=D0=BC,=20=D0=BA=D0=BE=D0=BD=D1=86=D0=B0=20?= =?UTF-8?q?=D0=B8=20=D0=BA=D1=80=D0=B0=D1=8F=20=D0=BD=D0=B5=D1=82=20=3D)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/act.comm.cpp | 12 ---- src/chars/char_player.cpp | 2 +- src/cmd.imm/act.wizard.cpp | 90 +++++++++++++------------- src/dg_scripts.cpp | 70 ++++++++++----------- src/dps.cpp | 108 +++++++++++++++++--------------- src/dps.hpp | 6 +- src/ext_money.cpp | 2 +- src/fightsystem/fight_hit.cpp | 83 ++++++++++-------------- src/fightsystem/fight_stuff.cpp | 7 +-- src/fightsystem/pk.cpp | 20 ++---- src/grp/grp.cmdprocessor.cpp | 10 +-- src/grp/grp.group.cpp | 101 +++++++++++++++++------------ src/grp/grp.main.h | 11 +++- src/grp/grp.roster.cpp | 18 +++--- src/handler.cpp | 15 ++--- src/utils.cpp | 11 ++++ src/utils.h | 2 + tests/char.leaders.cpp | 28 +++++++++ tests/char.utilities.cpp | 26 +++++++- tests/char.utilities.hpp | 13 ++++ 20 files changed, 346 insertions(+), 289 deletions(-) diff --git a/src/act.comm.cpp b/src/act.comm.cpp index 4717b2a89..84dad07b5 100644 --- a/src/act.comm.cpp +++ b/src/act.comm.cpp @@ -120,18 +120,6 @@ void do_say(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/){ } } -bool tell_can_see(CHAR_DATA *ch, CHAR_DATA *vict) -{ - if (CAN_SEE_CHAR(vict, ch) || IS_IMMORTAL(ch) || GET_INVIS_LEV(ch)) - { - return true; - } - else - { - return false; - } -} - void perform_tell(CHAR_DATA * ch, CHAR_DATA * vict, char *arg) { // shapirus: не позволим телять, если жертва не видит и включила diff --git a/src/chars/char_player.cpp b/src/chars/char_player.cpp index a6821569f..bffece51c 100644 --- a/src/chars/char_player.cpp +++ b/src/chars/char_player.cpp @@ -403,7 +403,7 @@ void Player::dps_add_dmg(int type, int dmg, int over_dmg, CHAR_DATA *ch) void Player::dps_clear(int type) { - dps_.clear(type); + dps_.clear(this, type); } void Player::dps_print_stats(CHAR_DATA *coder) diff --git a/src/cmd.imm/act.wizard.cpp b/src/cmd.imm/act.wizard.cpp index ceae036c6..63a23ec77 100644 --- a/src/cmd.imm/act.wizard.cpp +++ b/src/cmd.imm/act.wizard.cpp @@ -15,65 +15,65 @@ #include "cmd.imm/act.wizard.hpp" #include "action.targeting.hpp" -#include "object.prototypes.hpp" -#include "world.objects.hpp" +#include "ban.hpp" +#include "birth_places.hpp" +#include "celebrates.hpp" +#include "char_obj_utils.inl" +#include "chars/char.hpp" +#include "chars/char_player.hpp" +#include "chars/player_races.hpp" #include "chars/world.characters.hpp" -#include "logger.hpp" -#include "command.shutdown.hpp" -#include "obj.hpp" #include "comm.h" -#include "interpreter.h" -#include "handler.h" -#include "db.h" -#include "spells.h" -#include "house.h" -#include "screen.h" -#include "skills.h" +#include "command.shutdown.hpp" +#include "conf.h" +#include "config.hpp" #include "constants.h" -#include "olc.h" +#include "corpse.hpp" +#include "db.h" +#include "depot.hpp" +#include "description.h" #include "dg_scripts.h" +#include "ext_money.hpp" +#include "fightsystem/fight.h" #include "fightsystem/pk.h" +#include "file_crc.hpp" +#include "genchar.h" +#include "global.objects.hpp" +#include "glory.hpp" +#include "glory_const.hpp" +#include "glory_misc.hpp" +#include "handler.h" +#include "heartbeat.hpp" +#include "house.h" #include "im.h" -#include "top.h" -#include "ban.hpp" -#include "description.h" -#include "title.hpp" +#include "interpreter.h" +#include "liquid.hpp" +#include "logger.hpp" +#include "mail.h" +#include "mob_stat.hpp" +#include "modify.h" #include "names.hpp" +#include "noob.hpp" +#include "obj.hpp" +#include "object.prototypes.hpp" +#include "olc.h" +#include "parcel.hpp" #include "password.hpp" #include "privilege.hpp" -#include "depot.hpp" -#include "glory.hpp" -#include "genchar.h" -#include "file_crc.hpp" -#include "chars/char.hpp" -#include "chars/char_player.hpp" -#include "parcel.hpp" -#include "liquid.hpp" -#include "modify.h" -#include "room.hpp" -#include "glory_misc.hpp" -#include "glory_const.hpp" -#include "shop_ext.hpp" -#include "celebrates.hpp" -#include "chars/player_races.hpp" -#include "birth_places.hpp" -#include "corpse.hpp" #include "pugixml.hpp" +#include "room.hpp" +#include "screen.h" #include "sets_drop.hpp" -#include "fightsystem/fight.h" -#include "ext_money.hpp" -#include "noob.hpp" -#include "mail.h" -#include "mob_stat.hpp" -#include "char_obj_utils.inl" -#include "utils.h" +#include "shop_ext.hpp" +#include "skills.h" +#include "spells.h" #include "structs.h" #include "sysdep.h" -#include "conf.h" -#include "config.hpp" #include "time_utils.hpp" -#include "global.objects.hpp" -#include "heartbeat.hpp" +#include "title.hpp" +#include "top.h" +#include "utils.h" +#include "world.objects.hpp" #include "zone.table.hpp" #include diff --git a/src/dg_scripts.cpp b/src/dg_scripts.cpp index f74773d45..78780be42 100644 --- a/src/dg_scripts.cpp +++ b/src/dg_scripts.cpp @@ -10,47 +10,48 @@ #include "dg_scripts.h" -#include "global.objects.hpp" +#include "backtrace.hpp" +#include "bonus.h" +#include "chars/char.hpp" +#include "chars/char_player.hpp" #include "chars/world.characters.hpp" -#include "heartbeat.hpp" -#include "find.obj.id.by.vnum.hpp" -#include "world.objects.hpp" -#include "object.prototypes.hpp" -#include "obj.hpp" #include "comm.h" -#include "interpreter.h" -#include "handler.h" -#include "dg_event.h" -#include "db.h" -#include "screen.h" -#include "house.h" +#include "conf.h" #include "constants.h" -#include "top.h" +#include "coredump.hpp" +#include "db.h" +#include "debug.utils.hpp" +#include "dg_db_scripts.hpp" +#include "dg_event.h" #include "features.hpp" -#include "chars/char.hpp" -#include "chars/char_player.hpp" -#include "name_list.hpp" +#include "find.obj.id.by.vnum.hpp" +#include "genchar.h" +#include "global.objects.hpp" +#include "grp/grp.main.h" +#include "handler.h" +#include "heartbeat.hpp" +#include "house.h" +#include "interpreter.h" +#include "logger.hpp" #include "modify.h" -#include "room.hpp" +#include "name_list.hpp" #include "named_stuff.hpp" +#include "noob.hpp" +#include "obj.hpp" +#include "object.prototypes.hpp" +#include "olc.h" +#include "privilege.hpp" +#include "room.hpp" +#include "screen.h" +#include "skills.h" #include "spell_parser.hpp" #include "spells.h" -#include "skills.h" -#include "noob.hpp" -#include "genchar.h" -#include "logger.hpp" -#include "utils.h" #include "structs.h" #include "sysdep.h" -#include "conf.h" -#include "dg_db_scripts.hpp" -#include "bonus.h" +#include "top.h" +#include "utils.h" +#include "world.objects.hpp" #include "zone.table.hpp" -#include "debug.utils.hpp" -#include "backtrace.hpp" -#include "coredump.hpp" -#include "olc.h" -#include "privilege.hpp" #define PULSES_PER_MUD_HOUR (SECS_PER_MUD_HOUR*PASSES_PER_SEC) @@ -2320,9 +2321,7 @@ void find_replacement(void* go, SCRIPT_DATA* sc, TRIG_DATA* trig, int type, char { for (f = k->followers; f; f = f->next) { - if (IS_NPC(f->follower) - || !AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP)) - { + if (IS_NPC(f->follower) || !IN_SAME_GROUP(k, f->follower)) { continue; } if ((GET_CLASS(f->follower) == 8 @@ -3089,8 +3088,9 @@ void find_replacement(void* go, SCRIPT_DATA* sc, TRIG_DATA* trig, int type, char if (!IN_GROUP(c)) { return; } - for (auto it : *(c->personGroup->getMembers())) { - sprintf(str + strlen(str), "%c%ld ", UID_CHAR, GET_ID(it)); + for (auto it : c->personGroup->getMembers()) { + if (it) + sprintf(str + strlen(str), "%c%ld ", UID_CHAR, GET_ID(it)); } } else if (!str_cmp(field, "attackers")) diff --git a/src/dps.cpp b/src/dps.cpp index ab4093b76..6400782b1 100644 --- a/src/dps.cpp +++ b/src/dps.cpp @@ -4,13 +4,16 @@ #include "dps.hpp" -#include "logger.hpp" -#include "utils.h" + #include "comm.h" #include "chars/char.hpp" -#include "interpreter.h" #include "db.h" +#include "grp/grp.main.h" #include "handler.h" +#include "interpreter.h" +#include "logger.hpp" +#include "utils.h" + #include @@ -111,6 +114,7 @@ void Dps::add_dmg(int type, CHAR_DATA *ch, int dmg, int over_dmg) pers_dps_.add_charm_dmg(ch, dmg, over_dmg); break; case GROUP_DPS: + // comes with properly defined group here add_group_dmg(ch, dmg, over_dmg); break; case GROUP_CHARM_DPS: @@ -148,7 +152,7 @@ void Dps::end_round(int type, CHAR_DATA *ch) } // * Очистка себя или группы. -void Dps::clear(int type) +void Dps::clear(CHAR_DATA* ch, int type) { switch (type) { @@ -164,7 +168,8 @@ void Dps::clear(int type) case PERS_CHARM_DPS: break; case GROUP_DPS: - group_dps_.clear(); + if (ch->personGroup) + ch->personGroup->_group_dps.clear(); break; case GROUP_CHARM_DPS: break; @@ -196,8 +201,11 @@ unsigned tmp_total_dmg = 0; void Dps::add_tmp_group_list(CHAR_DATA *ch) { - GroupListType::iterator it = group_dps_.find(GET_ID(ch)); - if (it != group_dps_.end()) + if (!ch->personGroup) return; + DpsSystem::GroupListType* group_dps_; + group_dps_ = &ch->personGroup->_group_dps; + GroupListType::iterator it = group_dps_->find(GET_ID(ch)); + if (it != group_dps_->end()) { sort_node tmp_node(it->second.get_name(), it->second.get_stat(), it->second.get_round_dmg(), it->second.get_over_dmg()); @@ -237,11 +245,9 @@ void Dps::print_stats(CHAR_DATA *ch, CHAR_DATA *coder) thousands_sep(abs(lost_exp_)).c_str(), balance >= 0 ? "+" : "-", thousands_sep(abs(balance)).c_str()); - if (AFF_FLAGGED(ch, EAffectFlag::AFF_GROUP)) - { + if (IN_GROUP(ch)) { tmp_total_dmg = 0; - CHAR_DATA *leader = ch->has_master() ? ch->get_master() : ch; - leader->dps_print_group_stats(ch, coder); + ch->dps_print_group_stats(ch, coder); } } @@ -252,25 +258,16 @@ void Dps::print_stats(CHAR_DATA *ch, CHAR_DATA *coder) */ void Dps::print_group_stats(CHAR_DATA *ch, CHAR_DATA *coder) { - if (!coder) - { + if (!coder) { coder = ch; } send_to_char("\r\nСтатистика вашей группы:\r\n" "---------------------------|--------------------|----------------|-------------|\r\n", coder); - - CHAR_DATA *leader = ch->has_master() ? ch->get_master() : ch; - for (follow_type *f = leader->followers; f; f = f->next) - { - if (f->follower - && !IS_NPC(f->follower) - && AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP)) - { - add_tmp_group_list(f->follower); - } + auto groupList = ch->personGroup->getMembers(); + for (auto it : groupList) { + add_tmp_group_list(it); } - add_tmp_group_list(leader); std::string out; for (SortGroupType::reverse_iterator it = tmp_group_list.rbegin(); it != tmp_group_list.rend(); ++it) @@ -285,8 +282,12 @@ void Dps::print_group_stats(CHAR_DATA *ch, CHAR_DATA *coder) void Dps::add_group_dmg(CHAR_DATA *ch, int dmg, int over_dmg) { - GroupListType::iterator it = group_dps_.find(GET_ID(ch)); - if (it != group_dps_.end()) + if (!ch->personGroup) return; + DpsSystem::GroupListType* group_dps_; + group_dps_ = &ch->personGroup->_group_dps; + + auto it = group_dps_->find(GET_ID(ch)); + if (it != group_dps_->end()) { it->second.add_dmg(dmg, over_dmg); } @@ -295,14 +296,17 @@ void Dps::add_group_dmg(CHAR_DATA *ch, int dmg, int over_dmg) PlayerDpsNode tmp_node; tmp_node.set_name(GET_NAME(ch)); tmp_node.add_dmg(dmg, over_dmg); - group_dps_.insert(std::make_pair(GET_ID(ch), tmp_node)); + group_dps_->insert(std::make_pair(GET_ID(ch), tmp_node)); } } void Dps::end_group_round(CHAR_DATA *ch) { - GroupListType::iterator it = group_dps_.find(GET_ID(ch)); - if (it != group_dps_.end()) + if (!ch->personGroup) return; + DpsSystem::GroupListType* group_dps_; + group_dps_ = &ch->personGroup->_group_dps; + GroupListType::iterator it = group_dps_->find(GET_ID(ch)); + if (it != group_dps_->end()) { it->second.end_round(); } @@ -311,14 +315,17 @@ void Dps::end_group_round(CHAR_DATA *ch) PlayerDpsNode tmp_node; tmp_node.set_name(GET_NAME(ch)); tmp_node.end_round(); - group_dps_.insert(std::make_pair(GET_ID(ch), tmp_node)); + group_dps_->insert(std::make_pair(GET_ID(ch), tmp_node)); } } void Dps::add_group_charm_dmg(CHAR_DATA *ch, int dmg, int over_dmg) { - GroupListType::iterator it = group_dps_.find(GET_ID(ch->get_master())); - if (it != group_dps_.end()) + if (!ch->personGroup) return; + DpsSystem::GroupListType* group_dps_; + group_dps_ = &ch->personGroup->_group_dps; + auto it = group_dps_->find(GET_ID(ch->get_master())); + if (it != group_dps_->end()) { it->second.add_charm_dmg(ch, dmg, over_dmg); } @@ -327,14 +334,17 @@ void Dps::add_group_charm_dmg(CHAR_DATA *ch, int dmg, int over_dmg) PlayerDpsNode tmp_node; tmp_node.set_name(GET_NAME(ch->get_master())); tmp_node.add_charm_dmg(ch, dmg, over_dmg); - group_dps_.insert(std::make_pair(GET_ID(ch->get_master()), tmp_node)); + group_dps_->insert(std::make_pair(GET_ID(ch->get_master()), tmp_node)); } } void Dps::end_group_charm_round(CHAR_DATA *ch) { - GroupListType::iterator it = group_dps_.find(GET_ID(ch->get_master())); - if (it != group_dps_.end()) + if (!ch->personGroup) return; + DpsSystem::GroupListType* group_dps_; + group_dps_ = &ch->personGroup->_group_dps; + GroupListType::iterator it = group_dps_->find(GET_ID(ch->get_master())); + if (it != group_dps_->end()) { it->second.end_charm_round(ch); } @@ -343,10 +353,10 @@ void Dps::end_group_charm_round(CHAR_DATA *ch) PlayerDpsNode tmp_node; tmp_node.set_name(GET_NAME(ch->get_master())); tmp_node.end_charm_round(ch); - group_dps_.insert(std::make_pair(GET_ID(ch->get_master()), tmp_node)); + group_dps_->insert(std::make_pair(GET_ID(ch->get_master()), tmp_node)); } } - +/* // * Чтобы не морочить голову в dps_copy, заменяем только груп.статистику. Dps & Dps::operator= (const Dps ©) { @@ -356,7 +366,7 @@ Dps & Dps::operator= (const Dps ©) } return *this; } - +*/ void Dps::add_exp(int exp) { if (exp >= 0) @@ -473,21 +483,17 @@ void PlayerDpsNode::print_group_charm_stats(CHAR_DATA *ch) const // * Подсчет дамаги за предыдущий раунд, дергается в начале раунда и по окончанию боя. void check_round(CHAR_DATA *ch) { - if (!IS_NPC(ch)) - { + if (!IS_NPC(ch)) { ch->dps_end_round(DpsSystem::PERS_DPS); - if (AFF_FLAGGED(ch, EAffectFlag::AFF_GROUP)) - { - CHAR_DATA *leader = ch->has_master() ? ch->get_master() : ch; + if (IN_GROUP(ch)) { + CHAR_DATA *leader = ch->personGroup->getLeader(); leader->dps_end_round(DpsSystem::GROUP_DPS, ch); } } - else if (IS_CHARMICE(ch) && ch->has_master()) - { + else if (IS_CHARMICE(ch) && ch->has_master()) { ch->get_master()->dps_end_round(DpsSystem::PERS_CHARM_DPS, ch); - if (AFF_FLAGGED(ch->get_master(), EAffectFlag::AFF_GROUP)) - { - CHAR_DATA *leader = ch->get_master()->has_master() ? ch->get_master()->get_master() : ch->get_master(); + if (IN_GROUP(ch->get_master())) { + CHAR_DATA *leader = ch->get_master()->personGroup->getLeader(); leader->dps_end_round(DpsSystem::GROUP_CHARM_DPS, ch); } } @@ -534,13 +540,11 @@ void do_dmeter(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) } else if (isname(name, "группа")) { - if (!AFF_FLAGGED(ch, EAffectFlag::AFF_GROUP)) - { + if (!IN_GROUP(ch)) { send_to_char("Вы не состоите в группе.\r\n", ch); return; } - if (ch->has_master()) - { + if (ch->personGroup->getLeader() != ch) { send_to_char("Вы не являетесь лидером группы.\r\n", ch); return; } diff --git a/src/dps.hpp b/src/dps.hpp index 0c428ebbe..498290cdb 100644 --- a/src/dps.hpp +++ b/src/dps.hpp @@ -78,10 +78,10 @@ class Dps { public: Dps() : exp_(0), battle_exp_(0), lost_exp_(0) {}; - Dps & operator= (const Dps ©); +// Dps & operator= (const Dps ©); void add_dmg(int type, CHAR_DATA *ch, int dmg, int over_dmg); - void clear(int type); + void clear(CHAR_DATA* ch, int type); void print_stats(CHAR_DATA *ch, CHAR_DATA *coder = 0); void print_group_stats(CHAR_DATA *ch, CHAR_DATA *coder = 0); void end_round(int type, CHAR_DATA *ch); @@ -96,7 +96,7 @@ class Dps void add_group_charm_dmg(CHAR_DATA *ch, int dmg, int over_dmg); void end_group_charm_round(CHAR_DATA *ch); - // групповая статистика + // групповая статистика уехало в группу =) GroupListType group_dps_; // персональная статистика и свои чармисы PlayerDpsNode pers_dps_; diff --git a/src/ext_money.cpp b/src/ext_money.cpp index ad48f4e4f..60f94ad27 100644 --- a/src/ext_money.cpp +++ b/src/ext_money.cpp @@ -728,7 +728,7 @@ void drop_torc(CHAR_DATA *mob) if (grp == nullptr) return; auto m_list = grp->getMembers(mob->in_room); // список живых мемберов в комнате - for (auto &m : *m_list) { + for (auto m : m_list) { if (GET_GOD_FLAG(m, GF_REMORT) && mob->get_attacker(m, ATTACKER_ROUNDS) >= damager.second / 2) { gain_torc(m, drop); } diff --git a/src/fightsystem/fight_hit.cpp b/src/fightsystem/fight_hit.cpp index b72b921f7..09d78d63e 100644 --- a/src/fightsystem/fight_hit.cpp +++ b/src/fightsystem/fight_hit.cpp @@ -2377,18 +2377,14 @@ void Damage::send_critical_message(CHAR_DATA *ch, CHAR_DATA *victim) void update_dps_stats(CHAR_DATA *ch, int real_dam, int over_dam) { - if (!IS_NPC(ch)) - { + if (!IS_NPC(ch)) { ch->dps_add_dmg(DpsSystem::PERS_DPS, real_dam, over_dam); log("DmetrLog. Name(player): %s, class: %d, remort:%d, level:%d, dmg: %d, over_dmg:%d", GET_NAME(ch), GET_CLASS(ch), GET_REMORT(ch), GET_LEVEL(ch), real_dam, over_dam); - if (AFF_FLAGGED(ch, EAffectFlag::AFF_GROUP)) - { - CHAR_DATA *leader = ch->has_master() ? ch->get_master() : ch; - leader->dps_add_dmg(DpsSystem::GROUP_DPS, real_dam, over_dam, ch); + if (IN_GROUP(ch)) { + ch->dps_add_dmg(DpsSystem::GROUP_DPS, real_dam, over_dam, ch); } } - else if (IS_CHARMICE(ch) - && ch->has_master()) + else if (IS_CHARMICE(ch) && ch->has_master()) { ch->get_master()->dps_add_dmg(DpsSystem::PERS_CHARM_DPS, real_dam, over_dam, ch); if (!IS_NPC(ch->get_master())) @@ -2398,54 +2394,39 @@ void update_dps_stats(CHAR_DATA *ch, int real_dam, int over_dam) GET_LEVEL(ch->get_master()), real_dam, over_dam); } - if (AFF_FLAGGED(ch->get_master(), EAffectFlag::AFF_GROUP)) - { - CHAR_DATA *leader = ch->get_master()->has_master() ? ch->get_master()->get_master() : ch->get_master(); - leader->dps_add_dmg(DpsSystem::GROUP_CHARM_DPS, real_dam, over_dam, ch); + if (IN_GROUP(ch->get_master())) { + ch->dps_add_dmg(DpsSystem::GROUP_CHARM_DPS, real_dam, over_dam, ch); } } } -void try_angel_sacrifice(CHAR_DATA *ch, CHAR_DATA *victim) -{ +void try_angel_sacrifice(CHAR_DATA* ch, CHAR_DATA* victim) { + CHAR_DATA* angel = nullptr; // если виктим в группе с кем-то с ангелом - вместо смерти виктима умирает ангел - if (GET_HIT(victim) <= 0 - && !IS_NPC(victim) - && AFF_FLAGGED(victim, EAffectFlag::AFF_GROUP)) - { - const auto people = world[IN_ROOM(victim)]->people; // make copy of people because keeper might be removed from this list inside the loop - for (const auto keeper : people) - { - if (IS_NPC(keeper) - && MOB_FLAGGED(keeper, MOB_ANGEL) - && keeper->has_master() - && AFF_FLAGGED(keeper->get_master(), EAffectFlag::AFF_GROUP)) - { - CHAR_DATA *keeper_leader = keeper->get_master()->has_master() - ? keeper->get_master()->get_master() - : keeper->get_master(); - CHAR_DATA *victim_leader = victim->has_master() - ? victim->get_master() - : victim; - - if ((keeper_leader == victim_leader) && (may_kill_here(keeper->get_master(), ch, NULL))) - { - if (!pk_agro_action(keeper->get_master(), ch)) - { - return; - } - send_to_char(victim, "%s пожертвовал%s своей жизнью, вытаскивая вас с того света!\r\n", - GET_PAD(keeper, 0), GET_CH_SUF_1(keeper)); - snprintf(buf, MAX_STRING_LENGTH, "%s пожертвовал%s своей жизнью, вытаскивая %s с того света!", - GET_PAD(keeper, 0), GET_CH_SUF_1(keeper), GET_PAD(victim, 3)); - act(buf, FALSE, victim, 0, 0, TO_ROOM | TO_ARENA_LISTEN); - - extract_char(keeper, 0); - GET_HIT(victim) = MIN(300, GET_MAX_HIT(victim) / 2); - } - } - } - } + if (GET_HIT(victim) <= 0 && !IS_NPC(victim) && IN_GROUP(victim)) { + // ищем первого попавшегося ангела из группы в комнате + for (auto npc : *victim->personGroup->getCharmee(victim->in_room)){ + if (MOB_FLAGGED(npc, MOB_ANGEL)) { + angel = npc; + break; + } + } + if (angel == nullptr) + return; + + if (may_kill_here(angel->get_master(), ch, NULL)) { + if (!pk_agro_action(angel->get_master(), ch)) { + return; + } + send_to_char(victim, "%s пожертвовал%s своей жизнью, вытаскивая вас с того света!\r\n", + GET_PAD(angel, 0), GET_CH_SUF_1(angel)); + snprintf(buf, MAX_STRING_LENGTH, "%s пожертвовал%s своей жизнью, вытаскивая %s с того света!", + GET_PAD(angel, 0), GET_CH_SUF_1(angel), GET_PAD(victim, 3)); + act(buf, FALSE, victim, 0, 0, TO_ROOM | TO_ARENA_LISTEN); + extract_char(angel, 0); + GET_HIT(victim) = MIN(300, GET_MAX_HIT(victim) / 2); + } + } } void update_pk_logs(CHAR_DATA *ch, CHAR_DATA *victim) diff --git a/src/fightsystem/fight_stuff.cpp b/src/fightsystem/fight_stuff.cpp index b8c356c1d..85fb1dd6c 100644 --- a/src/fightsystem/fight_stuff.cpp +++ b/src/fightsystem/fight_stuff.cpp @@ -1347,14 +1347,11 @@ void char_dam_message(int dam, CHAR_DATA * ch, CHAR_DATA * victim, bool noflee) break; case POS_DEAD: // нежить и саммоны не оставляют трупов - if (IS_NPC(victim) && (MOB_FLAGGED(victim,MOB_CORPSE) - || MOB_FLAGGED(victim, MOB_PLAYER_SUMMON))) - { + if (MOB_FLAGGED(victim,MOB_CORPSE) || MOB_FLAGGED(victim, MOB_PLAYER_SUMMON)) { act("$n вспыхнул$g и рассыпал$u в прах.", FALSE, victim, 0, 0, TO_ROOM | TO_ARENA_LISTEN); send_to_char("Похоже вас убили и даже тела не оставили!\r\n", victim); } - else - { + else { if (IS_POLY(victim)) act("$n мертвы, их души медленно подымаются в небеса.", FALSE, victim, 0, 0, TO_ROOM | TO_ARENA_LISTEN); else diff --git a/src/fightsystem/pk.cpp b/src/fightsystem/pk.cpp index f7f7f47fd..28a01fa9d 100644 --- a/src/fightsystem/pk.cpp +++ b/src/fightsystem/pk.cpp @@ -14,6 +14,7 @@ #include "pk.h" #include "global.objects.hpp" +#include "grp/grp.main.h" #include "screen.h" #include "house.h" #include "handler.h" @@ -1098,22 +1099,9 @@ int check_pkill(CHAR_DATA * ch, CHAR_DATA * opponent, const std::string &arg) // Проверяет, есть ли члены любого клан в группе чара и находятся ли они // в одной с ним комнате bool has_clan_members_in_group(CHAR_DATA * ch) { - CHAR_DATA *leader; - struct follow_type *f; - leader = ch->has_master() ? ch->get_master() : ch; - - // проверяем, был ли в группе клановый чар - if (CLAN(leader)) { - return true; - } - else { - for (f = leader->followers; f; f = f->next) { - if (AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP) && IN_ROOM(f->follower) == ch->in_room && CLAN(f->follower)) { - return true; - } - } - } - return false; + if (!ch || ch->purged() || !IN_GROUP(ch)) + return false; + return ch->personGroup->has_clan_members_in_group(ch); } diff --git a/src/grp/grp.cmdprocessor.cpp b/src/grp/grp.cmdprocessor.cpp index 6464e4b7e..ee86ebc67 100644 --- a/src/grp/grp.cmdprocessor.cpp +++ b/src/grp/grp.cmdprocessor.cpp @@ -4,9 +4,11 @@ #include "global.objects.hpp" #include "handler.h" +#include "house.h" #include "screen.h" #include "msdp.constants.hpp" #include "magic.h" +#include "remember.hpp" extern GroupRoster& groupRoster; @@ -590,10 +592,6 @@ void do_report(CHAR_DATA *ch, char* /*argument*/, int/* cmd*/, int/* subcmd*/) send_to_char("Вы доложили о состоянии всем членам вашей группы.\r\n", ch); } -void do_split(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) { - do_split(ch,argument,0,0,0); -} - void do_split(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/,int currency) { int amount, num, share, rest; @@ -731,3 +729,7 @@ void do_split(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/,int cur return; } } + +void do_split(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) { + do_split(ch,argument,0,0,0); +} diff --git a/src/grp/grp.group.cpp b/src/grp/grp.group.cpp index 31552afc2..e11288a12 100644 --- a/src/grp/grp.group.cpp +++ b/src/grp/grp.group.cpp @@ -52,7 +52,8 @@ Group::Group(CHAR_DATA *leader, u_long uid){ _memberCap = 0; _uid = uid; _memberList = new std::map>; - addMember(leader); + _npcRoster = new std::unordered_set; + addMember(leader, true); _setLeader(leader); } @@ -142,39 +143,45 @@ char_info::~char_info() { mudlog("[~char_info]", BRF, LVL_IMMORT, SYSLOG, TRUE); } -void Group::addMember(CHAR_DATA *member) { - if (IS_NPC(member)) - return; +void Group::addMember(CHAR_DATA *member, bool silent) { if (member->personGroup == this) return; // в другой группе, вышвыриваем if (member->personGroup != nullptr) member->personGroup->_removeMember(member); - auto it = _memberList->find(member->get_uid()); - if (it == _memberList->end()) { - // рисуем сообщение до добавления - actToGroup(member, GRP_COMM_ALL, "$N принят$A в группу."); - auto ci = new char_info(member->get_uid(), member, member->get_pc_name()); - _memberList->emplace(member->get_uid(), std::make_shared(ci)); - send_to_char(member, "Вас приняли в группу.\r\n"); - } - else { - sprintf(buf, "Group::addMember: группа id=%lu, попытка повторного добавления персонажа с тем же uid", getUid()); - mudlog(buf, BRF, LVL_IMMORT, SYSLOG, TRUE); - return; - } - member->personGroup = this; - auto r = groupRoster.findRequest(member->get_pc_name().c_str(), getLeaderName().c_str(), RQ_ANY); - if (r) - groupRoster.deleteRequest(r); - // добавляем чармисов группу - for (auto f = member->followers; f; f = f->next) { - auto ff = f->follower; - if (IS_CHARMICE(ff)) { - _npcRoster->insert(ff); - ff->personGroup = this; + if (IS_CHARMICE(member)) { + auto it = _npcRoster->find(member); + if (it == _npcRoster->end()) + _npcRoster->emplace(member); + } else { + auto it = _memberList->find(member->get_uid()); + if (it == _memberList->end()) { + // рисуем сообщение до добавления + if (!silent) { + actToGroup(member, GRP_COMM_ALL, "$N принят$A в группу."); + send_to_char(member, "Вас приняли в группу.\r\n"); + } + auto ci = new char_info(member->get_uid(), member, member->get_pc_name()); + _memberList->emplace(member->get_uid(), std::make_shared(ci)); + } else { + sprintf(buf, "Group::addMember: группа id=%lu, попытка повторного добавления персонажа с тем же uid", + getUid()); + mudlog(buf, BRF, LVL_IMMORT, SYSLOG, TRUE); + return; + } + member->personGroup = this; + auto r = groupRoster.findRequest(member->get_pc_name().c_str(), getLeaderName().c_str(), RQ_ANY); + if (r) + groupRoster.deleteRequest(r); + // добавляем чармисов группу + for (auto f = member->followers; f; f = f->next) { + auto ff = f->follower; + if (IS_CHARMICE(ff)) { + this->addMember(ff); // рекурсия! + } } } + } bool Group::_restoreMember(CHAR_DATA *member) { @@ -267,6 +274,8 @@ void Group::_clear(bool silentMode) { } _npcRoster->clear(); _memberList->clear(); + delete _memberList; + delete _npcRoster; } void Group::promote(char *applicant) { @@ -277,12 +286,12 @@ void Group::promote(char *applicant) { } _setLeader((*_memberList->at(memberId))->member); sendToGroup(GRP_COMM_ALL, "Изменился лидер группы на %.", (*_memberList->at(memberId))->memberName.c_str()); - _memberCap = 1; - auto diff = (u_short)(_memberList->size() - _memberCap); + auto diff = (u_short)_memberCap - (u_short)_memberList->size(); + if (diff < 0) diff = 0; if (diff > 0){ u_short i = 0; CHAR_DATA* expellList[diff]; - for (auto it = _memberList->begin(); it!= _memberList->end() || diff > i; it++){ + for (auto it = _memberList->begin(); it!= _memberList->end() || i > diff; it++){ if ((*it->second)->member != _leader ) { expellList[i] = (*it->second)->member; ++i; @@ -498,7 +507,6 @@ void Group::_printPCLine(CHAR_DATA* ch, CHAR_DATA* pc, int header) { void Group::printGroup(CHAR_DATA *ch) { int gfound = 0, cfound = 0; CHAR_DATA *leader; - struct follow_type *f, *g; if (ch->personGroup) leader = ch->personGroup->getLeader(); @@ -519,12 +527,14 @@ void Group::printGroup(CHAR_DATA *ch) { } } - // допечатываем чармисов - for (auto & it: *_npcRoster) { - if (!cfound) - send_to_char("Ваши последователи:\r\n", ch); - if (it->get_master() == ch) - _printNPCLine(ch, it, cfound++); + // допечатываем чармисов, которые прямые последователи + for (auto f = ch->followers; f; f = f->next) + { + if (IS_CHARMICE(f->follower)) { + if (!cfound) + send_to_char("Ваши последователи:\r\n", ch); + _printNPCLine(ch, f->follower, cfound++); + } } if (!gfound && !cfound) { @@ -619,13 +629,13 @@ CHAR_DATA* Group::get_random_pc_group() { // room_rnum = 0 - ignore cd_v Group::getMembers(rnum_t room_rnum) { - cd_v retval = std::make_shared>(); + cd_v retval; for (const auto& it : *_memberList) { auto c = (*it.second)->member; if (room_rnum == (room_rnum == 0 ? room_rnum : c->in_room) && !c->purged()) - retval->push_back((*it.second)->member); + retval.push_back((*it.second)->member); } - return cd_v(); + return retval; } npc_r Group::getCharmee(rnum_t room_rnum) { @@ -652,6 +662,17 @@ u_short Group::size(rnum_t room_rnum) { return retval; } +bool Group::has_clan_members_in_group(CHAR_DATA *victim) { + for (const auto& it : *_memberList) { + auto gm = (*it.second)->member; + if (gm == nullptr) + continue; + if (CLAN(gm) && gm->in_room == victim->in_room) + return true; + } + return false; +} + bool same_group(CHAR_DATA * ch, CHAR_DATA * tch) { if (!ch || !tch) diff --git a/src/grp/grp.main.h b/src/grp/grp.main.h index f31583856..df9215170 100644 --- a/src/grp/grp.main.h +++ b/src/grp/grp.main.h @@ -4,6 +4,7 @@ #include #include "chars/char.hpp" #include "structs.h" +#include "dps.hpp" enum RQ_TYPE {RQ_GROUP, RQ_PERSON, RQ_ANY}; enum GRP_COMM {GRP_COMM_LEADER, GRP_COMM_ALL, GRP_COMM_OTHER}; @@ -26,12 +27,13 @@ using namespace std::chrono; using grp_ptr = std::shared_ptr; using rq_ptr = std::shared_ptr; using sclock_t = time_point; -using cd_v = std::shared_ptr>; +using cd_v = std::vector; using npc_r = std::unordered_set *; const duration DEF_EXPIRY_TIME = 600s; -inline bool IN_GROUP(CHAR_DATA* ch) {return ch != nullptr && ch->personGroup != nullptr;}; +inline bool IN_GROUP(CHAR_DATA* ch) {return ch != nullptr && ch->personGroup != nullptr;} +inline bool IN_SAME_GROUP(CHAR_DATA* p1, CHAR_DATA* p2) {return IN_GROUP(p1) && IN_GROUP(p2) && p1->personGroup == p2->personGroup;} struct char_info { char_info(int memberUid, CHAR_DATA *member, const std::string& memberName); @@ -83,7 +85,7 @@ class Group { bool _sameGroup(CHAR_DATA * ch, CHAR_DATA * vict); public: void addFollowers(CHAR_DATA* leader); - void addMember(CHAR_DATA *member); + void addMember(CHAR_DATA *member, bool silent = false); void expellMember(char* memberName); bool _restoreMember(CHAR_DATA *member); @@ -102,6 +104,9 @@ class Group { public: // всякий унаследованный стафф CHAR_DATA* get_random_pc_group(); + // лень обвязывать, тупо переместил объект + DpsSystem::GroupListType _group_dps; + bool has_clan_members_in_group(CHAR_DATA *victim); }; class Request { diff --git a/src/grp/grp.roster.cpp b/src/grp/grp.roster.cpp index 80dd1c50f..b4ff56ab1 100644 --- a/src/grp/grp.roster.cpp +++ b/src/grp/grp.roster.cpp @@ -67,22 +67,22 @@ void GroupRoster::processGroupCommands(CHAR_DATA *ch, char *argument) { if (grp != nullptr) // в группе - покидаем grp->_removeMember(ch); groupRoster.addGroup(ch); - send_to_char(ch, "Вы создали группу c максимальным числом последователей %d.\r\n", ch->personGroup ? ch->personGroup->_getMemberCap() : 0); return; } else if (isname(subcmd, strLIST.c_str())) { grp->listMembers(ch); return; } else if (isname(subcmd, strALL.c_str())) { - // если в группе, но не лидером - покидаем - if (grp != nullptr && grp->getLeader() != ch){ - grp->_removeMember(ch); - grp = nullptr; - } - // если нет группы - создаем и становимся лидером + // если в группе, печатаем полный список. if (grp == nullptr) { grp = groupRoster.addGroup(ch).get(); + grp->addFollowers(ch); + return; + } + if (grp->getLeader() != ch){ + grp->printGroup(ch); + return; } - // сюда приходим лидером + // сюда приходим лидером и добавляем всех, кто следует. grp->addFollowers(ch); return; } else if (isname(subcmd, strLEAVE.c_str())){ @@ -370,8 +370,8 @@ void GroupRoster::acceptInvite(CHAR_DATA* who, char* author) { } auto r = findRequest(who->get_pc_name().c_str(), author, RQ_TYPE::RQ_GROUP); + send_to_char(r->_applicant, "Вы приняли приглашение.\r\n"); r->_group->addMember(r->_applicant); // и удалит заявку, если есть - send_to_char(r->_applicant, "Вы приняли приглашение.\r\n", r->_group->getLeaderName().c_str()); } void GroupRoster::runTests(CHAR_DATA *leader) { diff --git a/src/handler.cpp b/src/handler.cpp index 9eee19268..d2e876e78 100644 --- a/src/handler.cpp +++ b/src/handler.cpp @@ -2372,16 +2372,10 @@ void extract_char(CHAR_DATA* ch, int clear_objs, bool zone_reset) // transfer equipment to room, if any // log("[Extract char] Drop equipment"); - for (i = 0; i < NUM_WEARS; i++) - { - if (GET_EQ(ch, i)) - { + for (i = 0; i < NUM_WEARS; i++) { + if (GET_EQ(ch, i)) { OBJ_DATA *obj_eq = unequip_char(ch, i); - if (!obj_eq) - { - continue; - } - + if (!obj_eq) { continue; } remove_otrigger(obj_eq, ch); drop_obj_on_zreset(ch, obj_eq, 0, zone_reset); } @@ -2407,8 +2401,7 @@ void extract_char(CHAR_DATA* ch, int clear_objs, bool zone_reset) } // log("[Extract char] Die followers"); - if ((ch->followers || ch->has_master()) - && die_follower(ch)) { + if ((ch->followers || ch->has_master()) && die_follower(ch)) { // TODO: странно все это с пуржем в stop_follower return; } diff --git a/src/utils.cpp b/src/utils.cpp index 0ed91994b..ea8673c4f 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -3953,4 +3953,15 @@ void koi_to_utf8(char *str_i, char *str_o) #endif // HAVE_ICONV +bool tell_can_see(CHAR_DATA *ch, CHAR_DATA *vict) +{ + if (CAN_SEE_CHAR(vict, ch) || IS_IMMORTAL(ch) || GET_INVIS_LEV(ch)) + { + return true; + } + else + { + return false; + } +} // vim: ts=4 sw=4 tw=0 noet syntax=cpp : diff --git a/src/utils.h b/src/utils.h index 3f75877a6..4e5a01cac 100644 --- a/src/utils.h +++ b/src/utils.h @@ -1781,6 +1781,8 @@ class StreamFlagsHolder std::ios::fmtflags m_flags; }; +bool tell_can_see(CHAR_DATA *ch, CHAR_DATA *vict); + #endif // _UTILS_H_ // vim: ts=4 sw=4 tw=0 noet syntax=cpp : diff --git a/tests/char.leaders.cpp b/tests/char.leaders.cpp index 62cb1e3ef..6c1c5ca24 100644 --- a/tests/char.leaders.cpp +++ b/tests/char.leaders.cpp @@ -1,7 +1,10 @@ #include "char.utilities.hpp" +#include "global.objects.hpp" #include + + TEST(CHAR_Leaders, Initialization) { test_utils::CharacterBuilder builder; @@ -65,4 +68,29 @@ TEST(CHAR_Leaders, SimpleLoop) EXPECT_EQ(nullptr, character->get_master()); } +TEST(CHAR_Leaders, Group) { + test_utils::CharacterBuilder builder; + + builder.create_new(); + auto leader = builder.get(); + builder.create_new("F7"); + builder.add_skill(ESkill::SKILL_LEADERSHIP, 10); + auto f7 = builder.get(); + leader->add_follower(f7.get()); + + for (int i = 0; i <12; i++) { + builder.create_new(); + auto follower = builder.get(); + leader->add_follower(follower.get()); + } + + + test_utils::GroupBuilder g; + auto grp = g._roster->addGroup(leader.get()); + grp->addFollowers(leader.get()); + EXPECT_EQ(12, leader->personGroup->size()); + grp->promote("F7"); + EXPECT_EQ(7, leader->personGroup->size()); + +} // vim: ts=4 sw=4 tw=0 noet syntax=cpp : diff --git a/tests/char.utilities.cpp b/tests/char.utilities.cpp index 9e7432eb0..c85e0e56d 100644 --- a/tests/char.utilities.cpp +++ b/tests/char.utilities.cpp @@ -1,7 +1,9 @@ #include "char.utilities.hpp" #include -#include +#include "chars/char.hpp" +#include "chars/char_player.hpp" +#include "chars/player_races.hpp" #include #include @@ -11,6 +13,8 @@ void CharacterBuilder::create_new() { const auto result = std::make_shared(); result->player_specials = std::make_shared(); + result->char_specials.saved.act = clear_flags; + result->char_specials.saved.affected_by = clear_flags; result->set_class(CLASS_DRUID); result->set_level(1); m_result = result; @@ -133,6 +137,26 @@ void CharacterBuilder::check_character_existance(result_t character) } } + void CharacterBuilder::add_skill(ESkill skill, short value) { + m_result->set_skill(skill, value); + } + + void CharacterBuilder::load_player(u_short idx) { + getcwd(buf, 3000); + Player* t_vict = new Player(); + if (load_char(_names[idx].c_str(), t_vict) == -1) + throw std::runtime_error("Character wasn't created."); + m_result = std::shared_ptr(t_vict); + } + + void CharacterBuilder::create_new(char *name) { + create_new(); + m_result->set_pc_name(name); + } + + GroupBuilder::GroupBuilder() { + _roster = new GroupRoster(); + } } // vim: ts=4 sw=4 tw=0 noet syntax=cpp : diff --git a/tests/char.utilities.hpp b/tests/char.utilities.hpp index 69cbd2fc4..49ac07b41 100644 --- a/tests/char.utilities.hpp +++ b/tests/char.utilities.hpp @@ -2,6 +2,7 @@ #define __CHAR__UTILITIES_HPP__ #include +#include "grp/grp.main.h" namespace test_utils { @@ -12,11 +13,14 @@ namespace test_utils using result_t = character_t::shared_ptr; void create_new(); + void create_new(char* name); + void load_player(u_short idx); void create_new_with_class(const short player_class); void create_character_with_one_removable_affect(); void create_character_with_two_removable_affects(); void create_character_with_two_removable_and_two_not_removable_affects(); + void add_skill(ESkill skill, short value); void add_poison(); void add_sleep(); void add_detect_invis(); @@ -34,7 +38,16 @@ namespace test_utils static void check_character_existance(result_t character); result_t m_result; + const std::string _names[13] = {"Бикбай", "Первый", "Второй", "третий", "четвертый", "пятый", "шестой", + "седьмой", "восьмой", "девятый", "десятый", "одиннадцатый", "двеннадцатый"}; }; + + class GroupBuilder { + public: + GroupRoster* _roster; + GroupBuilder(); + }; + } #endif // __CHAR__UTILITIES_HPP__ From 8a1b222fbd9a4c8612118a98264e8cb7be9419de Mon Sep 17 00:00:00 2001 From: bikbai Date: Mon, 4 Jan 2021 22:22:59 +0300 Subject: [PATCH 14/40] =?UTF-8?q?=D0=A1=D0=BB=D1=83=D1=87=D0=B8=D0=BB?= =?UTF-8?q?=D0=BE=D1=81=D1=8C=20=D0=B5=D1=89=D0=B5=20=D0=BD=D0=B5=D0=BC?= =?UTF-8?q?=D0=BD=D0=BE=D0=B6=D0=BA=D0=BE=20=D1=80=D0=B5=D1=84=D0=B0=D0=BA?= =?UTF-8?q?=D1=82=D0=BE=D1=80=D0=B8=D0=BD=D0=B3=D0=B0,=20=D0=B8=D0=B1?= =?UTF-8?q?=D0=BE=20=D1=80=D0=B0=D1=81=D1=87=D0=B5=D1=82=20=D0=B3=D1=80?= =?UTF-8?q?=D1=83=D0=BF=D0=BF=D0=BE=D0=B2=D0=BE=D0=B9=20=D1=8D=D0=BA=D1=81?= =?UTF-8?q?=D0=BF=D1=8B,=20=D0=B0=20=D0=BD=D0=B0=20=D1=8D=D1=82=D0=BE?= =?UTF-8?q?=D1=82=20=D1=84=D0=B0=D1=80=D1=88=20=D1=83=20=D0=BC=D0=B5=D0=BD?= =?UTF-8?q?=D1=8F=20=D0=B0=D0=BB=D0=BB=D0=B5=D1=80=D0=B3=D0=B8=D1=8F.=20?= =?UTF-8?q?=D0=92=D1=81=D1=91=20=D1=80=D0=B0=D0=B2=D0=BD=D0=BE=20=D0=BC?= =?UTF-8?q?=D0=B5=D1=88=D0=B0=D0=BD=D0=B8=D0=BD=D0=B0=20:(?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 34 +- src/act.comm.cpp | 2 +- src/act.informative.cpp | 75 +- src/act.item.cpp | 2 +- src/act.movement.cpp | 2 +- src/act.other.cpp | 2 +- src/boot.data.files.cpp | 6 +- src/celebrates.cpp | 4 +- src/chars/char.cpp | 7 +- src/chars/char_player.cpp | 16 +- src/chars/char_player.hpp | 21 +- src/class.cpp | 675 +---------- src/class.hpp | 14 - src/cmd.imm/act.wizard.cpp | 10 +- src/cmd/whoami.cpp | 2 +- src/comm.cpp | 52 +- src/config.cpp | 19 - src/corpse.cpp | 2 +- src/db.cpp | 13 +- src/{ => dg}/dg_comm.cpp | 0 src/{ => dg}/dg_db_scripts.cpp | 0 src/{ => dg}/dg_db_scripts.hpp | 0 src/{ => dg}/dg_event.cpp | 0 src/{ => dg}/dg_event.h | 0 src/{ => dg}/dg_handler.cpp | 0 src/{ => dg}/dg_misc.cpp | 0 src/{ => dg}/dg_mobcmd.cpp | 32 +- src/{ => dg}/dg_objcmd.cpp | 38 +- src/{ => dg}/dg_olc.cpp | 0 src/{ => dg}/dg_olc.h | 0 src/{ => dg}/dg_scripts.cpp | 10 +- src/{ => dg}/dg_scripts.h | 0 src/{ => dg}/dg_triggers.cpp | 0 src/{ => dg}/dg_wldcmd.cpp | 40 +- src/ext_money.cpp | 1630 +++++++++++++-------------- src/ext_money.hpp | 35 +- src/features.cpp | 4 +- src/fightsystem/fight.cpp | 31 +- src/fightsystem/fight.h | 2 - src/fightsystem/fight.penalties.cpp | 92 -- src/fightsystem/fight.penalties.hpp | 22 - src/fightsystem/fight_hit.cpp | 65 +- src/fightsystem/fight_stuff.cpp | 419 +------ src/find.obj.id.by.vnum.cpp | 2 +- src/glory.cpp | 1 - src/glory_const.cpp | 1 - src/grp/follow.cpp | 56 +- src/grp/grouppenalties.cpp | 111 ++ src/grp/grouppenaltycalculator.cpp | 90 ++ src/grp/grp.group.cpp | 316 +++++- src/grp/grp.main.h | 53 +- src/handler.cpp | 4 +- src/heartbeat.cpp | 2 +- src/house_exp.cpp | 22 + src/house_exp.hpp | 1 + src/interpreter.cpp | 4 +- src/limits.cpp | 187 +-- src/magic.cpp | 2 +- src/medit.cpp | 2 +- src/msdp.reporters.cpp | 44 +- src/named_stuff.cpp | 2 +- src/obj.cpp | 2 +- src/objsave.cpp | 2 +- src/oedit.cpp | 2 +- src/olc.cpp | 2 +- src/redit.cpp | 2 +- src/room.cpp | 2 +- src/room.hpp | 2 +- src/sets_drop.cpp | 2 +- src/skills.cpp | 2 +- src/skills/flee.cpp | 7 +- src/spec_procs.cpp | 41 +- src/spell_parser.cpp | 2 +- src/spells.cpp | 2 +- src/structs.cpp | 5 +- src/structs.h | 15 +- src/stuff.cpp | 2 +- src/utils.cpp | 2 +- src/utils.h | 3 - src/world.objects.cpp | 2 +- src/zedit.cpp | 2 +- tests/char.leaders.cpp | 2 +- tests/fight.penalties.cpp | 2 +- tests/obj.copy.cpp | 2 +- tests/triggers.list.cpp | 2 +- 85 files changed, 1778 insertions(+), 2609 deletions(-) rename src/{ => dg}/dg_comm.cpp (100%) rename src/{ => dg}/dg_db_scripts.cpp (100%) rename src/{ => dg}/dg_db_scripts.hpp (100%) rename src/{ => dg}/dg_event.cpp (100%) rename src/{ => dg}/dg_event.h (100%) rename src/{ => dg}/dg_handler.cpp (100%) rename src/{ => dg}/dg_misc.cpp (100%) rename src/{ => dg}/dg_mobcmd.cpp (99%) rename src/{ => dg}/dg_objcmd.cpp (99%) rename src/{ => dg}/dg_olc.cpp (100%) rename src/{ => dg}/dg_olc.h (100%) rename src/{ => dg}/dg_scripts.cpp (99%) rename src/{ => dg}/dg_scripts.h (100%) rename src/{ => dg}/dg_triggers.cpp (100%) rename src/{ => dg}/dg_wldcmd.cpp (99%) create mode 100644 src/grp/grouppenalties.cpp create mode 100644 src/grp/grouppenaltycalculator.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 9b5224f4e..ac032fa2b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,17 +67,17 @@ set(SOURCES src/debug.utils.cpp src/depot.cpp src/description.cpp - src/dg_comm.cpp - src/dg_db_scripts.cpp - src/dg_event.cpp - src/dg_handler.cpp - src/dg_misc.cpp - src/dg_mobcmd.cpp - src/dg_objcmd.cpp - src/dg_olc.cpp - src/dg_scripts.cpp - src/dg_triggers.cpp - src/dg_wldcmd.cpp + src/dg/dg_comm.cpp + src/dg/dg_db_scripts.cpp + src/dg/dg_event.cpp + src/dg/dg_handler.cpp + src/dg/dg_misc.cpp + src/dg/dg_mobcmd.cpp + src/dg/dg_objcmd.cpp + src/dg/dg_olc.cpp + src/dg/dg_scripts.cpp + src/dg/dg_triggers.cpp + src/dg/dg_wldcmd.cpp src/dictionary.cpp src/diskio.cpp src/dps.cpp @@ -211,9 +211,11 @@ set(SOURCES src/world.objects.cpp src/zedit.cpp src/zone.table.cpp - ) + src/core/leveling.cpp + src/grp/grouppenalties.cpp src/grp/grouppenaltycalculator.cpp) set(HEADERS + src/core/leveling.h src/grp/follow.h src/grp/grp.main.h src/core/affect_data.h @@ -294,10 +296,10 @@ set(HEADERS src/deathtrap.hpp src/depot.hpp src/description.h - src/dg_db_scripts.hpp - src/dg_event.h - src/dg_olc.h - src/dg_scripts.h + src/dg/dg_db_scripts.hpp + src/dg/dg_event.h + src/dg/dg_olc.h + src/dg/dg_scripts.h src/dictionary.hpp src/diskio.h src/double_map.hpp diff --git a/src/act.comm.cpp b/src/act.comm.cpp index 84dad07b5..c858877d0 100644 --- a/src/act.comm.cpp +++ b/src/act.comm.cpp @@ -17,7 +17,7 @@ #include "handler.h" #include "db.h" #include "screen.h" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "auction.h" #include "privilege.hpp" #include "chars/char.hpp" diff --git a/src/act.informative.cpp b/src/act.informative.cpp index ba506f0ab..557d52a62 100644 --- a/src/act.informative.cpp +++ b/src/act.informative.cpp @@ -12,56 +12,57 @@ * $Revision$ * ************************************************************************ */ -#include "world.objects.hpp" +#include "bonus.h" +#include "char_obj_utils.inl" +#include "chars/char.hpp" +#include "chars/char_player.hpp" +#include "chars/player_races.hpp" #include "chars/world.characters.hpp" -#include "object.prototypes.hpp" -#include "logger.hpp" -#include "shutdown.parameters.hpp" -#include "obj.hpp" +#include "class.hpp" #include "comm.h" -#include "interpreter.h" -#include "handler.h" +#include "core/leveling.h" +#include "conf.h" +#include "constants.h" +#include "corpse.hpp" #include "db.h" -#include "spells.h" -#include "skills.h" +#include "depot.hpp" +#include "description.h" +#include "dg/dg_scripts.h" +#include "ext_money.hpp" +#include "features.hpp" #include "fightsystem/fight.h" #include "fightsystem/fight_hit.hpp" -#include "screen.h" -#include "constants.h" #include "fightsystem/pk.h" -#include "dg_scripts.h" +#include "glory.hpp" +#include "glory_const.hpp" +#include "grp/grp.main.h" +#include "handler.h" +#include "help.hpp" +#include "house.h" +#include "im.h" +#include "interpreter.h" +#include "liquid.hpp" +#include "logger.hpp" #include "mail.h" +#include "map.hpp" +#include "mob_stat.hpp" +#include "modify.h" +#include "obj.hpp" +#include "object.prototypes.hpp" +#include "parcel.hpp" #include "parcel.hpp" -#include "features.hpp" -#include "im.h" -#include "house.h" -#include "description.h" #include "privilege.hpp" -#include "depot.hpp" -#include "glory.hpp" #include "random.hpp" -#include "chars/char.hpp" -#include "chars/char_player.hpp" -#include "parcel.hpp" -#include "liquid.hpp" -#include "modify.h" #include "room.hpp" -#include "glory_const.hpp" -#include "chars/player_races.hpp" -#include "corpse.hpp" +#include "screen.h" #include "sets_drop.hpp" -#include "help.hpp" -#include "map.hpp" -#include "ext_money.hpp" -#include "mob_stat.hpp" -#include "char_obj_utils.inl" -#include "class.hpp" -#include "zone.table.hpp" +#include "shutdown.parameters.hpp" +#include "skills.h" +#include "spells.h" #include "structs.h" #include "sysdep.h" -#include "bonus.h" -#include "conf.h" - +#include "world.objects.hpp" +#include "zone.table.hpp" #include #include @@ -72,6 +73,7 @@ #include using std::string; +using namespace ExpCalc; // extern variables extern DESCRIPTOR_DATA *descriptor_list; @@ -93,7 +95,6 @@ extern int nameserver_is_slow; //config.cpp extern std::vector cities; // extern functions long find_class_bitvector(char arg); -int level_exp(CHAR_DATA * ch, int level); TIME_INFO_DATA *real_time_passed(time_t t2, time_t t1); int compute_armor_class(CHAR_DATA * ch); int pk_count(CHAR_DATA * ch); diff --git a/src/act.item.cpp b/src/act.item.cpp index f99fd9fef..04bd4bebf 100644 --- a/src/act.item.cpp +++ b/src/act.item.cpp @@ -22,7 +22,7 @@ #include "constants.h" #include "db.h" #include "depot.hpp" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "features.hpp" #include "fightsystem/fight.h" #include "handler.h" diff --git a/src/act.movement.cpp b/src/act.movement.cpp index 5965b00d0..94c77ab29 100644 --- a/src/act.movement.cpp +++ b/src/act.movement.cpp @@ -26,7 +26,7 @@ #include "skills.h" #include "house.h" #include "constants.h" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "screen.h" #include "fightsystem/pk.h" #include "fightsystem/mobact.hpp" diff --git a/src/act.other.cpp b/src/act.other.cpp index e363f4d3f..2d789d3a2 100644 --- a/src/act.other.cpp +++ b/src/act.other.cpp @@ -22,7 +22,7 @@ #include "constants.h" #include "db.h" #include "depot.hpp" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "features.hpp" #include "fightsystem/fight.h" #include "fightsystem/fight_hit.hpp" diff --git a/src/boot.data.files.cpp b/src/boot.data.files.cpp index 1dfaad995..1ff394487 100644 --- a/src/boot.data.files.cpp +++ b/src/boot.data.files.cpp @@ -2,8 +2,8 @@ #include "object.prototypes.hpp" #include "logger.hpp" -#include "dg_scripts.h" -#include "dg_olc.h" +#include "dg/dg_scripts.h" +#include "dg/dg_olc.h" #include "boards.h" #include "constants.h" #include "room.hpp" @@ -11,7 +11,7 @@ #include "im.h" #include "chars/char.hpp" #include "help.hpp" -#include "dg_db_scripts.hpp" +#include "dg/dg_db_scripts.hpp" #include "zone.table.hpp" #include "utils.h" diff --git a/src/celebrates.cpp b/src/celebrates.cpp index 3bfea305c..2cc954045 100644 --- a/src/celebrates.cpp +++ b/src/celebrates.cpp @@ -4,8 +4,8 @@ #include "obj.hpp" #include "comm.h" #include "db.h" -#include "dg_db_scripts.hpp" -#include "dg_scripts.h" +#include "dg/dg_db_scripts.hpp" +#include "dg/dg_scripts.h" #include "chars/char.hpp" #include "room.hpp" #include "handler.h" diff --git a/src/chars/char.cpp b/src/chars/char.cpp index 0b7f341fa..f706172f5 100644 --- a/src/chars/char.cpp +++ b/src/chars/char.cpp @@ -29,7 +29,7 @@ #include "utils.h" #include "msdp.constants.hpp" #include "backtrace.hpp" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "zone.table.hpp" #include @@ -898,6 +898,7 @@ OBJ_DATA * CHAR_DATA::get_cast_obj() const bool IS_CHARMICE(const CHAR_DATA* ch) { + if (!ch) return false; return IS_NPC(ch) && (AFF_FLAGGED(ch, EAffectFlag::AFF_HELPER) || AFF_FLAGGED(ch, EAffectFlag::AFF_CHARM)); @@ -905,6 +906,7 @@ bool IS_CHARMICE(const CHAR_DATA* ch) bool MORT_CAN_SEE(const CHAR_DATA* sub, const CHAR_DATA* obj) { + if (!sub) return false; return HERE(obj) && INVIS_OK(sub, obj) && (IS_LIGHT((obj)->in_room) @@ -913,6 +915,7 @@ bool MORT_CAN_SEE(const CHAR_DATA* sub, const CHAR_DATA* obj) bool MAY_SEE(const CHAR_DATA* ch, const CHAR_DATA* sub, const CHAR_DATA* obj) { + if (!ch) return false; return !(GET_INVIS_LEV(ch) > 30) && !AFF_FLAGGED(sub, EAffectFlag::AFF_BLIND) && (!IS_DARK(sub->in_room) @@ -2033,6 +2036,8 @@ void CHAR_DATA::msdp_report(const std::string& name) { void CHAR_DATA::removeGroupFlags() { AFF_FLAGS(this).unset(EAffectFlag::AFF_GROUP); PRF_FLAGS(this).unset(PRF_SKIRMISHER); + if (personGroup != nullptr && IS_CHARMICE(this)) + personGroup->_removeMember(this); } void CHAR_DATA::add_follower(CHAR_DATA* ch) { diff --git a/src/chars/char_player.cpp b/src/chars/char_player.cpp index bffece51c..97d265494 100644 --- a/src/chars/char_player.cpp +++ b/src/chars/char_player.cpp @@ -7,7 +7,7 @@ #include "logger.hpp" #include "utils.h" #include "db.h" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "handler.h" #include "boards.h" #include "file_crc.hpp" @@ -18,6 +18,7 @@ #include "im.h" #include "olc.h" #include "comm.h" +#include "core/leveling.h" #include "fightsystem/pk.h" #include "diskio.h" #include "interpreter.h" @@ -48,7 +49,7 @@ #include -int level_exp(CHAR_DATA * ch, int level); + extern std::vector cities; extern std::string default_str_cities; namespace @@ -1235,9 +1236,9 @@ int Player::load_char_ascii(const char *name, bool reboot, const bool find_id /* // если с загруженными выше полями что-то хочется делать после лоада - делайте это здесь //Indexing experience - if his exp is lover than required for his level - set it to required - if (GET_EXP(this) < level_exp(this, GET_LEVEL(this))) + if (GET_EXP(this) < ExpCalc::level_exp(this, GET_LEVEL(this))) { - set_exp(level_exp(this, GET_LEVEL(this))); + set_exp(ExpCalc::level_exp(this, GET_LEVEL(this))); } if (reboot) @@ -2555,4 +2556,11 @@ unsigned long int Player::getTelegramId() { return this->player_specials->saved.telegram_id; } +// * Перерасчет максимальных родных хп персонажа. +// * При входе в игру, левеле/делевеле, добавлении/удалении славы. +void check_max_hp(CHAR_DATA *ch) +{ + GET_MAX_HIT(ch) = PlayerSystem::con_natural_hp(ch); +} + // vim: ts=4 sw=4 tw=0 noet syntax=cpp : diff --git a/src/chars/char_player.hpp b/src/chars/char_player.hpp index e8c9d9c9f..7fb091f2c 100644 --- a/src/chars/char_player.hpp +++ b/src/chars/char_player.hpp @@ -6,25 +6,30 @@ #define CHAR_PLAYER_HPP_INCLUDED #include "conf.h" -#include "sysdep.h" -#include "structs.h" -#include "quested.hpp" -#include "mobmax.hpp" -#include "remember.hpp" +#include "boards.types.hpp" #include "char.hpp" +#include "cmd/cmd.generic.h" #include "dps.hpp" +#include "ext_money.hpp" #include "map.hpp" -#include "reset_stats.hpp" -#include "boards.types.hpp" +#include "mobmax.hpp" #include "quest.hpp" +#include "quested.hpp" +#include "remember.hpp" +#include "reset_stats.hpp" #include "stigmas.hpp" -#include "cmd/cmd.generic.h" +#include "structs.h" +#include "sysdep.h" #include #include #include #include +// * Перерасчет максимальных родных хп персонажа. +// * При входе в игру, левеле/делевеле, добавлении/удалении славы. +void check_max_hp(CHAR_DATA *ch); + // кол-во сохраняемых стартовых статов в файле const int START_STATS_TOTAL = 6; diff --git a/src/class.cpp b/src/class.cpp index cd73811e2..8418957bd 100644 --- a/src/class.cpp +++ b/src/class.cpp @@ -21,38 +21,38 @@ #include "class.hpp" #include "world.objects.hpp" -#include "obj.hpp" +#include "chars/char.hpp" +#include "chars/char_player.hpp" +#include "chars/player_races.hpp" #include "comm.h" -#include "db.h" -#include "spell_parser.hpp" -#include "spells.h" -#include "skills.h" -#include "interpreter.h" -#include "handler.h" +#include "core/leveling.h" +#include "conf.h" #include "constants.h" -#include "fightsystem/pk.h" -#include "top.h" +#include "db.h" +#include "exchange.h" #include "features.hpp" +#include "fightsystem/pk.h" +#include "handler.h" #include "im.h" -#include "chars/char.hpp" -#include "spam.hpp" -#include "screen.h" -#include "chars/char_player.hpp" +#include "interpreter.h" +#include "logger.hpp" #include "named_stuff.hpp" -#include "chars/player_races.hpp" #include "noob.hpp" -#include "exchange.h" -#include "logger.hpp" -#include "utils.h" +#include "obj.hpp" +#include "screen.h" +#include "skills.h" +#include "spam.hpp" +#include "spell_parser.hpp" +#include "spells.h" #include "structs.h" #include "sysdep.h" -#include "conf.h" +#include "top.h" +#include "utils.h" #include extern int siteok_everyone; extern struct spell_create_type spell_create[]; -extern double exp_coefficients[]; struct skillvariables_dig dig_vars; struct skillvariables_insgem insgem_vars; @@ -65,7 +65,7 @@ int thaco(int class_num, int level); void do_start(CHAR_DATA * ch, int newbie); int invalid_anti_class(CHAR_DATA * ch, const OBJ_DATA * obj); int invalid_no_class(CHAR_DATA * ch, const OBJ_DATA * obj); -int level_exp(CHAR_DATA * ch, int level); + byte extend_saving_throws(int class_num, int type, int level); int invalid_unique(CHAR_DATA * ch, const OBJ_DATA * obj); extern bool char_to_pk_clan(CHAR_DATA *ch); @@ -2069,7 +2069,7 @@ void do_start(CHAR_DATA * ch, int newbie) } } - advance_level(ch); + ExpCalc::advance_level(ch); sprintf(buf, "%s advanced to level %d", GET_NAME(ch), GET_LEVEL(ch)); mudlog(buf, BRF, LVL_IMPL, SYSLOG, TRUE); @@ -2087,138 +2087,6 @@ void do_start(CHAR_DATA * ch, int newbie) } } -// * Перерасчет максимальных родных хп персонажа. -// * При входе в игру, левеле/делевеле, добавлении/удалении славы. -void check_max_hp(CHAR_DATA *ch) -{ - GET_MAX_HIT(ch) = PlayerSystem::con_natural_hp(ch); -} - -// * Обработка событий при левел-апе. -void levelup_events(CHAR_DATA *ch) -{ - if (SpamSystem::MIN_OFFTOP_LVL == GET_LEVEL(ch) - && !ch->get_disposable_flag(DIS_OFFTOP_MESSAGE)) - { - PRF_FLAGS(ch).set(PRF_OFFTOP_MODE); - ch->set_disposable_flag(DIS_OFFTOP_MESSAGE); - send_to_char(ch, - "%sТеперь вы можете пользоваться каналом оффтоп ('справка оффтоп').%s\r\n", - CCIGRN(ch, C_SPR), CCNRM(ch, C_SPR)); - } - if (EXCHANGE_MIN_CHAR_LEV == GET_LEVEL(ch) - && !ch->get_disposable_flag(DIS_EXCHANGE_MESSAGE)) - { - // по умолчанию базар у всех включен, поэтому не спамим даже однократно - if (GET_REMORT(ch) <= 0) - { - send_to_char(ch, - "%sТеперь вы можете покупать и продавать вещи на базаре ('справка базар!').%s\r\n", - CCIGRN(ch, C_SPR), CCNRM(ch, C_SPR)); - } - ch->set_disposable_flag(DIS_EXCHANGE_MESSAGE); - } -} - -void advance_level(CHAR_DATA * ch) -{ - int add_move = 0, i; - - switch (GET_CLASS(ch)) - { - case CLASS_BATTLEMAGE: - case CLASS_DEFENDERMAGE: - case CLASS_CHARMMAGE: - case CLASS_NECROMANCER: - add_move = 2; - break; - - case CLASS_CLERIC: - case CLASS_DRUID: - add_move = 2; - break; - - case CLASS_THIEF: - case CLASS_ASSASINE: - case CLASS_MERCHANT: - add_move = number(ch->get_inborn_dex() / 6 + 1, ch->get_inborn_dex() / 5 + 1); - break; - - case CLASS_WARRIOR: - add_move = number(ch->get_inborn_dex() / 6 + 1, ch->get_inborn_dex() / 5 + 1); - break; - - case CLASS_GUARD: - case CLASS_RANGER: - case CLASS_PALADINE: - case CLASS_SMITH: - add_move = number(ch->get_inborn_dex() / 6 + 1, ch->get_inborn_dex() / 5 + 1); - break; - } - - check_max_hp(ch); - ch->points.max_move += MAX(1, add_move); - - setAllInbornFeatures(ch); - - if (IS_IMMORTAL(ch)) - { - for (i = 0; i < 3; i++) - GET_COND(ch, i) = (char) - 1; - PRF_FLAGS(ch).set(PRF_HOLYLIGHT); - } - - TopPlayer::Refresh(ch); - levelup_events(ch); - ch->save_char(); -} - -void decrease_level(CHAR_DATA * ch) -{ - int add_move = 0; - - switch (GET_CLASS(ch)) - { - case CLASS_BATTLEMAGE: - case CLASS_DEFENDERMAGE: - case CLASS_CHARMMAGE: - case CLASS_NECROMANCER: - add_move = 2; - break; - - case CLASS_CLERIC: - case CLASS_DRUID: - add_move = 2; - break; - - case CLASS_THIEF: - case CLASS_ASSASINE: - case CLASS_MERCHANT: - add_move = ch->get_inborn_dex() / 5 + 1; - break; - - case CLASS_WARRIOR: - case CLASS_GUARD: - case CLASS_PALADINE: - case CLASS_RANGER: - case CLASS_SMITH: - add_move = ch->get_inborn_dex() / 5 + 1; - break; - } - - check_max_hp(ch); - ch->points.max_move -= MIN(ch->points.max_move, MAX(1, add_move)); - - GET_WIMP_LEV(ch) = MAX(0, MIN(GET_WIMP_LEV(ch), GET_REAL_MAX_HIT(ch) / 2)); - if (!IS_IMMORTAL(ch)) - { - PRF_FLAGS(ch).unset(PRF_HOLYLIGHT); - } - - TopPlayer::Refresh(ch); - ch->save_char(); -} - /* * invalid_class is used by handler.cpp to determine if a piece of equipment is * usable by a particular class, based on the ITEM_ANTI_{class} bitvectors. @@ -2969,505 +2837,4 @@ void init_basic_values(void) fclose(magic); return; } - -/* - Берет misc/grouping, первый столбик цифр считает номерами мортов, - остальные столбики - значение макс. разрыва в уровнях для конкретного - класса. На момент написания этого в конфиге присутствует 26 строк, макс. - морт равен 50 - строки с мортами с 26 по 50 копируются с 25-мортовой строки. -*/ -int GroupPenalties::init(void) -{ - char buf[MAX_INPUT_LENGTH]; - int clss = 0, remorts = 0, rows_assigned = 0, levels = 0, pos = 0, max_rows = MAX_REMORT+1; - - // пре-инициализация - for (remorts = 0; remorts < max_rows; remorts++) //Строк в массиве должно быть на 1 больше, чем макс. морт - { - for (clss = 0; clss < NUM_PLAYER_CLASSES; clss++) //Столбцов в массиве должно быть ровно столько же, сколько есть классов - { - m_grouping[clss][remorts] = -1; - } - } - - FILE* f = fopen(LIB_MISC "grouping", "r"); - if (!f) - { - log("Невозможно открыть файл %s", LIB_MISC "grouping"); - return 1; - } - - while (get_line(f, buf)) - { - if (!buf[0] || buf[0] == ';' || buf[0] == '\n') //Строка пустая или строка-коммент - { - continue; - } - clss = 0; - pos = 0; - while (sscanf(&buf[pos], "%d", &levels) == 1) - { - while (buf[pos] == ' ' || buf[pos] == '\t') - { - pos++; - } - if (clss == 0) //Первый проход цикла по строке - { - remorts = levels; //Номера строк - if (m_grouping[0][remorts] != -1) - { - log("Ошибка при чтении файла %s: дублирование параметров для %d ремортов", - LIB_MISC "grouping", remorts); - return 2; - } - if (remorts > MAX_REMORT || remorts < 0) - { - log("Ошибка при чтении файла %s: неверное значение количества ремортов: %d, " - "должно быть в промежутке от 0 до %d", - LIB_MISC "grouping", remorts, MAX_REMORT); - return 3; - } - } - else - { - m_grouping[clss - 1][remorts] = levels; // -1 потому что в массиве нет столбца с кол-вом мортов - } - clss++; //+Номер столбца массива - while (buf[pos] != ' ' && buf[pos] != '\t' && buf[pos] != 0) - { - pos++; //Ищем следующее число в строке конфига - } - } - if (clss != NUM_PLAYER_CLASSES+1) - { - log("Ошибка при чтении файла %s: неверный формат строки '%s', должно быть %d " - "целых чисел, прочитали %d", LIB_MISC "grouping", buf, NUM_PLAYER_CLASSES+1, clss); - return 4; - } - rows_assigned++; - } - - if (rows_assigned < max_rows) - { - for (levels = remorts; levels < max_rows; levels++) //Берем свободную переменную - { - for (clss = 0; clss < NUM_PLAYER_CLASSES; clss++) - { - m_grouping[clss][levels] = m_grouping[clss][remorts]; //Копируем последнюю строку на все морты, для которых нет строк - } - } - } - fclose(f); - return 0; -} - -/* - * This is the exp given to implementors -- it must always be greater - * than the exp required for immortality, plus at least 20,000 or so. - */ - -// Function to return the exp required for each class/level -int level_exp(CHAR_DATA * ch, int level) -{ - if (level > LVL_IMPL || level < 0) - { - log("SYSERR: Requesting exp for invalid level %d!", level); - return 0; - } - - /* - * Gods have exp close to EXP_MAX. This statement should never have to - * changed, regardless of how many mortal or immortal levels exist. - */ - if (level > LVL_IMMORT) - { - return EXP_IMPL - ((LVL_IMPL - level) * 1000); - } - - // Exp required for normal mortals is below - float exp_modifier; - if (GET_REMORT(ch) < MAX_EXP_COEFFICIENTS_USED) - exp_modifier = exp_coefficients[GET_REMORT(ch)]; - else - exp_modifier = exp_coefficients[MAX_EXP_COEFFICIENTS_USED]; - - switch (GET_CLASS(ch)) - { - - case CLASS_BATTLEMAGE: - case CLASS_DEFENDERMAGE: - case CLASS_CHARMMAGE: - case CLASS_NECROMANCER: - switch (level) - { - case 0: - return 0; - case 1: - return 1; - case 2: - return int (exp_modifier * 2500); - case 3: - return int (exp_modifier * 5000); - case 4: - return int (exp_modifier * 9000); - case 5: - return int (exp_modifier * 17000); - case 6: - return int (exp_modifier * 27000); - case 7: - return int (exp_modifier * 47000); - case 8: - return int (exp_modifier * 77000); - case 9: - return int (exp_modifier * 127000); - case 10: - return int (exp_modifier * 197000); - case 11: - return int (exp_modifier * 297000); - case 12: - return int (exp_modifier * 427000); - case 13: - return int (exp_modifier * 587000); - case 14: - return int (exp_modifier * 817000); - case 15: - return int (exp_modifier * 1107000); - case 16: - return int (exp_modifier * 1447000); - case 17: - return int (exp_modifier * 1847000); - case 18: - return int (exp_modifier * 2310000); - case 19: - return int (exp_modifier * 2830000); - case 20: - return int (exp_modifier * 3580000); - case 21: - return int (exp_modifier * 4580000); - case 22: - return int (exp_modifier * 5830000); - case 23: - return int (exp_modifier * 7330000); - case 24: - return int (exp_modifier * 9080000); - case 25: - return int (exp_modifier * 11080000); - case 26: - return int (exp_modifier * 15000000); - case 27: - return int (exp_modifier * 22000000); - case 28: - return int (exp_modifier * 33000000); - case 29: - return int (exp_modifier * 47000000); - case 30: - return int (exp_modifier * 64000000); - // add new levels here - case LVL_IMMORT: - return int (exp_modifier * 94000000); - } - break; - - case CLASS_CLERIC: - case CLASS_DRUID: - switch (level) - { - case 0: - return 0; - case 1: - return 1; - case 2: - return int (exp_modifier * 2500); - case 3: - return int (exp_modifier * 5000); - case 4: - return int (exp_modifier * 9000); - case 5: - return int (exp_modifier * 17000); - case 6: - return int (exp_modifier * 27000); - case 7: - return int (exp_modifier * 47000); - case 8: - return int (exp_modifier * 77000); - case 9: - return int (exp_modifier * 127000); - case 10: - return int (exp_modifier * 197000); - case 11: - return int (exp_modifier * 297000); - case 12: - return int (exp_modifier * 427000); - case 13: - return int (exp_modifier * 587000); - case 14: - return int (exp_modifier * 817000); - case 15: - return int (exp_modifier * 1107000); - case 16: - return int (exp_modifier * 1447000); - case 17: - return int (exp_modifier * 1847000); - case 18: - return int (exp_modifier * 2310000); - case 19: - return int (exp_modifier * 2830000); - case 20: - return int (exp_modifier * 3580000); - case 21: - return int (exp_modifier * 4580000); - case 22: - return int (exp_modifier * 5830000); - case 23: - return int (exp_modifier * 7330000); - case 24: - return int (exp_modifier * 9080000); - case 25: - return int (exp_modifier * 11080000); - case 26: - return int (exp_modifier * 15000000); - case 27: - return int (exp_modifier * 22000000); - case 28: - return int (exp_modifier * 33000000); - case 29: - return int (exp_modifier * 47000000); - case 30: - return int (exp_modifier * 64000000); - // add new levels here - case LVL_IMMORT: - return int (exp_modifier * 87000000); - } - break; - - case CLASS_THIEF: - switch (level) - { - case 0: - return 0; - case 1: - return 1; - case 2: - return int (exp_modifier * 1000); - case 3: - return int (exp_modifier * 2000); - case 4: - return int (exp_modifier * 4000); - case 5: - return int (exp_modifier * 8000); - case 6: - return int (exp_modifier * 15000); - case 7: - return int (exp_modifier * 28000); - case 8: - return int (exp_modifier * 52000); - case 9: - return int (exp_modifier * 85000); - case 10: - return int (exp_modifier * 131000); - case 11: - return int (exp_modifier * 192000); - case 12: - return int (exp_modifier * 271000); - case 13: - return int (exp_modifier * 372000); - case 14: - return int (exp_modifier * 512000); - case 15: - return int (exp_modifier * 672000); - case 16: - return int (exp_modifier * 854000); - case 17: - return int (exp_modifier * 1047000); - case 18: - return int (exp_modifier * 1274000); - case 19: - return int (exp_modifier * 1534000); - case 20: - return int (exp_modifier * 1860000); - case 21: - return int (exp_modifier * 2520000); - case 22: - return int (exp_modifier * 3380000); - case 23: - return int (exp_modifier * 4374000); - case 24: - return int (exp_modifier * 5500000); - case 25: - return int (exp_modifier * 6827000); - case 26: - return int (exp_modifier * 8667000); - case 27: - return int (exp_modifier * 13334000); - case 28: - return int (exp_modifier * 20000000); - case 29: - return int (exp_modifier * 28667000); - case 30: - return int (exp_modifier * 40000000); - // add new levels here - case LVL_IMMORT: - return int (exp_modifier * 53000000); - } - break; - - case CLASS_ASSASINE: - case CLASS_MERCHANT: - switch (level) - { - case 0: - return 0; - case 1: - return 1; - case 2: - return int (exp_modifier * 1500); - case 3: - return int (exp_modifier * 3000); - case 4: - return int (exp_modifier * 6000); - case 5: - return int (exp_modifier * 12000); - case 6: - return int (exp_modifier * 22000); - case 7: - return int (exp_modifier * 42000); - case 8: - return int (exp_modifier * 77000); - case 9: - return int (exp_modifier * 127000); - case 10: - return int (exp_modifier * 197000); - case 11: - return int (exp_modifier * 287000); - case 12: - return int (exp_modifier * 407000); - case 13: - return int (exp_modifier * 557000); - case 14: - return int (exp_modifier * 767000); - case 15: - return int (exp_modifier * 1007000); - case 16: - return int (exp_modifier * 1280000); - case 17: - return int (exp_modifier * 1570000); - case 18: - return int (exp_modifier * 1910000); - case 19: - return int (exp_modifier * 2300000); - case 20: - return int (exp_modifier * 2790000); - case 21: - return int (exp_modifier * 3780000); - case 22: - return int (exp_modifier * 5070000); - case 23: - return int (exp_modifier * 6560000); - case 24: - return int (exp_modifier * 8250000); - case 25: - return int (exp_modifier * 10240000); - case 26: - return int (exp_modifier * 13000000); - case 27: - return int (exp_modifier * 20000000); - case 28: - return int (exp_modifier * 30000000); - case 29: - return int (exp_modifier * 43000000); - case 30: - return int (exp_modifier * 59000000); - // add new levels here - case LVL_IMMORT: - return int (exp_modifier * 79000000); - } - break; - - case CLASS_WARRIOR: - case CLASS_GUARD: - case CLASS_PALADINE: - case CLASS_RANGER: - case CLASS_SMITH: - switch (level) - { - case 0: - return 0; - case 1: - return 1; - case 2: - return int (exp_modifier * 2000); - case 3: - return int (exp_modifier * 4000); - case 4: - return int (exp_modifier * 8000); - case 5: - return int (exp_modifier * 14000); - case 6: - return int (exp_modifier * 24000); - case 7: - return int (exp_modifier * 39000); - case 8: - return int (exp_modifier * 69000); - case 9: - return int (exp_modifier * 119000); - case 10: - return int (exp_modifier * 189000); - case 11: - return int (exp_modifier * 289000); - case 12: - return int (exp_modifier * 419000); - case 13: - return int (exp_modifier * 579000); - case 14: - return int (exp_modifier * 800000); - case 15: - return int (exp_modifier * 1070000); - case 16: - return int (exp_modifier * 1340000); - case 17: - return int (exp_modifier * 1660000); - case 18: - return int (exp_modifier * 2030000); - case 19: - return int (exp_modifier * 2450000); - case 20: - return int (exp_modifier * 2950000); - case 21: - return int (exp_modifier * 3950000); - case 22: - return int (exp_modifier * 5250000); - case 23: - return int (exp_modifier * 6750000); - case 24: - return int (exp_modifier * 8450000); - case 25: - return int (exp_modifier * 10350000); - case 26: - return int (exp_modifier * 14000000); - case 27: - return int (exp_modifier * 21000000); - case 28: - return int (exp_modifier * 31000000); - case 29: - return int (exp_modifier * 44000000); - case 30: - return int (exp_modifier * 64000000); - // add new levels here - case LVL_IMMORT: - return int (exp_modifier * 79000000); - } - break; - } - - /* - * This statement should never be reached if the exp tables in this function - * are set up properly. If you see exp of 123456 then the tables above are - * incomplete -- so, complete them! - */ - log("SYSERR: XP tables not set up correctly in class.c!"); - return 123456; -} - -GroupPenalties grouping; ///< TODO: get rid of this global variable. - // vim: ts=4 sw=4 tw=0 noet syntax=cpp : diff --git a/src/class.hpp b/src/class.hpp index d1839d212..1ec9f8e97 100644 --- a/src/class.hpp +++ b/src/class.hpp @@ -8,20 +8,6 @@ int invalid_no_class(CHAR_DATA * ch, const OBJ_DATA * obj); int extra_damroll(int class_num, int level); -class GroupPenalties -{ -public: - using class_penalties_t = std::array; - using penalties_t = std::array; - - int init(); - const auto& operator[](const size_t character_class) const { return m_grouping[character_class]; } - -private: - penalties_t m_grouping; -}; - -extern GroupPenalties grouping; #endif // __CLASS_HPP__ diff --git a/src/cmd.imm/act.wizard.cpp b/src/cmd.imm/act.wizard.cpp index 63a23ec77..02df535ab 100644 --- a/src/cmd.imm/act.wizard.cpp +++ b/src/cmd.imm/act.wizard.cpp @@ -25,6 +25,7 @@ #include "chars/world.characters.hpp" #include "comm.h" #include "command.shutdown.hpp" +#include "core/leveling.h" #include "conf.h" #include "config.hpp" #include "constants.h" @@ -32,7 +33,7 @@ #include "db.h" #include "depot.hpp" #include "description.h" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "ext_money.hpp" #include "fightsystem/fight.h" #include "fightsystem/pk.h" @@ -87,6 +88,7 @@ using std::ifstream; using std::fstream; +using namespace ExpCalc; // external vars extern bool need_warn; @@ -115,7 +117,7 @@ extern bool CompareBits(const FLAG_DATA& flags, const char *names[], int affect) void do_recall(CHAR_DATA *ch, char *argument, int cmd, int subcmd); void save_zone_count_reset(); // extern functions -int level_exp(CHAR_DATA * ch, int level); + void appear(CHAR_DATA * ch); void reset_zone(zone_rnum zone); int parse_class(char arg); @@ -4129,7 +4131,7 @@ void do_advance(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) imm_log("%s has advanced %s to level %d (from %d)", GET_NAME(ch), GET_NAME(victim), newlevel, oldlevel); } - gain_exp_regardless(victim, level_exp(victim, newlevel) + ExpCalc::gain_exp_regardless(victim, ExpCalc::level_exp(victim, newlevel) - GET_EXP(victim)); victim->save_char(); } @@ -5646,7 +5648,7 @@ void do_show(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) } break; case 25: // remort - Remort::show_config(ch); + ExtMoney::show_config(ch); break; default: send_to_char("Извините, неверная команда.\r\n", ch); diff --git a/src/cmd/whoami.cpp b/src/cmd/whoami.cpp index 3c009fe59..a6f5fa506 100644 --- a/src/cmd/whoami.cpp +++ b/src/cmd/whoami.cpp @@ -42,5 +42,5 @@ void do_whoami(CHAR_DATA *ch, char *, int, int) { send_to_char(ch, "Подключен Телеграм, chat_id: %lu\r\n", ch->player_specials->saved.telegram_id); } if (ch->personGroup) - send_to_char(ch, "Состоит в группе #%d лидера %s\r\n", ch->personGroup->getUid(), ch->personGroup->getLeaderName().c_str()); + send_to_char(ch, "Состоит в группе #%lu лидера %s\r\n", ch->personGroup->getUid(), ch->personGroup->getLeaderName().c_str()); } diff --git a/src/comm.cpp b/src/comm.cpp index 6275394f4..e10a25b70 100644 --- a/src/comm.cpp +++ b/src/comm.cpp @@ -32,37 +32,39 @@ #include "comm.h" #include "global.objects.hpp" -#include "magic.h" -#include "world.objects.hpp" -#include "chars/world.characters.hpp" -#include "shutdown.parameters.hpp" -#include "object.prototypes.hpp" -#include "external.trigger.hpp" -#include "handler.h" -#include "house.h" -#include "olc.h" -#include "screen.h" #include "ban.hpp" -#include "exchange.h" -#include "title.hpp" +#include "char_obj_utils.inl" +#include "chars/world.characters.hpp" +#include "core/leveling.h" +#include "corpse.hpp" +#include "db.h" #include "depot.hpp" -#include "glory.hpp" +#include "exchange.h" +#include "external.trigger.hpp" #include "file_crc.hpp" -#include "corpse.hpp" -#include "glory_misc.hpp" +#include "glory.hpp" #include "glory_const.hpp" -#include "shop_ext.hpp" -#include "sets_drop.hpp" +#include "glory_misc.hpp" +#include "handler.h" +#include "heartbeat.hpp" +#include "house.h" +#include "logger.hpp" +#include "magic.h" #include "mail.h" #include "mob_stat.hpp" -#include "char_obj_utils.inl" -#include "logger.hpp" -#include "msdp.hpp" #include "msdp.constants.hpp" -#include "heartbeat.hpp" -#include "zone.table.hpp" -#include "db.h" +#include "msdp.hpp" +#include "object.prototypes.hpp" +#include "olc.h" +#include "screen.h" +#include "sets_drop.hpp" +#include "shop_ext.hpp" +#include "shutdown.parameters.hpp" +#include "title.hpp" #include "utils.h" +#include "world.objects.hpp" +#include "zone.table.hpp" + #if defined WITH_SCRIPTING #include "scripting.hpp" @@ -592,7 +594,7 @@ void zedit_save_to_disk(int zone_num); int real_zone(int number); void Crash_ldsave(CHAR_DATA * ch); void Crash_save_all_rent(); -int level_exp(CHAR_DATA * ch, int level); + unsigned long TxtToIp(const char * text); #ifdef __CXREF__ @@ -1842,7 +1844,7 @@ char *make_prompt(DESCRIPTOR_DATA * d) count += sprintf(prompt + count, "??? "); else count += sprintf(prompt + count, "%ldо ", - level_exp(d->character.get(), + ExpCalc::level_exp(d->character.get(), GET_LEVEL(d->character) + 1) - GET_EXP(d->character)); } // Mem Info diff --git a/src/config.cpp b/src/config.cpp index 22dc4e45e..6e21f8b5b 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -57,8 +57,6 @@ * */ -int level_exp(CHAR_DATA * ch, int level); - // GAME PLAY OPTIONS // exp change limits @@ -291,23 +289,6 @@ const char *START_MESSG = " Твоя задача непроста, но надеемся, что ты сумеешь достойно решить ее.\r\n" " В добрый час, путник, и да будет скатертью тебе дорога...\r\n" "\r\n"; -int max_exp_gain_pc(CHAR_DATA * ch) -{ - int result = 1; - if (!IS_NPC(ch)) - { - int max_per_lev = - level_exp(ch, GET_LEVEL(ch) + 1) - level_exp(ch, GET_LEVEL(ch) + 0); - result = max_per_lev / (10 + GET_REMORT(ch)); - } - return result; -} - -int max_exp_loss_pc(CHAR_DATA * ch) -{ - return (IS_NPC(ch) ? 1 : (level_exp(ch, GET_LEVEL(ch) + 1) - level_exp(ch, GET_LEVEL(ch) + 0)) / 3); -} - int calc_loadroom(const CHAR_DATA* ch, int bplace_mode /*= BIRTH_PLACE_UNDEFINED*/) { if (IS_IMMORTAL(ch)) diff --git a/src/corpse.cpp b/src/corpse.cpp index 22600e72e..b15c56cb5 100644 --- a/src/corpse.cpp +++ b/src/corpse.cpp @@ -12,7 +12,7 @@ #include "chars/char.hpp" #include "comm.h" #include "handler.h" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "im.h" #include "room.hpp" #include "pugixml.hpp" diff --git a/src/db.cpp b/src/db.cpp index 5ef161ee5..ddcacca63 100644 --- a/src/db.cpp +++ b/src/db.cpp @@ -39,8 +39,8 @@ #include "corpse.hpp" #include "deathtrap.hpp" #include "depot.hpp" -#include "dg_db_scripts.hpp" -#include "dg_scripts.h" +#include "dg/dg_db_scripts.hpp" +#include "dg/dg_scripts.h" #include "ext_money.hpp" #include "fightsystem/fight.h" #include "file_crc.hpp" @@ -52,6 +52,7 @@ #include "help.hpp" #include "house.h" #include "item.creation.hpp" +#include "core/leveling.h" #include "liquid.hpp" #include "logger.hpp" #include "mail.h" @@ -230,7 +231,7 @@ void calc_easter(void); void do_start(CHAR_DATA * ch, int newbie); extern void repop_decay(zone_rnum zone); // рассыпание обьектов ITEM_REPOP_DECAY int real_zone(int number); -int level_exp(CHAR_DATA * ch, int level); + extern char *fread_action(FILE * fl, int nr); void load_mobraces(); @@ -5409,7 +5410,7 @@ long cmp_ptable_by_name(char *name, int len) long get_ptable_by_name(const char *name) { - one_argument(name, arg); + one_argument(name, smallBuf); /* Anton Gorev (2015/12/29): see (MAPHELPER) comment. */ for (std::size_t i = 0; i < player_table.size(); i++) { @@ -5419,7 +5420,7 @@ long get_ptable_by_name(const char *name) return static_cast(i); } } - sprintf(buf, "Char %s(%s) not found !!!", name, arg); + sprintf(buf, "Char %s(%s) not found !!!", name, smallBuf); mudlog(buf, LGH, LVL_IMMORT, SYSLOG, FALSE); return (-1); } @@ -5687,7 +5688,7 @@ void do_remort(CHAR_DATA *ch, char *argument, int/* cmd*/, int subcmd) send_to_char("Вам это, похоже, совсем ни к чему.\r\n", ch); return; } - if (GET_EXP(ch) < level_exp(ch, LVL_IMMORT) - 1) + if (GET_EXP(ch) < ExpCalc::level_exp(ch, LVL_IMMORT) - 1) { send_to_char("ЧАВО???\r\n", ch); return; diff --git a/src/dg_comm.cpp b/src/dg/dg_comm.cpp similarity index 100% rename from src/dg_comm.cpp rename to src/dg/dg_comm.cpp diff --git a/src/dg_db_scripts.cpp b/src/dg/dg_db_scripts.cpp similarity index 100% rename from src/dg_db_scripts.cpp rename to src/dg/dg_db_scripts.cpp diff --git a/src/dg_db_scripts.hpp b/src/dg/dg_db_scripts.hpp similarity index 100% rename from src/dg_db_scripts.hpp rename to src/dg/dg_db_scripts.hpp diff --git a/src/dg_event.cpp b/src/dg/dg_event.cpp similarity index 100% rename from src/dg_event.cpp rename to src/dg/dg_event.cpp diff --git a/src/dg_event.h b/src/dg/dg_event.h similarity index 100% rename from src/dg_event.h rename to src/dg/dg_event.h diff --git a/src/dg_handler.cpp b/src/dg/dg_handler.cpp similarity index 100% rename from src/dg_handler.cpp rename to src/dg/dg_handler.cpp diff --git a/src/dg_misc.cpp b/src/dg/dg_misc.cpp similarity index 100% rename from src/dg_misc.cpp rename to src/dg/dg_misc.cpp diff --git a/src/dg_mobcmd.cpp b/src/dg/dg_mobcmd.cpp similarity index 99% rename from src/dg_mobcmd.cpp rename to src/dg/dg_mobcmd.cpp index e90f4defe..d7742f476 100644 --- a/src/dg_mobcmd.cpp +++ b/src/dg/dg_mobcmd.cpp @@ -23,27 +23,29 @@ * $Revision$ * ***************************************************************************/ -#include "world.objects.hpp" -#include "object.prototypes.hpp" -#include "dg_scripts.h" -#include "obj.hpp" -#include "db.h" -#include "handler.h" -#include "interpreter.h" +#include "chars/char.hpp" #include "comm.h" -#include "spell_parser.hpp" -#include "spells.h" -#include "im.h" +#include "core/leveling.h" +#include "conf.h" +#include "db.h" +#include "dg_scripts.h" #include "features.hpp" -#include "chars/char.hpp" -#include "skills.h" -#include "room.hpp" #include "fightsystem/fight.h" #include "fightsystem/fight_hit.hpp" +#include "handler.h" +#include "im.h" +#include "interpreter.h" #include "logger.hpp" +#include "obj.hpp" +#include "object.prototypes.hpp" +#include "room.hpp" +#include "skills.h" +#include "spell_parser.hpp" +#include "spells.h" #include "structs.h" #include "sysdep.h" -#include "conf.h" +#include "world.objects.hpp" + struct mob_command_info { @@ -811,7 +813,7 @@ void do_mexp(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) } sprintf(buf, "mexp: victim (%s) получил опыт %d", name, atoi(amount)); mob_log(ch, buf); - gain_exp(victim, atoi(amount)); + ExpCalc::gain_exp(victim, atoi(amount)); } // increases the target's gold diff --git a/src/dg_objcmd.cpp b/src/dg/dg_objcmd.cpp similarity index 99% rename from src/dg_objcmd.cpp rename to src/dg/dg_objcmd.cpp index f0b1238f9..7b5939465 100644 --- a/src/dg_objcmd.cpp +++ b/src/dg/dg_objcmd.cpp @@ -9,30 +9,32 @@ * $Revision$ * **************************************************************************/ -#include "world.objects.hpp" -#include "object.prototypes.hpp" -#include "obj.hpp" -#include "screen.h" -#include "dg_scripts.h" + +#include "chars/char.hpp" #include "comm.h" -#include "interpreter.h" -#include "handler.h" +#include "core/leveling.h" +#include "conf.h" #include "db.h" -#include "spell_parser.hpp" -#include "spells.h" +#include "dg_scripts.h" +#include "features.hpp" +#include "fightsystem/fight.h" +#include "handler.h" #include "im.h" -#include "chars/char.hpp" -#include "skills.h" +#include "interpreter.h" +#include "logger.hpp" +#include "magic.h" #include "name_list.hpp" +#include "obj.hpp" +#include "object.prototypes.hpp" #include "room.hpp" -#include "magic.h" -#include "fightsystem/fight.h" -#include "features.hpp" -#include "logger.hpp" -#include "utils.h" +#include "screen.h" +#include "skills.h" +#include "spell_parser.hpp" +#include "spells.h" #include "structs.h" #include "sysdep.h" -#include "conf.h" +#include "utils.h" +#include "world.objects.hpp" extern const char *dirs[]; extern int up_obj_where(OBJ_DATA * obj); @@ -290,7 +292,7 @@ void do_oexp(OBJ_DATA *obj, char *argument, int/* cmd*/, int/* subcmd*/) if ((ch = get_char_by_obj(obj, name))) { - gain_exp(ch, atoi(amount)); + ExpCalc::gain_exp(ch, atoi(amount)); sprintf(buf, "oexp: victim (%s) получил опыт %d", GET_NAME(ch) , atoi(amount)); obj_log(obj, buf); } diff --git a/src/dg_olc.cpp b/src/dg/dg_olc.cpp similarity index 100% rename from src/dg_olc.cpp rename to src/dg/dg_olc.cpp diff --git a/src/dg_olc.h b/src/dg/dg_olc.h similarity index 100% rename from src/dg_olc.h rename to src/dg/dg_olc.h diff --git a/src/dg_scripts.cpp b/src/dg/dg_scripts.cpp similarity index 99% rename from src/dg_scripts.cpp rename to src/dg/dg_scripts.cpp index 78780be42..2561afce8 100644 --- a/src/dg_scripts.cpp +++ b/src/dg/dg_scripts.cpp @@ -16,25 +16,20 @@ #include "chars/char_player.hpp" #include "chars/world.characters.hpp" #include "comm.h" -#include "conf.h" +#include "core/leveling.h" #include "constants.h" -#include "coredump.hpp" #include "db.h" #include "debug.utils.hpp" #include "dg_db_scripts.hpp" #include "dg_event.h" #include "features.hpp" #include "find.obj.id.by.vnum.hpp" -#include "genchar.h" #include "global.objects.hpp" -#include "grp/grp.main.h" #include "handler.h" -#include "heartbeat.hpp" #include "house.h" #include "interpreter.h" #include "logger.hpp" #include "modify.h" -#include "name_list.hpp" #include "named_stuff.hpp" #include "noob.hpp" #include "obj.hpp" @@ -48,11 +43,12 @@ #include "spells.h" #include "structs.h" #include "sysdep.h" -#include "top.h" #include "utils.h" #include "world.objects.hpp" #include "zone.table.hpp" +using namespace ExpCalc; + #define PULSES_PER_MUD_HOUR (SECS_PER_MUD_HOUR*PASSES_PER_SEC) #define IS_CHARMED(ch) (IS_HORSE(ch)||AFF_FLAGGED(ch, EAffectFlag::AFF_CHARM)) diff --git a/src/dg_scripts.h b/src/dg/dg_scripts.h similarity index 100% rename from src/dg_scripts.h rename to src/dg/dg_scripts.h diff --git a/src/dg_triggers.cpp b/src/dg/dg_triggers.cpp similarity index 100% rename from src/dg_triggers.cpp rename to src/dg/dg_triggers.cpp diff --git a/src/dg_wldcmd.cpp b/src/dg/dg_wldcmd.cpp similarity index 99% rename from src/dg_wldcmd.cpp rename to src/dg/dg_wldcmd.cpp index d190a42d7..748bd3efe 100644 --- a/src/dg_wldcmd.cpp +++ b/src/dg/dg_wldcmd.cpp @@ -9,31 +9,33 @@ * $Revision$ * **************************************************************************/ -#include "world.objects.hpp" -#include "object.prototypes.hpp" -#include "obj.hpp" -#include "skills/townportal.h" -#include "dg_scripts.h" +#include "chars/char.hpp" #include "comm.h" -#include "interpreter.h" -#include "handler.h" -#include "spell_parser.hpp" -#include "spells.h" +#include "core/leveling.h" +#include "conf.h" #include "db.h" -#include "im.h" #include "deathtrap.hpp" -#include "chars/char.hpp" -#include "skills.h" -#include "room.hpp" -#include "magic.h" -#include "fightsystem/fight.h" +#include "dg_scripts.h" #include "features.hpp" -#include "zone.table.hpp" +#include "fightsystem/fight.h" +#include "handler.h" +#include "im.h" +#include "interpreter.h" #include "logger.hpp" -#include "utils.h" +#include "magic.h" +#include "obj.hpp" +#include "object.prototypes.hpp" +#include "room.hpp" +#include "skills.h" +#include "skills/townportal.h" +#include "spell_parser.hpp" +#include "spells.h" #include "structs.h" #include "sysdep.h" -#include "conf.h" +#include "utils.h" +#include "world.objects.hpp" +#include "zone.table.hpp" + extern const char *dirs[]; @@ -501,7 +503,7 @@ void do_wexp(ROOM_DATA *room, char *argument, int/* cmd*/, int/* subcmd*/) if ((ch = get_char_by_room(room, name))) { - gain_exp(ch, atoi(amount)); + ExpCalc::gain_exp(ch, atoi(amount)); sprintf(buf, "wexp: victim (%s) получил опыт %d", GET_NAME(ch), atoi(amount)); wld_log(room, buf); } diff --git a/src/ext_money.cpp b/src/ext_money.cpp index 60f94ad27..65d866c0a 100644 --- a/src/ext_money.cpp +++ b/src/ext_money.cpp @@ -15,6 +15,7 @@ #include "zone.table.hpp" #include "utils.h" #include "grp/grp.main.h" +#include "core/leveling.h" #include #include @@ -24,6 +25,9 @@ using namespace ExtMoney; using namespace Remort; +// строка, глобальная зачем то... +std::string Remort::WHERE_TO_REMORT_STR = ""; + namespace { @@ -31,872 +35,730 @@ void message_low_torc(CHAR_DATA *ch, unsigned type, int amount, const char *add_ } // namespace -namespace Remort -{ - -const char *CONFIG_FILE = LIB_MISC"remort.xml"; -std::string WHERE_TO_REMORT_STR; - -void init(); -bool can_remort_now(CHAR_DATA *ch); -void show_config(CHAR_DATA *ch); -int calc_torc_daily(int rmrt); -bool need_torc(CHAR_DATA *ch); - -} // namespace Remort - -namespace ExtMoney -{ - -// на все эти переменные смотреть init() -int TORC_EXCH_RATE = 999; -std::map plural_name_currency_map = { - { "куны" , "денег" }, - { "слава" , "славы" }, - { "лед" , "льда" }, - { "гривны", "гривен" } -}; - -std::string name_currency_plural(std::string name) -{ - auto it = plural_name_currency_map.find(name); - if (it != plural_name_currency_map.end()) - { - return (*it).second; - } - return "неизвестной валюты"; -} - -struct type_node -{ - type_node() : MORT_REQ(99), MORT_REQ_ADD_PER_MORT(99), MORT_NUM(99), - DROP_LVL(99), DROP_AMOUNT(0), DROP_AMOUNT_ADD_PER_LVL(0), MINIMUM_DAYS(99), - DESC_MESSAGE_NUM(0), DESC_MESSAGE_U_NUM(0) {}; - // сколько гривен требуется на соответствующее право морта - int MORT_REQ; - // сколько добавлять к требованиям за каждый морт сверху - int MORT_REQ_ADD_PER_MORT; - // с какого морта требуются какие гривны - int MORT_NUM; - // с боссов зон какого мин. среднего уровня - int DROP_LVL; - // сколько дропать с базового ср. уровня - int DROP_AMOUNT; - // сколько накидывать за каждый уровень выше базового - int DROP_AMOUNT_ADD_PER_LVL; - // делитель требования гривен, определяет сколько дней минимум потребуется - // для набора необходимого на морт кол-ва гривен. например если делитель - // равен 7, а гривен для след. морта нужно 70 золотых, то за сутки персонажу - // дропнется не более 10 золотых гривен или их эквивалента - int MINIMUM_DAYS; - // для сообщений через desc_count - int DESC_MESSAGE_NUM; - int DESC_MESSAGE_U_NUM; -}; - -// список типов гривен со всеми их параметрами -std::array type_list; - -struct TorcReq -{ - TorcReq(int rmrt); - // тип гривн - unsigned type; - // кол-во - int amount; -}; - -TorcReq::TorcReq(int rmrt) -{ - // type - if (rmrt >= type_list[TORC_GOLD].MORT_NUM) - { - type = TORC_GOLD; - } - else if (rmrt >= type_list[TORC_SILVER].MORT_NUM) - { - type = TORC_SILVER; - } - else if (rmrt >= type_list[TORC_BRONZE].MORT_NUM) - { - type = TORC_BRONZE; - } - else - { - type = TOTAL_TYPES; - } - // amount - if (type != TOTAL_TYPES) - { - amount = type_list[type].MORT_REQ + - (rmrt - type_list[type].MORT_NUM) * type_list[type].MORT_REQ_ADD_PER_MORT; - } - else - { - amount = 0; - } -} - -// обмен гривн -void torc_exch_menu(CHAR_DATA *ch); -void parse_inc_exch(CHAR_DATA *ch, int amount, int num); -void parse_dec_exch(CHAR_DATA *ch, int amount, int num); -int check_input_amount(CHAR_DATA *ch, int num1, int num2); -bool check_equal_exch(CHAR_DATA *ch); -void torc_exch_parse(CHAR_DATA *ch, const char *arg); -// дроп гривн -std::string create_message(CHAR_DATA *ch, int gold, int silver, int bronze); -bool has_connected_bosses(CHAR_DATA *ch); -unsigned calc_type_by_zone_lvl(int zone_lvl); -int calc_drop_torc(int zone_lvl, int members); -int check_daily_limit(CHAR_DATA *ch, int drop); -void gain_torc(CHAR_DATA *ch, int drop); -void drop_torc(CHAR_DATA *mob); - -} // namespace ExtMoney - namespace ExtMoney { -// распечатка меню обмена гривен ('менять' у глашатая) -void torc_exch_menu(CHAR_DATA *ch) -{ - boost::format menu(" %s%d) %s%-17s%s -> %s%-17s%s [%d -> %d]\r\n"); - std::stringstream out; - - out << "\r\n" - " Курсы обмена гривен:\r\n" - " " << TORC_EXCH_RATE << " бронзовых <-> 1 серебряная\r\n" - " " << TORC_EXCH_RATE << " серебряных <-> 1 золотая\r\n\r\n"; - - out << " Текущий баланс: " - << CCIYEL(ch, C_NRM) << ch->desc->ext_money[TORC_GOLD] << "з " - << CCWHT(ch, C_NRM) << ch->desc->ext_money[TORC_SILVER] << "с " - << CCYEL(ch, C_NRM) << ch->desc->ext_money[TORC_BRONZE] << "б\r\n\r\n"; - - out << menu - % CCGRN(ch, C_NRM) % 1 - % CCYEL(ch, C_NRM) % "Бронзовые гривны" % CCNRM(ch, C_NRM) - % CCWHT(ch, C_NRM) % "Серебряные гривны" % CCNRM(ch, C_NRM) - % TORC_EXCH_RATE % 1; - out << menu - % CCGRN(ch, C_NRM) % 2 - % CCWHT(ch, C_NRM) % "Серебряные гривны" % CCNRM(ch, C_NRM) - % CCIYEL(ch, C_NRM) % "Золотые гривны" % CCNRM(ch, C_NRM) - % TORC_EXCH_RATE % 1; - out << "\r\n" - << menu - % CCGRN(ch, C_NRM) % 3 - % CCIYEL(ch, C_NRM) % "Золотые гривны" % CCNRM(ch, C_NRM) - % CCWHT(ch, C_NRM) % "Серебряные гривны" % CCNRM(ch, C_NRM) - % 1 % TORC_EXCH_RATE; - out << menu - % CCGRN(ch, C_NRM) % 4 - % CCWHT(ch, C_NRM) % "Серебряные гривны" % CCNRM(ch, C_NRM) - % CCYEL(ch, C_NRM) % "Бронзовые гривны" % CCNRM(ch, C_NRM) - % 1 % TORC_EXCH_RATE; - - out << "\r\n" - " <номер действия> - один минимальный обмен указанного вида\r\n" - " <номер действия> <число х> - обмен х имеющихся гривен\r\n\r\n"; - - out << CCGRN(ch, C_NRM) << " 5)" - << CCNRM(ch, C_NRM) << " Отменить обмен и выйти\r\n" - << CCGRN(ch, C_NRM) << " 6)" - << CCNRM(ch, C_NRM) << " Подтвердить обмен и выйти\r\n\r\n" - << " Ваш выбор:"; - - send_to_char(out.str(), ch); -} - -// обмен в сторону больших гривен -void parse_inc_exch(CHAR_DATA *ch, int amount, int num) -{ - int torc_from = TORC_BRONZE; - int torc_to = TORC_SILVER; - int torc_rate = TORC_EXCH_RATE; - - if (num == 2) - { - torc_from = TORC_SILVER; - torc_to = TORC_GOLD; - } - - if (ch->desc->ext_money[torc_from] < amount) - { - if (ch->desc->ext_money[torc_from] < torc_rate) - { - send_to_char("Нет необходимого количества гривен!\r\n", ch); - } - else - { - amount = ch->desc->ext_money[torc_from] / torc_rate * torc_rate; - send_to_char(ch, "Количество меняемых гривен уменьшено до %d.\r\n", amount); - ch->desc->ext_money[torc_from] -= amount; - ch->desc->ext_money[torc_to] += amount / torc_rate; - send_to_char(ch, "Произведен обмен: %d -> %d.\r\n", amount, amount / torc_rate); - } - } - else - { - int real_amount = amount / torc_rate * torc_rate; - if (real_amount != amount) - { - send_to_char(ch, "Количество меняемых гривен уменьшено до %d.\r\n", real_amount); - } - ch->desc->ext_money[torc_from] -= real_amount; - ch->desc->ext_money[torc_to] += real_amount / torc_rate; - send_to_char(ch, "Произведен обмен: %d -> %d.\r\n", real_amount, real_amount / torc_rate); - } -} - -// обмен в сторону меньших гривен -void parse_dec_exch(CHAR_DATA *ch, int amount, int num) -{ - int torc_from = TORC_GOLD; - int torc_to = TORC_SILVER; - int torc_rate = TORC_EXCH_RATE; - - if (num == 4) - { - torc_from = TORC_SILVER; - torc_to = TORC_BRONZE; - } - - if (ch->desc->ext_money[torc_from] < amount) - { - if (ch->desc->ext_money[torc_from] < 1) - { - send_to_char("Нет необходимого количества гривен!\r\n", ch); - } - else - { - amount = ch->desc->ext_money[torc_from]; - send_to_char(ch, "Количество меняемых гривен уменьшено до %d.\r\n", amount); - - ch->desc->ext_money[torc_from] -= amount; - ch->desc->ext_money[torc_to] += amount * torc_rate; - send_to_char(ch, "Произведен обмен: %d -> %d.\r\n", amount, amount * torc_rate); - } - } - else - { - ch->desc->ext_money[torc_from] -= amount; - ch->desc->ext_money[torc_to] += amount * torc_rate; - send_to_char(ch, "Произведен обмен: %d -> %d.\r\n", amount, amount * torc_rate); - } -} - -// кол-во меняемых гривен -int check_input_amount(CHAR_DATA* /*ch*/, int num1, int num2) -{ - if ((num1 == 1 || num1 == 2) && num2 < TORC_EXCH_RATE) - { - return TORC_EXCH_RATE; - } - else if ((num1 == 3 || num1 == 4) && num2 < 1) - { - return 1; - } - return 0; -} - -// проверка после обмена, что ничего лишнего не сгенерили случайно -bool check_equal_exch(CHAR_DATA *ch) -{ - int before = 0, after = 0; - for (unsigned i = 0; i < TOTAL_TYPES; ++i) - { - if (i == TORC_BRONZE) - { - before += ch->get_ext_money(i); - after += ch->desc->ext_money[i]; - } - if (i == TORC_SILVER) - { - before += ch->get_ext_money(i) * TORC_EXCH_RATE; - after += ch->desc->ext_money[i] * TORC_EXCH_RATE; - } - else if (i == TORC_GOLD) - { - before += ch->get_ext_money(i) * TORC_EXCH_RATE * TORC_EXCH_RATE; - after += ch->desc->ext_money[i] * TORC_EXCH_RATE * TORC_EXCH_RATE; - } - } - if (before != after) - { - sprintf(buf, "SYSERROR: Torc exch by %s not equal: %d -> %d", - GET_NAME(ch), before, after); - mudlog(buf, DEF, LVL_IMMORT, SYSLOG, TRUE); - return false; - } - return true; -} - -// парс ввода при обмене гривен -void torc_exch_parse(CHAR_DATA *ch, const char *arg) -{ - if (!*arg || !a_isdigit(*arg)) - { - send_to_char("Неверный выбор!\r\n", ch); - torc_exch_menu(ch); - return; - } - - std::string param2(arg), param1; - GetOneParam(param2, param1); - boost::trim(param2); - - int num1 = 0, num2 = 0; - - try - { - num1 = std::stoi(param1, nullptr, 10); - if (!param2.empty()) - { - num2 = std::stoi(param2, nullptr, 10); - } - } - catch (const std::invalid_argument&) - { - log("SYSERROR: invalid_argument arg=%s (%s %s %d)", - arg, __FILE__, __func__, __LINE__); - - send_to_char("Неверный выбор!\r\n", ch); - torc_exch_menu(ch); - return; - } - - int amount = num2; - if (!param2.empty()) - { - // ввели два числа - проверка пользовательского ввода кол-ва гривен - int min_amount = check_input_amount(ch, num1, amount); - if (min_amount > 0) - { - send_to_char(ch, "Минимальное количество гривен для данного обмена: %d.", min_amount); - torc_exch_menu(ch); - return; - } - } - else - { - // ввели одно число - проставляем минимальный обмен - if (num1 == 1 || num1 == 2) - { - amount = TORC_EXCH_RATE; - } - else if (num1 == 3 || num1 == 4) - { - amount = 1; - } - } - // собсна обмен - if (num1 == 1 || num1 == 2) - { - parse_inc_exch(ch, amount, num1); - } - else if (num1 == 3 || num1 == 4) - { - parse_dec_exch(ch, amount, num1); - } - else if (num1 == 5) - { - STATE(ch->desc) = CON_PLAYING; - send_to_char("Обмен отменен.\r\n", ch); - return; - } - else if (num1 == 6) - { - if (!check_equal_exch(ch)) - { - send_to_char("Обмен отменен по техническим причинам, обратитесь к Богам.\r\n", ch); - } - else - { - for (unsigned i = 0; i < TOTAL_TYPES; ++i) - { - ch->set_ext_money(i, ch->desc->ext_money[i]); - } - STATE(ch->desc) = CON_PLAYING; - send_to_char("Обмен произведен.\r\n", ch); - } - return; - } - else - { - send_to_char("Неверный выбор!\r\n", ch); - } - torc_exch_menu(ch); -} - -// формирование сообщения о награде гривнами при смерти босса -// пишет в одну строку о нескольких видах гривен, если таковые были -std::string create_message(CHAR_DATA *ch, int gold, int silver, int bronze) -{ - std::stringstream out; - int cnt = 0; - - if (gold > 0) - { - out << CCIYEL(ch, C_NRM) << gold << " " - << desc_count(gold, type_list[TORC_GOLD].DESC_MESSAGE_U_NUM); - if (silver <= 0 && bronze <= 0) - { - out << " " << desc_count(gold, WHAT_TORCu); - } - out << CCNRM(ch, C_NRM); - ++cnt; - } - if (silver > 0) - { - if (cnt > 0) - { - if (bronze > 0) - { - out << ", " << CCWHT(ch, C_NRM) << silver << " " - << desc_count(silver, type_list[TORC_SILVER].DESC_MESSAGE_U_NUM) - << CCNRM(ch, C_NRM) << " и "; - } - else - { - out << " и " << CCWHT(ch, C_NRM) << silver << " " - << desc_count(silver, type_list[TORC_SILVER].DESC_MESSAGE_U_NUM) - << " " << desc_count(silver, WHAT_TORCu) - << CCNRM(ch, C_NRM); - } - } - else - { - out << CCWHT(ch, C_NRM) << silver << " " - << desc_count(silver, type_list[TORC_SILVER].DESC_MESSAGE_U_NUM); - if (bronze > 0) - { - out << CCNRM(ch, C_NRM) << " и "; - } - else - { - out << " " << desc_count(silver, WHAT_TORCu) << CCNRM(ch, C_NRM); - } - } - } - if (bronze > 0) - { - out << CCYEL(ch, C_NRM) << bronze << " " - << desc_count(bronze, type_list[TORC_BRONZE].DESC_MESSAGE_U_NUM) - << " " << desc_count(bronze, WHAT_TORCu) - << CCNRM(ch, C_NRM); - } - - return out.str(); -} - -// проверка на случай нескольких физических боссов, -// которые логически являются одной группой, предотвращающая лишний дроп гривен -bool has_connected_bosses(CHAR_DATA *ch) -{ - // если в комнате есть другие живые боссы - for (const auto i : world[ch->in_room]->people) - { - if (i != ch - && IS_NPC(i) - && !IS_CHARMICE(i) - && i->get_role(MOB_ROLE_BOSS)) - { - return true; - } - } - // если у данного моба есть живые последователи-боссы - for (follow_type *i = ch->followers; i; i = i->next) - { - if (i->follower != ch - && IS_NPC(i->follower) - && !IS_CHARMICE(i->follower) - && i->follower->get_master() == ch - && i->follower->get_role(MOB_ROLE_BOSS)) - { - return true; - } - } - // если он сам следует за каким-то боссом - if (ch->has_master() && ch->get_master()->get_role(MOB_ROLE_BOSS)) - { - return true; - } - - return false; -} - -// выясняет какой тип гривен дропать с зоны -unsigned calc_type_by_zone_lvl(int zone_lvl) -{ - if (zone_lvl >= type_list[TORC_GOLD].DROP_LVL) - { - return TORC_GOLD; - } - else if (zone_lvl >= type_list[TORC_SILVER].DROP_LVL) - { - return TORC_SILVER; - } - else if (zone_lvl >= type_list[TORC_BRONZE].DROP_LVL) - { - return TORC_BRONZE; - } - return TOTAL_TYPES; -} - -// возвращает кол-во гривен с босса зоны уровня zone_lvl, поделенных -// с учетом группы members и пересчитанных в бронзу -int calc_drop_torc(int zone_lvl, int members) -{ - const unsigned type = calc_type_by_zone_lvl(zone_lvl); - if (type >= TOTAL_TYPES) - { - return 0; - } - const int add = zone_lvl - type_list[type].DROP_LVL; - int drop = type_list[type].DROP_AMOUNT + add * type_list[type].DROP_AMOUNT_ADD_PER_LVL; - - // пересчитываем дроп к минимальному типу - if (type == TORC_GOLD) - { - drop = drop * TORC_EXCH_RATE * TORC_EXCH_RATE; - } - else if (type == TORC_SILVER) - { - drop = drop * TORC_EXCH_RATE; - } - - // есть ли вообще что дропать - if (drop < members) - { - return 0; - } - - // после этого уже применяем делитель группы - if (members > 1) - { - drop = drop / members; - } - - return drop; -} - -// по дефолту отрисовка * за каждую 1/5 от суточного лимита гривен -// если imm_stat == true, то вместо звездочек конкретные цифры тек/макс -std::string draw_daily_limit(CHAR_DATA *ch, bool imm_stat) -{ - const int today_torc = ch->get_today_torc(); - const int torc_req_daily = calc_torc_daily(GET_REMORT(ch)); - - TorcReq torc_req(GET_REMORT(ch)); - if (torc_req.type >= TOTAL_TYPES) - { - torc_req.type = TORC_BRONZE; - } - const int daily_torc_limit = torc_req_daily / type_list[torc_req.type].MINIMUM_DAYS; - - std::string out("["); - if (!imm_stat) - { - for (int i = 1; i <= 5; ++i) - { - if (daily_torc_limit / 5 * i <= today_torc) - { - out += "*"; - } - else - { - out += "."; - } - } - } - else - { - out += boost::str(boost::format("%d/%d") % today_torc % daily_torc_limit); - } - out += "]"; - - return out; -} - -// проверка дропа гривен на суточный замакс -int check_daily_limit(CHAR_DATA *ch, int drop) -{ - const int today_torc = ch->get_today_torc(); - const int torc_req_daily = calc_torc_daily(GET_REMORT(ch)); - - // из calc_torc_daily в любом случае взялось какое-то число бронзы - // даже если чар не имеет мортов для требования гривен - TorcReq torc_req(GET_REMORT(ch)); - if (torc_req.type >= TOTAL_TYPES) - { - torc_req.type = TORC_BRONZE; - } - const int daily_torc_limit = torc_req_daily / type_list[torc_req.type].MINIMUM_DAYS; - - if (today_torc + drop > daily_torc_limit) - { - int add = daily_torc_limit - today_torc; - if (add > 0) - { - ch->add_today_torc(add); - return add; - } - else - { - return 0; - } - } - - ch->add_today_torc(drop); - return drop; -} - -// процесс дропа гривен конкретному чару -void gain_torc(CHAR_DATA *ch, int drop) -{ - // проверка на индивидуальный суточный замакс гривн - int bronze = check_daily_limit(ch, drop); - if (bronze <= 0) - { - return; - } - int gold = 0, silver = 0; - // и разносим что осталось обратно по типам - if (bronze >= TORC_EXCH_RATE * TORC_EXCH_RATE) - { - gold += bronze / (TORC_EXCH_RATE * TORC_EXCH_RATE); - bronze -= gold * TORC_EXCH_RATE * TORC_EXCH_RATE; - ch->set_ext_money(TORC_GOLD, gold + ch->get_ext_money(TORC_GOLD)); - } - if (bronze >= TORC_EXCH_RATE) - { - silver += bronze / TORC_EXCH_RATE; - bronze -= silver * TORC_EXCH_RATE; - ch->set_ext_money(TORC_SILVER, silver + ch->get_ext_money(TORC_SILVER)); - } - ch->set_ext_money(TORC_BRONZE, bronze + ch->get_ext_money(TORC_BRONZE)); - - std::string out = create_message(ch, gold, silver, bronze); - send_to_char(ch, "В награду за свершенный подвиг вы получили от Богов %s.\r\n", out.c_str()); - -} + // на все эти переменные смотреть init() + int TORC_EXCH_RATE = 999; + std::map plural_name_currency_map = { + { "куны" , "денег" }, + { "слава" , "славы" }, + { "лед" , "льда" }, + { "гривны", "гривен" } + }; -// дергается из экстракт_чар, у босса берется макс дамагер, находящийся -// в той же комнате, группе готорого и раскидываются гривны, если есть -// кому раскидывать (флаг GF_REMORT, проверка на делимость гривен, проверка на -// то, что чар находился в комнате с мобом не менее половины раундов дамагера) -void drop_torc(CHAR_DATA *mob) -{ - if (!mob->get_role(MOB_ROLE_BOSS) - || has_connected_bosses(mob)) - { - return; - } - - log("[Extract char] Checking %s for ExtMoney.", mob->get_name().c_str()); + std::string name_currency_plural(std::string name) + { + auto it = plural_name_currency_map.find(name); + if (it != plural_name_currency_map.end()) + { + return (*it).second; + } + return "неизвестной валюты"; + } - std::pair damager = mob->get_max_damager_in_room(); - DESCRIPTOR_DATA *d = nullptr; - if (damager.first > 0) { - d = DescByUID(damager.first); - } - if (!d) { - return; - } - auto grp = d->character->personGroup; - CHAR_DATA *leader = grp != nullptr? d->character->personGroup->getLeader() : d->character.get(); + struct type_node + { + type_node() : MORT_REQ(99), MORT_REQ_ADD_PER_MORT(99), MORT_NUM(99), + DROP_LVL(99), DROP_AMOUNT(0), DROP_AMOUNT_ADD_PER_LVL(0), MINIMUM_DAYS(99), + DESC_MESSAGE_NUM(0), DESC_MESSAGE_U_NUM(0) {}; + // сколько гривен требуется на соответствующее право морта + int MORT_REQ; + // сколько добавлять к требованиям за каждый морт сверху + int MORT_REQ_ADD_PER_MORT; + // с какого морта требуются какие гривны + int MORT_NUM; + // с боссов зон какого мин. среднего уровня + int DROP_LVL; + // сколько дропать с базового ср. уровня + int DROP_AMOUNT; + // сколько накидывать за каждый уровень выше базового + int DROP_AMOUNT_ADD_PER_LVL; + // делитель требования гривен, определяет сколько дней минимум потребуется + // для набора необходимого на морт кол-ва гривен. например если делитель + // равен 7, а гривен для след. морта нужно 70 золотых, то за сутки персонажу + // дропнется не более 10 золотых гривен или их эквивалента + int MINIMUM_DAYS; + // для сообщений через desc_count + int DESC_MESSAGE_NUM; + int DESC_MESSAGE_U_NUM; + }; + + // список типов гривен со всеми их параметрами + std::array type_list; + + // обмен гривн + void torc_exch_menu(CHAR_DATA *ch); + void parse_inc_exch(CHAR_DATA *ch, int amount, int num); + void parse_dec_exch(CHAR_DATA *ch, int amount, int num); + int check_input_amount(CHAR_DATA *ch, int num1, int num2); + bool check_equal_exch(CHAR_DATA *ch); + void torc_exch_parse(CHAR_DATA *ch, const char *arg); + // дроп гривн + std::string create_message(CHAR_DATA *ch, int gold, int silver, int bronze); + bool has_connected_bosses(CHAR_DATA *ch); + unsigned calc_type_by_zone_lvl(int zone_lvl); + int calc_drop_torc(int zone_lvl, int members); + int check_daily_limit(CHAR_DATA *ch, int drop); + void gain_torc(CHAR_DATA *ch, int drop); + void drop_torc(CHAR_DATA *mob); + + // распечатка меню обмена гривен ('менять' у глашатая) + void torc_exch_menu(CHAR_DATA *ch) + { + boost::format menu(" %s%d) %s%-17s%s -> %s%-17s%s [%d -> %d]\r\n"); + std::stringstream out; + + out << "\r\n" + " Курсы обмена гривен:\r\n" + " " << TORC_EXCH_RATE << " бронзовых <-> 1 серебряная\r\n" + " " << TORC_EXCH_RATE << " серебряных <-> 1 золотая\r\n\r\n"; + + out << " Текущий баланс: " + << CCIYEL(ch, C_NRM) << ch->desc->ext_money[TORC_GOLD] << "з " + << CCWHT(ch, C_NRM) << ch->desc->ext_money[TORC_SILVER] << "с " + << CCYEL(ch, C_NRM) << ch->desc->ext_money[TORC_BRONZE] << "б\r\n\r\n"; + + out << menu + % CCGRN(ch, C_NRM) % 1 + % CCYEL(ch, C_NRM) % "Бронзовые гривны" % CCNRM(ch, C_NRM) + % CCWHT(ch, C_NRM) % "Серебряные гривны" % CCNRM(ch, C_NRM) + % TORC_EXCH_RATE % 1; + out << menu + % CCGRN(ch, C_NRM) % 2 + % CCWHT(ch, C_NRM) % "Серебряные гривны" % CCNRM(ch, C_NRM) + % CCIYEL(ch, C_NRM) % "Золотые гривны" % CCNRM(ch, C_NRM) + % TORC_EXCH_RATE % 1; + out << "\r\n" + << menu + % CCGRN(ch, C_NRM) % 3 + % CCIYEL(ch, C_NRM) % "Золотые гривны" % CCNRM(ch, C_NRM) + % CCWHT(ch, C_NRM) % "Серебряные гривны" % CCNRM(ch, C_NRM) + % 1 % TORC_EXCH_RATE; + out << menu + % CCGRN(ch, C_NRM) % 4 + % CCWHT(ch, C_NRM) % "Серебряные гривны" % CCNRM(ch, C_NRM) + % CCYEL(ch, C_NRM) % "Бронзовые гривны" % CCNRM(ch, C_NRM) + % 1 % TORC_EXCH_RATE; + + out << "\r\n" + " <номер действия> - один минимальный обмен указанного вида\r\n" + " <номер действия> <число х> - обмен х имеющихся гривен\r\n\r\n"; + + out << CCGRN(ch, C_NRM) << " 5)" + << CCNRM(ch, C_NRM) << " Отменить обмен и выйти\r\n" + << CCGRN(ch, C_NRM) << " 6)" + << CCNRM(ch, C_NRM) << " Подтвердить обмен и выйти\r\n\r\n" + << " Ваш выбор:"; + + send_to_char(out.str(), ch); + } - int members = 1; - members = leader->personGroup->size(mob->in_room); + // обмен в сторону больших гривен + void parse_inc_exch(CHAR_DATA *ch, int amount, int num) + { + int torc_from = TORC_BRONZE; + int torc_to = TORC_SILVER; + int torc_rate = TORC_EXCH_RATE; + + if (num == 2) + { + torc_from = TORC_SILVER; + torc_to = TORC_GOLD; + } + + if (ch->desc->ext_money[torc_from] < amount) + { + if (ch->desc->ext_money[torc_from] < torc_rate) + { + send_to_char("Нет необходимого количества гривен!\r\n", ch); + } + else + { + amount = ch->desc->ext_money[torc_from] / torc_rate * torc_rate; + send_to_char(ch, "Количество меняемых гривен уменьшено до %d.\r\n", amount); + ch->desc->ext_money[torc_from] -= amount; + ch->desc->ext_money[torc_to] += amount / torc_rate; + send_to_char(ch, "Произведен обмен: %d -> %d.\r\n", amount, amount / torc_rate); + } + } + else + { + int real_amount = amount / torc_rate * torc_rate; + if (real_amount != amount) + { + send_to_char(ch, "Количество меняемых гривен уменьшено до %d.\r\n", real_amount); + } + ch->desc->ext_money[torc_from] -= real_amount; + ch->desc->ext_money[torc_to] += real_amount / torc_rate; + send_to_char(ch, "Произведен обмен: %d -> %d.\r\n", real_amount, real_amount / torc_rate); + } + } - const int zone_lvl = zone_table[mob_index[GET_MOB_RNUM(mob)].zone].mob_level; - const int drop = calc_drop_torc(zone_lvl, members); - if (drop <= 0) - { - return; - } + // обмен в сторону меньших гривен + void parse_dec_exch(CHAR_DATA *ch, int amount, int num) + { + int torc_from = TORC_GOLD; + int torc_to = TORC_SILVER; + int torc_rate = TORC_EXCH_RATE; + + if (num == 4) + { + torc_from = TORC_SILVER; + torc_to = TORC_BRONZE; + } + + if (ch->desc->ext_money[torc_from] < amount) + { + if (ch->desc->ext_money[torc_from] < 1) + { + send_to_char("Нет необходимого количества гривен!\r\n", ch); + } + else + { + amount = ch->desc->ext_money[torc_from]; + send_to_char(ch, "Количество меняемых гривен уменьшено до %d.\r\n", amount); + + ch->desc->ext_money[torc_from] -= amount; + ch->desc->ext_money[torc_to] += amount * torc_rate; + send_to_char(ch, "Произведен обмен: %d -> %d.\r\n", amount, amount * torc_rate); + } + } + else + { + ch->desc->ext_money[torc_from] -= amount; + ch->desc->ext_money[torc_to] += amount * torc_rate; + send_to_char(ch, "Произведен обмен: %d -> %d.\r\n", amount, amount * torc_rate); + } + } - if (IN_ROOM(leader) == IN_ROOM(mob) - && GET_GOD_FLAG(leader, GF_REMORT) - && (GET_UNIQUE(leader) == damager.first - || mob->get_attacker(leader, ATTACKER_ROUNDS) >= damager.second / 2)) { - gain_torc(leader, drop); - } - // он был один, сваливаем - if (grp == nullptr) - return; - auto m_list = grp->getMembers(mob->in_room); // список живых мемберов в комнате - for (auto m : m_list) { - if (GET_GOD_FLAG(m, GF_REMORT) && mob->get_attacker(m, ATTACKER_ROUNDS) >= damager.second / 2) { - gain_torc(m, drop); - } - } -} + // кол-во меняемых гривен + int check_input_amount(CHAR_DATA* /*ch*/, int num1, int num2) + { + if ((num1 == 1 || num1 == 2) && num2 < TORC_EXCH_RATE) + { + return TORC_EXCH_RATE; + } + else if ((num1 == 3 || num1 == 4) && num2 < 1) + { + return 1; + } + return 0; + } -void player_drop_log(CHAR_DATA *ch, unsigned type, int diff) -{ - int total_bronze = ch->get_ext_money(TORC_BRONZE); - total_bronze += ch->get_ext_money(TORC_SILVER) * TORC_EXCH_RATE; - total_bronze += ch->get_ext_money(TORC_GOLD) * TORC_EXCH_RATE * TORC_EXCH_RATE; - - log("ExtMoney: %s%s%d%s, sum=%d", - ch->get_name().c_str(), - (diff > 0 ? " +" : " "), - diff, - ((type == TORC_GOLD) ? "g" : (type == TORC_SILVER) ? "s" : "b"), - total_bronze); -} + // проверка после обмена, что ничего лишнего не сгенерили случайно + bool check_equal_exch(CHAR_DATA *ch) + { + int before = 0, after = 0; + for (unsigned i = 0; i < TOTAL_TYPES; ++i) + { + if (i == TORC_BRONZE) + { + before += ch->get_ext_money(i); + after += ch->desc->ext_money[i]; + } + if (i == TORC_SILVER) + { + before += ch->get_ext_money(i) * TORC_EXCH_RATE; + after += ch->desc->ext_money[i] * TORC_EXCH_RATE; + } + else if (i == TORC_GOLD) + { + before += ch->get_ext_money(i) * TORC_EXCH_RATE * TORC_EXCH_RATE; + after += ch->desc->ext_money[i] * TORC_EXCH_RATE * TORC_EXCH_RATE; + } + } + if (before != after) + { + sprintf(buf, "SYSERROR: Torc exch by %s not equal: %d -> %d", + GET_NAME(ch), before, after); + mudlog(buf, DEF, LVL_IMMORT, SYSLOG, TRUE); + return false; + } + return true; + } -} // namespace ExtMoney + // парс ввода при обмене гривен + void torc_exch_parse(CHAR_DATA *ch, const char *arg) + { + if (!*arg || !a_isdigit(*arg)) + { + send_to_char("Неверный выбор!\r\n", ch); + torc_exch_menu(ch); + return; + } + + std::string param2(arg), param1; + GetOneParam(param2, param1); + boost::trim(param2); + + int num1 = 0, num2 = 0; + + try + { + num1 = std::stoi(param1, nullptr, 10); + if (!param2.empty()) + { + num2 = std::stoi(param2, nullptr, 10); + } + } + catch (const std::invalid_argument&) + { + log("SYSERROR: invalid_argument arg=%s (%s %s %d)", + arg, __FILE__, __func__, __LINE__); + + send_to_char("Неверный выбор!\r\n", ch); + torc_exch_menu(ch); + return; + } + + int amount = num2; + if (!param2.empty()) + { + // ввели два числа - проверка пользовательского ввода кол-ва гривен + int min_amount = check_input_amount(ch, num1, amount); + if (min_amount > 0) + { + send_to_char(ch, "Минимальное количество гривен для данного обмена: %d.", min_amount); + torc_exch_menu(ch); + return; + } + } + else + { + // ввели одно число - проставляем минимальный обмен + if (num1 == 1 || num1 == 2) + { + amount = TORC_EXCH_RATE; + } + else if (num1 == 3 || num1 == 4) + { + amount = 1; + } + } + // собсна обмен + if (num1 == 1 || num1 == 2) + { + parse_inc_exch(ch, amount, num1); + } + else if (num1 == 3 || num1 == 4) + { + parse_dec_exch(ch, amount, num1); + } + else if (num1 == 5) + { + STATE(ch->desc) = CON_PLAYING; + send_to_char("Обмен отменен.\r\n", ch); + return; + } + else if (num1 == 6) + { + if (!check_equal_exch(ch)) + { + send_to_char("Обмен отменен по техническим причинам, обратитесь к Богам.\r\n", ch); + } + else + { + for (unsigned i = 0; i < TOTAL_TYPES; ++i) + { + ch->set_ext_money(i, ch->desc->ext_money[i]); + } + STATE(ch->desc) = CON_PLAYING; + send_to_char("Обмен произведен.\r\n", ch); + } + return; + } + else + { + send_to_char("Неверный выбор!\r\n", ch); + } + torc_exch_menu(ch); + } -namespace Remort -{ + // формирование сообщения о награде гривнами при смерти босса + // пишет в одну строку о нескольких видах гривен, если таковые были + std::string create_message(CHAR_DATA *ch, int gold, int silver, int bronze) + { + std::stringstream out; + int cnt = 0; + + if (gold > 0) + { + out << CCIYEL(ch, C_NRM) << gold << " " + << desc_count(gold, type_list[TORC_GOLD].DESC_MESSAGE_U_NUM); + if (silver <= 0 && bronze <= 0) + { + out << " " << desc_count(gold, WHAT_TORCu); + } + out << CCNRM(ch, C_NRM); + ++cnt; + } + if (silver > 0) + { + if (cnt > 0) + { + if (bronze > 0) + { + out << ", " << CCWHT(ch, C_NRM) << silver << " " + << desc_count(silver, type_list[TORC_SILVER].DESC_MESSAGE_U_NUM) + << CCNRM(ch, C_NRM) << " и "; + } + else + { + out << " и " << CCWHT(ch, C_NRM) << silver << " " + << desc_count(silver, type_list[TORC_SILVER].DESC_MESSAGE_U_NUM) + << " " << desc_count(silver, WHAT_TORCu) + << CCNRM(ch, C_NRM); + } + } + else + { + out << CCWHT(ch, C_NRM) << silver << " " + << desc_count(silver, type_list[TORC_SILVER].DESC_MESSAGE_U_NUM); + if (bronze > 0) + { + out << CCNRM(ch, C_NRM) << " и "; + } + else + { + out << " " << desc_count(silver, WHAT_TORCu) << CCNRM(ch, C_NRM); + } + } + } + if (bronze > 0) + { + out << CCYEL(ch, C_NRM) << bronze << " " + << desc_count(bronze, type_list[TORC_BRONZE].DESC_MESSAGE_U_NUM) + << " " << desc_count(bronze, WHAT_TORCu) + << CCNRM(ch, C_NRM); + } + + return out.str(); + } -// релоадится через 'reload remort.xml' -void init() -{ - pugi::xml_document doc; - pugi::xml_parse_result result = doc.load_file(CONFIG_FILE); - if (!result) - { - snprintf(buf, MAX_STRING_LENGTH, "...%s", result.description()); - mudlog(buf, CMP, LVL_IMMORT, SYSLOG, TRUE); - return; - } + // проверка на случай нескольких физических боссов, + // которые логически являются одной группой, предотвращающая лишний дроп гривен + bool has_connected_bosses(CHAR_DATA *ch) + { + // если в комнате есть другие живые боссы + for (const auto i : world[ch->in_room]->people) + { + if (i != ch + && IS_NPC(i) + && !IS_CHARMICE(i) + && i->get_role(MOB_ROLE_BOSS)) + { + return true; + } + } + // если у данного моба есть живые последователи-боссы + for (follow_type *i = ch->followers; i; i = i->next) + { + if (i->follower != ch + && IS_NPC(i->follower) + && !IS_CHARMICE(i->follower) + && i->follower->get_master() == ch + && i->follower->get_role(MOB_ROLE_BOSS)) + { + return true; + } + } + // если он сам следует за каким-то боссом + if (ch->has_master() && ch->get_master()->get_role(MOB_ROLE_BOSS)) + { + return true; + } + + return false; + } - pugi::xml_node main_node = doc.child("remort"); - if (!main_node) + // выясняет какой тип гривен дропать с зоны + unsigned calc_type_by_zone_lvl(int zone_lvl) { - snprintf(buf, MAX_STRING_LENGTH, "... read fail"); - mudlog(buf, CMP, LVL_IMMORT, SYSLOG, TRUE); - return; + if (zone_lvl >= type_list[TORC_GOLD].DROP_LVL) + { + return TORC_GOLD; + } + else if (zone_lvl >= type_list[TORC_SILVER].DROP_LVL) + { + return TORC_SILVER; + } + else if (zone_lvl >= type_list[TORC_BRONZE].DROP_LVL) + { + return TORC_BRONZE; + } + return TOTAL_TYPES; } - WHERE_TO_REMORT_STR = Parse::child_value_str(main_node, "WHERE_TO_REMORT_STR"); - TORC_EXCH_RATE = Parse::child_value_int(main_node, "TORC_EXCH_RATE"); - - type_list[TORC_GOLD].MORT_NUM = Parse::child_value_int(main_node, "GOLD_MORT_NUM"); - type_list[TORC_GOLD].MORT_REQ = Parse::child_value_int(main_node, "GOLD_MORT_REQ"); - type_list[TORC_GOLD].MORT_REQ_ADD_PER_MORT = Parse::child_value_int(main_node, "GOLD_MORT_REQ_ADD_PER_MORT"); - type_list[TORC_GOLD].DROP_LVL = Parse::child_value_int(main_node, "GOLD_DROP_LVL"); - type_list[TORC_GOLD].DROP_AMOUNT = Parse::child_value_int(main_node, "GOLD_DROP_AMOUNT"); - type_list[TORC_GOLD].DROP_AMOUNT_ADD_PER_LVL = Parse::child_value_int(main_node, "GOLD_DROP_AMOUNT_ADD_PER_LVL"); - type_list[TORC_GOLD].MINIMUM_DAYS = Parse::child_value_int(main_node, "GOLD_MINIMUM_DAYS"); - - type_list[TORC_SILVER].MORT_NUM = Parse::child_value_int(main_node, "SILVER_MORT_NUM"); - type_list[TORC_SILVER].MORT_REQ = Parse::child_value_int(main_node, "SILVER_MORT_REQ"); - type_list[TORC_SILVER].MORT_REQ_ADD_PER_MORT = Parse::child_value_int(main_node, "SILVER_MORT_REQ_ADD_PER_MORT"); - type_list[TORC_SILVER].DROP_LVL = Parse::child_value_int(main_node, "SILVER_DROP_LVL"); - type_list[TORC_SILVER].DROP_AMOUNT = Parse::child_value_int(main_node, "SILVER_DROP_AMOUNT"); - type_list[TORC_SILVER].DROP_AMOUNT_ADD_PER_LVL = Parse::child_value_int(main_node, "SILVER_DROP_AMOUNT_ADD_PER_LVL"); - type_list[TORC_SILVER].MINIMUM_DAYS = Parse::child_value_int(main_node, "SILVER_MINIMUM_DAYS"); - - type_list[TORC_BRONZE].MORT_NUM = Parse::child_value_int(main_node, "BRONZE_MORT_NUM"); - type_list[TORC_BRONZE].MORT_REQ = Parse::child_value_int(main_node, "BRONZE_MORT_REQ"); - type_list[TORC_BRONZE].MORT_REQ_ADD_PER_MORT = Parse::child_value_int(main_node, "BRONZE_MORT_REQ_ADD_PER_MORT"); - type_list[TORC_BRONZE].DROP_LVL = Parse::child_value_int(main_node, "BRONZE_DROP_LVL"); - type_list[TORC_BRONZE].DROP_AMOUNT = Parse::child_value_int(main_node, "BRONZE_DROP_AMOUNT"); - type_list[TORC_BRONZE].DROP_AMOUNT_ADD_PER_LVL = Parse::child_value_int(main_node, "BRONZE_DROP_AMOUNT_ADD_PER_LVL"); - type_list[TORC_BRONZE].MINIMUM_DAYS = Parse::child_value_int(main_node, "BRONZE_MINIMUM_DAYS"); - - // не из конфига, но инится заодно со всеми - type_list[TORC_GOLD].DESC_MESSAGE_NUM = WHAT_TGOLD; - type_list[TORC_SILVER].DESC_MESSAGE_NUM = WHAT_TSILVER; - type_list[TORC_BRONZE].DESC_MESSAGE_NUM = WHAT_TBRONZE; - type_list[TORC_GOLD].DESC_MESSAGE_U_NUM = WHAT_TGOLDu; - type_list[TORC_SILVER].DESC_MESSAGE_U_NUM = WHAT_TSILVERu; - type_list[TORC_BRONZE].DESC_MESSAGE_U_NUM = WHAT_TBRONZEu; -} + // возвращает кол-во гривен с босса зоны уровня zone_lvl, поделенных + // с учетом группы members и пересчитанных в бронзу + int calc_drop_torc(int zone_lvl, int members) + { + const unsigned type = calc_type_by_zone_lvl(zone_lvl); + if (type >= TOTAL_TYPES) + { + return 0; + } + const int add = zone_lvl - type_list[type].DROP_LVL; + int drop = type_list[type].DROP_AMOUNT + add * type_list[type].DROP_AMOUNT_ADD_PER_LVL; + + // пересчитываем дроп к минимальному типу + if (type == TORC_GOLD) + { + drop = drop * TORC_EXCH_RATE * TORC_EXCH_RATE; + } + else if (type == TORC_SILVER) + { + drop = drop * TORC_EXCH_RATE; + } + + // есть ли вообще что дропать + if (drop < members) + { + return 0; + } + + // после этого уже применяем делитель группы + if (members > 1) + { + drop = drop / members; + } + + return drop; + } -// проверка, мешает ли что-то чару уйти в реморт -bool can_remort_now(CHAR_DATA *ch) -{ - if (PRF_FLAGGED(ch, PRF_CAN_REMORT) || !need_torc(ch)) - { - return true; - } - return false; -} + // возвращает требование гривен на морт в пересчете на бронзу + // для лоу-мортов берется требование бронзы базового уровня + int calc_torc_daily(int rmrt) + { + TorcReq torc_req(rmrt); + int num = 0; + + if (torc_req.type < TOTAL_TYPES) + { + num = type_list[torc_req.type].MORT_REQ; + + if (torc_req.type == TORC_GOLD) + { + num = num * TORC_EXCH_RATE * TORC_EXCH_RATE; + } + else if (torc_req.type == TORC_SILVER) + { + num = num * TORC_EXCH_RATE; + } + } + else + { + num = type_list[TORC_BRONZE].MORT_REQ; + } + + return num; + } -// распечатка переменных из конфига -void show_config(CHAR_DATA *ch) -{ - std::stringstream out; - out << "&SТекущие значения основных параметров:\r\n" - << "WHERE_TO_REMORT_STR = " << WHERE_TO_REMORT_STR << "\r\n" - << "TORC_EXCH_RATE = " << TORC_EXCH_RATE << "\r\n" - - << "GOLD_MORT_NUM = " << type_list[TORC_GOLD].MORT_NUM << "\r\n" - << "GOLD_MORT_REQ = " << type_list[TORC_GOLD].MORT_REQ << "\r\n" - << "GOLD_MORT_REQ_ADD_PER_MORT = " << type_list[TORC_GOLD].MORT_REQ_ADD_PER_MORT << "\r\n" - << "GOLD_DROP_LVL = " << type_list[TORC_GOLD].DROP_LVL << "\r\n" - << "GOLD_DROP_AMOUNT = " << type_list[TORC_GOLD].DROP_AMOUNT << "\r\n" - << "GOLD_DROP_AMOUNT_ADD_PER_LVL = " << type_list[TORC_GOLD].DROP_AMOUNT_ADD_PER_LVL << "\r\n" - << "GOLD_MINIMUM_DAYS = " << type_list[TORC_GOLD].MINIMUM_DAYS << "\r\n" - - << "SILVER_MORT_NUM = " << type_list[TORC_SILVER].MORT_NUM << "\r\n" - << "SILVER_MORT_REQ = " << type_list[TORC_SILVER].MORT_REQ << "\r\n" - << "SILVER_MORT_REQ_ADD_PER_MORT = " << type_list[TORC_SILVER].MORT_REQ_ADD_PER_MORT << "\r\n" - << "SILVER_DROP_LVL = " << type_list[TORC_SILVER].DROP_LVL << "\r\n" - << "SILVER_DROP_AMOUNT = " << type_list[TORC_SILVER].DROP_AMOUNT << "\r\n" - << "SILVER_DROP_AMOUNT_ADD_PER_LVL = " << type_list[TORC_SILVER].DROP_AMOUNT_ADD_PER_LVL << "\r\n" - << "SILVER_MINIMUM_DAYS = " << type_list[TORC_SILVER].MINIMUM_DAYS << "\r\n" - - << "BRONZE_MORT_NUM = " << type_list[TORC_BRONZE].MORT_NUM << "\r\n" - << "BRONZE_MORT_REQ = " << type_list[TORC_BRONZE].MORT_REQ << "\r\n" - << "BRONZE_MORT_REQ_ADD_PER_MORT = " << type_list[TORC_BRONZE].MORT_REQ_ADD_PER_MORT << "\r\n" - << "BRONZE_DROP_LVL = " << type_list[TORC_BRONZE].DROP_LVL << "\r\n" - << "BRONZE_DROP_AMOUNT = " << type_list[TORC_BRONZE].DROP_AMOUNT << "\r\n" - << "BRONZE_DROP_AMOUNT_ADD_PER_LVL = " << type_list[TORC_BRONZE].DROP_AMOUNT_ADD_PER_LVL << "\r\n" - << "BRONZE_MINIMUM_DAYS = " << type_list[TORC_BRONZE].MINIMUM_DAYS << "\r\n"; - - send_to_char(out.str(), ch); -} + // по дефолту отрисовка * за каждую 1/5 от суточного лимита гривен + // если imm_stat == true, то вместо звездочек конкретные цифры тек/макс + std::string draw_daily_limit(CHAR_DATA *ch, bool imm_stat) + { + const int today_torc = ch->get_today_torc(); + const int torc_req_daily = calc_torc_daily(GET_REMORT(ch)); + + TorcReq torc_req(GET_REMORT(ch)); + if (torc_req.type >= TOTAL_TYPES) + { + torc_req.type = TORC_BRONZE; + } + const int daily_torc_limit = torc_req_daily / type_list[torc_req.type].MINIMUM_DAYS; + + std::string out("["); + if (!imm_stat) + { + for (int i = 1; i <= 5; ++i) + { + if (daily_torc_limit / 5 * i <= today_torc) + { + out += "*"; + } + else + { + out += "."; + } + } + } + else + { + out += boost::str(boost::format("%d/%d") % today_torc % daily_torc_limit); + } + out += "]"; + + return out; + } -// возвращает требование гривен на морт в пересчете на бронзу -// для лоу-мортов берется требование бронзы базового уровня -int calc_torc_daily(int rmrt) -{ - TorcReq torc_req(rmrt); - int num = 0; + // проверка дропа гривен на суточный замакс + int check_daily_limit(CHAR_DATA *ch, int drop) + { + const int today_torc = ch->get_today_torc(); + const int torc_req_daily = calc_torc_daily(GET_REMORT(ch)); + + // из calc_torc_daily в любом случае взялось какое-то число бронзы + // даже если чар не имеет мортов для требования гривен + TorcReq torc_req(GET_REMORT(ch)); + if (torc_req.type >= TOTAL_TYPES) + { + torc_req.type = TORC_BRONZE; + } + const int daily_torc_limit = torc_req_daily / type_list[torc_req.type].MINIMUM_DAYS; + + if (today_torc + drop > daily_torc_limit) + { + int add = daily_torc_limit - today_torc; + if (add > 0) + { + ch->add_today_torc(add); + return add; + } + else + { + return 0; + } + } + + ch->add_today_torc(drop); + return drop; + } - if (torc_req.type < TOTAL_TYPES) - { - num = type_list[torc_req.type].MORT_REQ; + // процесс дропа гривен конкретному чару + void gain_torc(CHAR_DATA *ch, int drop) + { + // проверка на индивидуальный суточный замакс гривн + int bronze = check_daily_limit(ch, drop); + if (bronze <= 0) + { + return; + } + int gold = 0, silver = 0; + // и разносим что осталось обратно по типам + if (bronze >= TORC_EXCH_RATE * TORC_EXCH_RATE) + { + gold += bronze / (TORC_EXCH_RATE * TORC_EXCH_RATE); + bronze -= gold * TORC_EXCH_RATE * TORC_EXCH_RATE; + ch->set_ext_money(TORC_GOLD, gold + ch->get_ext_money(TORC_GOLD)); + } + if (bronze >= TORC_EXCH_RATE) + { + silver += bronze / TORC_EXCH_RATE; + bronze -= silver * TORC_EXCH_RATE; + ch->set_ext_money(TORC_SILVER, silver + ch->get_ext_money(TORC_SILVER)); + } + ch->set_ext_money(TORC_BRONZE, bronze + ch->get_ext_money(TORC_BRONZE)); + + std::string out = create_message(ch, gold, silver, bronze); + send_to_char(ch, "В награду за свершенный подвиг вы получили от Богов %s.\r\n", out.c_str()); - if (torc_req.type == TORC_GOLD) - { - num = num * TORC_EXCH_RATE * TORC_EXCH_RATE; - } - else if (torc_req.type == TORC_SILVER) - { - num = num * TORC_EXCH_RATE; - } - } - else - { - num = type_list[TORC_BRONZE].MORT_REQ; - } + } - return num; -} + // дергается из экстракт_чар, у босса берется макс дамагер, находящийся + // в той же комнате, группе готорого и раскидываются гривны, если есть + // кому раскидывать (флаг GF_REMORT, проверка на делимость гривен, проверка на + // то, что чар находился в комнате с мобом не менее половины раундов дамагера) + void drop_torc(CHAR_DATA *mob) + { + if (!mob->get_role(MOB_ROLE_BOSS) + || has_connected_bosses(mob)) + { + return; + } + + log("[Extract char] Checking %s for ExtMoney.", mob->get_name().c_str()); + + std::pair damager = mob->get_max_damager_in_room(); + DESCRIPTOR_DATA *d = nullptr; + if (damager.first > 0) { + d = DescByUID(damager.first); + } + if (!d) { + return; + } + auto grp = d->character->personGroup; + CHAR_DATA *leader = grp != nullptr? d->character->personGroup->getLeader() : d->character.get(); + + int members = 1; + members = leader->personGroup->size(mob->in_room); + + const int zone_lvl = zone_table[mob_index[GET_MOB_RNUM(mob)].zone].mob_level; + const int drop = calc_drop_torc(zone_lvl, members); + if (drop <= 0) + { + return; + } + + if (IN_ROOM(leader) == IN_ROOM(mob) + && GET_GOD_FLAG(leader, GF_REMORT) + && (GET_UNIQUE(leader) == damager.first + || mob->get_attacker(leader, ATTACKER_ROUNDS) >= damager.second / 2)) { + gain_torc(leader, drop); + } + // он был один, сваливаем + if (grp == nullptr) + return; + auto m_list = grp->getMembers(mob->in_room); // список живых мемберов в комнате + for (auto m : m_list) { + if (GET_GOD_FLAG(m, GF_REMORT) && mob->get_attacker(m, ATTACKER_ROUNDS) >= damager.second / 2) { + gain_torc(m, drop); + } + } + } -// проверка, требуется ли от чара жертвовать для реморта -bool need_torc(CHAR_DATA *ch) -{ - TorcReq torc_req(GET_REMORT(ch)); + void player_drop_log(CHAR_DATA *ch, unsigned type, int diff) + { + int total_bronze = ch->get_ext_money(TORC_BRONZE); + total_bronze += ch->get_ext_money(TORC_SILVER) * TORC_EXCH_RATE; + total_bronze += ch->get_ext_money(TORC_GOLD) * TORC_EXCH_RATE * TORC_EXCH_RATE; + + log("ExtMoney: %s%s%d%s, sum=%d", + ch->get_name().c_str(), + (diff > 0 ? " +" : " "), + diff, + ((type == TORC_GOLD) ? "g" : (type == TORC_SILVER) ? "s" : "b"), + total_bronze); + } - if (torc_req.type < TOTAL_TYPES && torc_req.amount > 0) - { - return true; - } + // распечатка переменных из конфига + void show_config(CHAR_DATA *ch) + { + std::stringstream out; + out << "&SТекущие значения основных параметров:\r\n" + << "WHERE_TO_REMORT_STR = " << WHERE_TO_REMORT_STR << "\r\n" + << "TORC_EXCH_RATE = " << TORC_EXCH_RATE << "\r\n" + + << "GOLD_MORT_NUM = " << type_list[TORC_GOLD].MORT_NUM << "\r\n" + << "GOLD_MORT_REQ = " << type_list[TORC_GOLD].MORT_REQ << "\r\n" + << "GOLD_MORT_REQ_ADD_PER_MORT = " << type_list[TORC_GOLD].MORT_REQ_ADD_PER_MORT << "\r\n" + << "GOLD_DROP_LVL = " << type_list[TORC_GOLD].DROP_LVL << "\r\n" + << "GOLD_DROP_AMOUNT = " << type_list[TORC_GOLD].DROP_AMOUNT << "\r\n" + << "GOLD_DROP_AMOUNT_ADD_PER_LVL = " << type_list[TORC_GOLD].DROP_AMOUNT_ADD_PER_LVL << "\r\n" + << "GOLD_MINIMUM_DAYS = " << type_list[TORC_GOLD].MINIMUM_DAYS << "\r\n" + + << "SILVER_MORT_NUM = " << type_list[TORC_SILVER].MORT_NUM << "\r\n" + << "SILVER_MORT_REQ = " << type_list[TORC_SILVER].MORT_REQ << "\r\n" + << "SILVER_MORT_REQ_ADD_PER_MORT = " << type_list[TORC_SILVER].MORT_REQ_ADD_PER_MORT << "\r\n" + << "SILVER_DROP_LVL = " << type_list[TORC_SILVER].DROP_LVL << "\r\n" + << "SILVER_DROP_AMOUNT = " << type_list[TORC_SILVER].DROP_AMOUNT << "\r\n" + << "SILVER_DROP_AMOUNT_ADD_PER_LVL = " << type_list[TORC_SILVER].DROP_AMOUNT_ADD_PER_LVL << "\r\n" + << "SILVER_MINIMUM_DAYS = " << type_list[TORC_SILVER].MINIMUM_DAYS << "\r\n" + + << "BRONZE_MORT_NUM = " << type_list[TORC_BRONZE].MORT_NUM << "\r\n" + << "BRONZE_MORT_REQ = " << type_list[TORC_BRONZE].MORT_REQ << "\r\n" + << "BRONZE_MORT_REQ_ADD_PER_MORT = " << type_list[TORC_BRONZE].MORT_REQ_ADD_PER_MORT << "\r\n" + << "BRONZE_DROP_LVL = " << type_list[TORC_BRONZE].DROP_LVL << "\r\n" + << "BRONZE_DROP_AMOUNT = " << type_list[TORC_BRONZE].DROP_AMOUNT << "\r\n" + << "BRONZE_DROP_AMOUNT_ADD_PER_LVL = " << type_list[TORC_BRONZE].DROP_AMOUNT_ADD_PER_LVL << "\r\n" + << "BRONZE_MINIMUM_DAYS = " << type_list[TORC_BRONZE].MINIMUM_DAYS << "\r\n"; + + send_to_char(out.str(), ch); + } - return false; -} +} // namespace ExtMoney -} // namespace Remort namespace { @@ -1029,4 +891,92 @@ int torc(CHAR_DATA *ch, void *me, int cmd, char* /*argument*/) return 0; } +void Remort::init() +{ + pugi::xml_document doc; + pugi::xml_parse_result result = doc.load_file(Remort::CONFIG_FILE); + if (!result) + { + snprintf(buf, MAX_STRING_LENGTH, "...%s", result.description()); + mudlog(buf, CMP, LVL_IMMORT, SYSLOG, TRUE); + return; + } + + pugi::xml_node main_node = doc.child("remort"); + if (!main_node) + { + snprintf(buf, MAX_STRING_LENGTH, "... read fail"); + mudlog(buf, CMP, LVL_IMMORT, SYSLOG, TRUE); + return; + } + + WHERE_TO_REMORT_STR = Parse::child_value_str(main_node, "WHERE_TO_REMORT_STR"); + TORC_EXCH_RATE = Parse::child_value_int(main_node, "TORC_EXCH_RATE"); + + type_list[TORC_GOLD].MORT_NUM = Parse::child_value_int(main_node, "GOLD_MORT_NUM"); + type_list[TORC_GOLD].MORT_REQ = Parse::child_value_int(main_node, "GOLD_MORT_REQ"); + type_list[TORC_GOLD].MORT_REQ_ADD_PER_MORT = Parse::child_value_int(main_node, "GOLD_MORT_REQ_ADD_PER_MORT"); + type_list[TORC_GOLD].DROP_LVL = Parse::child_value_int(main_node, "GOLD_DROP_LVL"); + type_list[TORC_GOLD].DROP_AMOUNT = Parse::child_value_int(main_node, "GOLD_DROP_AMOUNT"); + type_list[TORC_GOLD].DROP_AMOUNT_ADD_PER_LVL = Parse::child_value_int(main_node, "GOLD_DROP_AMOUNT_ADD_PER_LVL"); + type_list[TORC_GOLD].MINIMUM_DAYS = Parse::child_value_int(main_node, "GOLD_MINIMUM_DAYS"); + + type_list[TORC_SILVER].MORT_NUM = Parse::child_value_int(main_node, "SILVER_MORT_NUM"); + type_list[TORC_SILVER].MORT_REQ = Parse::child_value_int(main_node, "SILVER_MORT_REQ"); + type_list[TORC_SILVER].MORT_REQ_ADD_PER_MORT = Parse::child_value_int(main_node, "SILVER_MORT_REQ_ADD_PER_MORT"); + type_list[TORC_SILVER].DROP_LVL = Parse::child_value_int(main_node, "SILVER_DROP_LVL"); + type_list[TORC_SILVER].DROP_AMOUNT = Parse::child_value_int(main_node, "SILVER_DROP_AMOUNT"); + type_list[TORC_SILVER].DROP_AMOUNT_ADD_PER_LVL = Parse::child_value_int(main_node, "SILVER_DROP_AMOUNT_ADD_PER_LVL"); + type_list[TORC_SILVER].MINIMUM_DAYS = Parse::child_value_int(main_node, "SILVER_MINIMUM_DAYS"); + + type_list[TORC_BRONZE].MORT_NUM = Parse::child_value_int(main_node, "BRONZE_MORT_NUM"); + type_list[TORC_BRONZE].MORT_REQ = Parse::child_value_int(main_node, "BRONZE_MORT_REQ"); + type_list[TORC_BRONZE].MORT_REQ_ADD_PER_MORT = Parse::child_value_int(main_node, "BRONZE_MORT_REQ_ADD_PER_MORT"); + type_list[TORC_BRONZE].DROP_LVL = Parse::child_value_int(main_node, "BRONZE_DROP_LVL"); + type_list[TORC_BRONZE].DROP_AMOUNT = Parse::child_value_int(main_node, "BRONZE_DROP_AMOUNT"); + type_list[TORC_BRONZE].DROP_AMOUNT_ADD_PER_LVL = Parse::child_value_int(main_node, "BRONZE_DROP_AMOUNT_ADD_PER_LVL"); + type_list[TORC_BRONZE].MINIMUM_DAYS = Parse::child_value_int(main_node, "BRONZE_MINIMUM_DAYS"); + + // не из конфига, но инится заодно со всеми + type_list[TORC_GOLD].DESC_MESSAGE_NUM = WHAT_TGOLD; + type_list[TORC_SILVER].DESC_MESSAGE_NUM = WHAT_TSILVER; + type_list[TORC_BRONZE].DESC_MESSAGE_NUM = WHAT_TBRONZE; + type_list[TORC_GOLD].DESC_MESSAGE_U_NUM = WHAT_TGOLDu; + type_list[TORC_SILVER].DESC_MESSAGE_U_NUM = WHAT_TSILVERu; + type_list[TORC_BRONZE].DESC_MESSAGE_U_NUM = WHAT_TBRONZEu; +} + + +TorcReq::TorcReq(int rmrt) +{ + // type + if (rmrt >= type_list[TORC_GOLD].MORT_NUM) + { + type = TORC_GOLD; + } + else if (rmrt >= type_list[TORC_SILVER].MORT_NUM) + { + type = TORC_SILVER; + } + else if (rmrt >= type_list[TORC_BRONZE].MORT_NUM) + { + type = TORC_BRONZE; + } + else + { + type = TOTAL_TYPES; + } + // amount + if (type != TOTAL_TYPES) + { + amount = type_list[type].MORT_REQ + + (rmrt - type_list[type].MORT_NUM) * type_list[type].MORT_REQ_ADD_PER_MORT; + } + else + { + amount = 0; + } +} + + // vim: ts=4 sw=4 tw=0 noet syntax=cpp : diff --git a/src/ext_money.hpp b/src/ext_money.hpp index ccea31ee6..7e1bc94f6 100644 --- a/src/ext_money.hpp +++ b/src/ext_money.hpp @@ -8,34 +8,33 @@ #include "conf.h" #include "sysdep.h" #include "structs.h" -#include "chars/char.hpp" +//#include "chars/char.hpp" #include -namespace ExtMoney -{ +class CHAR_DATA; -void torc_exch_menu(CHAR_DATA *ch); -void torc_exch_parse(CHAR_DATA *ch, const char *arg); +namespace ExtMoney { + void torc_exch_menu(CHAR_DATA *ch); + void torc_exch_parse(CHAR_DATA *ch, const char *arg); -void drop_torc(CHAR_DATA *mob); -std::string draw_daily_limit(CHAR_DATA *ch, bool imm_stat = false); + void drop_torc(CHAR_DATA *mob); + std::string draw_daily_limit(CHAR_DATA *ch, bool imm_stat = false); -void player_drop_log(CHAR_DATA *ch, unsigned type, int num); -std::string name_currency_plural(std::string name); + void player_drop_log(CHAR_DATA *ch, unsigned type, int num); + std::string name_currency_plural(std::string name); + void show_config(CHAR_DATA *ch); } // namespace ExtMoney -namespace Remort +struct TorcReq { + TorcReq(int rmrt); + // тип гривн + unsigned type; + // кол-во + int amount; +}; -extern std::string WHERE_TO_REMORT_STR; - -bool can_remort_now(CHAR_DATA *ch); -void init(); -void show_config(CHAR_DATA *ch); -bool need_torc(CHAR_DATA *ch); - -} // namespace Remort int torc(CHAR_DATA *ch, void *me, int cmd, char* argument); diff --git a/src/features.cpp b/src/features.cpp index ecd895679..725e8c293 100644 --- a/src/features.cpp +++ b/src/features.cpp @@ -23,12 +23,12 @@ #include "room.hpp" #include "screen.h" #include "fightsystem/pk.h" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "room.hpp" #include "house.h" #include "screen.h" #include "fightsystem/pk.h" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "utils.h" #include "structs.h" #include "sysdep.h" diff --git a/src/fightsystem/fight.cpp b/src/fightsystem/fight.cpp index 9265aa303..d603aaebd 100644 --- a/src/fightsystem/fight.cpp +++ b/src/fightsystem/fight.cpp @@ -36,7 +36,7 @@ #include "spells.h" #include "screen.h" #include "constants.h" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "im.h" #include "skills.h" #include "features.hpp" @@ -2390,33 +2390,4 @@ void perform_violence() } } -int calc_leadership(CHAR_DATA * ch) -{ - int prob, percent; - - if (IS_NPC(ch) || ch->personGroup == nullptr) { - return FALSE; - } - - CHAR_DATA* leader = ch->personGroup->getLeader(); - - // если лидер умер или нет в комнате - фиг вам, а не бонусы - if (leader == nullptr || IN_ROOM(ch) != IN_ROOM(leader)) { - return FALSE; - } - - if (!leader->get_skill(SKILL_LEADERSHIP)) { - return (FALSE); - } - - percent = number(1, 101); - prob = calculate_skill(leader, SKILL_LEADERSHIP, 0); - if (percent > prob) { - return (FALSE); - } - else { - return (TRUE); - } -} - // vim: ts=4 sw=4 tw=0 noet syntax=cpp : diff --git a/src/fightsystem/fight.h b/src/fightsystem/fight.h index f129e82d4..da6e261fa 100644 --- a/src/fightsystem/fight.h +++ b/src/fightsystem/fight.h @@ -161,8 +161,6 @@ void alt_equip(CHAR_DATA *ch, int pos, int dam, int chance); void char_dam_message(int dam, CHAR_DATA *ch, CHAR_DATA *victim, bool mayflee); void test_self_hitroll(CHAR_DATA *ch); -int calc_leadership(CHAR_DATA * ch); - #endif // vim: ts=4 sw=4 tw=0 noet syntax=cpp : diff --git a/src/fightsystem/fight.penalties.cpp b/src/fightsystem/fight.penalties.cpp index 79db0bc93..e69de29bb 100644 --- a/src/fightsystem/fight.penalties.cpp +++ b/src/fightsystem/fight.penalties.cpp @@ -1,92 +0,0 @@ -#include "fight.penalties.hpp" - -#include "logger.hpp" -#include "utils.h" -#include "chars/char.hpp" - -int GroupPenaltyCalculator::get() const -{ - const bool leader_is_npc = IS_NPC(m_leader); - const bool leader_in_group = AFF_FLAGGED(m_leader, EAffectFlag::AFF_GROUP); - const bool leader_is_in_room = leader_in_group - && m_leader->in_room == IN_ROOM(m_killer); - if (!leader_is_npc - && leader_is_in_room) - { - int penalty = 0; - if (penalty_by_leader(m_leader, penalty)) - { - return penalty; - } - } - - for (auto f = m_leader->followers; f; f = f->next) - { - const bool follower_is_npc = IS_NPC(f->follower); - const bool follower_is_in_room = AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP) - && f->follower->in_room == IN_ROOM(m_killer); - - if (follower_is_npc - || !follower_is_in_room) - { - continue; - } - - int penalty = 0; - if (penalty_by_leader(f->follower, penalty)) - { - if (0 < penalty) - { - return penalty; - } - } - } - - return 0; -} - -bool GroupPenaltyCalculator::penalty_by_leader(const CHAR_DATA* player, int& penalty) const -{ - const int player_remorts = static_cast(GET_REMORT(player)); - const int player_class = static_cast(GET_CLASS(player)); - const int player_level = GET_LEVEL(player); - - if (IS_NPC(player)) - { - log("LOGIC ERROR: try to get penalty for NPC [%s], VNum: %d\n", - player->get_name().c_str(), - GET_MOB_VNUM(player)); - penalty = DEFAULT_PENALTY; - return true; - } - - if (0 > player_class - || player_class > NUM_PLAYER_CLASSES) - { - log("LOGIC ERROR: wrong player class: %d for player [%s]", - player_class, - player->get_name().c_str()); - penalty = DEFAULT_PENALTY; - return true; - } - - if (0 > player_remorts - || player_remorts > MAX_REMORT) - { - log("LOGIC ERROR: wrong number of remorts: %d for player [%s]", - player_remorts, - player->get_name().c_str()); - penalty = DEFAULT_PENALTY; - return true; - } - - bool result = false; - if (m_max_level - player_level > m_grouping[player_class][player_remorts]) - { - penalty = 50 + 2 * (m_max_level - player_level - m_grouping[player_class][player_remorts]); - result = true; - } - - return result; -} - diff --git a/src/fightsystem/fight.penalties.hpp b/src/fightsystem/fight.penalties.hpp index 736f17fb3..c2af01df2 100644 --- a/src/fightsystem/fight.penalties.hpp +++ b/src/fightsystem/fight.penalties.hpp @@ -5,28 +5,6 @@ class CHAR_DATA; // to avoid inclusion of "char.hpp" -class GroupPenaltyCalculator -{ -public: - constexpr static int DEFAULT_PENALTY = 100; - GroupPenaltyCalculator(const CHAR_DATA* killer, const CHAR_DATA* leader, const int max_level, const GroupPenalties& grouping): - m_killer(killer), - m_leader(leader), - m_max_level(max_level), - m_grouping(grouping) - { - } - - int get() const; - -private: - const CHAR_DATA* m_killer; - const CHAR_DATA* m_leader; - const int m_max_level; - const GroupPenalties& m_grouping; - - bool penalty_by_leader(const CHAR_DATA* player, int& penalty) const; -}; #endif // __FIGHT_PENALTIES_HPP__ diff --git a/src/fightsystem/fight_hit.cpp b/src/fightsystem/fight_hit.cpp index 09d78d63e..84b50e694 100644 --- a/src/fightsystem/fight_hit.cpp +++ b/src/fightsystem/fight_hit.cpp @@ -3,7 +3,7 @@ #include "logger.hpp" #include "handler.h" #include "screen.h" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "skills.h" #include "magic.h" #include "pk.h" @@ -2456,60 +2456,41 @@ void update_pk_logs(CHAR_DATA *ch, CHAR_DATA *victim) void Damage::process_death(CHAR_DATA *ch, CHAR_DATA *victim) { - CHAR_DATA *killer = NULL; - - if (IS_NPC(victim) || victim->desc) - { - if (victim == ch && IN_ROOM(victim) != NOWHERE) - { - if (spell_num == SPELL_POISON) - { - for (const auto poisoner : world[IN_ROOM(victim)]->people) - { - if (poisoner != victim - && GET_ID(poisoner) == victim->Poisoner) - { + CHAR_DATA *killer = nullptr; + + // вычисляем, чем убили и кто убивец. Но тут какая то херня :( + if (IS_NPC(victim) || victim->desc) { + if (victim == ch && IN_ROOM(victim) != NOWHERE) { + if (spell_num == SPELL_POISON) { + for (const auto poisoner : world[IN_ROOM(victim)]->people) { + if (poisoner != victim && GET_ID(poisoner) == victim->Poisoner) { killer = poisoner; } } - } - else if (msg_num == TYPE_SUFFERING) - { - for (const auto attacker : world[IN_ROOM(victim)]->people) - { - if (attacker->get_fighting() == victim) - { + } else if (msg_num == TYPE_SUFFERING) { + for (const auto attacker : world[IN_ROOM(victim)]->people) { + if (attacker->get_fighting() == victim) { killer = attacker; } } } } - - if (ch != victim) - { + if (ch != victim) { killer = ch; } } - if (killer) - { - if (AFF_FLAGGED(killer, EAffectFlag::AFF_GROUP)) - { + if (killer) { + // и чармис и хозяин в одной группе + if (IN_GROUP(killer)) + group_gain(killer, victim); + + if (AFF_FLAGGED(killer, EAffectFlag::AFF_GROUP)) { // т.к. помечен флагом AFF_GROUP - точно PC - group_gain(killer, victim); - } - else if ((AFF_FLAGGED(killer, EAffectFlag::AFF_CHARM) - || AFF_FLAGGED(killer, EAffectFlag::AFF_HELPER) - || MOB_FLAGGED(killer, MOB_PLAYER_SUMMON)) - && killer->has_master()) - // killer - зачармленный NPC с хозяином - { - // по логике надо бы сделать, что если хозяина нет в клетке, но - // кто-то из группы хозяина в клетке, то опыт накинуть согруппам, - // которые рядом с убившим моба чармисом. - if (AFF_FLAGGED(killer->get_master(), EAffectFlag::AFF_GROUP) - && IN_ROOM(killer) == IN_ROOM(killer->get_master())) - { + + } else + if ((IS_CHARMICE(killer) || MOB_FLAGGED(killer, MOB_PLAYER_SUMMON)) && killer->has_master()) { + if (IN_GROUP(killer->get_master()) && IN_ROOM(killer) == IN_ROOM(killer->get_master())) { // Хозяин - PC в группе => опыт группе group_gain(killer->get_master(), victim); } diff --git a/src/fightsystem/fight_stuff.cpp b/src/fightsystem/fight_stuff.cpp index 85fb1dd6c..676237d69 100644 --- a/src/fightsystem/fight_stuff.cpp +++ b/src/fightsystem/fight_stuff.cpp @@ -1,48 +1,47 @@ // Part of Bylins http://www.mud.ru #include "core/affect_data.h" -#include "mobact.hpp" -#include "obj.hpp" -#include "skills/flee.h" +#include "core/leveling.h" +#include "backtrace.hpp" +#include "bonus.h" +#include "chars/char.hpp" +#include "chars/char_player.hpp" #include "chars/world.characters.hpp" +#include "constants.h" +#include "corpse.hpp" +#include "db.h" +#include "dg/dg_scripts.h" #include "fight.h" -#include "fight.penalties.hpp" #include "fight_hit.hpp" -#include "chars/char.hpp" -#include "skills.h" +#include "grp/grp.main.h" #include "handler.h" -#include "db.h" -#include "room.hpp" -#include "spells.h" -#include "dg_scripts.h" -#include "corpse.hpp" #include "house.h" -#include "pk.h" -#include "stuff.hpp" -#include "sets_drop.hpp" -#include "top.h" -#include "constants.h" -#include "screen.h" +#include "logger.hpp" #include "magic.h" #include "mob_stat.hpp" -#include "logger.hpp" -#include "bonus.h" -#include "backtrace.hpp" +#include "mob_stat.hpp" +#include "mobact.hpp" +#include "obj.hpp" +#include "object.prototypes.hpp" +#include "pk.h" +#include "room.hpp" +#include "screen.h" +#include "sets_drop.hpp" +#include "skills.h" +#include "skills/flee.h" #include "spell_parser.hpp" +#include "spells.h" +#include "stuff.hpp" +#include "top.h" #include "world.objects.hpp" -#include "object.prototypes.hpp" #include "zone.table.hpp" -#include "chars/char_player.hpp" -#include "mob_stat.hpp" -#include +#include #include // extern void perform_drop_gold(CHAR_DATA * ch, int amount); -int level_exp(CHAR_DATA * ch, int chlevel); -int max_exp_gain_pc(CHAR_DATA * ch); -int max_exp_loss_pc(CHAR_DATA * ch); + void get_from_container(CHAR_DATA * ch, OBJ_DATA * cont, char *arg, int mode, int amount, bool autoloot); int slot_for_char(CHAR_DATA * ch, int i); void set_wait(CHAR_DATA * ch, int waittime, int victim_in_room); @@ -53,6 +52,11 @@ extern int max_exp_gain_npc; //интервал в секундах между восстановлением кругов после рипа extern const unsigned RECALL_SPELLS_INTERVAL; const unsigned RECALL_SPELLS_INTERVAL = 28; +// using + +using namespace ExpCalc; + + void process_mobmax(CHAR_DATA *ch, CHAR_DATA *killer) { bool leader_partner = false; @@ -331,7 +335,8 @@ bool check_tester_death(CHAR_DATA *ch, CHAR_DATA *killer) void die(CHAR_DATA *ch, CHAR_DATA *killer) { - int dec_exp = 0, e = GET_EXP(ch); + int dec_exp = 0; + long currentExp = GET_EXP(ch); if (!IS_NPC(ch) && (ch->in_room == NOWHERE)) { @@ -362,23 +367,16 @@ void die(CHAR_DATA *ch, CHAR_DATA *killer) return; } - if (IS_NPC(ch) - || !ROOM_FLAGGED(ch->in_room, ROOM_ARENA) - || RENTABLE(ch)) - { + if (IS_NPC(ch) || !ROOM_FLAGGED(ch->in_room, ROOM_ARENA) || RENTABLE(ch)) { if (!(IS_NPC(ch) || IS_IMMORTAL(ch) || GET_GOD_FLAG(ch, GF_GODSLIKE) || (killer && PRF_FLAGGED(killer, PRF_EXECUTOR))))//если убил не палач { - if (!RENTABLE(ch)) - dec_exp = (level_exp(ch, GET_LEVEL(ch) + 1) - level_exp(ch, GET_LEVEL(ch))) / (3 + MIN(3, GET_REMORT(ch) / 5)) / ch->death_player_count(); - else - dec_exp = (level_exp(ch, GET_LEVEL(ch) + 1) - level_exp(ch, GET_LEVEL(ch))) / (3 + MIN(3, GET_REMORT(ch) / 5)); - gain_exp(ch, -dec_exp); - dec_exp = e - GET_EXP(ch); - sprintf(buf, "Вы потеряли %d %s опыта.\r\n", - dec_exp, desc_count(dec_exp, WHAT_POINT)); + dec_exp = calcDeathExp(ch); + ExpCalc::gain_exp(ch, -dec_exp); + dec_exp = (int)(currentExp - GET_EXP(ch)); + sprintf(buf, "Вы потеряли %d %s опыта.\r\n", dec_exp, desc_count(dec_exp, WHAT_POINT)); send_to_char(buf, ch); } @@ -794,215 +792,6 @@ void raw_kill(CHAR_DATA *ch, CHAR_DATA *killer) } } -int get_remort_mobmax(CHAR_DATA * ch) -{ - int remort = GET_REMORT(ch); - if (remort >= 18) - return 15; - if (remort >= 14) - return 7; - if (remort >= 9) - return 4; - return 0; -} - -float get_npc_long_live_exp_bounus(int vnum) -{ - if (vnum == -1) { - return 1.0f; - } - int nowTime = time(0); - int lastKillTime = mob_stat::last_time_killed_mob(vnum); - if ( lastKillTime > 0) { - int deltaTime = nowTime - lastKillTime; - int delay = 60 * 60 * 24 * 30 ; // 30 days - float timeMultiplicator = floor(deltaTime/delay); - if (timeMultiplicator<1.0f) { - timeMultiplicator = 1.0f; - } - if (timeMultiplicator>10.0f) { - timeMultiplicator = 10.0f; - } - return timeMultiplicator; - } - return 1.0f; -} - -int get_extend_exp(int exp, CHAR_DATA * ch, CHAR_DATA * victim) -{ - int base, diff; - int koef; - - if (!IS_NPC(victim) || IS_NPC(ch)) - return (exp); - // если моб убивается первый раз, то повышаем экспу в несколько раз - // стимулируем изучение новых зон! - ch->send_to_TC(false, true, false, - "&RУ моба еще %d убийств без замакса, экспа %d, убито %d&n\r\n", - mob_proto[victim->get_rnum()].mob_specials.MaxFactor, - exp, - ch->mobmax_get(GET_MOB_VNUM(victim))); -// все равно таблица корявая, учитываются только уникальные мобы и глючит -/* - // даем увеличенную экспу за давно не убитых мобов. - // за совсем неубитых мобов не даем, что бы новые зоны не давали x10 экспу. - exp *= get_npc_long_live_exp_bounus(GET_MOB_VNUM(victim)); -*/ -/* бонусы за непопулярных мобов круче - if (ch->mobmax_get(GET_MOB_VNUM(victim)) == 0) { - // так чуть-чуть поприятней - exp *= 1.5; - exp /= std::max(1.0, 0.5 * (GET_REMORT(ch) - MAX_EXP_COEFFICIENTS_USED)); - return (exp); - } -*/ - exp += exp * (ch->add_abils.percent_exp_add) / 100.0; - for (koef = 100, base = 0, diff = ch->mobmax_get(GET_MOB_VNUM(victim)) - mob_proto[victim->get_rnum()].mob_specials.MaxFactor; - base < diff && koef > 5; base++, koef = koef * (95 - get_remort_mobmax(ch)) / 100); - // минимальный опыт при замаксе 15% от полного опыта - exp = exp * MAX(15, koef) / 100; - - // делим на реморты - exp /= std::max(1.0, 0.5 * (GET_REMORT(ch) - MAX_EXP_COEFFICIENTS_USED)); - return (exp); -} - -// When ch kills victim -void change_alignment(CHAR_DATA * ch, CHAR_DATA * victim) -{ - /* - * new alignment change algorithm: if you kill a monster with alignment A, - * you move 1/16th of the way to having alignment -A. Simple and fast. - */ - GET_ALIGNMENT(ch) += (-GET_ALIGNMENT(victim) - GET_ALIGNMENT(ch)) / 16; -} - -/*++ - Функция начисления опыта - ch - кому опыт начислять - Вызов этой функции для NPC смысла не имеет, но все равно - какие-то проверки внутри зачем то делаются ---*/ -void perform_group_gain(CHAR_DATA * ch, CHAR_DATA * victim, int members, int koef) -{ - - -// Странно, но для NPC эта функция тоже должна работать -// if (IS_NPC(ch) || !OK_GAIN_EXP(ch,victim)) - if (!OK_GAIN_EXP(ch, victim)) - { - send_to_char("Ваше деяние никто не оценил.\r\n", ch); - return; - } - - // 1. Опыт делится поровну на всех - int exp = GET_EXP(victim) / MAX(members, 1); - - if(victim->get_zone_group() > 1 && members < victim->get_zone_group()) - { - // в случае груп-зоны своего рода планка на мин кол-во человек в группе - exp = GET_EXP(victim) / victim->get_zone_group(); - } - - // 2. Учитывается коэффициент (лидерство, разность уровней) - // На мой взгляд его правильней использовать тут а не в конце процедуры, - // хотя в большинстве случаев это все равно - exp = exp * koef / 100; - - // 3. Вычисление опыта для PC и NPC - if (IS_NPC(ch)) - { - exp = MIN(max_exp_gain_npc, exp); - exp += MAX(0, (exp * MIN(4, (GET_LEVEL(victim) - GET_LEVEL(ch)))) / 8); - } - else - exp = MIN(max_exp_gain_pc(ch), get_extend_exp(exp, ch, victim)); - // 4. Последняя проверка - exp = MAX(1, exp); - if (exp > 1) - { - if (Bonus::is_bonus(Bonus::BONUS_EXP)) - { - exp *= Bonus::get_mult_bonus(); - } - - if (!IS_NPC(ch) && !ch->affected.empty()) - { - for (const auto aff : ch->affected) - { - if (aff->location == APPLY_BONUS_EXP) // скушал свиток с эксп бонусом - { - exp *= MIN(3, aff->modifier); // бонус макс тройной - } - } - } - - int long_live_bonus = floor(get_npc_long_live_exp_bounus( GET_MOB_VNUM(victim))); - if (long_live_bonus>1) { - std::string mess = ""; - switch (long_live_bonus) - { - case 2: - mess = "Редкая удача! Опыт повышен!\r\n"; - break; - case 3: - mess = "Очень редкая удача! Опыт повышен!\r\n"; - break; - case 4: - mess = "Очень-очень редкая удача! Опыт повышен!\r\n"; - break; - case 5: - mess = "Вы везунчик! Опыт повышен!\r\n"; - break; - case 6: - mess = "Ваша удача велика! Опыт повышен!\r\n"; - break; - case 7: - mess = "Ваша удача достигла небес! Опыт повышен!\r\n"; - break; - case 8: - mess = "Ваша удача коснулась луны! Опыт повышен!\r\n"; - break; - case 9: - mess = "Ваша удача затмевает солнце! Опыт повышен!\r\n"; - break; - case 10: - mess = "Ваша удача выше звезд! Опыт повышен!\r\n"; - break; - default: - mess = "Ваша удача выше звезд! Опыт повышен!\r\n"; - break; - } - send_to_char(mess.c_str(), ch); - } - - exp = MIN(max_exp_gain_pc(ch), exp); - send_to_char(ch, "Ваш опыт повысился на %d %s.\r\n", exp, desc_count(exp, WHAT_POINT)); - } - else if (exp == 1) { - send_to_char("Ваш опыт повысился всего лишь на маленькую единичку.\r\n", ch); - } - gain_exp(ch, exp); - change_alignment(ch, victim); - TopPlayer::Refresh(ch); - - if (!EXTRA_FLAGGED(victim, EXTRA_GRP_KILL_COUNT) - && !IS_NPC(ch) - && !IS_IMMORTAL(ch) - && IS_NPC(victim) - && !IS_CHARMICE(victim) - && !ROOM_FLAGGED(IN_ROOM(victim), ROOM_ARENA)) - { - mob_stat::add_mob(victim, members); - EXTRA_FLAGS(victim).set(EXTRA_GRP_KILL_COUNT); - } - else if (IS_NPC(ch) && !IS_NPC(victim) - && !ROOM_FLAGGED(IN_ROOM(victim), ROOM_ARENA)) - { - mob_stat::add_mob(ch, 0); - } -} - int grouping_koef(int player_class, int player_remort) { @@ -1012,138 +801,6 @@ int grouping_koef(int player_class, int player_remort) } - -/*++ - Функция расчитывает всякие бонусы для группы при получении опыта, - после чего вызывает функцию получения опыта для всех членов группы - Т.к. членом группы может быть только PC, то эта функция раздаст опыт только PC - - ch - обязательно член группы, из чего следует: - 1. Это не NPC - 2. Он находится в группе лидера (или сам лидер) - - Просто для PC-последователей эта функция не вызывается - ---*/ -void group_gain(CHAR_DATA * killer, CHAR_DATA * victim) -{ - int inroom_members, koef = 100, maxlevel; - struct follow_type *f; - int partner_count = 0; - int total_group_members = 1; - bool use_partner_exp = false; - - // если наем лидер, то тоже режем экспу - if (can_use_feat(killer, CYNIC_FEAT)) - { - maxlevel = 300; - } - else - { - maxlevel = GET_LEVEL(killer); - } - - auto leader = killer->get_master(); - if (nullptr == leader) - { - leader = killer; - } - - // k - подозрение на лидера группы - const bool leader_inroom = AFF_FLAGGED(leader, EAffectFlag::AFF_GROUP) - && leader->in_room == IN_ROOM(killer); - - // Количество согрупников в комнате - if (leader_inroom) - { - inroom_members = 1; - maxlevel = GET_LEVEL(leader); - } - else - { - inroom_members = 0; - } - - // Вычисляем максимальный уровень в группе - for (f = leader->followers; f; f = f->next) - { - if (AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP)) ++total_group_members; - if (AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP) - && f->follower->in_room == IN_ROOM(killer)) - { - // если в группе наем, то режим опыт всей группе - // дабы наема не выгодно было бы брать в группу - // ставим 300, чтобы вообще под ноль резало - if (can_use_feat(f->follower, CYNIC_FEAT)) - { - maxlevel = 300; - } - // просмотр членов группы в той же комнате - // член группы => PC автоматически - ++inroom_members; - maxlevel = MAX(maxlevel, GET_LEVEL(f->follower)); - if (!IS_NPC(f->follower)) - { - partner_count++; - } - } - } - - GroupPenaltyCalculator group_penalty(killer, leader, maxlevel, grouping); - koef -= group_penalty.get(); - - koef = MAX(0, koef); - - // Лидерство используется, если в комнате лидер и есть еще хоть кто-то - // из группы из PC (последователи типа лошади или чармисов не считаются) - if (koef >= 100 && leader_inroom && (inroom_members > 1) && calc_leadership(leader)) - { - koef += 20; - } - - // Раздача опыта - - // если групповой уровень зоны равняется единице - if (zone_table[world[killer->in_room]->zone].group < 2) - { - // чтобы не абьюзили на суммонах, когда в группе на самом деле больше - // двух мемберов, но лишних реколят перед непосредственным рипом - use_partner_exp = total_group_members == 2; - } - - // если лидер группы в комнате - if (leader_inroom) - { - // если у лидера группы есть способность напарник - if (can_use_feat(leader, PARTNER_FEAT) && use_partner_exp) - { - // если в группе всего двое человек - // k - лидер, и один последователь - if (partner_count == 1) - { - // и если кожф. больше или равен 100 - if (koef >= 100) - { - if (leader->get_zone_group() < 2) - { - koef += 100; - } - } - } - } - perform_group_gain(leader, victim, inroom_members, koef); - } - - for (f = leader->followers; f; f = f->next) - { - if (AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP) - && f->follower->in_room == IN_ROOM(killer)) - { - perform_group_gain(f->follower, victim, inroom_members, koef); - } - } -} - void gain_battle_exp(CHAR_DATA *ch, CHAR_DATA *victim, int dam) { // не даем получать батлу с себя по зеркалу? diff --git a/src/find.obj.id.by.vnum.cpp b/src/find.obj.id.by.vnum.cpp index 38e0ee05a..d8fb0299b 100644 --- a/src/find.obj.id.by.vnum.cpp +++ b/src/find.obj.id.by.vnum.cpp @@ -1,7 +1,7 @@ #include "find.obj.id.by.vnum.hpp" #include "logger.hpp" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "world.objects.hpp" #include "chars/char.hpp" #include "obj.hpp" diff --git a/src/glory.cpp b/src/glory.cpp index f6b2146a0..34b09e88c 100644 --- a/src/glory.cpp +++ b/src/glory.cpp @@ -24,7 +24,6 @@ #include extern void add_karma(CHAR_DATA * ch, const char * punish , const char * reason); -extern void check_max_hp(CHAR_DATA *ch); namespace Glory { diff --git a/src/glory_const.cpp b/src/glory_const.cpp index 37f5eb3ce..b328873a0 100644 --- a/src/glory_const.cpp +++ b/src/glory_const.cpp @@ -30,7 +30,6 @@ #include extern void add_karma(CHAR_DATA * ch, const char * punish , const char * reason); -extern void check_max_hp(CHAR_DATA *ch); namespace GloryConst { diff --git a/src/grp/follow.cpp b/src/grp/follow.cpp index b890a439b..657ad8026 100644 --- a/src/grp/follow.cpp +++ b/src/grp/follow.cpp @@ -45,61 +45,46 @@ bool stop_follower(CHAR_DATA * ch, int mode) } //log("[Stop follower] Remove from followers list"); - if (!ch->get_master()->followers) - { + if (!ch->get_master()->followers) { log("[Stop follower] SYSERR: Followers absent for %s (master %s).", GET_NAME(ch), GET_NAME(ch->get_master())); - } - else if (ch->get_master()->followers->follower == ch) // Head of follower-list? - { + } else if (ch->get_master()->followers->follower == ch) { k = ch->get_master()->followers; ch->get_master()->followers = k->next; - if (!ch->get_master()->followers - && !ch->get_master()->has_master()) - { - //AFF_FLAGS(ch->get_master()).unset(EAffectFlag::AFF_GROUP); + + if (!ch->get_master()->followers && !ch->get_master()->has_master()) { ch->get_master()->removeGroupFlags(); } free(k); } - else // locate follower who is not head of list - { + else { // locate follower who is not head of list for (k = ch->get_master()->followers; k->next && k->next->follower != ch; k = k->next); - if (!k->next) - { - log("[Stop follower] SYSERR: Undefined %s in %s followers list.", GET_NAME(ch), GET_NAME(ch->get_master())); - } - else - { - j = k->next; - k->next = j->next; - free(j); - } + if (!k->next) { + log("[Stop follower] SYSERR: Undefined %s in %s followers list.", GET_NAME(ch), GET_NAME(ch->get_master())); + } + else { + j = k->next; + k->next = j->next; + free(j); + } } ch->set_master(nullptr); - //AFF_FLAGS(ch).unset(EAffectFlag::AFF_GROUP); ch->removeGroupFlags(); - if (AFF_FLAGGED(ch, EAffectFlag::AFF_CHARM) - || AFF_FLAGGED(ch, EAffectFlag::AFF_HELPER) - || IS_SET(mode, SF_CHARMLOST)) - { - if (affected_by_spell(ch, SPELL_CHARM)) - { + if (IS_CHARMICE(ch)|| IS_SET(mode, SF_CHARMLOST)) { + if (affected_by_spell(ch, SPELL_CHARM)) { affect_from_char(ch, SPELL_CHARM); } EXTRACT_TIMER(ch) = 5; + AFF_FLAGS(ch).unset(EAffectFlag::AFF_CHARM); - if (ch->get_fighting()) - { + if (ch->get_fighting()) { stop_fighting(ch, TRUE); } - if (IS_NPC(ch)) - { - if (MOB_FLAGGED(ch, MOB_PLAYER_SUMMON)) - { + if (IS_NPC(ch)) { + if (MOB_FLAGGED(ch, MOB_PLAYER_SUMMON)) { act("Налетевший ветер развеял $n3, не оставив и следа.", TRUE, ch, 0, 0, TO_ROOM | TO_ARENA_LISTEN); GET_LASTROOM(ch) = GET_ROOM_VNUM(ch->in_room); perform_drop_gold(ch, ch->get_gold()); @@ -107,8 +92,7 @@ bool stop_follower(CHAR_DATA * ch, int mode) extract_char(ch, FALSE); return (TRUE); } - else if (AFF_FLAGGED(ch, EAffectFlag::AFF_HELPER)) - { + else if (AFF_FLAGGED(ch, EAffectFlag::AFF_HELPER)) { AFF_FLAGS(ch).unset(EAffectFlag::AFF_HELPER); } } diff --git a/src/grp/grouppenalties.cpp b/src/grp/grouppenalties.cpp new file mode 100644 index 000000000..c3d9f49d1 --- /dev/null +++ b/src/grp/grouppenalties.cpp @@ -0,0 +1,111 @@ +/* ************************************************************************ +* File: GroupPenalties.cpp Part of Bylins * +* Класс расчета всяких штрафов при вступлении в группу * +* * +* All rights reserved. See license.doc for complete information. * +* * +* Copyright (C) 1993, 94 by the Trustees of the Johns Hopkins University * +* CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991. * +* * +************************************************************************ */ + +#include "grp.main.h" + +#include "chars/char_player.hpp" +#include "db.h" +#include "logger.hpp" +#include "structs.h" +#include "sysdep.h" +#include "utils.h" + +/* + Берет misc/grouping, первый столбик цифр считает номерами мортов, + остальные столбики - значение макс. разрыва в уровнях для конкретного + класса. На момент написания этого в конфиге присутствует 26 строк, макс. + морт равен 50 - строки с мортами с 26 по 50 копируются с 25-мортовой строки. +*/ +int GroupPenalties::init() +{ + int clss = 0, remorts = 0, rows_assigned = 0, levels = 0, pos = 0, max_rows = MAX_REMORT+1; + + // пре-инициализация + for (remorts = 0; remorts < max_rows; remorts++) //Строк в массиве должно быть на 1 больше, чем макс. морт + { + for (clss = 0; clss < NUM_PLAYER_CLASSES; clss++) //Столбцов в массиве должно быть ровно столько же, сколько есть классов + { + m_grouping[clss][remorts] = -1; + } + } + + FILE* f = fopen(LIB_MISC "grouping", "r"); + if (!f) + { + log("Невозможно открыть файл %s", LIB_MISC "grouping"); + return 1; + } + + while (get_line(f, buf)) + { + if (!buf[0] || buf[0] == ';' || buf[0] == '\n') //Строка пустая или строка-коммент + { + continue; + } + clss = 0; + pos = 0; + while (sscanf(&buf[pos], "%d", &levels) == 1) + { + while (buf[pos] == ' ' || buf[pos] == '\t') + { + pos++; + } + if (clss == 0) //Первый проход цикла по строке + { + remorts = levels; //Номера строк + if (m_grouping[0][remorts] != -1) + { + log("Ошибка при чтении файла %s: дублирование параметров для %d ремортов", + LIB_MISC "grouping", remorts); + return 2; + } + if (remorts > MAX_REMORT || remorts < 0) + { + log("Ошибка при чтении файла %s: неверное значение количества ремортов: %d, " + "должно быть в промежутке от 0 до %d", + LIB_MISC "grouping", remorts, MAX_REMORT); + return 3; + } + } + else + { + m_grouping[clss - 1][remorts] = levels; // -1 потому что в массиве нет столбца с кол-вом мортов + } + clss++; //+Номер столбца массива + while (buf[pos] != ' ' && buf[pos] != '\t' && buf[pos] != 0) + { + pos++; //Ищем следующее число в строке конфига + } + } + if (clss != NUM_PLAYER_CLASSES+1) + { + log("Ошибка при чтении файла %s: неверный формат строки '%s', должно быть %d " + "целых чисел, прочитали %d", LIB_MISC "grouping", buf, NUM_PLAYER_CLASSES+1, clss); + return 4; + } + rows_assigned++; + } + + if (rows_assigned < max_rows) + { + for (levels = remorts; levels < max_rows; levels++) //Берем свободную переменную + { + for (clss = 0; clss < NUM_PLAYER_CLASSES; clss++) + { + m_grouping[clss][levels] = m_grouping[clss][remorts]; //Копируем последнюю строку на все морты, для которых нет строк + } + } + } + fclose(f); + return 0; +} + +GroupPenalties grouping; ///< TODO: get rid of this global variable. diff --git a/src/grp/grouppenaltycalculator.cpp b/src/grp/grouppenaltycalculator.cpp new file mode 100644 index 000000000..2d1363165 --- /dev/null +++ b/src/grp/grouppenaltycalculator.cpp @@ -0,0 +1,90 @@ +#include "grp.main.h" + +#include "logger.hpp" +#include "utils.h" + +int GroupPenaltyCalculator::get() const +{ + const bool leader_is_npc = IS_NPC(m_leader); + const bool leader_in_group = AFF_FLAGGED(m_leader, EAffectFlag::AFF_GROUP); + const bool leader_is_in_room = leader_in_group + && m_leader->in_room == IN_ROOM(m_killer); + if (!leader_is_npc + && leader_is_in_room) + { + int penalty = 0; + if (penalty_by_leader(m_leader, penalty)) + { + return penalty; + } + } + + for (auto f = m_leader->followers; f; f = f->next) + { + const bool follower_is_npc = IS_NPC(f->follower); + const bool follower_is_in_room = AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP) + && f->follower->in_room == IN_ROOM(m_killer); + + if (follower_is_npc + || !follower_is_in_room) + { + continue; + } + + int penalty = 0; + if (penalty_by_leader(f->follower, penalty)) + { + if (0 < penalty) + { + return penalty; + } + } + } + + return 0; +} + +bool GroupPenaltyCalculator::penalty_by_leader(const CHAR_DATA* player, int& penalty) const +{ + const int player_remorts = static_cast(GET_REMORT(player)); + const int player_class = static_cast(GET_CLASS(player)); + const int player_level = GET_LEVEL(player); + + if (IS_NPC(player)) + { + log("LOGIC ERROR: try to get penalty for NPC [%s], VNum: %d\n", + player->get_name().c_str(), + GET_MOB_VNUM(player)); + penalty = DEFAULT_PENALTY; + return true; + } + + if (0 > player_class + || player_class > NUM_PLAYER_CLASSES) + { + log("LOGIC ERROR: wrong player class: %d for player [%s]", + player_class, + player->get_name().c_str()); + penalty = DEFAULT_PENALTY; + return true; + } + + if (0 > player_remorts + || player_remorts > MAX_REMORT) + { + log("LOGIC ERROR: wrong number of remorts: %d for player [%s]", + player_remorts, + player->get_name().c_str()); + penalty = DEFAULT_PENALTY; + return true; + } + + bool result = false; + if (m_max_level - player_level > m_grouping[player_class][player_remorts]) + { + penalty = 50 + 2 * (m_max_level - player_level - m_grouping[player_class][player_remorts]); + result = true; + } + + return result; +} \ No newline at end of file diff --git a/src/grp/grp.group.cpp b/src/grp/grp.group.cpp index e11288a12..acb3c43a6 100644 --- a/src/grp/grp.group.cpp +++ b/src/grp/grp.group.cpp @@ -1,13 +1,33 @@ +/* ************************************************************************ +* File: grp.main.h Part of Bylins * +* Методы и функции работы с группами персонажей * +* * +* All rights reserved. See license.doc for complete information. * +* * +* Copyright (C) 1993, 94 by the Trustees of the Johns Hopkins University * +* CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991. * +* * +************************************************************************ */ + #include #include "grp.main.h" +#include "bonus.h" #include "comm.h" #include "msdp.constants.hpp" #include "screen.h" +#include "core/leveling.h" #include "magic.h" +#include "mob_stat.hpp" +#include "top.h" +#include "zone.table.hpp" + +using namespace ExpCalc; extern GroupRoster& groupRoster; +extern int max_exp_gain_npc; +extern zone_table_t& zone_table; const char *WORD_STATE[] = { "При смерти", "Оч.тяж.ран", @@ -628,13 +648,18 @@ CHAR_DATA* Group::get_random_pc_group() { } // room_rnum = 0 - ignore -cd_v Group::getMembers(rnum_t room_rnum) { +cd_v Group::getMembers(rnum_t room_rnum, bool includeCharmee) { cd_v retval; for (const auto& it : *_memberList) { auto c = (*it.second)->member; if (room_rnum == (room_rnum == 0 ? room_rnum : c->in_room) && !c->purged()) retval.push_back((*it.second)->member); } + if (includeCharmee) + for (const auto c : *_npcRoster) { + if (room_rnum == (room_rnum == 0 ? room_rnum : c->in_room) && !c->purged()) + retval.push_back(c); + } return retval; } @@ -681,3 +706,292 @@ bool same_group(CHAR_DATA * ch, CHAR_DATA * tch) return true; return false; } + + +/*++ + Функция начисления опыта + ch - кому опыт начислять + Вызов этой функции для NPC смысла не имеет, но все равно + какие-то проверки внутри зачем то делаются +--*/ +void perform_group_gain(CHAR_DATA * ch, CHAR_DATA * victim, int members, int koef) +{ + + +// Странно, но для NPC эта функция тоже должна работать +// if (IS_NPC(ch) || !OK_GAIN_EXP(ch,victim)) + if (!OK_GAIN_EXP(ch, victim)) + { + send_to_char("Ваше деяние никто не оценил.\r\n", ch); + return; + } + + // 1. Опыт делится поровну на всех + int exp = GET_EXP(victim) / MAX(members, 1); + + if(victim->get_zone_group() > 1 && members < victim->get_zone_group()) + { + // в случае груп-зоны своего рода планка на мин кол-во человек в группе + exp = GET_EXP(victim) / victim->get_zone_group(); + } + + // 2. Учитывается коэффициент (лидерство, разность уровней) + // На мой взгляд его правильней использовать тут а не в конце процедуры, + // хотя в большинстве случаев это все равно + exp = exp * koef / 100; + + // 3. Вычисление опыта для PC и NPC + if (IS_NPC(ch)) + { + exp = MIN(max_exp_gain_npc, exp); + exp += MAX(0, (exp * MIN(4, (GET_LEVEL(victim) - GET_LEVEL(ch)))) / 8); + } + else + exp = MIN(max_exp_gain_pc(ch), get_extend_exp(exp, ch, victim)); + // 4. Последняя проверка + exp = MAX(1, exp); + if (exp > 1) + { + if (Bonus::is_bonus(Bonus::BONUS_EXP)) + { + exp *= Bonus::get_mult_bonus(); + } + + if (!IS_NPC(ch) && !ch->affected.empty()) + { + for (const auto aff : ch->affected) + { + if (aff->location == APPLY_BONUS_EXP) // скушал свиток с эксп бонусом + { + exp *= MIN(3, aff->modifier); // бонус макс тройной + } + } + } + + int long_live_bonus = floor(ExpCalc::get_npc_long_live_exp_bounus( GET_MOB_VNUM(victim))); + if (long_live_bonus>1) { + std::string mess = ""; + switch (long_live_bonus) + { + case 2: + mess = "Редкая удача! Опыт повышен!\r\n"; + break; + case 3: + mess = "Очень редкая удача! Опыт повышен!\r\n"; + break; + case 4: + mess = "Очень-очень редкая удача! Опыт повышен!\r\n"; + break; + case 5: + mess = "Вы везунчик! Опыт повышен!\r\n"; + break; + case 6: + mess = "Ваша удача велика! Опыт повышен!\r\n"; + break; + case 7: + mess = "Ваша удача достигла небес! Опыт повышен!\r\n"; + break; + case 8: + mess = "Ваша удача коснулась луны! Опыт повышен!\r\n"; + break; + case 9: + mess = "Ваша удача затмевает солнце! Опыт повышен!\r\n"; + break; + case 10: + mess = "Ваша удача выше звезд! Опыт повышен!\r\n"; + break; + default: + mess = "Ваша удача выше звезд! Опыт повышен!\r\n"; + break; + } + send_to_char(mess.c_str(), ch); + } + + exp = MIN(max_exp_gain_pc(ch), exp); + send_to_char(ch, "Ваш опыт повысился на %d %s.\r\n", exp, desc_count(exp, WHAT_POINT)); + } + else if (exp == 1) { + send_to_char("Ваш опыт повысился всего лишь на маленькую единичку.\r\n", ch); + } + gain_exp(ch, exp); + GET_ALIGNMENT(ch) += (-GET_ALIGNMENT(victim) - GET_ALIGNMENT(ch)) / 16; + TopPlayer::Refresh(ch); + + if (!EXTRA_FLAGGED(victim, EXTRA_GRP_KILL_COUNT) + && !IS_NPC(ch) + && !IS_IMMORTAL(ch) + && IS_NPC(victim) + && !IS_CHARMICE(victim) + && !ROOM_FLAGGED(IN_ROOM(victim), ROOM_ARENA)) + { + mob_stat::add_mob(victim, members); + EXTRA_FLAGS(victim).set(EXTRA_GRP_KILL_COUNT); + } + else if (IS_NPC(ch) && !IS_NPC(victim) + && !ROOM_FLAGGED(IN_ROOM(victim), ROOM_ARENA)) + { + mob_stat::add_mob(ch, 0); + } +} + +/*++ + Функция расчитывает всякие бонусы для группы при получении опыта, + после чего вызывает функцию получения опыта для всех членов группы + Т.к. членом группы может быть только PC, то эта функция раздаст опыт только PC + + ch - обязательно член группы, из чего следует: + 1. Это не NPC + 2. Он находится в группе лидера (или сам лидер) + + Просто для PC-последователей эта функция не вызывается + +--*/ + +void group_gain(CHAR_DATA * killer, CHAR_DATA * victim) +{ + int inroom_members, koef = 100, maxlevel; + struct follow_type *f; + int partner_count = 0; + int total_group_members = 1; + bool use_partner_exp = false; + + // если наем лидер, то тоже режем экспу + if (can_use_feat(killer, CYNIC_FEAT)) + { + maxlevel = 300; + } + else + { + maxlevel = GET_LEVEL(killer); + } + + auto leader = killer->get_master(); + if (nullptr == leader) + { + leader = killer; + } + + // k - подозрение на лидера группы + const bool leader_inroom = AFF_FLAGGED(leader, EAffectFlag::AFF_GROUP) + && leader->in_room == IN_ROOM(killer); + + // Количество согрупников в комнате + if (leader_inroom) + { + inroom_members = 1; + maxlevel = GET_LEVEL(leader); + } + else + { + inroom_members = 0; + } + + // Вычисляем максимальный уровень в группе + for (f = leader->followers; f; f = f->next) + { + if (AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP)) ++total_group_members; + if (AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP) + && f->follower->in_room == IN_ROOM(killer)) + { + // если в группе наем, то режим опыт всей группе + // дабы наема не выгодно было бы брать в группу + // ставим 300, чтобы вообще под ноль резало + if (can_use_feat(f->follower, CYNIC_FEAT)) + { + maxlevel = 300; + } + // просмотр членов группы в той же комнате + // член группы => PC автоматически + ++inroom_members; + maxlevel = MAX(maxlevel, GET_LEVEL(f->follower)); + if (!IS_NPC(f->follower)) + { + partner_count++; + } + } + } + + GroupPenaltyCalculator group_penalty(killer, leader, maxlevel, grouping); + koef -= group_penalty.get(); + + koef = MAX(0, koef); + + // Лидерство используется, если в комнате лидер и есть еще хоть кто-то + // из группы из PC (последователи типа лошади или чармисов не считаются) + if (koef >= 100 && leader_inroom && (inroom_members > 1) && calc_leadership(leader)) + { + koef += 20; + } + + // Раздача опыта + + // если групповой уровень зоны равняется единице + if (zone_table[world[killer->in_room]->zone].group < 2) + { + // чтобы не абьюзили на суммонах, когда в группе на самом деле больше + // двух мемберов, но лишних реколят перед непосредственным рипом + use_partner_exp = total_group_members == 2; + } + + // если лидер группы в комнате + if (leader_inroom) + { + // если у лидера группы есть способность напарник + if (can_use_feat(leader, PARTNER_FEAT) && use_partner_exp) + { + // если в группе всего двое человек + // k - лидер, и один последователь + if (partner_count == 1) + { + // и если кожф. больше или равен 100 + if (koef >= 100) + { + if (leader->get_zone_group() < 2) + { + koef += 100; + } + } + } + } + perform_group_gain(leader, victim, inroom_members, koef); + } + + for (f = leader->followers; f; f = f->next) + { + if (AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP) + && f->follower->in_room == IN_ROOM(killer)) + { + perform_group_gain(f->follower, victim, inroom_members, koef); + } + } +} + + +int calc_leadership(CHAR_DATA * ch) +{ + int prob, percent; + + if (IS_NPC(ch) || ch->personGroup == nullptr) { + return FALSE; + } + + CHAR_DATA* leader = ch->personGroup->getLeader(); + + // если лидер умер или нет в комнате - фиг вам, а не бонусы + if (leader == nullptr || IN_ROOM(ch) != IN_ROOM(leader)) { + return FALSE; + } + + if (!leader->get_skill(SKILL_LEADERSHIP)) { + return (FALSE); + } + + percent = number(1, 101); + prob = calculate_skill(leader, SKILL_LEADERSHIP, 0); + if (percent > prob) { + return (FALSE); + } + else { + return (TRUE); + } +} diff --git a/src/grp/grp.main.h b/src/grp/grp.main.h index df9215170..edf8561a9 100644 --- a/src/grp/grp.main.h +++ b/src/grp/grp.main.h @@ -1,3 +1,14 @@ +/* ************************************************************************ +* File: grp.main.h Part of Bylins * +* Заголовок всех функций и классов работы с группами * +* * +* All rights reserved. See license.doc for complete information. * +* * +* Copyright (C) 1993, 94 by the Trustees of the Johns Hopkins University * +* CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991. * +* * +************************************************************************ */ + #ifndef BYLINS_GRP_MAIN_H #define BYLINS_GRP_MAIN_H @@ -19,6 +30,7 @@ void do_ungroup(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/); void do_report(CHAR_DATA *ch, char* /*argument*/, int/* cmd*/, int/* subcmd*/); int max_group_size(CHAR_DATA *ch); bool isGroupedFollower(CHAR_DATA* master, CHAR_DATA* vict); +int calc_leadership(CHAR_DATA * ch); class Group; class Request; @@ -99,7 +111,7 @@ class Group { void sendToGroup(GRP_COMM mode, const char *msg, ...); void actToGroup(CHAR_DATA* vict, GRP_COMM mode, const char *msg, ...); - cd_v getMembers(rnum_t room_rnum = 0); + cd_v getMembers(rnum_t room_rnum = 0, bool includeCharmee = false); npc_r getCharmee(rnum_t room_rnum = 0); public: // всякий унаследованный стафф @@ -107,6 +119,7 @@ class Group { // лень обвязывать, тупо переместил объект DpsSystem::GroupListType _group_dps; bool has_clan_members_in_group(CHAR_DATA *victim); + }; class Request { @@ -153,4 +166,42 @@ class GroupRoster { Request* findRequest(const char* targetPerson, const char* group, RQ_TYPE type); }; +class GroupPenalties +{ +public: + using class_penalties_t = std::array; + using penalties_t = std::array; + + int init(); + const auto& operator[](const size_t character_class) const { return m_grouping[character_class]; } + +private: + penalties_t m_grouping; +}; + +class GroupPenaltyCalculator +{ +public: + constexpr static int DEFAULT_PENALTY = 100; + + GroupPenaltyCalculator(const CHAR_DATA* killer, const CHAR_DATA* leader, const int max_level, const GroupPenalties& grouping): + m_killer(killer), + m_leader(leader), + m_max_level(max_level), + m_grouping(grouping) + { + } + + int get() const; + +private: + const CHAR_DATA* m_killer; + const CHAR_DATA* m_leader; + const int m_max_level; + const GroupPenalties& m_grouping; + + bool penalty_by_leader(const CHAR_DATA* player, int& penalty) const; +}; + +extern GroupPenalties grouping; #endif //BYLINS_GRP_MAIN_H diff --git a/src/handler.cpp b/src/handler.cpp index d2e876e78..5b776c610 100644 --- a/src/handler.cpp +++ b/src/handler.cpp @@ -25,8 +25,8 @@ #include "spells.h" #include "skills.h" #include "screen.h" -#include "dg_db_scripts.hpp" -#include "dg_scripts.h" +#include "dg/dg_db_scripts.hpp" +#include "dg/dg_scripts.h" #include "auction.h" #include "features.hpp" #include "house.h" diff --git a/src/heartbeat.cpp b/src/heartbeat.cpp index af6d9327a..2aa35e6b4 100644 --- a/src/heartbeat.cpp +++ b/src/heartbeat.cpp @@ -25,7 +25,7 @@ #include "objsave.h" #include "limits.hpp" #include "fightsystem/mobact.hpp" -#include "dg_event.h" +#include "dg/dg_event.h" #include "corpse.hpp" #include "chars/char.hpp" #include "shutdown.parameters.hpp" diff --git a/src/house_exp.cpp b/src/house_exp.cpp index ff8e8170c..35f009253 100644 --- a/src/house_exp.cpp +++ b/src/house_exp.cpp @@ -20,6 +20,28 @@ #include #include +void update_clan_exp(CHAR_DATA *ch, int gain) +{ + if (CLAN(ch) && gain != 0) + { + // экспа для уровня клана (+ только на праве, - любой, но /5) + if (gain < 0 || GET_GOD_FLAG(ch, GF_REMORT)) + { + int tmp = gain > 0 ? gain : gain / 5; + CLAN(ch)->SetClanExp(ch, tmp); + } + // экспа для топа кланов за месяц (учитываются все + и -) + CLAN(ch)->last_exp.add_temp(gain); + // экспа для топа кланов за все время (учитываются все + и -) + CLAN(ch)->AddTopExp(ch, gain); + // экспа для авто-очистки кланов (учитываются только +) + if (gain > 0) + { + CLAN(ch)->exp_history.add_exp(gain); + } + } +} + namespace { diff --git a/src/house_exp.hpp b/src/house_exp.hpp index 7f157726a..db634a7ca 100644 --- a/src/house_exp.hpp +++ b/src/house_exp.hpp @@ -13,6 +13,7 @@ #include "sysdep.h" #include "chars/char.hpp" +void update_clan_exp(CHAR_DATA *ch, int gain); void update_clan_exp(); void save_clan_exp(); diff --git a/src/interpreter.cpp b/src/interpreter.cpp index 49308b795..c7ab0b8c1 100644 --- a/src/interpreter.cpp +++ b/src/interpreter.cpp @@ -27,7 +27,7 @@ #include "craft.commands.hpp" #include "db.h" #include "depot.hpp" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "features.hpp" #include "fightsystem/assist.h" #include "fightsystem/mobact.hpp" @@ -83,6 +83,7 @@ #include "scripting.hpp" #endif #include "chars/player_races.hpp" +#include "chars/char_player.hpp" #include "birth_places.hpp" #include "help.hpp" #include "map.hpp" @@ -168,7 +169,6 @@ void medit_parse(DESCRIPTOR_DATA * d, char *arg); void trigedit_parse(DESCRIPTOR_DATA * d, char *arg); int find_social(char *name); extern int CheckProxy(DESCRIPTOR_DATA * ch); -extern void check_max_hp(CHAR_DATA *ch); // local functions int perform_dupe_check(DESCRIPTOR_DATA * d); struct alias_data *find_alias(struct alias_data *alias_list, char *str); diff --git a/src/limits.cpp b/src/limits.cpp index 2841f8cef..0d0a79ce0 100644 --- a/src/limits.cpp +++ b/src/limits.cpp @@ -23,7 +23,7 @@ #include "screen.h" #include "interpreter.h" #include "constants.h" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "house.h" #include "constants.h" #include "exchange.h" @@ -71,14 +71,11 @@ extern const unsigned RECALL_SPELLS_INTERVAL; extern int CheckProxy(DESCRIPTOR_DATA * ch); extern int check_death_ice(int room, CHAR_DATA * ch); -void decrease_level(CHAR_DATA * ch); -int max_exp_gain_pc(CHAR_DATA * ch); -int max_exp_loss_pc(CHAR_DATA * ch); int average_day_temp(void); // local functions int graf(int age, int p0, int p1, int p2, int p3, int p4, int p5, int p6); -int level_exp(CHAR_DATA * ch, int level); + void update_char_objects(CHAR_DATA * ch); // handler.cpp // Delete this, if you delete overflow fix in beat_points_update below. void die(CHAR_DATA * ch, CHAR_DATA * killer); @@ -870,186 +867,6 @@ void beat_points_update(int pulse) }); } -void update_clan_exp(CHAR_DATA *ch, int gain) -{ - if (CLAN(ch) && gain != 0) - { - // экспа для уровня клана (+ только на праве, - любой, но /5) - if (gain < 0 || GET_GOD_FLAG(ch, GF_REMORT)) - { - int tmp = gain > 0 ? gain : gain / 5; - CLAN(ch)->SetClanExp(ch, tmp); - } - // экспа для топа кланов за месяц (учитываются все + и -) - CLAN(ch)->last_exp.add_temp(gain); - // экспа для топа кланов за все время (учитываются все + и -) - CLAN(ch)->AddTopExp(ch, gain); - // экспа для авто-очистки кланов (учитываются только +) - if (gain > 0) - { - CLAN(ch)->exp_history.add_exp(gain); - } - } -} - -void gain_exp(CHAR_DATA * ch, int gain) -{ - int is_altered = FALSE; - int num_levels = 0; - char buf[128]; - - if (IS_NPC(ch)) - { - ch->set_exp(ch->get_exp() + gain); - return; - } - else - { - ch->dps_add_exp(gain); - ZoneExpStat::add(zone_table[world[ch->in_room]->zone].number, gain); - } - - if (!IS_NPC(ch) && ((GET_LEVEL(ch) < 1 || GET_LEVEL(ch) >= LVL_IMMORT))) - return; - - if (gain > 0 && GET_LEVEL(ch) < LVL_IMMORT) - { - gain = MIN(max_exp_gain_pc(ch), gain); // put a cap on the max gain per kill - ch->set_exp(ch->get_exp() + gain); - if (GET_EXP(ch) >= level_exp(ch, LVL_IMMORT)) - { - if (!GET_GOD_FLAG(ch, GF_REMORT) && GET_REMORT(ch) < MAX_REMORT) - { - if (Remort::can_remort_now(ch)) - { - send_to_char(ch, "%sПоздравляем, вы получили право на перевоплощение!%s\r\n", - CCIGRN(ch, C_NRM), CCNRM(ch, C_NRM)); - } - else - { - send_to_char(ch, - "%sПоздравляем, вы набрали максимальное количество опыта!\r\n" - "%s%s\r\n", CCIGRN(ch, C_NRM), Remort::WHERE_TO_REMORT_STR.c_str(), CCNRM(ch, C_NRM)); - } - SET_GOD_FLAG(ch, GF_REMORT); - } - } - ch->set_exp(MIN(GET_EXP(ch), level_exp(ch, LVL_IMMORT) - 1)); - while (GET_LEVEL(ch) < LVL_IMMORT && GET_EXP(ch) >= level_exp(ch, GET_LEVEL(ch) + 1)) - { - ch->set_level(ch->get_level() + 1); - num_levels++; - sprintf(buf, "%sВы достигли следующего уровня!%s\r\n", CCWHT(ch, C_NRM), CCNRM(ch, C_NRM)); - send_to_char(buf, ch); - advance_level(ch); - is_altered = TRUE; - } - - if (is_altered) - { - sprintf(buf, "%s advanced %d level%s to level %d.", - GET_NAME(ch), num_levels, num_levels == 1 ? "" : "s", GET_LEVEL(ch)); - mudlog(buf, BRF, LVL_IMPL, SYSLOG, TRUE); - } - } - else if (gain < 0 && GET_LEVEL(ch) < LVL_IMMORT) - { - gain = MAX(-max_exp_loss_pc(ch), gain); // Cap max exp lost per death - ch->set_exp(ch->get_exp() + gain); - while (GET_LEVEL(ch) > 1 && GET_EXP(ch) < level_exp(ch, GET_LEVEL(ch))) - { - ch->set_level(ch->get_level() - 1); - num_levels++; - sprintf(buf, - "%sВы потеряли уровень. Вам должно быть стыдно!%s\r\n", - CCIRED(ch, C_NRM), CCNRM(ch, C_NRM)); - send_to_char(buf, ch); - decrease_level(ch); - is_altered = TRUE; - } - if (is_altered) - { - sprintf(buf, "%s decreases %d level%s to level %d.", - GET_NAME(ch), num_levels, num_levels == 1 ? "" : "s", GET_LEVEL(ch)); - mudlog(buf, BRF, LVL_IMPL, SYSLOG, TRUE); - } - } - if ((GET_EXP(ch) < level_exp(ch, LVL_IMMORT) - 1) - && GET_GOD_FLAG(ch, GF_REMORT) - && gain - && (GET_LEVEL(ch) < LVL_IMMORT)) - { - if (Remort::can_remort_now(ch)) - { - send_to_char(ch, "%sВы потеряли право на перевоплощение!%s\r\n", - CCIRED(ch, C_NRM), CCNRM(ch, C_NRM)); - } - CLR_GOD_FLAG(ch, GF_REMORT); - } - - char_stat::add_class_exp(GET_CLASS(ch), gain); - update_clan_exp(ch, gain); -} - -// юзается исключительно в act.wizards.cpp в имм командах "advance" и "set exp". -void gain_exp_regardless(CHAR_DATA * ch, int gain) -{ - int is_altered = FALSE; - int num_levels = 0; - - ch->set_exp(ch->get_exp() + gain); - if (!IS_NPC(ch)) - { - if (gain > 0) - { - while (GET_LEVEL(ch) < LVL_IMPL && GET_EXP(ch) >= level_exp(ch, GET_LEVEL(ch) + 1)) - { - ch->set_level(ch->get_level() + 1); - num_levels++; - sprintf(buf, "%sВы достигли следующего уровня!%s\r\n", - CCWHT(ch, C_NRM), CCNRM(ch, C_NRM)); - send_to_char(buf, ch); - - advance_level(ch); - is_altered = TRUE; - } - - if (is_altered) - { - sprintf(buf, "%s advanced %d level%s to level %d.", - GET_NAME(ch), num_levels, num_levels == 1 ? "" : "s", GET_LEVEL(ch)); - mudlog(buf, BRF, LVL_IMPL, SYSLOG, TRUE); - } - } - else if (gain < 0) - { - // Pereplut: глупый участок кода. - // gain = MAX(-max_exp_loss_pc(ch), gain); // Cap max exp lost per death - // GET_EXP(ch) += gain; - // if (GET_EXP(ch) < 0) - // GET_EXP(ch) = 0; - while (GET_LEVEL(ch) > 1 && GET_EXP(ch) < level_exp(ch, GET_LEVEL(ch))) - { - ch->set_level(ch->get_level() - 1); - num_levels++; - sprintf(buf, - "%sВы потеряли уровень!%s\r\n", - CCIRED(ch, C_NRM), CCNRM(ch, C_NRM)); - send_to_char(buf, ch); - decrease_level(ch); - is_altered = TRUE; - } - if (is_altered) - { - sprintf(buf, "%s decreases %d level%s to level %d.", - GET_NAME(ch), num_levels, num_levels == 1 ? "" : "s", GET_LEVEL(ch)); - mudlog(buf, BRF, LVL_IMPL, SYSLOG, TRUE); - } - } - - } -} - void gain_condition(CHAR_DATA * ch, unsigned condition, int value) { int cond_state = GET_COND(ch, condition); diff --git a/src/magic.cpp b/src/magic.cpp index f9997e57f..13fa1a938 100644 --- a/src/magic.cpp +++ b/src/magic.cpp @@ -29,7 +29,7 @@ #include "interpreter.h" #include "screen.h" #include "constants.h" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "fightsystem/pk.h" #include "features.hpp" #include "fightsystem/fight.h" diff --git a/src/medit.cpp b/src/medit.cpp index 034137cf4..08e03adef 100644 --- a/src/medit.cpp +++ b/src/medit.cpp @@ -14,7 +14,7 @@ #include "db.h" #include "olc.h" #include "handler.h" -#include "dg_olc.h" +#include "dg/dg_olc.h" #include "constants.h" #include "features.hpp" #include "im.h" diff --git a/src/msdp.reporters.cpp b/src/msdp.reporters.cpp index 860f4dff1..3aaca2334 100644 --- a/src/msdp.reporters.cpp +++ b/src/msdp.reporters.cpp @@ -1,11 +1,12 @@ #include "msdp.reporters.hpp" -#include "structs.h" -#include "utils.h" -#include "db.h" #include "chars/char.hpp" +#include "grp/grp.main.h" +#include "db.h" #include "magic.h" #include "msdp.constants.hpp" +#include "structs.h" +#include "utils.h" #include "zone.table.hpp" namespace msdp @@ -302,36 +303,13 @@ namespace msdp */ const auto group_descriptor = std::make_shared(); const auto ch = descriptor()->character.get(); - const auto master = ch->has_master() ? ch->get_master() : ch; - append_char(group_descriptor, ch, master, true); - for (auto f = master->followers; f; f = f->next) - { - if (!AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP) - && !(MOB_FLAGGED(f->follower, MOB_PLAYER_SUMMON))) - { - continue; - } - - append_char(group_descriptor, ch, f->follower, false); - - // followers of a follower - if (!AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP)) - { - continue; - } - - for (auto ff = f->follower->followers; ff; ff = ff->next) - { - if (!(AFF_FLAGGED(ff->follower, EAffectFlag::AFF_CHARM) - || MOB_FLAGGED(ff->follower, MOB_PLAYER_SUMMON))) - { - continue; - } - - append_char(group_descriptor, ch, ff->follower, false); - } - } - + auto grp = ch->personGroup; + if (grp == nullptr) + return; + auto gl = grp->getMembers(0, true); + for (auto it : gl) { + append_char(group_descriptor, ch, it, true); + } response = std::make_shared(constants::GROUP, group_descriptor); } } diff --git a/src/named_stuff.cpp b/src/named_stuff.cpp index 0a7ed2880..229789e98 100644 --- a/src/named_stuff.cpp +++ b/src/named_stuff.cpp @@ -13,7 +13,7 @@ #include "db.h" #include "handler.h" #include "house.h" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "pugixml.hpp" #include "logger.hpp" #include "utils.h" diff --git a/src/obj.cpp b/src/obj.cpp index f82227290..cbdaa114e 100644 --- a/src/obj.cpp +++ b/src/obj.cpp @@ -9,7 +9,7 @@ #include "object.prototypes.hpp" #include "parse.hpp" #include "handler.h" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "screen.h" #include "celebrates.hpp" #include "fightsystem/pk.h" diff --git a/src/objsave.cpp b/src/objsave.cpp index 07d7b4598..3b137ba68 100644 --- a/src/objsave.cpp +++ b/src/objsave.cpp @@ -31,7 +31,7 @@ #include "named_stuff.hpp" #include "room.hpp" #include "mail.h" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "features.hpp" #include "char_obj_utils.inl" #include "structs.h" diff --git a/src/oedit.cpp b/src/oedit.cpp index a87e6aac7..7ff1f7da2 100644 --- a/src/oedit.cpp +++ b/src/oedit.cpp @@ -19,7 +19,7 @@ #include "utils.h" #include "db.h" #include "olc.h" -#include "dg_olc.h" +#include "dg/dg_olc.h" #include "im.h" #include "features.hpp" #include "depot.hpp" diff --git a/src/olc.cpp b/src/olc.cpp index a5a9b3836..ff448089b 100644 --- a/src/olc.cpp +++ b/src/olc.cpp @@ -15,7 +15,7 @@ #include "interpreter.h" #include "comm.h" #include "db.h" -#include "dg_olc.h" +#include "dg/dg_olc.h" #include "screen.h" #include "item.creation.hpp" #include "im.h" diff --git a/src/redit.cpp b/src/redit.cpp index f89a506bd..5ae9ab69e 100644 --- a/src/redit.cpp +++ b/src/redit.cpp @@ -12,7 +12,7 @@ #include "comm.h" #include "db.h" #include "olc.h" -#include "dg_olc.h" +#include "dg/dg_olc.h" #include "constants.h" #include "im.h" #include "description.h" diff --git a/src/room.cpp b/src/room.cpp index d08a9297e..e940bb5c9 100644 --- a/src/room.cpp +++ b/src/room.cpp @@ -6,7 +6,7 @@ #include "room.hpp" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" EXIT_DATA::EXIT_DATA(): keyword(nullptr), vkeyword(nullptr), diff --git a/src/room.hpp b/src/room.hpp index 61c887d7f..e25a907c5 100644 --- a/src/room.hpp +++ b/src/room.hpp @@ -5,7 +5,7 @@ #ifndef ROOM_HPP_INCLUDED #define ROOM_HPP_INCLUDED -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "obj.hpp" #include "constants.h" #include "structs.h" diff --git a/src/sets_drop.cpp b/src/sets_drop.cpp index 78f7a1317..19c02baf0 100644 --- a/src/sets_drop.cpp +++ b/src/sets_drop.cpp @@ -9,7 +9,7 @@ #include "chars/char.hpp" #include "comm.h" #include "handler.h" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "im.h" #include "room.hpp" #include "pugixml.hpp" diff --git a/src/skills.cpp b/src/skills.cpp index 2e7bab654..72977167e 100644 --- a/src/skills.cpp +++ b/src/skills.cpp @@ -17,7 +17,7 @@ #include "interpreter.h" #include "spells.h" #include "screen.h" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "constants.h" #include "im.h" #include "features.hpp" diff --git a/src/skills/flee.cpp b/src/skills/flee.cpp index 32e330172..81101ffb6 100644 --- a/src/skills/flee.cpp +++ b/src/skills/flee.cpp @@ -1,6 +1,9 @@ -#include "skills/flee.h" + #include "act.movement.hpp" +#include "core/leveling.h" +#include "skills/flee.h" #include "random.hpp" + #include void reduce_exp_after_flee(CHAR_DATA* ch, CHAR_DATA* victim, room_rnum room) @@ -9,7 +12,7 @@ void reduce_exp_after_flee(CHAR_DATA* ch, CHAR_DATA* victim, room_rnum room) return; const auto loss = MAX(1, GET_REAL_MAX_HIT(victim) - GET_HIT(victim)) * GET_LEVEL(victim); - gain_exp(ch, -loss); + ExpCalc::gain_exp(ch, -loss); } // ********************* FLEE PROCEDURE diff --git a/src/spec_procs.cpp b/src/spec_procs.cpp index 958d75ebd..eef3db29f 100644 --- a/src/spec_procs.cpp +++ b/src/spec_procs.cpp @@ -13,37 +13,38 @@ ************************************************************************ */ #include "act.movement.hpp" -#include "cmd/cmd.generic.h" -#include "obj.hpp" -#include "comm.h" -#include "interpreter.h" -#include "handler.h" -#include "db.h" -#include "spell_parser.hpp" -#include "spells.h" -#include "skills.h" -#include "screen.h" -#include "dg_scripts.h" -#include "constants.h" -#include "features.hpp" -#include "house.h" +#include "char_obj_utils.inl" #include "chars/char.hpp" #include "chars/char_player.hpp" #include "chars/mount.h" -#include "depot.hpp" #include "chars/player_races.hpp" -#include "magic.h" +#include "chars/world.characters.hpp" +#include "cmd/cmd.generic.h" +#include "comm.h" +#include "constants.h" +#include "core/leveling.h" +#include "db.h" +#include "depot.hpp" +#include "dg/dg_scripts.h" +#include "features.hpp" #include "fightsystem/fight.h" #include "fightsystem/fight_hit.hpp" -#include "char_obj_utils.inl" -#include "chars/world.characters.hpp" +#include "handler.h" +#include "house.h" +#include "interpreter.h" #include "logger.hpp" +#include "magic.h" +#include "obj.hpp" +#include "screen.h" +#include "skills.h" +#include "spell_parser.hpp" +#include "spells.h" #include "structs.h" #include "sysdep.h" #include "temp_spells.hpp" + #include #include - #include // external vars @@ -2910,7 +2911,7 @@ int dump(CHAR_DATA *ch, void* /*me*/, int cmd, char* argument) send_to_char("Боги оценили вашу жертву.\r\n", ch); act("$n оценен$y Богами.", TRUE, ch, 0, 0, TO_ROOM); if (GET_LEVEL(ch) < 3) - gain_exp(ch, value); + ExpCalc::gain_exp(ch, value); else ch->add_gold(value); } diff --git a/src/spell_parser.cpp b/src/spell_parser.cpp index 2aef49858..afe7bf468 100644 --- a/src/spell_parser.cpp +++ b/src/spell_parser.cpp @@ -26,7 +26,7 @@ #include "db.h" #include "screen.h" #include "constants.h" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "fightsystem/pk.h" #include "features.hpp" #include "im.h" diff --git a/src/spells.cpp b/src/spells.cpp index 4a0467cb8..160395a3e 100644 --- a/src/spells.cpp +++ b/src/spells.cpp @@ -28,7 +28,7 @@ #include "db.h" #include "constants.h" #include "interpreter.h" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "screen.h" #include "house.h" #include "fightsystem/pk.h" diff --git a/src/structs.cpp b/src/structs.cpp index f7430e67e..aeb9ebd6f 100644 --- a/src/structs.cpp +++ b/src/structs.cpp @@ -1,8 +1,5 @@ #include "structs.h" #include "chars/char.hpp" -#include "spells.h" -#include "utils.h" -#include "logger.hpp" #include "msdp.hpp" #include "msdp.constants.hpp" @@ -51,7 +48,7 @@ void asciiflag_conv(const char *flag, void *to) int ext_search_block(const char *arg, const char * const * const list, int exact) { - int i, j, o; + long i, j, o; if (exact) { diff --git a/src/structs.h b/src/structs.h index 60b27f83c..36ef27b0f 100644 --- a/src/structs.h +++ b/src/structs.h @@ -16,6 +16,7 @@ #define _STRUCTS_H_ #include "boards.types.hpp" +#include "ext_money.hpp" #include "sysdep.h" #include @@ -29,17 +30,17 @@ #include #include -namespace ExtMoney -{ + +namespace ExtMoney { // золотые гривны -const unsigned TORC_GOLD = 0; + const unsigned TORC_GOLD = 0; // серебряные гривны -const unsigned TORC_SILVER = 1; + const unsigned TORC_SILVER = 1; // бронзовые гривны -const unsigned TORC_BRONZE = 2; + const unsigned TORC_BRONZE = 2; // терминатор всегда в конце -const unsigned TOTAL_TYPES = 3; -} // namespace ExtMoney + const unsigned TOTAL_TYPES = 3; +} namespace currency { diff --git a/src/stuff.cpp b/src/stuff.cpp index 035ef6d57..2eff51bfd 100644 --- a/src/stuff.cpp +++ b/src/stuff.cpp @@ -15,7 +15,7 @@ #include "comm.h" #include "db.h" #include "handler.h" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "chars/char.hpp" #include "room.hpp" #include "corpse.hpp" diff --git a/src/utils.cpp b/src/utils.cpp index ea8673c4f..905d262ac 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -26,7 +26,7 @@ #include "interpreter.h" #include "constants.h" #include "im.h" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "features.hpp" #include "privilege.hpp" #include "chars/char.hpp" diff --git a/src/utils.h b/src/utils.h index 4e5a01cac..7bcedac8f 100644 --- a/src/utils.h +++ b/src/utils.h @@ -219,9 +219,6 @@ int perform_move(CHAR_DATA * ch, int dir, int following, int checkmob, CHAR_DATA int mana_gain(const CHAR_DATA * ch); int hit_gain(CHAR_DATA * ch); int move_gain(CHAR_DATA * ch); -void advance_level(CHAR_DATA * ch); -void gain_exp(CHAR_DATA * ch, int gain); -void gain_exp_regardless(CHAR_DATA * ch, int gain); void gain_condition(CHAR_DATA * ch, unsigned condition, int value); void check_idling(CHAR_DATA * ch); void point_update(void); diff --git a/src/world.objects.cpp b/src/world.objects.cpp index fb19e0dab..ae4c09215 100644 --- a/src/world.objects.cpp +++ b/src/world.objects.cpp @@ -5,7 +5,7 @@ #include "logger.hpp" #include "db.h" #include "liquid.hpp" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "utils.h" #include "global.objects.hpp" diff --git a/src/zedit.cpp b/src/zedit.cpp index e4b10a999..725d5a070 100644 --- a/src/zedit.cpp +++ b/src/zedit.cpp @@ -9,7 +9,7 @@ #include "comm.h" #include "db.h" #include "olc.h" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "chars/char.hpp" #include "room.hpp" #include "help.hpp" diff --git a/tests/char.leaders.cpp b/tests/char.leaders.cpp index 6c1c5ca24..90a8b3deb 100644 --- a/tests/char.leaders.cpp +++ b/tests/char.leaders.cpp @@ -72,6 +72,7 @@ TEST(CHAR_Leaders, Group) { test_utils::CharacterBuilder builder; builder.create_new(); + builder.add_skill(ESkill::SKILL_LEADERSHIP, 200); auto leader = builder.get(); builder.create_new("F7"); builder.add_skill(ESkill::SKILL_LEADERSHIP, 10); @@ -84,7 +85,6 @@ TEST(CHAR_Leaders, Group) { leader->add_follower(follower.get()); } - test_utils::GroupBuilder g; auto grp = g._roster->addGroup(leader.get()); grp->addFollowers(leader.get()); diff --git a/tests/fight.penalties.cpp b/tests/fight.penalties.cpp index 61a73f43a..dca86612c 100644 --- a/tests/fight.penalties.cpp +++ b/tests/fight.penalties.cpp @@ -1,4 +1,4 @@ -#include "fightsystem/fight.penalties.hpp" +#include "grp/grp.main.h" #include "char.utilities.hpp" diff --git a/tests/obj.copy.cpp b/tests/obj.copy.cpp index 41a3fd1ce..3dbfa736e 100644 --- a/tests/obj.copy.cpp +++ b/tests/obj.copy.cpp @@ -1,7 +1,7 @@ #include #include -#include +#include constexpr obj_vnum OBJECT_VNUM = 100500; constexpr obj_vnum PROTOTYPE_VNUM = 100501; diff --git a/tests/triggers.list.cpp b/tests/triggers.list.cpp index bb4d60dcf..ea9cabd87 100644 --- a/tests/triggers.list.cpp +++ b/tests/triggers.list.cpp @@ -1,6 +1,6 @@ #include -#include +#include #include class TriggersList_F : public ::testing::Test From 7b3f5809b167f89b32fe0ef18da8303d7f34fbd7 Mon Sep 17 00:00:00 2001 From: bikbai Date: Mon, 4 Jan 2021 22:22:59 +0300 Subject: [PATCH 15/40] =?UTF-8?q?=D0=A1=D0=BB=D1=83=D1=87=D0=B8=D0=BB?= =?UTF-8?q?=D0=BE=D1=81=D1=8C=20=D0=B5=D1=89=D0=B5=20=D0=BD=D0=B5=D0=BC?= =?UTF-8?q?=D0=BD=D0=BE=D0=B6=D0=BA=D0=BE=20=D1=80=D0=B5=D1=84=D0=B0=D0=BA?= =?UTF-8?q?=D1=82=D0=BE=D1=80=D0=B8=D0=BD=D0=B3=D0=B0,=20=D0=B8=D0=B1?= =?UTF-8?q?=D0=BE=20=D1=80=D0=B0=D1=81=D1=87=D0=B5=D1=82=20=D0=B3=D1=80?= =?UTF-8?q?=D1=83=D0=BF=D0=BF=D0=BE=D0=B2=D0=BE=D0=B9=20=D1=8D=D0=BA=D1=81?= =?UTF-8?q?=D0=BF=D1=8B,=20=D0=B0=20=D0=BD=D0=B0=20=D1=8D=D1=82=D0=BE?= =?UTF-8?q?=D1=82=20=D1=84=D0=B0=D1=80=D1=88=20=D1=83=20=D0=BC=D0=B5=D0=BD?= =?UTF-8?q?=D1=8F=20=D0=B0=D0=BB=D0=BB=D0=B5=D1=80=D0=B3=D0=B8=D1=8F.=20?= =?UTF-8?q?=D0=92=D1=81=D1=91=20=D1=80=D0=B0=D0=B2=D0=BD=D0=BE=20=D0=BC?= =?UTF-8?q?=D0=B5=D1=88=D0=B0=D0=BD=D0=B8=D0=BD=D0=B0=20:(?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 34 +- src/act.comm.cpp | 2 +- src/act.informative.cpp | 75 +- src/act.item.cpp | 2 +- src/act.movement.cpp | 2 +- src/act.other.cpp | 2 +- src/boot.data.files.cpp | 6 +- src/celebrates.cpp | 4 +- src/chars/char.cpp | 7 +- src/chars/char_player.cpp | 16 +- src/chars/char_player.hpp | 21 +- src/class.cpp | 675 +---------- src/class.hpp | 14 - src/cmd.imm/act.wizard.cpp | 10 +- src/cmd/whoami.cpp | 2 +- src/comm.cpp | 52 +- src/config.cpp | 19 - src/core/leveling.cpp | 830 ++++++++++++++ src/core/leveling.h | 34 + src/corpse.cpp | 2 +- src/db.cpp | 13 +- src/{ => dg}/dg_comm.cpp | 0 src/{ => dg}/dg_db_scripts.cpp | 0 src/{ => dg}/dg_db_scripts.hpp | 0 src/{ => dg}/dg_event.cpp | 0 src/{ => dg}/dg_event.h | 0 src/{ => dg}/dg_handler.cpp | 0 src/{ => dg}/dg_misc.cpp | 0 src/{ => dg}/dg_mobcmd.cpp | 32 +- src/{ => dg}/dg_objcmd.cpp | 38 +- src/{ => dg}/dg_olc.cpp | 0 src/{ => dg}/dg_olc.h | 0 src/{ => dg}/dg_scripts.cpp | 10 +- src/{ => dg}/dg_scripts.h | 0 src/{ => dg}/dg_triggers.cpp | 0 src/{ => dg}/dg_wldcmd.cpp | 40 +- src/ext_money.cpp | 1630 +++++++++++++-------------- src/ext_money.hpp | 35 +- src/features.cpp | 4 +- src/fightsystem/fight.cpp | 31 +- src/fightsystem/fight.h | 2 - src/fightsystem/fight.penalties.cpp | 92 -- src/fightsystem/fight.penalties.hpp | 22 - src/fightsystem/fight_hit.cpp | 65 +- src/fightsystem/fight_stuff.cpp | 419 +------ src/find.obj.id.by.vnum.cpp | 2 +- src/glory.cpp | 1 - src/glory_const.cpp | 1 - src/grp/follow.cpp | 56 +- src/grp/grouppenalties.cpp | 111 ++ src/grp/grouppenaltycalculator.cpp | 90 ++ src/grp/grp.group.cpp | 316 +++++- src/grp/grp.main.h | 53 +- src/handler.cpp | 4 +- src/heartbeat.cpp | 2 +- src/house_exp.cpp | 22 + src/house_exp.hpp | 1 + src/interpreter.cpp | 4 +- src/limits.cpp | 187 +-- src/magic.cpp | 2 +- src/medit.cpp | 2 +- src/msdp.reporters.cpp | 44 +- src/named_stuff.cpp | 2 +- src/obj.cpp | 2 +- src/objsave.cpp | 2 +- src/oedit.cpp | 2 +- src/olc.cpp | 2 +- src/redit.cpp | 2 +- src/room.cpp | 2 +- src/room.hpp | 2 +- src/sets_drop.cpp | 2 +- src/skills.cpp | 2 +- src/skills/flee.cpp | 7 +- src/spec_procs.cpp | 41 +- src/spell_parser.cpp | 2 +- src/spells.cpp | 2 +- src/structs.cpp | 5 +- src/structs.h | 15 +- src/stuff.cpp | 2 +- src/utils.cpp | 2 +- src/utils.h | 3 - src/world.objects.cpp | 2 +- src/zedit.cpp | 2 +- tests/char.leaders.cpp | 2 +- tests/fight.penalties.cpp | 2 +- tests/obj.copy.cpp | 2 +- tests/triggers.list.cpp | 2 +- 87 files changed, 2642 insertions(+), 2609 deletions(-) create mode 100644 src/core/leveling.cpp create mode 100644 src/core/leveling.h rename src/{ => dg}/dg_comm.cpp (100%) rename src/{ => dg}/dg_db_scripts.cpp (100%) rename src/{ => dg}/dg_db_scripts.hpp (100%) rename src/{ => dg}/dg_event.cpp (100%) rename src/{ => dg}/dg_event.h (100%) rename src/{ => dg}/dg_handler.cpp (100%) rename src/{ => dg}/dg_misc.cpp (100%) rename src/{ => dg}/dg_mobcmd.cpp (99%) rename src/{ => dg}/dg_objcmd.cpp (99%) rename src/{ => dg}/dg_olc.cpp (100%) rename src/{ => dg}/dg_olc.h (100%) rename src/{ => dg}/dg_scripts.cpp (99%) rename src/{ => dg}/dg_scripts.h (100%) rename src/{ => dg}/dg_triggers.cpp (100%) rename src/{ => dg}/dg_wldcmd.cpp (99%) create mode 100644 src/grp/grouppenalties.cpp create mode 100644 src/grp/grouppenaltycalculator.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 9b5224f4e..ac032fa2b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,17 +67,17 @@ set(SOURCES src/debug.utils.cpp src/depot.cpp src/description.cpp - src/dg_comm.cpp - src/dg_db_scripts.cpp - src/dg_event.cpp - src/dg_handler.cpp - src/dg_misc.cpp - src/dg_mobcmd.cpp - src/dg_objcmd.cpp - src/dg_olc.cpp - src/dg_scripts.cpp - src/dg_triggers.cpp - src/dg_wldcmd.cpp + src/dg/dg_comm.cpp + src/dg/dg_db_scripts.cpp + src/dg/dg_event.cpp + src/dg/dg_handler.cpp + src/dg/dg_misc.cpp + src/dg/dg_mobcmd.cpp + src/dg/dg_objcmd.cpp + src/dg/dg_olc.cpp + src/dg/dg_scripts.cpp + src/dg/dg_triggers.cpp + src/dg/dg_wldcmd.cpp src/dictionary.cpp src/diskio.cpp src/dps.cpp @@ -211,9 +211,11 @@ set(SOURCES src/world.objects.cpp src/zedit.cpp src/zone.table.cpp - ) + src/core/leveling.cpp + src/grp/grouppenalties.cpp src/grp/grouppenaltycalculator.cpp) set(HEADERS + src/core/leveling.h src/grp/follow.h src/grp/grp.main.h src/core/affect_data.h @@ -294,10 +296,10 @@ set(HEADERS src/deathtrap.hpp src/depot.hpp src/description.h - src/dg_db_scripts.hpp - src/dg_event.h - src/dg_olc.h - src/dg_scripts.h + src/dg/dg_db_scripts.hpp + src/dg/dg_event.h + src/dg/dg_olc.h + src/dg/dg_scripts.h src/dictionary.hpp src/diskio.h src/double_map.hpp diff --git a/src/act.comm.cpp b/src/act.comm.cpp index 84dad07b5..c858877d0 100644 --- a/src/act.comm.cpp +++ b/src/act.comm.cpp @@ -17,7 +17,7 @@ #include "handler.h" #include "db.h" #include "screen.h" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "auction.h" #include "privilege.hpp" #include "chars/char.hpp" diff --git a/src/act.informative.cpp b/src/act.informative.cpp index ba506f0ab..557d52a62 100644 --- a/src/act.informative.cpp +++ b/src/act.informative.cpp @@ -12,56 +12,57 @@ * $Revision$ * ************************************************************************ */ -#include "world.objects.hpp" +#include "bonus.h" +#include "char_obj_utils.inl" +#include "chars/char.hpp" +#include "chars/char_player.hpp" +#include "chars/player_races.hpp" #include "chars/world.characters.hpp" -#include "object.prototypes.hpp" -#include "logger.hpp" -#include "shutdown.parameters.hpp" -#include "obj.hpp" +#include "class.hpp" #include "comm.h" -#include "interpreter.h" -#include "handler.h" +#include "core/leveling.h" +#include "conf.h" +#include "constants.h" +#include "corpse.hpp" #include "db.h" -#include "spells.h" -#include "skills.h" +#include "depot.hpp" +#include "description.h" +#include "dg/dg_scripts.h" +#include "ext_money.hpp" +#include "features.hpp" #include "fightsystem/fight.h" #include "fightsystem/fight_hit.hpp" -#include "screen.h" -#include "constants.h" #include "fightsystem/pk.h" -#include "dg_scripts.h" +#include "glory.hpp" +#include "glory_const.hpp" +#include "grp/grp.main.h" +#include "handler.h" +#include "help.hpp" +#include "house.h" +#include "im.h" +#include "interpreter.h" +#include "liquid.hpp" +#include "logger.hpp" #include "mail.h" +#include "map.hpp" +#include "mob_stat.hpp" +#include "modify.h" +#include "obj.hpp" +#include "object.prototypes.hpp" +#include "parcel.hpp" #include "parcel.hpp" -#include "features.hpp" -#include "im.h" -#include "house.h" -#include "description.h" #include "privilege.hpp" -#include "depot.hpp" -#include "glory.hpp" #include "random.hpp" -#include "chars/char.hpp" -#include "chars/char_player.hpp" -#include "parcel.hpp" -#include "liquid.hpp" -#include "modify.h" #include "room.hpp" -#include "glory_const.hpp" -#include "chars/player_races.hpp" -#include "corpse.hpp" +#include "screen.h" #include "sets_drop.hpp" -#include "help.hpp" -#include "map.hpp" -#include "ext_money.hpp" -#include "mob_stat.hpp" -#include "char_obj_utils.inl" -#include "class.hpp" -#include "zone.table.hpp" +#include "shutdown.parameters.hpp" +#include "skills.h" +#include "spells.h" #include "structs.h" #include "sysdep.h" -#include "bonus.h" -#include "conf.h" - +#include "world.objects.hpp" +#include "zone.table.hpp" #include #include @@ -72,6 +73,7 @@ #include using std::string; +using namespace ExpCalc; // extern variables extern DESCRIPTOR_DATA *descriptor_list; @@ -93,7 +95,6 @@ extern int nameserver_is_slow; //config.cpp extern std::vector cities; // extern functions long find_class_bitvector(char arg); -int level_exp(CHAR_DATA * ch, int level); TIME_INFO_DATA *real_time_passed(time_t t2, time_t t1); int compute_armor_class(CHAR_DATA * ch); int pk_count(CHAR_DATA * ch); diff --git a/src/act.item.cpp b/src/act.item.cpp index f99fd9fef..04bd4bebf 100644 --- a/src/act.item.cpp +++ b/src/act.item.cpp @@ -22,7 +22,7 @@ #include "constants.h" #include "db.h" #include "depot.hpp" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "features.hpp" #include "fightsystem/fight.h" #include "handler.h" diff --git a/src/act.movement.cpp b/src/act.movement.cpp index 5965b00d0..94c77ab29 100644 --- a/src/act.movement.cpp +++ b/src/act.movement.cpp @@ -26,7 +26,7 @@ #include "skills.h" #include "house.h" #include "constants.h" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "screen.h" #include "fightsystem/pk.h" #include "fightsystem/mobact.hpp" diff --git a/src/act.other.cpp b/src/act.other.cpp index e363f4d3f..2d789d3a2 100644 --- a/src/act.other.cpp +++ b/src/act.other.cpp @@ -22,7 +22,7 @@ #include "constants.h" #include "db.h" #include "depot.hpp" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "features.hpp" #include "fightsystem/fight.h" #include "fightsystem/fight_hit.hpp" diff --git a/src/boot.data.files.cpp b/src/boot.data.files.cpp index 1dfaad995..1ff394487 100644 --- a/src/boot.data.files.cpp +++ b/src/boot.data.files.cpp @@ -2,8 +2,8 @@ #include "object.prototypes.hpp" #include "logger.hpp" -#include "dg_scripts.h" -#include "dg_olc.h" +#include "dg/dg_scripts.h" +#include "dg/dg_olc.h" #include "boards.h" #include "constants.h" #include "room.hpp" @@ -11,7 +11,7 @@ #include "im.h" #include "chars/char.hpp" #include "help.hpp" -#include "dg_db_scripts.hpp" +#include "dg/dg_db_scripts.hpp" #include "zone.table.hpp" #include "utils.h" diff --git a/src/celebrates.cpp b/src/celebrates.cpp index 3bfea305c..2cc954045 100644 --- a/src/celebrates.cpp +++ b/src/celebrates.cpp @@ -4,8 +4,8 @@ #include "obj.hpp" #include "comm.h" #include "db.h" -#include "dg_db_scripts.hpp" -#include "dg_scripts.h" +#include "dg/dg_db_scripts.hpp" +#include "dg/dg_scripts.h" #include "chars/char.hpp" #include "room.hpp" #include "handler.h" diff --git a/src/chars/char.cpp b/src/chars/char.cpp index 0b7f341fa..f706172f5 100644 --- a/src/chars/char.cpp +++ b/src/chars/char.cpp @@ -29,7 +29,7 @@ #include "utils.h" #include "msdp.constants.hpp" #include "backtrace.hpp" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "zone.table.hpp" #include @@ -898,6 +898,7 @@ OBJ_DATA * CHAR_DATA::get_cast_obj() const bool IS_CHARMICE(const CHAR_DATA* ch) { + if (!ch) return false; return IS_NPC(ch) && (AFF_FLAGGED(ch, EAffectFlag::AFF_HELPER) || AFF_FLAGGED(ch, EAffectFlag::AFF_CHARM)); @@ -905,6 +906,7 @@ bool IS_CHARMICE(const CHAR_DATA* ch) bool MORT_CAN_SEE(const CHAR_DATA* sub, const CHAR_DATA* obj) { + if (!sub) return false; return HERE(obj) && INVIS_OK(sub, obj) && (IS_LIGHT((obj)->in_room) @@ -913,6 +915,7 @@ bool MORT_CAN_SEE(const CHAR_DATA* sub, const CHAR_DATA* obj) bool MAY_SEE(const CHAR_DATA* ch, const CHAR_DATA* sub, const CHAR_DATA* obj) { + if (!ch) return false; return !(GET_INVIS_LEV(ch) > 30) && !AFF_FLAGGED(sub, EAffectFlag::AFF_BLIND) && (!IS_DARK(sub->in_room) @@ -2033,6 +2036,8 @@ void CHAR_DATA::msdp_report(const std::string& name) { void CHAR_DATA::removeGroupFlags() { AFF_FLAGS(this).unset(EAffectFlag::AFF_GROUP); PRF_FLAGS(this).unset(PRF_SKIRMISHER); + if (personGroup != nullptr && IS_CHARMICE(this)) + personGroup->_removeMember(this); } void CHAR_DATA::add_follower(CHAR_DATA* ch) { diff --git a/src/chars/char_player.cpp b/src/chars/char_player.cpp index bffece51c..97d265494 100644 --- a/src/chars/char_player.cpp +++ b/src/chars/char_player.cpp @@ -7,7 +7,7 @@ #include "logger.hpp" #include "utils.h" #include "db.h" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "handler.h" #include "boards.h" #include "file_crc.hpp" @@ -18,6 +18,7 @@ #include "im.h" #include "olc.h" #include "comm.h" +#include "core/leveling.h" #include "fightsystem/pk.h" #include "diskio.h" #include "interpreter.h" @@ -48,7 +49,7 @@ #include -int level_exp(CHAR_DATA * ch, int level); + extern std::vector cities; extern std::string default_str_cities; namespace @@ -1235,9 +1236,9 @@ int Player::load_char_ascii(const char *name, bool reboot, const bool find_id /* // если с загруженными выше полями что-то хочется делать после лоада - делайте это здесь //Indexing experience - if his exp is lover than required for his level - set it to required - if (GET_EXP(this) < level_exp(this, GET_LEVEL(this))) + if (GET_EXP(this) < ExpCalc::level_exp(this, GET_LEVEL(this))) { - set_exp(level_exp(this, GET_LEVEL(this))); + set_exp(ExpCalc::level_exp(this, GET_LEVEL(this))); } if (reboot) @@ -2555,4 +2556,11 @@ unsigned long int Player::getTelegramId() { return this->player_specials->saved.telegram_id; } +// * Перерасчет максимальных родных хп персонажа. +// * При входе в игру, левеле/делевеле, добавлении/удалении славы. +void check_max_hp(CHAR_DATA *ch) +{ + GET_MAX_HIT(ch) = PlayerSystem::con_natural_hp(ch); +} + // vim: ts=4 sw=4 tw=0 noet syntax=cpp : diff --git a/src/chars/char_player.hpp b/src/chars/char_player.hpp index e8c9d9c9f..7fb091f2c 100644 --- a/src/chars/char_player.hpp +++ b/src/chars/char_player.hpp @@ -6,25 +6,30 @@ #define CHAR_PLAYER_HPP_INCLUDED #include "conf.h" -#include "sysdep.h" -#include "structs.h" -#include "quested.hpp" -#include "mobmax.hpp" -#include "remember.hpp" +#include "boards.types.hpp" #include "char.hpp" +#include "cmd/cmd.generic.h" #include "dps.hpp" +#include "ext_money.hpp" #include "map.hpp" -#include "reset_stats.hpp" -#include "boards.types.hpp" +#include "mobmax.hpp" #include "quest.hpp" +#include "quested.hpp" +#include "remember.hpp" +#include "reset_stats.hpp" #include "stigmas.hpp" -#include "cmd/cmd.generic.h" +#include "structs.h" +#include "sysdep.h" #include #include #include #include +// * Перерасчет максимальных родных хп персонажа. +// * При входе в игру, левеле/делевеле, добавлении/удалении славы. +void check_max_hp(CHAR_DATA *ch); + // кол-во сохраняемых стартовых статов в файле const int START_STATS_TOTAL = 6; diff --git a/src/class.cpp b/src/class.cpp index cd73811e2..8418957bd 100644 --- a/src/class.cpp +++ b/src/class.cpp @@ -21,38 +21,38 @@ #include "class.hpp" #include "world.objects.hpp" -#include "obj.hpp" +#include "chars/char.hpp" +#include "chars/char_player.hpp" +#include "chars/player_races.hpp" #include "comm.h" -#include "db.h" -#include "spell_parser.hpp" -#include "spells.h" -#include "skills.h" -#include "interpreter.h" -#include "handler.h" +#include "core/leveling.h" +#include "conf.h" #include "constants.h" -#include "fightsystem/pk.h" -#include "top.h" +#include "db.h" +#include "exchange.h" #include "features.hpp" +#include "fightsystem/pk.h" +#include "handler.h" #include "im.h" -#include "chars/char.hpp" -#include "spam.hpp" -#include "screen.h" -#include "chars/char_player.hpp" +#include "interpreter.h" +#include "logger.hpp" #include "named_stuff.hpp" -#include "chars/player_races.hpp" #include "noob.hpp" -#include "exchange.h" -#include "logger.hpp" -#include "utils.h" +#include "obj.hpp" +#include "screen.h" +#include "skills.h" +#include "spam.hpp" +#include "spell_parser.hpp" +#include "spells.h" #include "structs.h" #include "sysdep.h" -#include "conf.h" +#include "top.h" +#include "utils.h" #include extern int siteok_everyone; extern struct spell_create_type spell_create[]; -extern double exp_coefficients[]; struct skillvariables_dig dig_vars; struct skillvariables_insgem insgem_vars; @@ -65,7 +65,7 @@ int thaco(int class_num, int level); void do_start(CHAR_DATA * ch, int newbie); int invalid_anti_class(CHAR_DATA * ch, const OBJ_DATA * obj); int invalid_no_class(CHAR_DATA * ch, const OBJ_DATA * obj); -int level_exp(CHAR_DATA * ch, int level); + byte extend_saving_throws(int class_num, int type, int level); int invalid_unique(CHAR_DATA * ch, const OBJ_DATA * obj); extern bool char_to_pk_clan(CHAR_DATA *ch); @@ -2069,7 +2069,7 @@ void do_start(CHAR_DATA * ch, int newbie) } } - advance_level(ch); + ExpCalc::advance_level(ch); sprintf(buf, "%s advanced to level %d", GET_NAME(ch), GET_LEVEL(ch)); mudlog(buf, BRF, LVL_IMPL, SYSLOG, TRUE); @@ -2087,138 +2087,6 @@ void do_start(CHAR_DATA * ch, int newbie) } } -// * Перерасчет максимальных родных хп персонажа. -// * При входе в игру, левеле/делевеле, добавлении/удалении славы. -void check_max_hp(CHAR_DATA *ch) -{ - GET_MAX_HIT(ch) = PlayerSystem::con_natural_hp(ch); -} - -// * Обработка событий при левел-апе. -void levelup_events(CHAR_DATA *ch) -{ - if (SpamSystem::MIN_OFFTOP_LVL == GET_LEVEL(ch) - && !ch->get_disposable_flag(DIS_OFFTOP_MESSAGE)) - { - PRF_FLAGS(ch).set(PRF_OFFTOP_MODE); - ch->set_disposable_flag(DIS_OFFTOP_MESSAGE); - send_to_char(ch, - "%sТеперь вы можете пользоваться каналом оффтоп ('справка оффтоп').%s\r\n", - CCIGRN(ch, C_SPR), CCNRM(ch, C_SPR)); - } - if (EXCHANGE_MIN_CHAR_LEV == GET_LEVEL(ch) - && !ch->get_disposable_flag(DIS_EXCHANGE_MESSAGE)) - { - // по умолчанию базар у всех включен, поэтому не спамим даже однократно - if (GET_REMORT(ch) <= 0) - { - send_to_char(ch, - "%sТеперь вы можете покупать и продавать вещи на базаре ('справка базар!').%s\r\n", - CCIGRN(ch, C_SPR), CCNRM(ch, C_SPR)); - } - ch->set_disposable_flag(DIS_EXCHANGE_MESSAGE); - } -} - -void advance_level(CHAR_DATA * ch) -{ - int add_move = 0, i; - - switch (GET_CLASS(ch)) - { - case CLASS_BATTLEMAGE: - case CLASS_DEFENDERMAGE: - case CLASS_CHARMMAGE: - case CLASS_NECROMANCER: - add_move = 2; - break; - - case CLASS_CLERIC: - case CLASS_DRUID: - add_move = 2; - break; - - case CLASS_THIEF: - case CLASS_ASSASINE: - case CLASS_MERCHANT: - add_move = number(ch->get_inborn_dex() / 6 + 1, ch->get_inborn_dex() / 5 + 1); - break; - - case CLASS_WARRIOR: - add_move = number(ch->get_inborn_dex() / 6 + 1, ch->get_inborn_dex() / 5 + 1); - break; - - case CLASS_GUARD: - case CLASS_RANGER: - case CLASS_PALADINE: - case CLASS_SMITH: - add_move = number(ch->get_inborn_dex() / 6 + 1, ch->get_inborn_dex() / 5 + 1); - break; - } - - check_max_hp(ch); - ch->points.max_move += MAX(1, add_move); - - setAllInbornFeatures(ch); - - if (IS_IMMORTAL(ch)) - { - for (i = 0; i < 3; i++) - GET_COND(ch, i) = (char) - 1; - PRF_FLAGS(ch).set(PRF_HOLYLIGHT); - } - - TopPlayer::Refresh(ch); - levelup_events(ch); - ch->save_char(); -} - -void decrease_level(CHAR_DATA * ch) -{ - int add_move = 0; - - switch (GET_CLASS(ch)) - { - case CLASS_BATTLEMAGE: - case CLASS_DEFENDERMAGE: - case CLASS_CHARMMAGE: - case CLASS_NECROMANCER: - add_move = 2; - break; - - case CLASS_CLERIC: - case CLASS_DRUID: - add_move = 2; - break; - - case CLASS_THIEF: - case CLASS_ASSASINE: - case CLASS_MERCHANT: - add_move = ch->get_inborn_dex() / 5 + 1; - break; - - case CLASS_WARRIOR: - case CLASS_GUARD: - case CLASS_PALADINE: - case CLASS_RANGER: - case CLASS_SMITH: - add_move = ch->get_inborn_dex() / 5 + 1; - break; - } - - check_max_hp(ch); - ch->points.max_move -= MIN(ch->points.max_move, MAX(1, add_move)); - - GET_WIMP_LEV(ch) = MAX(0, MIN(GET_WIMP_LEV(ch), GET_REAL_MAX_HIT(ch) / 2)); - if (!IS_IMMORTAL(ch)) - { - PRF_FLAGS(ch).unset(PRF_HOLYLIGHT); - } - - TopPlayer::Refresh(ch); - ch->save_char(); -} - /* * invalid_class is used by handler.cpp to determine if a piece of equipment is * usable by a particular class, based on the ITEM_ANTI_{class} bitvectors. @@ -2969,505 +2837,4 @@ void init_basic_values(void) fclose(magic); return; } - -/* - Берет misc/grouping, первый столбик цифр считает номерами мортов, - остальные столбики - значение макс. разрыва в уровнях для конкретного - класса. На момент написания этого в конфиге присутствует 26 строк, макс. - морт равен 50 - строки с мортами с 26 по 50 копируются с 25-мортовой строки. -*/ -int GroupPenalties::init(void) -{ - char buf[MAX_INPUT_LENGTH]; - int clss = 0, remorts = 0, rows_assigned = 0, levels = 0, pos = 0, max_rows = MAX_REMORT+1; - - // пре-инициализация - for (remorts = 0; remorts < max_rows; remorts++) //Строк в массиве должно быть на 1 больше, чем макс. морт - { - for (clss = 0; clss < NUM_PLAYER_CLASSES; clss++) //Столбцов в массиве должно быть ровно столько же, сколько есть классов - { - m_grouping[clss][remorts] = -1; - } - } - - FILE* f = fopen(LIB_MISC "grouping", "r"); - if (!f) - { - log("Невозможно открыть файл %s", LIB_MISC "grouping"); - return 1; - } - - while (get_line(f, buf)) - { - if (!buf[0] || buf[0] == ';' || buf[0] == '\n') //Строка пустая или строка-коммент - { - continue; - } - clss = 0; - pos = 0; - while (sscanf(&buf[pos], "%d", &levels) == 1) - { - while (buf[pos] == ' ' || buf[pos] == '\t') - { - pos++; - } - if (clss == 0) //Первый проход цикла по строке - { - remorts = levels; //Номера строк - if (m_grouping[0][remorts] != -1) - { - log("Ошибка при чтении файла %s: дублирование параметров для %d ремортов", - LIB_MISC "grouping", remorts); - return 2; - } - if (remorts > MAX_REMORT || remorts < 0) - { - log("Ошибка при чтении файла %s: неверное значение количества ремортов: %d, " - "должно быть в промежутке от 0 до %d", - LIB_MISC "grouping", remorts, MAX_REMORT); - return 3; - } - } - else - { - m_grouping[clss - 1][remorts] = levels; // -1 потому что в массиве нет столбца с кол-вом мортов - } - clss++; //+Номер столбца массива - while (buf[pos] != ' ' && buf[pos] != '\t' && buf[pos] != 0) - { - pos++; //Ищем следующее число в строке конфига - } - } - if (clss != NUM_PLAYER_CLASSES+1) - { - log("Ошибка при чтении файла %s: неверный формат строки '%s', должно быть %d " - "целых чисел, прочитали %d", LIB_MISC "grouping", buf, NUM_PLAYER_CLASSES+1, clss); - return 4; - } - rows_assigned++; - } - - if (rows_assigned < max_rows) - { - for (levels = remorts; levels < max_rows; levels++) //Берем свободную переменную - { - for (clss = 0; clss < NUM_PLAYER_CLASSES; clss++) - { - m_grouping[clss][levels] = m_grouping[clss][remorts]; //Копируем последнюю строку на все морты, для которых нет строк - } - } - } - fclose(f); - return 0; -} - -/* - * This is the exp given to implementors -- it must always be greater - * than the exp required for immortality, plus at least 20,000 or so. - */ - -// Function to return the exp required for each class/level -int level_exp(CHAR_DATA * ch, int level) -{ - if (level > LVL_IMPL || level < 0) - { - log("SYSERR: Requesting exp for invalid level %d!", level); - return 0; - } - - /* - * Gods have exp close to EXP_MAX. This statement should never have to - * changed, regardless of how many mortal or immortal levels exist. - */ - if (level > LVL_IMMORT) - { - return EXP_IMPL - ((LVL_IMPL - level) * 1000); - } - - // Exp required for normal mortals is below - float exp_modifier; - if (GET_REMORT(ch) < MAX_EXP_COEFFICIENTS_USED) - exp_modifier = exp_coefficients[GET_REMORT(ch)]; - else - exp_modifier = exp_coefficients[MAX_EXP_COEFFICIENTS_USED]; - - switch (GET_CLASS(ch)) - { - - case CLASS_BATTLEMAGE: - case CLASS_DEFENDERMAGE: - case CLASS_CHARMMAGE: - case CLASS_NECROMANCER: - switch (level) - { - case 0: - return 0; - case 1: - return 1; - case 2: - return int (exp_modifier * 2500); - case 3: - return int (exp_modifier * 5000); - case 4: - return int (exp_modifier * 9000); - case 5: - return int (exp_modifier * 17000); - case 6: - return int (exp_modifier * 27000); - case 7: - return int (exp_modifier * 47000); - case 8: - return int (exp_modifier * 77000); - case 9: - return int (exp_modifier * 127000); - case 10: - return int (exp_modifier * 197000); - case 11: - return int (exp_modifier * 297000); - case 12: - return int (exp_modifier * 427000); - case 13: - return int (exp_modifier * 587000); - case 14: - return int (exp_modifier * 817000); - case 15: - return int (exp_modifier * 1107000); - case 16: - return int (exp_modifier * 1447000); - case 17: - return int (exp_modifier * 1847000); - case 18: - return int (exp_modifier * 2310000); - case 19: - return int (exp_modifier * 2830000); - case 20: - return int (exp_modifier * 3580000); - case 21: - return int (exp_modifier * 4580000); - case 22: - return int (exp_modifier * 5830000); - case 23: - return int (exp_modifier * 7330000); - case 24: - return int (exp_modifier * 9080000); - case 25: - return int (exp_modifier * 11080000); - case 26: - return int (exp_modifier * 15000000); - case 27: - return int (exp_modifier * 22000000); - case 28: - return int (exp_modifier * 33000000); - case 29: - return int (exp_modifier * 47000000); - case 30: - return int (exp_modifier * 64000000); - // add new levels here - case LVL_IMMORT: - return int (exp_modifier * 94000000); - } - break; - - case CLASS_CLERIC: - case CLASS_DRUID: - switch (level) - { - case 0: - return 0; - case 1: - return 1; - case 2: - return int (exp_modifier * 2500); - case 3: - return int (exp_modifier * 5000); - case 4: - return int (exp_modifier * 9000); - case 5: - return int (exp_modifier * 17000); - case 6: - return int (exp_modifier * 27000); - case 7: - return int (exp_modifier * 47000); - case 8: - return int (exp_modifier * 77000); - case 9: - return int (exp_modifier * 127000); - case 10: - return int (exp_modifier * 197000); - case 11: - return int (exp_modifier * 297000); - case 12: - return int (exp_modifier * 427000); - case 13: - return int (exp_modifier * 587000); - case 14: - return int (exp_modifier * 817000); - case 15: - return int (exp_modifier * 1107000); - case 16: - return int (exp_modifier * 1447000); - case 17: - return int (exp_modifier * 1847000); - case 18: - return int (exp_modifier * 2310000); - case 19: - return int (exp_modifier * 2830000); - case 20: - return int (exp_modifier * 3580000); - case 21: - return int (exp_modifier * 4580000); - case 22: - return int (exp_modifier * 5830000); - case 23: - return int (exp_modifier * 7330000); - case 24: - return int (exp_modifier * 9080000); - case 25: - return int (exp_modifier * 11080000); - case 26: - return int (exp_modifier * 15000000); - case 27: - return int (exp_modifier * 22000000); - case 28: - return int (exp_modifier * 33000000); - case 29: - return int (exp_modifier * 47000000); - case 30: - return int (exp_modifier * 64000000); - // add new levels here - case LVL_IMMORT: - return int (exp_modifier * 87000000); - } - break; - - case CLASS_THIEF: - switch (level) - { - case 0: - return 0; - case 1: - return 1; - case 2: - return int (exp_modifier * 1000); - case 3: - return int (exp_modifier * 2000); - case 4: - return int (exp_modifier * 4000); - case 5: - return int (exp_modifier * 8000); - case 6: - return int (exp_modifier * 15000); - case 7: - return int (exp_modifier * 28000); - case 8: - return int (exp_modifier * 52000); - case 9: - return int (exp_modifier * 85000); - case 10: - return int (exp_modifier * 131000); - case 11: - return int (exp_modifier * 192000); - case 12: - return int (exp_modifier * 271000); - case 13: - return int (exp_modifier * 372000); - case 14: - return int (exp_modifier * 512000); - case 15: - return int (exp_modifier * 672000); - case 16: - return int (exp_modifier * 854000); - case 17: - return int (exp_modifier * 1047000); - case 18: - return int (exp_modifier * 1274000); - case 19: - return int (exp_modifier * 1534000); - case 20: - return int (exp_modifier * 1860000); - case 21: - return int (exp_modifier * 2520000); - case 22: - return int (exp_modifier * 3380000); - case 23: - return int (exp_modifier * 4374000); - case 24: - return int (exp_modifier * 5500000); - case 25: - return int (exp_modifier * 6827000); - case 26: - return int (exp_modifier * 8667000); - case 27: - return int (exp_modifier * 13334000); - case 28: - return int (exp_modifier * 20000000); - case 29: - return int (exp_modifier * 28667000); - case 30: - return int (exp_modifier * 40000000); - // add new levels here - case LVL_IMMORT: - return int (exp_modifier * 53000000); - } - break; - - case CLASS_ASSASINE: - case CLASS_MERCHANT: - switch (level) - { - case 0: - return 0; - case 1: - return 1; - case 2: - return int (exp_modifier * 1500); - case 3: - return int (exp_modifier * 3000); - case 4: - return int (exp_modifier * 6000); - case 5: - return int (exp_modifier * 12000); - case 6: - return int (exp_modifier * 22000); - case 7: - return int (exp_modifier * 42000); - case 8: - return int (exp_modifier * 77000); - case 9: - return int (exp_modifier * 127000); - case 10: - return int (exp_modifier * 197000); - case 11: - return int (exp_modifier * 287000); - case 12: - return int (exp_modifier * 407000); - case 13: - return int (exp_modifier * 557000); - case 14: - return int (exp_modifier * 767000); - case 15: - return int (exp_modifier * 1007000); - case 16: - return int (exp_modifier * 1280000); - case 17: - return int (exp_modifier * 1570000); - case 18: - return int (exp_modifier * 1910000); - case 19: - return int (exp_modifier * 2300000); - case 20: - return int (exp_modifier * 2790000); - case 21: - return int (exp_modifier * 3780000); - case 22: - return int (exp_modifier * 5070000); - case 23: - return int (exp_modifier * 6560000); - case 24: - return int (exp_modifier * 8250000); - case 25: - return int (exp_modifier * 10240000); - case 26: - return int (exp_modifier * 13000000); - case 27: - return int (exp_modifier * 20000000); - case 28: - return int (exp_modifier * 30000000); - case 29: - return int (exp_modifier * 43000000); - case 30: - return int (exp_modifier * 59000000); - // add new levels here - case LVL_IMMORT: - return int (exp_modifier * 79000000); - } - break; - - case CLASS_WARRIOR: - case CLASS_GUARD: - case CLASS_PALADINE: - case CLASS_RANGER: - case CLASS_SMITH: - switch (level) - { - case 0: - return 0; - case 1: - return 1; - case 2: - return int (exp_modifier * 2000); - case 3: - return int (exp_modifier * 4000); - case 4: - return int (exp_modifier * 8000); - case 5: - return int (exp_modifier * 14000); - case 6: - return int (exp_modifier * 24000); - case 7: - return int (exp_modifier * 39000); - case 8: - return int (exp_modifier * 69000); - case 9: - return int (exp_modifier * 119000); - case 10: - return int (exp_modifier * 189000); - case 11: - return int (exp_modifier * 289000); - case 12: - return int (exp_modifier * 419000); - case 13: - return int (exp_modifier * 579000); - case 14: - return int (exp_modifier * 800000); - case 15: - return int (exp_modifier * 1070000); - case 16: - return int (exp_modifier * 1340000); - case 17: - return int (exp_modifier * 1660000); - case 18: - return int (exp_modifier * 2030000); - case 19: - return int (exp_modifier * 2450000); - case 20: - return int (exp_modifier * 2950000); - case 21: - return int (exp_modifier * 3950000); - case 22: - return int (exp_modifier * 5250000); - case 23: - return int (exp_modifier * 6750000); - case 24: - return int (exp_modifier * 8450000); - case 25: - return int (exp_modifier * 10350000); - case 26: - return int (exp_modifier * 14000000); - case 27: - return int (exp_modifier * 21000000); - case 28: - return int (exp_modifier * 31000000); - case 29: - return int (exp_modifier * 44000000); - case 30: - return int (exp_modifier * 64000000); - // add new levels here - case LVL_IMMORT: - return int (exp_modifier * 79000000); - } - break; - } - - /* - * This statement should never be reached if the exp tables in this function - * are set up properly. If you see exp of 123456 then the tables above are - * incomplete -- so, complete them! - */ - log("SYSERR: XP tables not set up correctly in class.c!"); - return 123456; -} - -GroupPenalties grouping; ///< TODO: get rid of this global variable. - // vim: ts=4 sw=4 tw=0 noet syntax=cpp : diff --git a/src/class.hpp b/src/class.hpp index d1839d212..1ec9f8e97 100644 --- a/src/class.hpp +++ b/src/class.hpp @@ -8,20 +8,6 @@ int invalid_no_class(CHAR_DATA * ch, const OBJ_DATA * obj); int extra_damroll(int class_num, int level); -class GroupPenalties -{ -public: - using class_penalties_t = std::array; - using penalties_t = std::array; - - int init(); - const auto& operator[](const size_t character_class) const { return m_grouping[character_class]; } - -private: - penalties_t m_grouping; -}; - -extern GroupPenalties grouping; #endif // __CLASS_HPP__ diff --git a/src/cmd.imm/act.wizard.cpp b/src/cmd.imm/act.wizard.cpp index 63a23ec77..02df535ab 100644 --- a/src/cmd.imm/act.wizard.cpp +++ b/src/cmd.imm/act.wizard.cpp @@ -25,6 +25,7 @@ #include "chars/world.characters.hpp" #include "comm.h" #include "command.shutdown.hpp" +#include "core/leveling.h" #include "conf.h" #include "config.hpp" #include "constants.h" @@ -32,7 +33,7 @@ #include "db.h" #include "depot.hpp" #include "description.h" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "ext_money.hpp" #include "fightsystem/fight.h" #include "fightsystem/pk.h" @@ -87,6 +88,7 @@ using std::ifstream; using std::fstream; +using namespace ExpCalc; // external vars extern bool need_warn; @@ -115,7 +117,7 @@ extern bool CompareBits(const FLAG_DATA& flags, const char *names[], int affect) void do_recall(CHAR_DATA *ch, char *argument, int cmd, int subcmd); void save_zone_count_reset(); // extern functions -int level_exp(CHAR_DATA * ch, int level); + void appear(CHAR_DATA * ch); void reset_zone(zone_rnum zone); int parse_class(char arg); @@ -4129,7 +4131,7 @@ void do_advance(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) imm_log("%s has advanced %s to level %d (from %d)", GET_NAME(ch), GET_NAME(victim), newlevel, oldlevel); } - gain_exp_regardless(victim, level_exp(victim, newlevel) + ExpCalc::gain_exp_regardless(victim, ExpCalc::level_exp(victim, newlevel) - GET_EXP(victim)); victim->save_char(); } @@ -5646,7 +5648,7 @@ void do_show(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) } break; case 25: // remort - Remort::show_config(ch); + ExtMoney::show_config(ch); break; default: send_to_char("Извините, неверная команда.\r\n", ch); diff --git a/src/cmd/whoami.cpp b/src/cmd/whoami.cpp index 3c009fe59..a6f5fa506 100644 --- a/src/cmd/whoami.cpp +++ b/src/cmd/whoami.cpp @@ -42,5 +42,5 @@ void do_whoami(CHAR_DATA *ch, char *, int, int) { send_to_char(ch, "Подключен Телеграм, chat_id: %lu\r\n", ch->player_specials->saved.telegram_id); } if (ch->personGroup) - send_to_char(ch, "Состоит в группе #%d лидера %s\r\n", ch->personGroup->getUid(), ch->personGroup->getLeaderName().c_str()); + send_to_char(ch, "Состоит в группе #%lu лидера %s\r\n", ch->personGroup->getUid(), ch->personGroup->getLeaderName().c_str()); } diff --git a/src/comm.cpp b/src/comm.cpp index 6275394f4..e10a25b70 100644 --- a/src/comm.cpp +++ b/src/comm.cpp @@ -32,37 +32,39 @@ #include "comm.h" #include "global.objects.hpp" -#include "magic.h" -#include "world.objects.hpp" -#include "chars/world.characters.hpp" -#include "shutdown.parameters.hpp" -#include "object.prototypes.hpp" -#include "external.trigger.hpp" -#include "handler.h" -#include "house.h" -#include "olc.h" -#include "screen.h" #include "ban.hpp" -#include "exchange.h" -#include "title.hpp" +#include "char_obj_utils.inl" +#include "chars/world.characters.hpp" +#include "core/leveling.h" +#include "corpse.hpp" +#include "db.h" #include "depot.hpp" -#include "glory.hpp" +#include "exchange.h" +#include "external.trigger.hpp" #include "file_crc.hpp" -#include "corpse.hpp" -#include "glory_misc.hpp" +#include "glory.hpp" #include "glory_const.hpp" -#include "shop_ext.hpp" -#include "sets_drop.hpp" +#include "glory_misc.hpp" +#include "handler.h" +#include "heartbeat.hpp" +#include "house.h" +#include "logger.hpp" +#include "magic.h" #include "mail.h" #include "mob_stat.hpp" -#include "char_obj_utils.inl" -#include "logger.hpp" -#include "msdp.hpp" #include "msdp.constants.hpp" -#include "heartbeat.hpp" -#include "zone.table.hpp" -#include "db.h" +#include "msdp.hpp" +#include "object.prototypes.hpp" +#include "olc.h" +#include "screen.h" +#include "sets_drop.hpp" +#include "shop_ext.hpp" +#include "shutdown.parameters.hpp" +#include "title.hpp" #include "utils.h" +#include "world.objects.hpp" +#include "zone.table.hpp" + #if defined WITH_SCRIPTING #include "scripting.hpp" @@ -592,7 +594,7 @@ void zedit_save_to_disk(int zone_num); int real_zone(int number); void Crash_ldsave(CHAR_DATA * ch); void Crash_save_all_rent(); -int level_exp(CHAR_DATA * ch, int level); + unsigned long TxtToIp(const char * text); #ifdef __CXREF__ @@ -1842,7 +1844,7 @@ char *make_prompt(DESCRIPTOR_DATA * d) count += sprintf(prompt + count, "??? "); else count += sprintf(prompt + count, "%ldо ", - level_exp(d->character.get(), + ExpCalc::level_exp(d->character.get(), GET_LEVEL(d->character) + 1) - GET_EXP(d->character)); } // Mem Info diff --git a/src/config.cpp b/src/config.cpp index 22dc4e45e..6e21f8b5b 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -57,8 +57,6 @@ * */ -int level_exp(CHAR_DATA * ch, int level); - // GAME PLAY OPTIONS // exp change limits @@ -291,23 +289,6 @@ const char *START_MESSG = " Твоя задача непроста, но надеемся, что ты сумеешь достойно решить ее.\r\n" " В добрый час, путник, и да будет скатертью тебе дорога...\r\n" "\r\n"; -int max_exp_gain_pc(CHAR_DATA * ch) -{ - int result = 1; - if (!IS_NPC(ch)) - { - int max_per_lev = - level_exp(ch, GET_LEVEL(ch) + 1) - level_exp(ch, GET_LEVEL(ch) + 0); - result = max_per_lev / (10 + GET_REMORT(ch)); - } - return result; -} - -int max_exp_loss_pc(CHAR_DATA * ch) -{ - return (IS_NPC(ch) ? 1 : (level_exp(ch, GET_LEVEL(ch) + 1) - level_exp(ch, GET_LEVEL(ch) + 0)) / 3); -} - int calc_loadroom(const CHAR_DATA* ch, int bplace_mode /*= BIRTH_PLACE_UNDEFINED*/) { if (IS_IMMORTAL(ch)) diff --git a/src/core/leveling.cpp b/src/core/leveling.cpp new file mode 100644 index 000000000..2be1e6c26 --- /dev/null +++ b/src/core/leveling.cpp @@ -0,0 +1,830 @@ +#include "leveling.h" + +#include "chars/char_player.hpp" +#include "class.hpp" +#include "exchange.h" +#include "ext_money.hpp" +#include "house_exp.hpp" +#include "mob_stat.hpp" +#include "screen.h" +#include "spam.hpp" +#include "top.h" +#include "utils.h" +#include "zone.table.hpp" + +extern double exp_coefficients[]; +const char* Remort::CONFIG_FILE = LIB_MISC"remort.xml"; + +int get_remort_mobmax(CHAR_DATA * ch) +{ + int remort = GET_REMORT(ch); + if (remort >= 18) + return 15; + if (remort >= 14) + return 7; + if (remort >= 9) + return 4; + return 0; +} + + +// * Обработка событий при левел-апе. +void levelup_events(CHAR_DATA *ch) +{ + if (SpamSystem::MIN_OFFTOP_LVL == GET_LEVEL(ch) + && !ch->get_disposable_flag(DIS_OFFTOP_MESSAGE)) + { + PRF_FLAGS(ch).set(PRF_OFFTOP_MODE); + ch->set_disposable_flag(DIS_OFFTOP_MESSAGE); + send_to_char(ch, + "%sТеперь вы можете пользоваться каналом оффтоп ('справка оффтоп').%s\r\n", + CCIGRN(ch, C_SPR), CCNRM(ch, C_SPR)); + } + if (EXCHANGE_MIN_CHAR_LEV == GET_LEVEL(ch) + && !ch->get_disposable_flag(DIS_EXCHANGE_MESSAGE)) + { + // по умолчанию базар у всех включен, поэтому не спамим даже однократно + if (GET_REMORT(ch) <= 0) + { + send_to_char(ch, + "%sТеперь вы можете покупать и продавать вещи на базаре ('справка базар!').%s\r\n", + CCIGRN(ch, C_SPR), CCNRM(ch, C_SPR)); + } + ch->set_disposable_flag(DIS_EXCHANGE_MESSAGE); + } +} + +void decrease_level(CHAR_DATA * ch) +{ + int add_move = 0; + + switch (GET_CLASS(ch)) + { + case CLASS_BATTLEMAGE: + case CLASS_DEFENDERMAGE: + case CLASS_CHARMMAGE: + case CLASS_NECROMANCER: + add_move = 2; + break; + + case CLASS_CLERIC: + case CLASS_DRUID: + add_move = 2; + break; + + case CLASS_THIEF: + case CLASS_ASSASINE: + case CLASS_MERCHANT: + add_move = ch->get_inborn_dex() / 5 + 1; + break; + + case CLASS_WARRIOR: + case CLASS_GUARD: + case CLASS_PALADINE: + case CLASS_RANGER: + case CLASS_SMITH: + add_move = ch->get_inborn_dex() / 5 + 1; + break; + } + + check_max_hp(ch); + ch->points.max_move -= MIN(ch->points.max_move, MAX(1, add_move)); + + GET_WIMP_LEV(ch) = MAX(0, MIN(GET_WIMP_LEV(ch), GET_REAL_MAX_HIT(ch) / 2)); + if (!IS_IMMORTAL(ch)) + { + PRF_FLAGS(ch).unset(PRF_HOLYLIGHT); + } + + TopPlayer::Refresh(ch); + ch->save_char(); +} + + +namespace ExpCalc { + void advance_level(CHAR_DATA * ch) + { + int add_move = 0, i; + + switch (GET_CLASS(ch)) + { + case CLASS_BATTLEMAGE: + case CLASS_DEFENDERMAGE: + case CLASS_CHARMMAGE: + case CLASS_NECROMANCER: + add_move = 2; + break; + + case CLASS_CLERIC: + case CLASS_DRUID: + add_move = 2; + break; + + case CLASS_THIEF: + case CLASS_ASSASINE: + case CLASS_MERCHANT: + add_move = number(ch->get_inborn_dex() / 6 + 1, ch->get_inborn_dex() / 5 + 1); + break; + + case CLASS_WARRIOR: + add_move = number(ch->get_inborn_dex() / 6 + 1, ch->get_inborn_dex() / 5 + 1); + break; + + case CLASS_GUARD: + case CLASS_RANGER: + case CLASS_PALADINE: + case CLASS_SMITH: + add_move = number(ch->get_inborn_dex() / 6 + 1, ch->get_inborn_dex() / 5 + 1); + break; + } + + check_max_hp(ch); + ch->points.max_move += MAX(1, add_move); + + setAllInbornFeatures(ch); + + if (IS_IMMORTAL(ch)) + { + for (i = 0; i < 3; i++) + GET_COND(ch, i) = (char) - 1; + PRF_FLAGS(ch).set(PRF_HOLYLIGHT); + } + + TopPlayer::Refresh(ch); + levelup_events(ch); + ch->save_char(); + } + + void gain_exp(CHAR_DATA * ch, int gain) + { + int is_altered = FALSE; + int num_levels = 0; + char buf[128]; + + if (IS_NPC(ch)) + { + ch->set_exp(ch->get_exp() + gain); + return; + } + else + { + ch->dps_add_exp(gain); + ZoneExpStat::add(zone_table[world[ch->in_room]->zone].number, gain); + } + + if (!IS_NPC(ch) && ((GET_LEVEL(ch) < 1 || GET_LEVEL(ch) >= LVL_IMMORT))) + return; + + if (gain > 0 && GET_LEVEL(ch) < LVL_IMMORT) + { + gain = MIN(gain, max_exp_gain_pc(ch)); // put a cap on the max gain per kill + ch->set_exp(ch->get_exp() + gain); + if (GET_EXP(ch) >= level_exp(ch, LVL_IMMORT)) + { + if (!GET_GOD_FLAG(ch, GF_REMORT) && GET_REMORT(ch) < MAX_REMORT) + { + if (Remort::can_remort_now(ch)) + { + send_to_char(ch, "%sПоздравляем, вы получили право на перевоплощение!%s\r\n", + CCIGRN(ch, C_NRM), CCNRM(ch, C_NRM)); + } + else + { + send_to_char(ch, + "%sПоздравляем, вы набрали максимальное количество опыта!\r\n" + "%s%s\r\n", CCIGRN(ch, C_NRM), Remort::WHERE_TO_REMORT_STR.c_str(), CCNRM(ch, C_NRM)); + } + SET_GOD_FLAG(ch, GF_REMORT); + } + } + ch->set_exp(MIN(GET_EXP(ch), level_exp(ch, LVL_IMMORT) - 1)); + while (GET_LEVEL(ch) < LVL_IMMORT && GET_EXP(ch) >= level_exp(ch, GET_LEVEL(ch) + 1)) + { + ch->set_level(ch->get_level() + 1); + num_levels++; + sprintf(buf, "%sВы достигли следующего уровня!%s\r\n", CCWHT(ch, C_NRM), CCNRM(ch, C_NRM)); + send_to_char(buf, ch); + advance_level(ch); + is_altered = TRUE; + } + + if (is_altered) + { + sprintf(buf, "%s advanced %d level%s to level %d.", + GET_NAME(ch), num_levels, num_levels == 1 ? "" : "s", GET_LEVEL(ch)); + mudlog(buf, BRF, LVL_IMPL, SYSLOG, TRUE); + } + } + else if (gain < 0 && GET_LEVEL(ch) < LVL_IMMORT) + { + gain = MAX(-max_exp_loss_pc(ch), gain); // Cap max exp lost per death + ch->set_exp(ch->get_exp() + gain); + while (GET_LEVEL(ch) > 1 && GET_EXP(ch) < level_exp(ch, GET_LEVEL(ch))) + { + ch->set_level(ch->get_level() - 1); + num_levels++; + sprintf(buf, + "%sВы потеряли уровень. Вам должно быть стыдно!%s\r\n", + CCIRED(ch, C_NRM), CCNRM(ch, C_NRM)); + send_to_char(buf, ch); + decrease_level(ch); + is_altered = TRUE; + } + if (is_altered) + { + sprintf(buf, "%s decreases %d level%s to level %d.", + GET_NAME(ch), num_levels, num_levels == 1 ? "" : "s", GET_LEVEL(ch)); + mudlog(buf, BRF, LVL_IMPL, SYSLOG, TRUE); + } + } + if ((GET_EXP(ch) < level_exp(ch, LVL_IMMORT) - 1) + && GET_GOD_FLAG(ch, GF_REMORT) + && gain + && (GET_LEVEL(ch) < LVL_IMMORT)) + { + if (Remort::can_remort_now(ch)) + { + send_to_char(ch, "%sВы потеряли право на перевоплощение!%s\r\n", + CCIRED(ch, C_NRM), CCNRM(ch, C_NRM)); + } + CLR_GOD_FLAG(ch, GF_REMORT); + } + + char_stat::add_class_exp(GET_CLASS(ch), gain); + update_clan_exp(ch, gain); + } + +// юзается исключительно в act.wizards.cpp в имм командах "advance" и "set exp". + void gain_exp_regardless(CHAR_DATA * ch, int gain) + { + int is_altered = FALSE; + int num_levels = 0; + + ch->set_exp(ch->get_exp() + gain); + if (!IS_NPC(ch)) + { + if (gain > 0) + { + while (GET_LEVEL(ch) < LVL_IMPL && GET_EXP(ch) >= level_exp(ch, GET_LEVEL(ch) + 1)) + { + ch->set_level(ch->get_level() + 1); + num_levels++; + sprintf(buf, "%sВы достигли следующего уровня!%s\r\n", + CCWHT(ch, C_NRM), CCNRM(ch, C_NRM)); + send_to_char(buf, ch); + + advance_level(ch); + is_altered = TRUE; + } + + if (is_altered) + { + sprintf(buf, "%s advanced %d level%s to level %d.", + GET_NAME(ch), num_levels, num_levels == 1 ? "" : "s", GET_LEVEL(ch)); + mudlog(buf, BRF, LVL_IMPL, SYSLOG, TRUE); + } + } + else if (gain < 0) + { + // Pereplut: глупый участок кода. + // gain = MAX(-max_exp_loss_pc(ch), gain); // Cap max exp lost per death + // GET_EXP(ch) += gain; + // if (GET_EXP(ch) < 0) + // GET_EXP(ch) = 0; + while (GET_LEVEL(ch) > 1 && GET_EXP(ch) < level_exp(ch, GET_LEVEL(ch))) + { + ch->set_level(ch->get_level() - 1); + num_levels++; + sprintf(buf, + "%sВы потеряли уровень!%s\r\n", + CCIRED(ch, C_NRM), CCNRM(ch, C_NRM)); + send_to_char(buf, ch); + decrease_level(ch); + is_altered = TRUE; + } + if (is_altered) + { + sprintf(buf, "%s decreases %d level%s to level %d.", + GET_NAME(ch), num_levels, num_levels == 1 ? "" : "s", GET_LEVEL(ch)); + mudlog(buf, BRF, LVL_IMPL, SYSLOG, TRUE); + } + } + + } + } + + int get_extend_exp(int exp, CHAR_DATA * ch, CHAR_DATA * victim) + { + int base, diff; + int koef; + + if (!IS_NPC(victim) || IS_NPC(ch)) + return (exp); + // если моб убивается первый раз, то повышаем экспу в несколько раз + // стимулируем изучение новых зон! + ch->send_to_TC(false, true, false, + "&RУ моба еще %d убийств без замакса, экспа %d, убито %d&n\r\n", + mob_proto[victim->get_rnum()].mob_specials.MaxFactor, + exp, + ch->mobmax_get(GET_MOB_VNUM(victim))); +// все равно таблица корявая, учитываются только уникальные мобы и глючит +/* + // даем увеличенную экспу за давно не убитых мобов. + // за совсем неубитых мобов не даем, что бы новые зоны не давали x10 экспу. + exp *= get_npc_long_live_exp_bounus(GET_MOB_VNUM(victim)); +*/ +/* бонусы за непопулярных мобов круче + if (ch->mobmax_get(GET_MOB_VNUM(victim)) == 0) { + // так чуть-чуть поприятней + exp *= 1.5; + exp /= std::max(1.0, 0.5 * (GET_REMORT(ch) - MAX_EXP_COEFFICIENTS_USED)); + return (exp); + } +*/ + exp += exp * (ch->add_abils.percent_exp_add) / 100.0; + for (koef = 100, base = 0, diff = ch->mobmax_get(GET_MOB_VNUM(victim)) - mob_proto[victim->get_rnum()].mob_specials.MaxFactor; + base < diff && koef > 5; base++, koef = koef * (95 - get_remort_mobmax(ch)) / 100); + // минимальный опыт при замаксе 15% от полного опыта + exp = exp * MAX(15, koef) / 100; + + // делим на реморты + exp /= std::max(1.0, 0.5 * (GET_REMORT(ch) - MAX_EXP_COEFFICIENTS_USED)); + return (exp); + } + + int max_exp_gain_pc(CHAR_DATA * ch) + { + int result = 1; + if (!IS_NPC(ch)) + { + int max_per_lev = + level_exp(ch, GET_LEVEL(ch) + 1) - level_exp(ch, GET_LEVEL(ch) + 0); + result = max_per_lev / (10 + GET_REMORT(ch)); + } + return result; + } + + int max_exp_loss_pc(CHAR_DATA * ch) + { + return (IS_NPC(ch) ? 1 : (level_exp(ch, GET_LEVEL(ch) + 1) - level_exp(ch, GET_LEVEL(ch) + 0)) / 3); + } + + float get_npc_long_live_exp_bounus(int vnum) + { + if (vnum == -1) { + return 1.0f; + } + int nowTime = time(0); + int lastKillTime = mob_stat::last_time_killed_mob(vnum); + if ( lastKillTime > 0) { + int deltaTime = nowTime - lastKillTime; + int delay = 60 * 60 * 24 * 30 ; // 30 days + float timeMultiplicator = floor(deltaTime/delay); + if (timeMultiplicator<1.0f) { + timeMultiplicator = 1.0f; + } + if (timeMultiplicator>10.0f) { + timeMultiplicator = 10.0f; + } + return timeMultiplicator; + } + return 1.0f; + } + +// Function to return the exp required for each class/level + int level_exp(CHAR_DATA * ch, int level) + { + if (level > LVL_IMPL || level < 0) + { + log("SYSERR: Requesting exp for invalid level %d!", level); + return 0; + } + + /* + * Gods have exp close to EXP_MAX. This statement should never have to + * changed, regardless of how many mortal or immortal levels exist. + */ + if (level > LVL_IMMORT) + { + return EXP_IMPL - ((LVL_IMPL - level) * 1000); + } + + // Exp required for normal mortals is below + float exp_modifier; + if (GET_REMORT(ch) < MAX_EXP_COEFFICIENTS_USED) + exp_modifier = exp_coefficients[GET_REMORT(ch)]; + else + exp_modifier = exp_coefficients[MAX_EXP_COEFFICIENTS_USED]; + + switch (GET_CLASS(ch)) + { + + case CLASS_BATTLEMAGE: + case CLASS_DEFENDERMAGE: + case CLASS_CHARMMAGE: + case CLASS_NECROMANCER: + switch (level) + { + case 0: + return 0; + case 1: + return 1; + case 2: + return int (exp_modifier * 2500); + case 3: + return int (exp_modifier * 5000); + case 4: + return int (exp_modifier * 9000); + case 5: + return int (exp_modifier * 17000); + case 6: + return int (exp_modifier * 27000); + case 7: + return int (exp_modifier * 47000); + case 8: + return int (exp_modifier * 77000); + case 9: + return int (exp_modifier * 127000); + case 10: + return int (exp_modifier * 197000); + case 11: + return int (exp_modifier * 297000); + case 12: + return int (exp_modifier * 427000); + case 13: + return int (exp_modifier * 587000); + case 14: + return int (exp_modifier * 817000); + case 15: + return int (exp_modifier * 1107000); + case 16: + return int (exp_modifier * 1447000); + case 17: + return int (exp_modifier * 1847000); + case 18: + return int (exp_modifier * 2310000); + case 19: + return int (exp_modifier * 2830000); + case 20: + return int (exp_modifier * 3580000); + case 21: + return int (exp_modifier * 4580000); + case 22: + return int (exp_modifier * 5830000); + case 23: + return int (exp_modifier * 7330000); + case 24: + return int (exp_modifier * 9080000); + case 25: + return int (exp_modifier * 11080000); + case 26: + return int (exp_modifier * 15000000); + case 27: + return int (exp_modifier * 22000000); + case 28: + return int (exp_modifier * 33000000); + case 29: + return int (exp_modifier * 47000000); + case 30: + return int (exp_modifier * 64000000); + // add new levels here + case LVL_IMMORT: + return int (exp_modifier * 94000000); + } + break; + + case CLASS_CLERIC: + case CLASS_DRUID: + switch (level) + { + case 0: + return 0; + case 1: + return 1; + case 2: + return int (exp_modifier * 2500); + case 3: + return int (exp_modifier * 5000); + case 4: + return int (exp_modifier * 9000); + case 5: + return int (exp_modifier * 17000); + case 6: + return int (exp_modifier * 27000); + case 7: + return int (exp_modifier * 47000); + case 8: + return int (exp_modifier * 77000); + case 9: + return int (exp_modifier * 127000); + case 10: + return int (exp_modifier * 197000); + case 11: + return int (exp_modifier * 297000); + case 12: + return int (exp_modifier * 427000); + case 13: + return int (exp_modifier * 587000); + case 14: + return int (exp_modifier * 817000); + case 15: + return int (exp_modifier * 1107000); + case 16: + return int (exp_modifier * 1447000); + case 17: + return int (exp_modifier * 1847000); + case 18: + return int (exp_modifier * 2310000); + case 19: + return int (exp_modifier * 2830000); + case 20: + return int (exp_modifier * 3580000); + case 21: + return int (exp_modifier * 4580000); + case 22: + return int (exp_modifier * 5830000); + case 23: + return int (exp_modifier * 7330000); + case 24: + return int (exp_modifier * 9080000); + case 25: + return int (exp_modifier * 11080000); + case 26: + return int (exp_modifier * 15000000); + case 27: + return int (exp_modifier * 22000000); + case 28: + return int (exp_modifier * 33000000); + case 29: + return int (exp_modifier * 47000000); + case 30: + return int (exp_modifier * 64000000); + // add new levels here + case LVL_IMMORT: + return int (exp_modifier * 87000000); + } + break; + + case CLASS_THIEF: + switch (level) + { + case 0: + return 0; + case 1: + return 1; + case 2: + return int (exp_modifier * 1000); + case 3: + return int (exp_modifier * 2000); + case 4: + return int (exp_modifier * 4000); + case 5: + return int (exp_modifier * 8000); + case 6: + return int (exp_modifier * 15000); + case 7: + return int (exp_modifier * 28000); + case 8: + return int (exp_modifier * 52000); + case 9: + return int (exp_modifier * 85000); + case 10: + return int (exp_modifier * 131000); + case 11: + return int (exp_modifier * 192000); + case 12: + return int (exp_modifier * 271000); + case 13: + return int (exp_modifier * 372000); + case 14: + return int (exp_modifier * 512000); + case 15: + return int (exp_modifier * 672000); + case 16: + return int (exp_modifier * 854000); + case 17: + return int (exp_modifier * 1047000); + case 18: + return int (exp_modifier * 1274000); + case 19: + return int (exp_modifier * 1534000); + case 20: + return int (exp_modifier * 1860000); + case 21: + return int (exp_modifier * 2520000); + case 22: + return int (exp_modifier * 3380000); + case 23: + return int (exp_modifier * 4374000); + case 24: + return int (exp_modifier * 5500000); + case 25: + return int (exp_modifier * 6827000); + case 26: + return int (exp_modifier * 8667000); + case 27: + return int (exp_modifier * 13334000); + case 28: + return int (exp_modifier * 20000000); + case 29: + return int (exp_modifier * 28667000); + case 30: + return int (exp_modifier * 40000000); + // add new levels here + case LVL_IMMORT: + return int (exp_modifier * 53000000); + } + break; + + case CLASS_ASSASINE: + case CLASS_MERCHANT: + switch (level) + { + case 0: + return 0; + case 1: + return 1; + case 2: + return int (exp_modifier * 1500); + case 3: + return int (exp_modifier * 3000); + case 4: + return int (exp_modifier * 6000); + case 5: + return int (exp_modifier * 12000); + case 6: + return int (exp_modifier * 22000); + case 7: + return int (exp_modifier * 42000); + case 8: + return int (exp_modifier * 77000); + case 9: + return int (exp_modifier * 127000); + case 10: + return int (exp_modifier * 197000); + case 11: + return int (exp_modifier * 287000); + case 12: + return int (exp_modifier * 407000); + case 13: + return int (exp_modifier * 557000); + case 14: + return int (exp_modifier * 767000); + case 15: + return int (exp_modifier * 1007000); + case 16: + return int (exp_modifier * 1280000); + case 17: + return int (exp_modifier * 1570000); + case 18: + return int (exp_modifier * 1910000); + case 19: + return int (exp_modifier * 2300000); + case 20: + return int (exp_modifier * 2790000); + case 21: + return int (exp_modifier * 3780000); + case 22: + return int (exp_modifier * 5070000); + case 23: + return int (exp_modifier * 6560000); + case 24: + return int (exp_modifier * 8250000); + case 25: + return int (exp_modifier * 10240000); + case 26: + return int (exp_modifier * 13000000); + case 27: + return int (exp_modifier * 20000000); + case 28: + return int (exp_modifier * 30000000); + case 29: + return int (exp_modifier * 43000000); + case 30: + return int (exp_modifier * 59000000); + // add new levels here + case LVL_IMMORT: + return int (exp_modifier * 79000000); + } + break; + + case CLASS_WARRIOR: + case CLASS_GUARD: + case CLASS_PALADINE: + case CLASS_RANGER: + case CLASS_SMITH: + switch (level) + { + case 0: + return 0; + case 1: + return 1; + case 2: + return int (exp_modifier * 2000); + case 3: + return int (exp_modifier * 4000); + case 4: + return int (exp_modifier * 8000); + case 5: + return int (exp_modifier * 14000); + case 6: + return int (exp_modifier * 24000); + case 7: + return int (exp_modifier * 39000); + case 8: + return int (exp_modifier * 69000); + case 9: + return int (exp_modifier * 119000); + case 10: + return int (exp_modifier * 189000); + case 11: + return int (exp_modifier * 289000); + case 12: + return int (exp_modifier * 419000); + case 13: + return int (exp_modifier * 579000); + case 14: + return int (exp_modifier * 800000); + case 15: + return int (exp_modifier * 1070000); + case 16: + return int (exp_modifier * 1340000); + case 17: + return int (exp_modifier * 1660000); + case 18: + return int (exp_modifier * 2030000); + case 19: + return int (exp_modifier * 2450000); + case 20: + return int (exp_modifier * 2950000); + case 21: + return int (exp_modifier * 3950000); + case 22: + return int (exp_modifier * 5250000); + case 23: + return int (exp_modifier * 6750000); + case 24: + return int (exp_modifier * 8450000); + case 25: + return int (exp_modifier * 10350000); + case 26: + return int (exp_modifier * 14000000); + case 27: + return int (exp_modifier * 21000000); + case 28: + return int (exp_modifier * 31000000); + case 29: + return int (exp_modifier * 44000000); + case 30: + return int (exp_modifier * 64000000); + // add new levels here + case LVL_IMMORT: + return int (exp_modifier * 79000000); + } + break; + } + + /* + * This statement should never be reached if the exp tables in this function + * are set up properly. If you see exp of 123456 then the tables above are + * incomplete -- so, complete them! + */ + log("SYSERR: XP tables not set up correctly in class.c!"); + return 123456; + } + + int calcDeathExp(CHAR_DATA* ch){ + if (!RENTABLE(ch)) + return (level_exp(ch, GET_LEVEL(ch) + 1) - level_exp(ch, GET_LEVEL(ch))) / (3 + MIN(3, GET_REMORT(ch) / 5)) / ch->death_player_count(); + else + return (level_exp(ch, GET_LEVEL(ch) + 1) - level_exp(ch, GET_LEVEL(ch))) / (3 + MIN(3, GET_REMORT(ch) / 5)); + } +} + +namespace Remort +{ + +// релоадится через 'reload remort.xml' + +// проверка, мешает ли что-то чару уйти в реморт + bool can_remort_now(CHAR_DATA *ch) + { + if (PRF_FLAGGED(ch, PRF_CAN_REMORT) || !need_torc(ch)) + { + return true; + } + return false; + } + +// проверка, требуется ли от чара жертвовать для реморта + bool need_torc(CHAR_DATA *ch) + { + TorcReq torc_req(GET_REMORT(ch)); + + if (torc_req.type < ExtMoney::TOTAL_TYPES && torc_req.amount > 0) { + return true; + } + return false; + } + +} // namespace Remort diff --git a/src/core/leveling.h b/src/core/leveling.h new file mode 100644 index 000000000..e64580ce9 --- /dev/null +++ b/src/core/leveling.h @@ -0,0 +1,34 @@ +// +// Created by SKWIZ on 04.01.2021. +// + +#ifndef BYLINS_LEVELING_H +#define BYLINS_LEVELING_H + +#include "chars/char.hpp" + +void levelup_events(CHAR_DATA *ch); + +namespace ExpCalc { + void gain_exp(CHAR_DATA * ch, int gain); + void gain_exp_regardless(CHAR_DATA * ch, int gain); + int get_extend_exp(int exp, CHAR_DATA * ch, CHAR_DATA * victim); + int max_exp_gain_pc(CHAR_DATA * ch); + int max_exp_loss_pc(CHAR_DATA * ch); + float get_npc_long_live_exp_bounus(int vnum); + int level_exp(CHAR_DATA * ch, int level); + int calcDeathExp(CHAR_DATA* ch); + void advance_level(CHAR_DATA * ch); +} + +namespace Remort +{ + extern const char *CONFIG_FILE; + extern std::string WHERE_TO_REMORT_STR; + bool can_remort_now(CHAR_DATA *ch); + void init(); + bool need_torc(CHAR_DATA *ch); +} // namespace Remort + + +#endif //BYLINS_LEVELING_H diff --git a/src/corpse.cpp b/src/corpse.cpp index 22600e72e..b15c56cb5 100644 --- a/src/corpse.cpp +++ b/src/corpse.cpp @@ -12,7 +12,7 @@ #include "chars/char.hpp" #include "comm.h" #include "handler.h" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "im.h" #include "room.hpp" #include "pugixml.hpp" diff --git a/src/db.cpp b/src/db.cpp index 5ef161ee5..ddcacca63 100644 --- a/src/db.cpp +++ b/src/db.cpp @@ -39,8 +39,8 @@ #include "corpse.hpp" #include "deathtrap.hpp" #include "depot.hpp" -#include "dg_db_scripts.hpp" -#include "dg_scripts.h" +#include "dg/dg_db_scripts.hpp" +#include "dg/dg_scripts.h" #include "ext_money.hpp" #include "fightsystem/fight.h" #include "file_crc.hpp" @@ -52,6 +52,7 @@ #include "help.hpp" #include "house.h" #include "item.creation.hpp" +#include "core/leveling.h" #include "liquid.hpp" #include "logger.hpp" #include "mail.h" @@ -230,7 +231,7 @@ void calc_easter(void); void do_start(CHAR_DATA * ch, int newbie); extern void repop_decay(zone_rnum zone); // рассыпание обьектов ITEM_REPOP_DECAY int real_zone(int number); -int level_exp(CHAR_DATA * ch, int level); + extern char *fread_action(FILE * fl, int nr); void load_mobraces(); @@ -5409,7 +5410,7 @@ long cmp_ptable_by_name(char *name, int len) long get_ptable_by_name(const char *name) { - one_argument(name, arg); + one_argument(name, smallBuf); /* Anton Gorev (2015/12/29): see (MAPHELPER) comment. */ for (std::size_t i = 0; i < player_table.size(); i++) { @@ -5419,7 +5420,7 @@ long get_ptable_by_name(const char *name) return static_cast(i); } } - sprintf(buf, "Char %s(%s) not found !!!", name, arg); + sprintf(buf, "Char %s(%s) not found !!!", name, smallBuf); mudlog(buf, LGH, LVL_IMMORT, SYSLOG, FALSE); return (-1); } @@ -5687,7 +5688,7 @@ void do_remort(CHAR_DATA *ch, char *argument, int/* cmd*/, int subcmd) send_to_char("Вам это, похоже, совсем ни к чему.\r\n", ch); return; } - if (GET_EXP(ch) < level_exp(ch, LVL_IMMORT) - 1) + if (GET_EXP(ch) < ExpCalc::level_exp(ch, LVL_IMMORT) - 1) { send_to_char("ЧАВО???\r\n", ch); return; diff --git a/src/dg_comm.cpp b/src/dg/dg_comm.cpp similarity index 100% rename from src/dg_comm.cpp rename to src/dg/dg_comm.cpp diff --git a/src/dg_db_scripts.cpp b/src/dg/dg_db_scripts.cpp similarity index 100% rename from src/dg_db_scripts.cpp rename to src/dg/dg_db_scripts.cpp diff --git a/src/dg_db_scripts.hpp b/src/dg/dg_db_scripts.hpp similarity index 100% rename from src/dg_db_scripts.hpp rename to src/dg/dg_db_scripts.hpp diff --git a/src/dg_event.cpp b/src/dg/dg_event.cpp similarity index 100% rename from src/dg_event.cpp rename to src/dg/dg_event.cpp diff --git a/src/dg_event.h b/src/dg/dg_event.h similarity index 100% rename from src/dg_event.h rename to src/dg/dg_event.h diff --git a/src/dg_handler.cpp b/src/dg/dg_handler.cpp similarity index 100% rename from src/dg_handler.cpp rename to src/dg/dg_handler.cpp diff --git a/src/dg_misc.cpp b/src/dg/dg_misc.cpp similarity index 100% rename from src/dg_misc.cpp rename to src/dg/dg_misc.cpp diff --git a/src/dg_mobcmd.cpp b/src/dg/dg_mobcmd.cpp similarity index 99% rename from src/dg_mobcmd.cpp rename to src/dg/dg_mobcmd.cpp index e90f4defe..d7742f476 100644 --- a/src/dg_mobcmd.cpp +++ b/src/dg/dg_mobcmd.cpp @@ -23,27 +23,29 @@ * $Revision$ * ***************************************************************************/ -#include "world.objects.hpp" -#include "object.prototypes.hpp" -#include "dg_scripts.h" -#include "obj.hpp" -#include "db.h" -#include "handler.h" -#include "interpreter.h" +#include "chars/char.hpp" #include "comm.h" -#include "spell_parser.hpp" -#include "spells.h" -#include "im.h" +#include "core/leveling.h" +#include "conf.h" +#include "db.h" +#include "dg_scripts.h" #include "features.hpp" -#include "chars/char.hpp" -#include "skills.h" -#include "room.hpp" #include "fightsystem/fight.h" #include "fightsystem/fight_hit.hpp" +#include "handler.h" +#include "im.h" +#include "interpreter.h" #include "logger.hpp" +#include "obj.hpp" +#include "object.prototypes.hpp" +#include "room.hpp" +#include "skills.h" +#include "spell_parser.hpp" +#include "spells.h" #include "structs.h" #include "sysdep.h" -#include "conf.h" +#include "world.objects.hpp" + struct mob_command_info { @@ -811,7 +813,7 @@ void do_mexp(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) } sprintf(buf, "mexp: victim (%s) получил опыт %d", name, atoi(amount)); mob_log(ch, buf); - gain_exp(victim, atoi(amount)); + ExpCalc::gain_exp(victim, atoi(amount)); } // increases the target's gold diff --git a/src/dg_objcmd.cpp b/src/dg/dg_objcmd.cpp similarity index 99% rename from src/dg_objcmd.cpp rename to src/dg/dg_objcmd.cpp index f0b1238f9..7b5939465 100644 --- a/src/dg_objcmd.cpp +++ b/src/dg/dg_objcmd.cpp @@ -9,30 +9,32 @@ * $Revision$ * **************************************************************************/ -#include "world.objects.hpp" -#include "object.prototypes.hpp" -#include "obj.hpp" -#include "screen.h" -#include "dg_scripts.h" + +#include "chars/char.hpp" #include "comm.h" -#include "interpreter.h" -#include "handler.h" +#include "core/leveling.h" +#include "conf.h" #include "db.h" -#include "spell_parser.hpp" -#include "spells.h" +#include "dg_scripts.h" +#include "features.hpp" +#include "fightsystem/fight.h" +#include "handler.h" #include "im.h" -#include "chars/char.hpp" -#include "skills.h" +#include "interpreter.h" +#include "logger.hpp" +#include "magic.h" #include "name_list.hpp" +#include "obj.hpp" +#include "object.prototypes.hpp" #include "room.hpp" -#include "magic.h" -#include "fightsystem/fight.h" -#include "features.hpp" -#include "logger.hpp" -#include "utils.h" +#include "screen.h" +#include "skills.h" +#include "spell_parser.hpp" +#include "spells.h" #include "structs.h" #include "sysdep.h" -#include "conf.h" +#include "utils.h" +#include "world.objects.hpp" extern const char *dirs[]; extern int up_obj_where(OBJ_DATA * obj); @@ -290,7 +292,7 @@ void do_oexp(OBJ_DATA *obj, char *argument, int/* cmd*/, int/* subcmd*/) if ((ch = get_char_by_obj(obj, name))) { - gain_exp(ch, atoi(amount)); + ExpCalc::gain_exp(ch, atoi(amount)); sprintf(buf, "oexp: victim (%s) получил опыт %d", GET_NAME(ch) , atoi(amount)); obj_log(obj, buf); } diff --git a/src/dg_olc.cpp b/src/dg/dg_olc.cpp similarity index 100% rename from src/dg_olc.cpp rename to src/dg/dg_olc.cpp diff --git a/src/dg_olc.h b/src/dg/dg_olc.h similarity index 100% rename from src/dg_olc.h rename to src/dg/dg_olc.h diff --git a/src/dg_scripts.cpp b/src/dg/dg_scripts.cpp similarity index 99% rename from src/dg_scripts.cpp rename to src/dg/dg_scripts.cpp index 78780be42..2561afce8 100644 --- a/src/dg_scripts.cpp +++ b/src/dg/dg_scripts.cpp @@ -16,25 +16,20 @@ #include "chars/char_player.hpp" #include "chars/world.characters.hpp" #include "comm.h" -#include "conf.h" +#include "core/leveling.h" #include "constants.h" -#include "coredump.hpp" #include "db.h" #include "debug.utils.hpp" #include "dg_db_scripts.hpp" #include "dg_event.h" #include "features.hpp" #include "find.obj.id.by.vnum.hpp" -#include "genchar.h" #include "global.objects.hpp" -#include "grp/grp.main.h" #include "handler.h" -#include "heartbeat.hpp" #include "house.h" #include "interpreter.h" #include "logger.hpp" #include "modify.h" -#include "name_list.hpp" #include "named_stuff.hpp" #include "noob.hpp" #include "obj.hpp" @@ -48,11 +43,12 @@ #include "spells.h" #include "structs.h" #include "sysdep.h" -#include "top.h" #include "utils.h" #include "world.objects.hpp" #include "zone.table.hpp" +using namespace ExpCalc; + #define PULSES_PER_MUD_HOUR (SECS_PER_MUD_HOUR*PASSES_PER_SEC) #define IS_CHARMED(ch) (IS_HORSE(ch)||AFF_FLAGGED(ch, EAffectFlag::AFF_CHARM)) diff --git a/src/dg_scripts.h b/src/dg/dg_scripts.h similarity index 100% rename from src/dg_scripts.h rename to src/dg/dg_scripts.h diff --git a/src/dg_triggers.cpp b/src/dg/dg_triggers.cpp similarity index 100% rename from src/dg_triggers.cpp rename to src/dg/dg_triggers.cpp diff --git a/src/dg_wldcmd.cpp b/src/dg/dg_wldcmd.cpp similarity index 99% rename from src/dg_wldcmd.cpp rename to src/dg/dg_wldcmd.cpp index d190a42d7..748bd3efe 100644 --- a/src/dg_wldcmd.cpp +++ b/src/dg/dg_wldcmd.cpp @@ -9,31 +9,33 @@ * $Revision$ * **************************************************************************/ -#include "world.objects.hpp" -#include "object.prototypes.hpp" -#include "obj.hpp" -#include "skills/townportal.h" -#include "dg_scripts.h" +#include "chars/char.hpp" #include "comm.h" -#include "interpreter.h" -#include "handler.h" -#include "spell_parser.hpp" -#include "spells.h" +#include "core/leveling.h" +#include "conf.h" #include "db.h" -#include "im.h" #include "deathtrap.hpp" -#include "chars/char.hpp" -#include "skills.h" -#include "room.hpp" -#include "magic.h" -#include "fightsystem/fight.h" +#include "dg_scripts.h" #include "features.hpp" -#include "zone.table.hpp" +#include "fightsystem/fight.h" +#include "handler.h" +#include "im.h" +#include "interpreter.h" #include "logger.hpp" -#include "utils.h" +#include "magic.h" +#include "obj.hpp" +#include "object.prototypes.hpp" +#include "room.hpp" +#include "skills.h" +#include "skills/townportal.h" +#include "spell_parser.hpp" +#include "spells.h" #include "structs.h" #include "sysdep.h" -#include "conf.h" +#include "utils.h" +#include "world.objects.hpp" +#include "zone.table.hpp" + extern const char *dirs[]; @@ -501,7 +503,7 @@ void do_wexp(ROOM_DATA *room, char *argument, int/* cmd*/, int/* subcmd*/) if ((ch = get_char_by_room(room, name))) { - gain_exp(ch, atoi(amount)); + ExpCalc::gain_exp(ch, atoi(amount)); sprintf(buf, "wexp: victim (%s) получил опыт %d", GET_NAME(ch), atoi(amount)); wld_log(room, buf); } diff --git a/src/ext_money.cpp b/src/ext_money.cpp index 60f94ad27..65d866c0a 100644 --- a/src/ext_money.cpp +++ b/src/ext_money.cpp @@ -15,6 +15,7 @@ #include "zone.table.hpp" #include "utils.h" #include "grp/grp.main.h" +#include "core/leveling.h" #include #include @@ -24,6 +25,9 @@ using namespace ExtMoney; using namespace Remort; +// строка, глобальная зачем то... +std::string Remort::WHERE_TO_REMORT_STR = ""; + namespace { @@ -31,872 +35,730 @@ void message_low_torc(CHAR_DATA *ch, unsigned type, int amount, const char *add_ } // namespace -namespace Remort -{ - -const char *CONFIG_FILE = LIB_MISC"remort.xml"; -std::string WHERE_TO_REMORT_STR; - -void init(); -bool can_remort_now(CHAR_DATA *ch); -void show_config(CHAR_DATA *ch); -int calc_torc_daily(int rmrt); -bool need_torc(CHAR_DATA *ch); - -} // namespace Remort - -namespace ExtMoney -{ - -// на все эти переменные смотреть init() -int TORC_EXCH_RATE = 999; -std::map plural_name_currency_map = { - { "куны" , "денег" }, - { "слава" , "славы" }, - { "лед" , "льда" }, - { "гривны", "гривен" } -}; - -std::string name_currency_plural(std::string name) -{ - auto it = plural_name_currency_map.find(name); - if (it != plural_name_currency_map.end()) - { - return (*it).second; - } - return "неизвестной валюты"; -} - -struct type_node -{ - type_node() : MORT_REQ(99), MORT_REQ_ADD_PER_MORT(99), MORT_NUM(99), - DROP_LVL(99), DROP_AMOUNT(0), DROP_AMOUNT_ADD_PER_LVL(0), MINIMUM_DAYS(99), - DESC_MESSAGE_NUM(0), DESC_MESSAGE_U_NUM(0) {}; - // сколько гривен требуется на соответствующее право морта - int MORT_REQ; - // сколько добавлять к требованиям за каждый морт сверху - int MORT_REQ_ADD_PER_MORT; - // с какого морта требуются какие гривны - int MORT_NUM; - // с боссов зон какого мин. среднего уровня - int DROP_LVL; - // сколько дропать с базового ср. уровня - int DROP_AMOUNT; - // сколько накидывать за каждый уровень выше базового - int DROP_AMOUNT_ADD_PER_LVL; - // делитель требования гривен, определяет сколько дней минимум потребуется - // для набора необходимого на морт кол-ва гривен. например если делитель - // равен 7, а гривен для след. морта нужно 70 золотых, то за сутки персонажу - // дропнется не более 10 золотых гривен или их эквивалента - int MINIMUM_DAYS; - // для сообщений через desc_count - int DESC_MESSAGE_NUM; - int DESC_MESSAGE_U_NUM; -}; - -// список типов гривен со всеми их параметрами -std::array type_list; - -struct TorcReq -{ - TorcReq(int rmrt); - // тип гривн - unsigned type; - // кол-во - int amount; -}; - -TorcReq::TorcReq(int rmrt) -{ - // type - if (rmrt >= type_list[TORC_GOLD].MORT_NUM) - { - type = TORC_GOLD; - } - else if (rmrt >= type_list[TORC_SILVER].MORT_NUM) - { - type = TORC_SILVER; - } - else if (rmrt >= type_list[TORC_BRONZE].MORT_NUM) - { - type = TORC_BRONZE; - } - else - { - type = TOTAL_TYPES; - } - // amount - if (type != TOTAL_TYPES) - { - amount = type_list[type].MORT_REQ + - (rmrt - type_list[type].MORT_NUM) * type_list[type].MORT_REQ_ADD_PER_MORT; - } - else - { - amount = 0; - } -} - -// обмен гривн -void torc_exch_menu(CHAR_DATA *ch); -void parse_inc_exch(CHAR_DATA *ch, int amount, int num); -void parse_dec_exch(CHAR_DATA *ch, int amount, int num); -int check_input_amount(CHAR_DATA *ch, int num1, int num2); -bool check_equal_exch(CHAR_DATA *ch); -void torc_exch_parse(CHAR_DATA *ch, const char *arg); -// дроп гривн -std::string create_message(CHAR_DATA *ch, int gold, int silver, int bronze); -bool has_connected_bosses(CHAR_DATA *ch); -unsigned calc_type_by_zone_lvl(int zone_lvl); -int calc_drop_torc(int zone_lvl, int members); -int check_daily_limit(CHAR_DATA *ch, int drop); -void gain_torc(CHAR_DATA *ch, int drop); -void drop_torc(CHAR_DATA *mob); - -} // namespace ExtMoney - namespace ExtMoney { -// распечатка меню обмена гривен ('менять' у глашатая) -void torc_exch_menu(CHAR_DATA *ch) -{ - boost::format menu(" %s%d) %s%-17s%s -> %s%-17s%s [%d -> %d]\r\n"); - std::stringstream out; - - out << "\r\n" - " Курсы обмена гривен:\r\n" - " " << TORC_EXCH_RATE << " бронзовых <-> 1 серебряная\r\n" - " " << TORC_EXCH_RATE << " серебряных <-> 1 золотая\r\n\r\n"; - - out << " Текущий баланс: " - << CCIYEL(ch, C_NRM) << ch->desc->ext_money[TORC_GOLD] << "з " - << CCWHT(ch, C_NRM) << ch->desc->ext_money[TORC_SILVER] << "с " - << CCYEL(ch, C_NRM) << ch->desc->ext_money[TORC_BRONZE] << "б\r\n\r\n"; - - out << menu - % CCGRN(ch, C_NRM) % 1 - % CCYEL(ch, C_NRM) % "Бронзовые гривны" % CCNRM(ch, C_NRM) - % CCWHT(ch, C_NRM) % "Серебряные гривны" % CCNRM(ch, C_NRM) - % TORC_EXCH_RATE % 1; - out << menu - % CCGRN(ch, C_NRM) % 2 - % CCWHT(ch, C_NRM) % "Серебряные гривны" % CCNRM(ch, C_NRM) - % CCIYEL(ch, C_NRM) % "Золотые гривны" % CCNRM(ch, C_NRM) - % TORC_EXCH_RATE % 1; - out << "\r\n" - << menu - % CCGRN(ch, C_NRM) % 3 - % CCIYEL(ch, C_NRM) % "Золотые гривны" % CCNRM(ch, C_NRM) - % CCWHT(ch, C_NRM) % "Серебряные гривны" % CCNRM(ch, C_NRM) - % 1 % TORC_EXCH_RATE; - out << menu - % CCGRN(ch, C_NRM) % 4 - % CCWHT(ch, C_NRM) % "Серебряные гривны" % CCNRM(ch, C_NRM) - % CCYEL(ch, C_NRM) % "Бронзовые гривны" % CCNRM(ch, C_NRM) - % 1 % TORC_EXCH_RATE; - - out << "\r\n" - " <номер действия> - один минимальный обмен указанного вида\r\n" - " <номер действия> <число х> - обмен х имеющихся гривен\r\n\r\n"; - - out << CCGRN(ch, C_NRM) << " 5)" - << CCNRM(ch, C_NRM) << " Отменить обмен и выйти\r\n" - << CCGRN(ch, C_NRM) << " 6)" - << CCNRM(ch, C_NRM) << " Подтвердить обмен и выйти\r\n\r\n" - << " Ваш выбор:"; - - send_to_char(out.str(), ch); -} - -// обмен в сторону больших гривен -void parse_inc_exch(CHAR_DATA *ch, int amount, int num) -{ - int torc_from = TORC_BRONZE; - int torc_to = TORC_SILVER; - int torc_rate = TORC_EXCH_RATE; - - if (num == 2) - { - torc_from = TORC_SILVER; - torc_to = TORC_GOLD; - } - - if (ch->desc->ext_money[torc_from] < amount) - { - if (ch->desc->ext_money[torc_from] < torc_rate) - { - send_to_char("Нет необходимого количества гривен!\r\n", ch); - } - else - { - amount = ch->desc->ext_money[torc_from] / torc_rate * torc_rate; - send_to_char(ch, "Количество меняемых гривен уменьшено до %d.\r\n", amount); - ch->desc->ext_money[torc_from] -= amount; - ch->desc->ext_money[torc_to] += amount / torc_rate; - send_to_char(ch, "Произведен обмен: %d -> %d.\r\n", amount, amount / torc_rate); - } - } - else - { - int real_amount = amount / torc_rate * torc_rate; - if (real_amount != amount) - { - send_to_char(ch, "Количество меняемых гривен уменьшено до %d.\r\n", real_amount); - } - ch->desc->ext_money[torc_from] -= real_amount; - ch->desc->ext_money[torc_to] += real_amount / torc_rate; - send_to_char(ch, "Произведен обмен: %d -> %d.\r\n", real_amount, real_amount / torc_rate); - } -} - -// обмен в сторону меньших гривен -void parse_dec_exch(CHAR_DATA *ch, int amount, int num) -{ - int torc_from = TORC_GOLD; - int torc_to = TORC_SILVER; - int torc_rate = TORC_EXCH_RATE; - - if (num == 4) - { - torc_from = TORC_SILVER; - torc_to = TORC_BRONZE; - } - - if (ch->desc->ext_money[torc_from] < amount) - { - if (ch->desc->ext_money[torc_from] < 1) - { - send_to_char("Нет необходимого количества гривен!\r\n", ch); - } - else - { - amount = ch->desc->ext_money[torc_from]; - send_to_char(ch, "Количество меняемых гривен уменьшено до %d.\r\n", amount); - - ch->desc->ext_money[torc_from] -= amount; - ch->desc->ext_money[torc_to] += amount * torc_rate; - send_to_char(ch, "Произведен обмен: %d -> %d.\r\n", amount, amount * torc_rate); - } - } - else - { - ch->desc->ext_money[torc_from] -= amount; - ch->desc->ext_money[torc_to] += amount * torc_rate; - send_to_char(ch, "Произведен обмен: %d -> %d.\r\n", amount, amount * torc_rate); - } -} - -// кол-во меняемых гривен -int check_input_amount(CHAR_DATA* /*ch*/, int num1, int num2) -{ - if ((num1 == 1 || num1 == 2) && num2 < TORC_EXCH_RATE) - { - return TORC_EXCH_RATE; - } - else if ((num1 == 3 || num1 == 4) && num2 < 1) - { - return 1; - } - return 0; -} - -// проверка после обмена, что ничего лишнего не сгенерили случайно -bool check_equal_exch(CHAR_DATA *ch) -{ - int before = 0, after = 0; - for (unsigned i = 0; i < TOTAL_TYPES; ++i) - { - if (i == TORC_BRONZE) - { - before += ch->get_ext_money(i); - after += ch->desc->ext_money[i]; - } - if (i == TORC_SILVER) - { - before += ch->get_ext_money(i) * TORC_EXCH_RATE; - after += ch->desc->ext_money[i] * TORC_EXCH_RATE; - } - else if (i == TORC_GOLD) - { - before += ch->get_ext_money(i) * TORC_EXCH_RATE * TORC_EXCH_RATE; - after += ch->desc->ext_money[i] * TORC_EXCH_RATE * TORC_EXCH_RATE; - } - } - if (before != after) - { - sprintf(buf, "SYSERROR: Torc exch by %s not equal: %d -> %d", - GET_NAME(ch), before, after); - mudlog(buf, DEF, LVL_IMMORT, SYSLOG, TRUE); - return false; - } - return true; -} - -// парс ввода при обмене гривен -void torc_exch_parse(CHAR_DATA *ch, const char *arg) -{ - if (!*arg || !a_isdigit(*arg)) - { - send_to_char("Неверный выбор!\r\n", ch); - torc_exch_menu(ch); - return; - } - - std::string param2(arg), param1; - GetOneParam(param2, param1); - boost::trim(param2); - - int num1 = 0, num2 = 0; - - try - { - num1 = std::stoi(param1, nullptr, 10); - if (!param2.empty()) - { - num2 = std::stoi(param2, nullptr, 10); - } - } - catch (const std::invalid_argument&) - { - log("SYSERROR: invalid_argument arg=%s (%s %s %d)", - arg, __FILE__, __func__, __LINE__); - - send_to_char("Неверный выбор!\r\n", ch); - torc_exch_menu(ch); - return; - } - - int amount = num2; - if (!param2.empty()) - { - // ввели два числа - проверка пользовательского ввода кол-ва гривен - int min_amount = check_input_amount(ch, num1, amount); - if (min_amount > 0) - { - send_to_char(ch, "Минимальное количество гривен для данного обмена: %d.", min_amount); - torc_exch_menu(ch); - return; - } - } - else - { - // ввели одно число - проставляем минимальный обмен - if (num1 == 1 || num1 == 2) - { - amount = TORC_EXCH_RATE; - } - else if (num1 == 3 || num1 == 4) - { - amount = 1; - } - } - // собсна обмен - if (num1 == 1 || num1 == 2) - { - parse_inc_exch(ch, amount, num1); - } - else if (num1 == 3 || num1 == 4) - { - parse_dec_exch(ch, amount, num1); - } - else if (num1 == 5) - { - STATE(ch->desc) = CON_PLAYING; - send_to_char("Обмен отменен.\r\n", ch); - return; - } - else if (num1 == 6) - { - if (!check_equal_exch(ch)) - { - send_to_char("Обмен отменен по техническим причинам, обратитесь к Богам.\r\n", ch); - } - else - { - for (unsigned i = 0; i < TOTAL_TYPES; ++i) - { - ch->set_ext_money(i, ch->desc->ext_money[i]); - } - STATE(ch->desc) = CON_PLAYING; - send_to_char("Обмен произведен.\r\n", ch); - } - return; - } - else - { - send_to_char("Неверный выбор!\r\n", ch); - } - torc_exch_menu(ch); -} - -// формирование сообщения о награде гривнами при смерти босса -// пишет в одну строку о нескольких видах гривен, если таковые были -std::string create_message(CHAR_DATA *ch, int gold, int silver, int bronze) -{ - std::stringstream out; - int cnt = 0; - - if (gold > 0) - { - out << CCIYEL(ch, C_NRM) << gold << " " - << desc_count(gold, type_list[TORC_GOLD].DESC_MESSAGE_U_NUM); - if (silver <= 0 && bronze <= 0) - { - out << " " << desc_count(gold, WHAT_TORCu); - } - out << CCNRM(ch, C_NRM); - ++cnt; - } - if (silver > 0) - { - if (cnt > 0) - { - if (bronze > 0) - { - out << ", " << CCWHT(ch, C_NRM) << silver << " " - << desc_count(silver, type_list[TORC_SILVER].DESC_MESSAGE_U_NUM) - << CCNRM(ch, C_NRM) << " и "; - } - else - { - out << " и " << CCWHT(ch, C_NRM) << silver << " " - << desc_count(silver, type_list[TORC_SILVER].DESC_MESSAGE_U_NUM) - << " " << desc_count(silver, WHAT_TORCu) - << CCNRM(ch, C_NRM); - } - } - else - { - out << CCWHT(ch, C_NRM) << silver << " " - << desc_count(silver, type_list[TORC_SILVER].DESC_MESSAGE_U_NUM); - if (bronze > 0) - { - out << CCNRM(ch, C_NRM) << " и "; - } - else - { - out << " " << desc_count(silver, WHAT_TORCu) << CCNRM(ch, C_NRM); - } - } - } - if (bronze > 0) - { - out << CCYEL(ch, C_NRM) << bronze << " " - << desc_count(bronze, type_list[TORC_BRONZE].DESC_MESSAGE_U_NUM) - << " " << desc_count(bronze, WHAT_TORCu) - << CCNRM(ch, C_NRM); - } - - return out.str(); -} - -// проверка на случай нескольких физических боссов, -// которые логически являются одной группой, предотвращающая лишний дроп гривен -bool has_connected_bosses(CHAR_DATA *ch) -{ - // если в комнате есть другие живые боссы - for (const auto i : world[ch->in_room]->people) - { - if (i != ch - && IS_NPC(i) - && !IS_CHARMICE(i) - && i->get_role(MOB_ROLE_BOSS)) - { - return true; - } - } - // если у данного моба есть живые последователи-боссы - for (follow_type *i = ch->followers; i; i = i->next) - { - if (i->follower != ch - && IS_NPC(i->follower) - && !IS_CHARMICE(i->follower) - && i->follower->get_master() == ch - && i->follower->get_role(MOB_ROLE_BOSS)) - { - return true; - } - } - // если он сам следует за каким-то боссом - if (ch->has_master() && ch->get_master()->get_role(MOB_ROLE_BOSS)) - { - return true; - } - - return false; -} - -// выясняет какой тип гривен дропать с зоны -unsigned calc_type_by_zone_lvl(int zone_lvl) -{ - if (zone_lvl >= type_list[TORC_GOLD].DROP_LVL) - { - return TORC_GOLD; - } - else if (zone_lvl >= type_list[TORC_SILVER].DROP_LVL) - { - return TORC_SILVER; - } - else if (zone_lvl >= type_list[TORC_BRONZE].DROP_LVL) - { - return TORC_BRONZE; - } - return TOTAL_TYPES; -} - -// возвращает кол-во гривен с босса зоны уровня zone_lvl, поделенных -// с учетом группы members и пересчитанных в бронзу -int calc_drop_torc(int zone_lvl, int members) -{ - const unsigned type = calc_type_by_zone_lvl(zone_lvl); - if (type >= TOTAL_TYPES) - { - return 0; - } - const int add = zone_lvl - type_list[type].DROP_LVL; - int drop = type_list[type].DROP_AMOUNT + add * type_list[type].DROP_AMOUNT_ADD_PER_LVL; - - // пересчитываем дроп к минимальному типу - if (type == TORC_GOLD) - { - drop = drop * TORC_EXCH_RATE * TORC_EXCH_RATE; - } - else if (type == TORC_SILVER) - { - drop = drop * TORC_EXCH_RATE; - } - - // есть ли вообще что дропать - if (drop < members) - { - return 0; - } - - // после этого уже применяем делитель группы - if (members > 1) - { - drop = drop / members; - } - - return drop; -} - -// по дефолту отрисовка * за каждую 1/5 от суточного лимита гривен -// если imm_stat == true, то вместо звездочек конкретные цифры тек/макс -std::string draw_daily_limit(CHAR_DATA *ch, bool imm_stat) -{ - const int today_torc = ch->get_today_torc(); - const int torc_req_daily = calc_torc_daily(GET_REMORT(ch)); - - TorcReq torc_req(GET_REMORT(ch)); - if (torc_req.type >= TOTAL_TYPES) - { - torc_req.type = TORC_BRONZE; - } - const int daily_torc_limit = torc_req_daily / type_list[torc_req.type].MINIMUM_DAYS; - - std::string out("["); - if (!imm_stat) - { - for (int i = 1; i <= 5; ++i) - { - if (daily_torc_limit / 5 * i <= today_torc) - { - out += "*"; - } - else - { - out += "."; - } - } - } - else - { - out += boost::str(boost::format("%d/%d") % today_torc % daily_torc_limit); - } - out += "]"; - - return out; -} - -// проверка дропа гривен на суточный замакс -int check_daily_limit(CHAR_DATA *ch, int drop) -{ - const int today_torc = ch->get_today_torc(); - const int torc_req_daily = calc_torc_daily(GET_REMORT(ch)); - - // из calc_torc_daily в любом случае взялось какое-то число бронзы - // даже если чар не имеет мортов для требования гривен - TorcReq torc_req(GET_REMORT(ch)); - if (torc_req.type >= TOTAL_TYPES) - { - torc_req.type = TORC_BRONZE; - } - const int daily_torc_limit = torc_req_daily / type_list[torc_req.type].MINIMUM_DAYS; - - if (today_torc + drop > daily_torc_limit) - { - int add = daily_torc_limit - today_torc; - if (add > 0) - { - ch->add_today_torc(add); - return add; - } - else - { - return 0; - } - } - - ch->add_today_torc(drop); - return drop; -} - -// процесс дропа гривен конкретному чару -void gain_torc(CHAR_DATA *ch, int drop) -{ - // проверка на индивидуальный суточный замакс гривн - int bronze = check_daily_limit(ch, drop); - if (bronze <= 0) - { - return; - } - int gold = 0, silver = 0; - // и разносим что осталось обратно по типам - if (bronze >= TORC_EXCH_RATE * TORC_EXCH_RATE) - { - gold += bronze / (TORC_EXCH_RATE * TORC_EXCH_RATE); - bronze -= gold * TORC_EXCH_RATE * TORC_EXCH_RATE; - ch->set_ext_money(TORC_GOLD, gold + ch->get_ext_money(TORC_GOLD)); - } - if (bronze >= TORC_EXCH_RATE) - { - silver += bronze / TORC_EXCH_RATE; - bronze -= silver * TORC_EXCH_RATE; - ch->set_ext_money(TORC_SILVER, silver + ch->get_ext_money(TORC_SILVER)); - } - ch->set_ext_money(TORC_BRONZE, bronze + ch->get_ext_money(TORC_BRONZE)); - - std::string out = create_message(ch, gold, silver, bronze); - send_to_char(ch, "В награду за свершенный подвиг вы получили от Богов %s.\r\n", out.c_str()); - -} + // на все эти переменные смотреть init() + int TORC_EXCH_RATE = 999; + std::map plural_name_currency_map = { + { "куны" , "денег" }, + { "слава" , "славы" }, + { "лед" , "льда" }, + { "гривны", "гривен" } + }; -// дергается из экстракт_чар, у босса берется макс дамагер, находящийся -// в той же комнате, группе готорого и раскидываются гривны, если есть -// кому раскидывать (флаг GF_REMORT, проверка на делимость гривен, проверка на -// то, что чар находился в комнате с мобом не менее половины раундов дамагера) -void drop_torc(CHAR_DATA *mob) -{ - if (!mob->get_role(MOB_ROLE_BOSS) - || has_connected_bosses(mob)) - { - return; - } - - log("[Extract char] Checking %s for ExtMoney.", mob->get_name().c_str()); + std::string name_currency_plural(std::string name) + { + auto it = plural_name_currency_map.find(name); + if (it != plural_name_currency_map.end()) + { + return (*it).second; + } + return "неизвестной валюты"; + } - std::pair damager = mob->get_max_damager_in_room(); - DESCRIPTOR_DATA *d = nullptr; - if (damager.first > 0) { - d = DescByUID(damager.first); - } - if (!d) { - return; - } - auto grp = d->character->personGroup; - CHAR_DATA *leader = grp != nullptr? d->character->personGroup->getLeader() : d->character.get(); + struct type_node + { + type_node() : MORT_REQ(99), MORT_REQ_ADD_PER_MORT(99), MORT_NUM(99), + DROP_LVL(99), DROP_AMOUNT(0), DROP_AMOUNT_ADD_PER_LVL(0), MINIMUM_DAYS(99), + DESC_MESSAGE_NUM(0), DESC_MESSAGE_U_NUM(0) {}; + // сколько гривен требуется на соответствующее право морта + int MORT_REQ; + // сколько добавлять к требованиям за каждый морт сверху + int MORT_REQ_ADD_PER_MORT; + // с какого морта требуются какие гривны + int MORT_NUM; + // с боссов зон какого мин. среднего уровня + int DROP_LVL; + // сколько дропать с базового ср. уровня + int DROP_AMOUNT; + // сколько накидывать за каждый уровень выше базового + int DROP_AMOUNT_ADD_PER_LVL; + // делитель требования гривен, определяет сколько дней минимум потребуется + // для набора необходимого на морт кол-ва гривен. например если делитель + // равен 7, а гривен для след. морта нужно 70 золотых, то за сутки персонажу + // дропнется не более 10 золотых гривен или их эквивалента + int MINIMUM_DAYS; + // для сообщений через desc_count + int DESC_MESSAGE_NUM; + int DESC_MESSAGE_U_NUM; + }; + + // список типов гривен со всеми их параметрами + std::array type_list; + + // обмен гривн + void torc_exch_menu(CHAR_DATA *ch); + void parse_inc_exch(CHAR_DATA *ch, int amount, int num); + void parse_dec_exch(CHAR_DATA *ch, int amount, int num); + int check_input_amount(CHAR_DATA *ch, int num1, int num2); + bool check_equal_exch(CHAR_DATA *ch); + void torc_exch_parse(CHAR_DATA *ch, const char *arg); + // дроп гривн + std::string create_message(CHAR_DATA *ch, int gold, int silver, int bronze); + bool has_connected_bosses(CHAR_DATA *ch); + unsigned calc_type_by_zone_lvl(int zone_lvl); + int calc_drop_torc(int zone_lvl, int members); + int check_daily_limit(CHAR_DATA *ch, int drop); + void gain_torc(CHAR_DATA *ch, int drop); + void drop_torc(CHAR_DATA *mob); + + // распечатка меню обмена гривен ('менять' у глашатая) + void torc_exch_menu(CHAR_DATA *ch) + { + boost::format menu(" %s%d) %s%-17s%s -> %s%-17s%s [%d -> %d]\r\n"); + std::stringstream out; + + out << "\r\n" + " Курсы обмена гривен:\r\n" + " " << TORC_EXCH_RATE << " бронзовых <-> 1 серебряная\r\n" + " " << TORC_EXCH_RATE << " серебряных <-> 1 золотая\r\n\r\n"; + + out << " Текущий баланс: " + << CCIYEL(ch, C_NRM) << ch->desc->ext_money[TORC_GOLD] << "з " + << CCWHT(ch, C_NRM) << ch->desc->ext_money[TORC_SILVER] << "с " + << CCYEL(ch, C_NRM) << ch->desc->ext_money[TORC_BRONZE] << "б\r\n\r\n"; + + out << menu + % CCGRN(ch, C_NRM) % 1 + % CCYEL(ch, C_NRM) % "Бронзовые гривны" % CCNRM(ch, C_NRM) + % CCWHT(ch, C_NRM) % "Серебряные гривны" % CCNRM(ch, C_NRM) + % TORC_EXCH_RATE % 1; + out << menu + % CCGRN(ch, C_NRM) % 2 + % CCWHT(ch, C_NRM) % "Серебряные гривны" % CCNRM(ch, C_NRM) + % CCIYEL(ch, C_NRM) % "Золотые гривны" % CCNRM(ch, C_NRM) + % TORC_EXCH_RATE % 1; + out << "\r\n" + << menu + % CCGRN(ch, C_NRM) % 3 + % CCIYEL(ch, C_NRM) % "Золотые гривны" % CCNRM(ch, C_NRM) + % CCWHT(ch, C_NRM) % "Серебряные гривны" % CCNRM(ch, C_NRM) + % 1 % TORC_EXCH_RATE; + out << menu + % CCGRN(ch, C_NRM) % 4 + % CCWHT(ch, C_NRM) % "Серебряные гривны" % CCNRM(ch, C_NRM) + % CCYEL(ch, C_NRM) % "Бронзовые гривны" % CCNRM(ch, C_NRM) + % 1 % TORC_EXCH_RATE; + + out << "\r\n" + " <номер действия> - один минимальный обмен указанного вида\r\n" + " <номер действия> <число х> - обмен х имеющихся гривен\r\n\r\n"; + + out << CCGRN(ch, C_NRM) << " 5)" + << CCNRM(ch, C_NRM) << " Отменить обмен и выйти\r\n" + << CCGRN(ch, C_NRM) << " 6)" + << CCNRM(ch, C_NRM) << " Подтвердить обмен и выйти\r\n\r\n" + << " Ваш выбор:"; + + send_to_char(out.str(), ch); + } - int members = 1; - members = leader->personGroup->size(mob->in_room); + // обмен в сторону больших гривен + void parse_inc_exch(CHAR_DATA *ch, int amount, int num) + { + int torc_from = TORC_BRONZE; + int torc_to = TORC_SILVER; + int torc_rate = TORC_EXCH_RATE; + + if (num == 2) + { + torc_from = TORC_SILVER; + torc_to = TORC_GOLD; + } + + if (ch->desc->ext_money[torc_from] < amount) + { + if (ch->desc->ext_money[torc_from] < torc_rate) + { + send_to_char("Нет необходимого количества гривен!\r\n", ch); + } + else + { + amount = ch->desc->ext_money[torc_from] / torc_rate * torc_rate; + send_to_char(ch, "Количество меняемых гривен уменьшено до %d.\r\n", amount); + ch->desc->ext_money[torc_from] -= amount; + ch->desc->ext_money[torc_to] += amount / torc_rate; + send_to_char(ch, "Произведен обмен: %d -> %d.\r\n", amount, amount / torc_rate); + } + } + else + { + int real_amount = amount / torc_rate * torc_rate; + if (real_amount != amount) + { + send_to_char(ch, "Количество меняемых гривен уменьшено до %d.\r\n", real_amount); + } + ch->desc->ext_money[torc_from] -= real_amount; + ch->desc->ext_money[torc_to] += real_amount / torc_rate; + send_to_char(ch, "Произведен обмен: %d -> %d.\r\n", real_amount, real_amount / torc_rate); + } + } - const int zone_lvl = zone_table[mob_index[GET_MOB_RNUM(mob)].zone].mob_level; - const int drop = calc_drop_torc(zone_lvl, members); - if (drop <= 0) - { - return; - } + // обмен в сторону меньших гривен + void parse_dec_exch(CHAR_DATA *ch, int amount, int num) + { + int torc_from = TORC_GOLD; + int torc_to = TORC_SILVER; + int torc_rate = TORC_EXCH_RATE; + + if (num == 4) + { + torc_from = TORC_SILVER; + torc_to = TORC_BRONZE; + } + + if (ch->desc->ext_money[torc_from] < amount) + { + if (ch->desc->ext_money[torc_from] < 1) + { + send_to_char("Нет необходимого количества гривен!\r\n", ch); + } + else + { + amount = ch->desc->ext_money[torc_from]; + send_to_char(ch, "Количество меняемых гривен уменьшено до %d.\r\n", amount); + + ch->desc->ext_money[torc_from] -= amount; + ch->desc->ext_money[torc_to] += amount * torc_rate; + send_to_char(ch, "Произведен обмен: %d -> %d.\r\n", amount, amount * torc_rate); + } + } + else + { + ch->desc->ext_money[torc_from] -= amount; + ch->desc->ext_money[torc_to] += amount * torc_rate; + send_to_char(ch, "Произведен обмен: %d -> %d.\r\n", amount, amount * torc_rate); + } + } - if (IN_ROOM(leader) == IN_ROOM(mob) - && GET_GOD_FLAG(leader, GF_REMORT) - && (GET_UNIQUE(leader) == damager.first - || mob->get_attacker(leader, ATTACKER_ROUNDS) >= damager.second / 2)) { - gain_torc(leader, drop); - } - // он был один, сваливаем - if (grp == nullptr) - return; - auto m_list = grp->getMembers(mob->in_room); // список живых мемберов в комнате - for (auto m : m_list) { - if (GET_GOD_FLAG(m, GF_REMORT) && mob->get_attacker(m, ATTACKER_ROUNDS) >= damager.second / 2) { - gain_torc(m, drop); - } - } -} + // кол-во меняемых гривен + int check_input_amount(CHAR_DATA* /*ch*/, int num1, int num2) + { + if ((num1 == 1 || num1 == 2) && num2 < TORC_EXCH_RATE) + { + return TORC_EXCH_RATE; + } + else if ((num1 == 3 || num1 == 4) && num2 < 1) + { + return 1; + } + return 0; + } -void player_drop_log(CHAR_DATA *ch, unsigned type, int diff) -{ - int total_bronze = ch->get_ext_money(TORC_BRONZE); - total_bronze += ch->get_ext_money(TORC_SILVER) * TORC_EXCH_RATE; - total_bronze += ch->get_ext_money(TORC_GOLD) * TORC_EXCH_RATE * TORC_EXCH_RATE; - - log("ExtMoney: %s%s%d%s, sum=%d", - ch->get_name().c_str(), - (diff > 0 ? " +" : " "), - diff, - ((type == TORC_GOLD) ? "g" : (type == TORC_SILVER) ? "s" : "b"), - total_bronze); -} + // проверка после обмена, что ничего лишнего не сгенерили случайно + bool check_equal_exch(CHAR_DATA *ch) + { + int before = 0, after = 0; + for (unsigned i = 0; i < TOTAL_TYPES; ++i) + { + if (i == TORC_BRONZE) + { + before += ch->get_ext_money(i); + after += ch->desc->ext_money[i]; + } + if (i == TORC_SILVER) + { + before += ch->get_ext_money(i) * TORC_EXCH_RATE; + after += ch->desc->ext_money[i] * TORC_EXCH_RATE; + } + else if (i == TORC_GOLD) + { + before += ch->get_ext_money(i) * TORC_EXCH_RATE * TORC_EXCH_RATE; + after += ch->desc->ext_money[i] * TORC_EXCH_RATE * TORC_EXCH_RATE; + } + } + if (before != after) + { + sprintf(buf, "SYSERROR: Torc exch by %s not equal: %d -> %d", + GET_NAME(ch), before, after); + mudlog(buf, DEF, LVL_IMMORT, SYSLOG, TRUE); + return false; + } + return true; + } -} // namespace ExtMoney + // парс ввода при обмене гривен + void torc_exch_parse(CHAR_DATA *ch, const char *arg) + { + if (!*arg || !a_isdigit(*arg)) + { + send_to_char("Неверный выбор!\r\n", ch); + torc_exch_menu(ch); + return; + } + + std::string param2(arg), param1; + GetOneParam(param2, param1); + boost::trim(param2); + + int num1 = 0, num2 = 0; + + try + { + num1 = std::stoi(param1, nullptr, 10); + if (!param2.empty()) + { + num2 = std::stoi(param2, nullptr, 10); + } + } + catch (const std::invalid_argument&) + { + log("SYSERROR: invalid_argument arg=%s (%s %s %d)", + arg, __FILE__, __func__, __LINE__); + + send_to_char("Неверный выбор!\r\n", ch); + torc_exch_menu(ch); + return; + } + + int amount = num2; + if (!param2.empty()) + { + // ввели два числа - проверка пользовательского ввода кол-ва гривен + int min_amount = check_input_amount(ch, num1, amount); + if (min_amount > 0) + { + send_to_char(ch, "Минимальное количество гривен для данного обмена: %d.", min_amount); + torc_exch_menu(ch); + return; + } + } + else + { + // ввели одно число - проставляем минимальный обмен + if (num1 == 1 || num1 == 2) + { + amount = TORC_EXCH_RATE; + } + else if (num1 == 3 || num1 == 4) + { + amount = 1; + } + } + // собсна обмен + if (num1 == 1 || num1 == 2) + { + parse_inc_exch(ch, amount, num1); + } + else if (num1 == 3 || num1 == 4) + { + parse_dec_exch(ch, amount, num1); + } + else if (num1 == 5) + { + STATE(ch->desc) = CON_PLAYING; + send_to_char("Обмен отменен.\r\n", ch); + return; + } + else if (num1 == 6) + { + if (!check_equal_exch(ch)) + { + send_to_char("Обмен отменен по техническим причинам, обратитесь к Богам.\r\n", ch); + } + else + { + for (unsigned i = 0; i < TOTAL_TYPES; ++i) + { + ch->set_ext_money(i, ch->desc->ext_money[i]); + } + STATE(ch->desc) = CON_PLAYING; + send_to_char("Обмен произведен.\r\n", ch); + } + return; + } + else + { + send_to_char("Неверный выбор!\r\n", ch); + } + torc_exch_menu(ch); + } -namespace Remort -{ + // формирование сообщения о награде гривнами при смерти босса + // пишет в одну строку о нескольких видах гривен, если таковые были + std::string create_message(CHAR_DATA *ch, int gold, int silver, int bronze) + { + std::stringstream out; + int cnt = 0; + + if (gold > 0) + { + out << CCIYEL(ch, C_NRM) << gold << " " + << desc_count(gold, type_list[TORC_GOLD].DESC_MESSAGE_U_NUM); + if (silver <= 0 && bronze <= 0) + { + out << " " << desc_count(gold, WHAT_TORCu); + } + out << CCNRM(ch, C_NRM); + ++cnt; + } + if (silver > 0) + { + if (cnt > 0) + { + if (bronze > 0) + { + out << ", " << CCWHT(ch, C_NRM) << silver << " " + << desc_count(silver, type_list[TORC_SILVER].DESC_MESSAGE_U_NUM) + << CCNRM(ch, C_NRM) << " и "; + } + else + { + out << " и " << CCWHT(ch, C_NRM) << silver << " " + << desc_count(silver, type_list[TORC_SILVER].DESC_MESSAGE_U_NUM) + << " " << desc_count(silver, WHAT_TORCu) + << CCNRM(ch, C_NRM); + } + } + else + { + out << CCWHT(ch, C_NRM) << silver << " " + << desc_count(silver, type_list[TORC_SILVER].DESC_MESSAGE_U_NUM); + if (bronze > 0) + { + out << CCNRM(ch, C_NRM) << " и "; + } + else + { + out << " " << desc_count(silver, WHAT_TORCu) << CCNRM(ch, C_NRM); + } + } + } + if (bronze > 0) + { + out << CCYEL(ch, C_NRM) << bronze << " " + << desc_count(bronze, type_list[TORC_BRONZE].DESC_MESSAGE_U_NUM) + << " " << desc_count(bronze, WHAT_TORCu) + << CCNRM(ch, C_NRM); + } + + return out.str(); + } -// релоадится через 'reload remort.xml' -void init() -{ - pugi::xml_document doc; - pugi::xml_parse_result result = doc.load_file(CONFIG_FILE); - if (!result) - { - snprintf(buf, MAX_STRING_LENGTH, "...%s", result.description()); - mudlog(buf, CMP, LVL_IMMORT, SYSLOG, TRUE); - return; - } + // проверка на случай нескольких физических боссов, + // которые логически являются одной группой, предотвращающая лишний дроп гривен + bool has_connected_bosses(CHAR_DATA *ch) + { + // если в комнате есть другие живые боссы + for (const auto i : world[ch->in_room]->people) + { + if (i != ch + && IS_NPC(i) + && !IS_CHARMICE(i) + && i->get_role(MOB_ROLE_BOSS)) + { + return true; + } + } + // если у данного моба есть живые последователи-боссы + for (follow_type *i = ch->followers; i; i = i->next) + { + if (i->follower != ch + && IS_NPC(i->follower) + && !IS_CHARMICE(i->follower) + && i->follower->get_master() == ch + && i->follower->get_role(MOB_ROLE_BOSS)) + { + return true; + } + } + // если он сам следует за каким-то боссом + if (ch->has_master() && ch->get_master()->get_role(MOB_ROLE_BOSS)) + { + return true; + } + + return false; + } - pugi::xml_node main_node = doc.child("remort"); - if (!main_node) + // выясняет какой тип гривен дропать с зоны + unsigned calc_type_by_zone_lvl(int zone_lvl) { - snprintf(buf, MAX_STRING_LENGTH, "... read fail"); - mudlog(buf, CMP, LVL_IMMORT, SYSLOG, TRUE); - return; + if (zone_lvl >= type_list[TORC_GOLD].DROP_LVL) + { + return TORC_GOLD; + } + else if (zone_lvl >= type_list[TORC_SILVER].DROP_LVL) + { + return TORC_SILVER; + } + else if (zone_lvl >= type_list[TORC_BRONZE].DROP_LVL) + { + return TORC_BRONZE; + } + return TOTAL_TYPES; } - WHERE_TO_REMORT_STR = Parse::child_value_str(main_node, "WHERE_TO_REMORT_STR"); - TORC_EXCH_RATE = Parse::child_value_int(main_node, "TORC_EXCH_RATE"); - - type_list[TORC_GOLD].MORT_NUM = Parse::child_value_int(main_node, "GOLD_MORT_NUM"); - type_list[TORC_GOLD].MORT_REQ = Parse::child_value_int(main_node, "GOLD_MORT_REQ"); - type_list[TORC_GOLD].MORT_REQ_ADD_PER_MORT = Parse::child_value_int(main_node, "GOLD_MORT_REQ_ADD_PER_MORT"); - type_list[TORC_GOLD].DROP_LVL = Parse::child_value_int(main_node, "GOLD_DROP_LVL"); - type_list[TORC_GOLD].DROP_AMOUNT = Parse::child_value_int(main_node, "GOLD_DROP_AMOUNT"); - type_list[TORC_GOLD].DROP_AMOUNT_ADD_PER_LVL = Parse::child_value_int(main_node, "GOLD_DROP_AMOUNT_ADD_PER_LVL"); - type_list[TORC_GOLD].MINIMUM_DAYS = Parse::child_value_int(main_node, "GOLD_MINIMUM_DAYS"); - - type_list[TORC_SILVER].MORT_NUM = Parse::child_value_int(main_node, "SILVER_MORT_NUM"); - type_list[TORC_SILVER].MORT_REQ = Parse::child_value_int(main_node, "SILVER_MORT_REQ"); - type_list[TORC_SILVER].MORT_REQ_ADD_PER_MORT = Parse::child_value_int(main_node, "SILVER_MORT_REQ_ADD_PER_MORT"); - type_list[TORC_SILVER].DROP_LVL = Parse::child_value_int(main_node, "SILVER_DROP_LVL"); - type_list[TORC_SILVER].DROP_AMOUNT = Parse::child_value_int(main_node, "SILVER_DROP_AMOUNT"); - type_list[TORC_SILVER].DROP_AMOUNT_ADD_PER_LVL = Parse::child_value_int(main_node, "SILVER_DROP_AMOUNT_ADD_PER_LVL"); - type_list[TORC_SILVER].MINIMUM_DAYS = Parse::child_value_int(main_node, "SILVER_MINIMUM_DAYS"); - - type_list[TORC_BRONZE].MORT_NUM = Parse::child_value_int(main_node, "BRONZE_MORT_NUM"); - type_list[TORC_BRONZE].MORT_REQ = Parse::child_value_int(main_node, "BRONZE_MORT_REQ"); - type_list[TORC_BRONZE].MORT_REQ_ADD_PER_MORT = Parse::child_value_int(main_node, "BRONZE_MORT_REQ_ADD_PER_MORT"); - type_list[TORC_BRONZE].DROP_LVL = Parse::child_value_int(main_node, "BRONZE_DROP_LVL"); - type_list[TORC_BRONZE].DROP_AMOUNT = Parse::child_value_int(main_node, "BRONZE_DROP_AMOUNT"); - type_list[TORC_BRONZE].DROP_AMOUNT_ADD_PER_LVL = Parse::child_value_int(main_node, "BRONZE_DROP_AMOUNT_ADD_PER_LVL"); - type_list[TORC_BRONZE].MINIMUM_DAYS = Parse::child_value_int(main_node, "BRONZE_MINIMUM_DAYS"); - - // не из конфига, но инится заодно со всеми - type_list[TORC_GOLD].DESC_MESSAGE_NUM = WHAT_TGOLD; - type_list[TORC_SILVER].DESC_MESSAGE_NUM = WHAT_TSILVER; - type_list[TORC_BRONZE].DESC_MESSAGE_NUM = WHAT_TBRONZE; - type_list[TORC_GOLD].DESC_MESSAGE_U_NUM = WHAT_TGOLDu; - type_list[TORC_SILVER].DESC_MESSAGE_U_NUM = WHAT_TSILVERu; - type_list[TORC_BRONZE].DESC_MESSAGE_U_NUM = WHAT_TBRONZEu; -} + // возвращает кол-во гривен с босса зоны уровня zone_lvl, поделенных + // с учетом группы members и пересчитанных в бронзу + int calc_drop_torc(int zone_lvl, int members) + { + const unsigned type = calc_type_by_zone_lvl(zone_lvl); + if (type >= TOTAL_TYPES) + { + return 0; + } + const int add = zone_lvl - type_list[type].DROP_LVL; + int drop = type_list[type].DROP_AMOUNT + add * type_list[type].DROP_AMOUNT_ADD_PER_LVL; + + // пересчитываем дроп к минимальному типу + if (type == TORC_GOLD) + { + drop = drop * TORC_EXCH_RATE * TORC_EXCH_RATE; + } + else if (type == TORC_SILVER) + { + drop = drop * TORC_EXCH_RATE; + } + + // есть ли вообще что дропать + if (drop < members) + { + return 0; + } + + // после этого уже применяем делитель группы + if (members > 1) + { + drop = drop / members; + } + + return drop; + } -// проверка, мешает ли что-то чару уйти в реморт -bool can_remort_now(CHAR_DATA *ch) -{ - if (PRF_FLAGGED(ch, PRF_CAN_REMORT) || !need_torc(ch)) - { - return true; - } - return false; -} + // возвращает требование гривен на морт в пересчете на бронзу + // для лоу-мортов берется требование бронзы базового уровня + int calc_torc_daily(int rmrt) + { + TorcReq torc_req(rmrt); + int num = 0; + + if (torc_req.type < TOTAL_TYPES) + { + num = type_list[torc_req.type].MORT_REQ; + + if (torc_req.type == TORC_GOLD) + { + num = num * TORC_EXCH_RATE * TORC_EXCH_RATE; + } + else if (torc_req.type == TORC_SILVER) + { + num = num * TORC_EXCH_RATE; + } + } + else + { + num = type_list[TORC_BRONZE].MORT_REQ; + } + + return num; + } -// распечатка переменных из конфига -void show_config(CHAR_DATA *ch) -{ - std::stringstream out; - out << "&SТекущие значения основных параметров:\r\n" - << "WHERE_TO_REMORT_STR = " << WHERE_TO_REMORT_STR << "\r\n" - << "TORC_EXCH_RATE = " << TORC_EXCH_RATE << "\r\n" - - << "GOLD_MORT_NUM = " << type_list[TORC_GOLD].MORT_NUM << "\r\n" - << "GOLD_MORT_REQ = " << type_list[TORC_GOLD].MORT_REQ << "\r\n" - << "GOLD_MORT_REQ_ADD_PER_MORT = " << type_list[TORC_GOLD].MORT_REQ_ADD_PER_MORT << "\r\n" - << "GOLD_DROP_LVL = " << type_list[TORC_GOLD].DROP_LVL << "\r\n" - << "GOLD_DROP_AMOUNT = " << type_list[TORC_GOLD].DROP_AMOUNT << "\r\n" - << "GOLD_DROP_AMOUNT_ADD_PER_LVL = " << type_list[TORC_GOLD].DROP_AMOUNT_ADD_PER_LVL << "\r\n" - << "GOLD_MINIMUM_DAYS = " << type_list[TORC_GOLD].MINIMUM_DAYS << "\r\n" - - << "SILVER_MORT_NUM = " << type_list[TORC_SILVER].MORT_NUM << "\r\n" - << "SILVER_MORT_REQ = " << type_list[TORC_SILVER].MORT_REQ << "\r\n" - << "SILVER_MORT_REQ_ADD_PER_MORT = " << type_list[TORC_SILVER].MORT_REQ_ADD_PER_MORT << "\r\n" - << "SILVER_DROP_LVL = " << type_list[TORC_SILVER].DROP_LVL << "\r\n" - << "SILVER_DROP_AMOUNT = " << type_list[TORC_SILVER].DROP_AMOUNT << "\r\n" - << "SILVER_DROP_AMOUNT_ADD_PER_LVL = " << type_list[TORC_SILVER].DROP_AMOUNT_ADD_PER_LVL << "\r\n" - << "SILVER_MINIMUM_DAYS = " << type_list[TORC_SILVER].MINIMUM_DAYS << "\r\n" - - << "BRONZE_MORT_NUM = " << type_list[TORC_BRONZE].MORT_NUM << "\r\n" - << "BRONZE_MORT_REQ = " << type_list[TORC_BRONZE].MORT_REQ << "\r\n" - << "BRONZE_MORT_REQ_ADD_PER_MORT = " << type_list[TORC_BRONZE].MORT_REQ_ADD_PER_MORT << "\r\n" - << "BRONZE_DROP_LVL = " << type_list[TORC_BRONZE].DROP_LVL << "\r\n" - << "BRONZE_DROP_AMOUNT = " << type_list[TORC_BRONZE].DROP_AMOUNT << "\r\n" - << "BRONZE_DROP_AMOUNT_ADD_PER_LVL = " << type_list[TORC_BRONZE].DROP_AMOUNT_ADD_PER_LVL << "\r\n" - << "BRONZE_MINIMUM_DAYS = " << type_list[TORC_BRONZE].MINIMUM_DAYS << "\r\n"; - - send_to_char(out.str(), ch); -} + // по дефолту отрисовка * за каждую 1/5 от суточного лимита гривен + // если imm_stat == true, то вместо звездочек конкретные цифры тек/макс + std::string draw_daily_limit(CHAR_DATA *ch, bool imm_stat) + { + const int today_torc = ch->get_today_torc(); + const int torc_req_daily = calc_torc_daily(GET_REMORT(ch)); + + TorcReq torc_req(GET_REMORT(ch)); + if (torc_req.type >= TOTAL_TYPES) + { + torc_req.type = TORC_BRONZE; + } + const int daily_torc_limit = torc_req_daily / type_list[torc_req.type].MINIMUM_DAYS; + + std::string out("["); + if (!imm_stat) + { + for (int i = 1; i <= 5; ++i) + { + if (daily_torc_limit / 5 * i <= today_torc) + { + out += "*"; + } + else + { + out += "."; + } + } + } + else + { + out += boost::str(boost::format("%d/%d") % today_torc % daily_torc_limit); + } + out += "]"; + + return out; + } -// возвращает требование гривен на морт в пересчете на бронзу -// для лоу-мортов берется требование бронзы базового уровня -int calc_torc_daily(int rmrt) -{ - TorcReq torc_req(rmrt); - int num = 0; + // проверка дропа гривен на суточный замакс + int check_daily_limit(CHAR_DATA *ch, int drop) + { + const int today_torc = ch->get_today_torc(); + const int torc_req_daily = calc_torc_daily(GET_REMORT(ch)); + + // из calc_torc_daily в любом случае взялось какое-то число бронзы + // даже если чар не имеет мортов для требования гривен + TorcReq torc_req(GET_REMORT(ch)); + if (torc_req.type >= TOTAL_TYPES) + { + torc_req.type = TORC_BRONZE; + } + const int daily_torc_limit = torc_req_daily / type_list[torc_req.type].MINIMUM_DAYS; + + if (today_torc + drop > daily_torc_limit) + { + int add = daily_torc_limit - today_torc; + if (add > 0) + { + ch->add_today_torc(add); + return add; + } + else + { + return 0; + } + } + + ch->add_today_torc(drop); + return drop; + } - if (torc_req.type < TOTAL_TYPES) - { - num = type_list[torc_req.type].MORT_REQ; + // процесс дропа гривен конкретному чару + void gain_torc(CHAR_DATA *ch, int drop) + { + // проверка на индивидуальный суточный замакс гривн + int bronze = check_daily_limit(ch, drop); + if (bronze <= 0) + { + return; + } + int gold = 0, silver = 0; + // и разносим что осталось обратно по типам + if (bronze >= TORC_EXCH_RATE * TORC_EXCH_RATE) + { + gold += bronze / (TORC_EXCH_RATE * TORC_EXCH_RATE); + bronze -= gold * TORC_EXCH_RATE * TORC_EXCH_RATE; + ch->set_ext_money(TORC_GOLD, gold + ch->get_ext_money(TORC_GOLD)); + } + if (bronze >= TORC_EXCH_RATE) + { + silver += bronze / TORC_EXCH_RATE; + bronze -= silver * TORC_EXCH_RATE; + ch->set_ext_money(TORC_SILVER, silver + ch->get_ext_money(TORC_SILVER)); + } + ch->set_ext_money(TORC_BRONZE, bronze + ch->get_ext_money(TORC_BRONZE)); + + std::string out = create_message(ch, gold, silver, bronze); + send_to_char(ch, "В награду за свершенный подвиг вы получили от Богов %s.\r\n", out.c_str()); - if (torc_req.type == TORC_GOLD) - { - num = num * TORC_EXCH_RATE * TORC_EXCH_RATE; - } - else if (torc_req.type == TORC_SILVER) - { - num = num * TORC_EXCH_RATE; - } - } - else - { - num = type_list[TORC_BRONZE].MORT_REQ; - } + } - return num; -} + // дергается из экстракт_чар, у босса берется макс дамагер, находящийся + // в той же комнате, группе готорого и раскидываются гривны, если есть + // кому раскидывать (флаг GF_REMORT, проверка на делимость гривен, проверка на + // то, что чар находился в комнате с мобом не менее половины раундов дамагера) + void drop_torc(CHAR_DATA *mob) + { + if (!mob->get_role(MOB_ROLE_BOSS) + || has_connected_bosses(mob)) + { + return; + } + + log("[Extract char] Checking %s for ExtMoney.", mob->get_name().c_str()); + + std::pair damager = mob->get_max_damager_in_room(); + DESCRIPTOR_DATA *d = nullptr; + if (damager.first > 0) { + d = DescByUID(damager.first); + } + if (!d) { + return; + } + auto grp = d->character->personGroup; + CHAR_DATA *leader = grp != nullptr? d->character->personGroup->getLeader() : d->character.get(); + + int members = 1; + members = leader->personGroup->size(mob->in_room); + + const int zone_lvl = zone_table[mob_index[GET_MOB_RNUM(mob)].zone].mob_level; + const int drop = calc_drop_torc(zone_lvl, members); + if (drop <= 0) + { + return; + } + + if (IN_ROOM(leader) == IN_ROOM(mob) + && GET_GOD_FLAG(leader, GF_REMORT) + && (GET_UNIQUE(leader) == damager.first + || mob->get_attacker(leader, ATTACKER_ROUNDS) >= damager.second / 2)) { + gain_torc(leader, drop); + } + // он был один, сваливаем + if (grp == nullptr) + return; + auto m_list = grp->getMembers(mob->in_room); // список живых мемберов в комнате + for (auto m : m_list) { + if (GET_GOD_FLAG(m, GF_REMORT) && mob->get_attacker(m, ATTACKER_ROUNDS) >= damager.second / 2) { + gain_torc(m, drop); + } + } + } -// проверка, требуется ли от чара жертвовать для реморта -bool need_torc(CHAR_DATA *ch) -{ - TorcReq torc_req(GET_REMORT(ch)); + void player_drop_log(CHAR_DATA *ch, unsigned type, int diff) + { + int total_bronze = ch->get_ext_money(TORC_BRONZE); + total_bronze += ch->get_ext_money(TORC_SILVER) * TORC_EXCH_RATE; + total_bronze += ch->get_ext_money(TORC_GOLD) * TORC_EXCH_RATE * TORC_EXCH_RATE; + + log("ExtMoney: %s%s%d%s, sum=%d", + ch->get_name().c_str(), + (diff > 0 ? " +" : " "), + diff, + ((type == TORC_GOLD) ? "g" : (type == TORC_SILVER) ? "s" : "b"), + total_bronze); + } - if (torc_req.type < TOTAL_TYPES && torc_req.amount > 0) - { - return true; - } + // распечатка переменных из конфига + void show_config(CHAR_DATA *ch) + { + std::stringstream out; + out << "&SТекущие значения основных параметров:\r\n" + << "WHERE_TO_REMORT_STR = " << WHERE_TO_REMORT_STR << "\r\n" + << "TORC_EXCH_RATE = " << TORC_EXCH_RATE << "\r\n" + + << "GOLD_MORT_NUM = " << type_list[TORC_GOLD].MORT_NUM << "\r\n" + << "GOLD_MORT_REQ = " << type_list[TORC_GOLD].MORT_REQ << "\r\n" + << "GOLD_MORT_REQ_ADD_PER_MORT = " << type_list[TORC_GOLD].MORT_REQ_ADD_PER_MORT << "\r\n" + << "GOLD_DROP_LVL = " << type_list[TORC_GOLD].DROP_LVL << "\r\n" + << "GOLD_DROP_AMOUNT = " << type_list[TORC_GOLD].DROP_AMOUNT << "\r\n" + << "GOLD_DROP_AMOUNT_ADD_PER_LVL = " << type_list[TORC_GOLD].DROP_AMOUNT_ADD_PER_LVL << "\r\n" + << "GOLD_MINIMUM_DAYS = " << type_list[TORC_GOLD].MINIMUM_DAYS << "\r\n" + + << "SILVER_MORT_NUM = " << type_list[TORC_SILVER].MORT_NUM << "\r\n" + << "SILVER_MORT_REQ = " << type_list[TORC_SILVER].MORT_REQ << "\r\n" + << "SILVER_MORT_REQ_ADD_PER_MORT = " << type_list[TORC_SILVER].MORT_REQ_ADD_PER_MORT << "\r\n" + << "SILVER_DROP_LVL = " << type_list[TORC_SILVER].DROP_LVL << "\r\n" + << "SILVER_DROP_AMOUNT = " << type_list[TORC_SILVER].DROP_AMOUNT << "\r\n" + << "SILVER_DROP_AMOUNT_ADD_PER_LVL = " << type_list[TORC_SILVER].DROP_AMOUNT_ADD_PER_LVL << "\r\n" + << "SILVER_MINIMUM_DAYS = " << type_list[TORC_SILVER].MINIMUM_DAYS << "\r\n" + + << "BRONZE_MORT_NUM = " << type_list[TORC_BRONZE].MORT_NUM << "\r\n" + << "BRONZE_MORT_REQ = " << type_list[TORC_BRONZE].MORT_REQ << "\r\n" + << "BRONZE_MORT_REQ_ADD_PER_MORT = " << type_list[TORC_BRONZE].MORT_REQ_ADD_PER_MORT << "\r\n" + << "BRONZE_DROP_LVL = " << type_list[TORC_BRONZE].DROP_LVL << "\r\n" + << "BRONZE_DROP_AMOUNT = " << type_list[TORC_BRONZE].DROP_AMOUNT << "\r\n" + << "BRONZE_DROP_AMOUNT_ADD_PER_LVL = " << type_list[TORC_BRONZE].DROP_AMOUNT_ADD_PER_LVL << "\r\n" + << "BRONZE_MINIMUM_DAYS = " << type_list[TORC_BRONZE].MINIMUM_DAYS << "\r\n"; + + send_to_char(out.str(), ch); + } - return false; -} +} // namespace ExtMoney -} // namespace Remort namespace { @@ -1029,4 +891,92 @@ int torc(CHAR_DATA *ch, void *me, int cmd, char* /*argument*/) return 0; } +void Remort::init() +{ + pugi::xml_document doc; + pugi::xml_parse_result result = doc.load_file(Remort::CONFIG_FILE); + if (!result) + { + snprintf(buf, MAX_STRING_LENGTH, "...%s", result.description()); + mudlog(buf, CMP, LVL_IMMORT, SYSLOG, TRUE); + return; + } + + pugi::xml_node main_node = doc.child("remort"); + if (!main_node) + { + snprintf(buf, MAX_STRING_LENGTH, "... read fail"); + mudlog(buf, CMP, LVL_IMMORT, SYSLOG, TRUE); + return; + } + + WHERE_TO_REMORT_STR = Parse::child_value_str(main_node, "WHERE_TO_REMORT_STR"); + TORC_EXCH_RATE = Parse::child_value_int(main_node, "TORC_EXCH_RATE"); + + type_list[TORC_GOLD].MORT_NUM = Parse::child_value_int(main_node, "GOLD_MORT_NUM"); + type_list[TORC_GOLD].MORT_REQ = Parse::child_value_int(main_node, "GOLD_MORT_REQ"); + type_list[TORC_GOLD].MORT_REQ_ADD_PER_MORT = Parse::child_value_int(main_node, "GOLD_MORT_REQ_ADD_PER_MORT"); + type_list[TORC_GOLD].DROP_LVL = Parse::child_value_int(main_node, "GOLD_DROP_LVL"); + type_list[TORC_GOLD].DROP_AMOUNT = Parse::child_value_int(main_node, "GOLD_DROP_AMOUNT"); + type_list[TORC_GOLD].DROP_AMOUNT_ADD_PER_LVL = Parse::child_value_int(main_node, "GOLD_DROP_AMOUNT_ADD_PER_LVL"); + type_list[TORC_GOLD].MINIMUM_DAYS = Parse::child_value_int(main_node, "GOLD_MINIMUM_DAYS"); + + type_list[TORC_SILVER].MORT_NUM = Parse::child_value_int(main_node, "SILVER_MORT_NUM"); + type_list[TORC_SILVER].MORT_REQ = Parse::child_value_int(main_node, "SILVER_MORT_REQ"); + type_list[TORC_SILVER].MORT_REQ_ADD_PER_MORT = Parse::child_value_int(main_node, "SILVER_MORT_REQ_ADD_PER_MORT"); + type_list[TORC_SILVER].DROP_LVL = Parse::child_value_int(main_node, "SILVER_DROP_LVL"); + type_list[TORC_SILVER].DROP_AMOUNT = Parse::child_value_int(main_node, "SILVER_DROP_AMOUNT"); + type_list[TORC_SILVER].DROP_AMOUNT_ADD_PER_LVL = Parse::child_value_int(main_node, "SILVER_DROP_AMOUNT_ADD_PER_LVL"); + type_list[TORC_SILVER].MINIMUM_DAYS = Parse::child_value_int(main_node, "SILVER_MINIMUM_DAYS"); + + type_list[TORC_BRONZE].MORT_NUM = Parse::child_value_int(main_node, "BRONZE_MORT_NUM"); + type_list[TORC_BRONZE].MORT_REQ = Parse::child_value_int(main_node, "BRONZE_MORT_REQ"); + type_list[TORC_BRONZE].MORT_REQ_ADD_PER_MORT = Parse::child_value_int(main_node, "BRONZE_MORT_REQ_ADD_PER_MORT"); + type_list[TORC_BRONZE].DROP_LVL = Parse::child_value_int(main_node, "BRONZE_DROP_LVL"); + type_list[TORC_BRONZE].DROP_AMOUNT = Parse::child_value_int(main_node, "BRONZE_DROP_AMOUNT"); + type_list[TORC_BRONZE].DROP_AMOUNT_ADD_PER_LVL = Parse::child_value_int(main_node, "BRONZE_DROP_AMOUNT_ADD_PER_LVL"); + type_list[TORC_BRONZE].MINIMUM_DAYS = Parse::child_value_int(main_node, "BRONZE_MINIMUM_DAYS"); + + // не из конфига, но инится заодно со всеми + type_list[TORC_GOLD].DESC_MESSAGE_NUM = WHAT_TGOLD; + type_list[TORC_SILVER].DESC_MESSAGE_NUM = WHAT_TSILVER; + type_list[TORC_BRONZE].DESC_MESSAGE_NUM = WHAT_TBRONZE; + type_list[TORC_GOLD].DESC_MESSAGE_U_NUM = WHAT_TGOLDu; + type_list[TORC_SILVER].DESC_MESSAGE_U_NUM = WHAT_TSILVERu; + type_list[TORC_BRONZE].DESC_MESSAGE_U_NUM = WHAT_TBRONZEu; +} + + +TorcReq::TorcReq(int rmrt) +{ + // type + if (rmrt >= type_list[TORC_GOLD].MORT_NUM) + { + type = TORC_GOLD; + } + else if (rmrt >= type_list[TORC_SILVER].MORT_NUM) + { + type = TORC_SILVER; + } + else if (rmrt >= type_list[TORC_BRONZE].MORT_NUM) + { + type = TORC_BRONZE; + } + else + { + type = TOTAL_TYPES; + } + // amount + if (type != TOTAL_TYPES) + { + amount = type_list[type].MORT_REQ + + (rmrt - type_list[type].MORT_NUM) * type_list[type].MORT_REQ_ADD_PER_MORT; + } + else + { + amount = 0; + } +} + + // vim: ts=4 sw=4 tw=0 noet syntax=cpp : diff --git a/src/ext_money.hpp b/src/ext_money.hpp index ccea31ee6..7e1bc94f6 100644 --- a/src/ext_money.hpp +++ b/src/ext_money.hpp @@ -8,34 +8,33 @@ #include "conf.h" #include "sysdep.h" #include "structs.h" -#include "chars/char.hpp" +//#include "chars/char.hpp" #include -namespace ExtMoney -{ +class CHAR_DATA; -void torc_exch_menu(CHAR_DATA *ch); -void torc_exch_parse(CHAR_DATA *ch, const char *arg); +namespace ExtMoney { + void torc_exch_menu(CHAR_DATA *ch); + void torc_exch_parse(CHAR_DATA *ch, const char *arg); -void drop_torc(CHAR_DATA *mob); -std::string draw_daily_limit(CHAR_DATA *ch, bool imm_stat = false); + void drop_torc(CHAR_DATA *mob); + std::string draw_daily_limit(CHAR_DATA *ch, bool imm_stat = false); -void player_drop_log(CHAR_DATA *ch, unsigned type, int num); -std::string name_currency_plural(std::string name); + void player_drop_log(CHAR_DATA *ch, unsigned type, int num); + std::string name_currency_plural(std::string name); + void show_config(CHAR_DATA *ch); } // namespace ExtMoney -namespace Remort +struct TorcReq { + TorcReq(int rmrt); + // тип гривн + unsigned type; + // кол-во + int amount; +}; -extern std::string WHERE_TO_REMORT_STR; - -bool can_remort_now(CHAR_DATA *ch); -void init(); -void show_config(CHAR_DATA *ch); -bool need_torc(CHAR_DATA *ch); - -} // namespace Remort int torc(CHAR_DATA *ch, void *me, int cmd, char* argument); diff --git a/src/features.cpp b/src/features.cpp index ecd895679..725e8c293 100644 --- a/src/features.cpp +++ b/src/features.cpp @@ -23,12 +23,12 @@ #include "room.hpp" #include "screen.h" #include "fightsystem/pk.h" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "room.hpp" #include "house.h" #include "screen.h" #include "fightsystem/pk.h" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "utils.h" #include "structs.h" #include "sysdep.h" diff --git a/src/fightsystem/fight.cpp b/src/fightsystem/fight.cpp index 9265aa303..d603aaebd 100644 --- a/src/fightsystem/fight.cpp +++ b/src/fightsystem/fight.cpp @@ -36,7 +36,7 @@ #include "spells.h" #include "screen.h" #include "constants.h" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "im.h" #include "skills.h" #include "features.hpp" @@ -2390,33 +2390,4 @@ void perform_violence() } } -int calc_leadership(CHAR_DATA * ch) -{ - int prob, percent; - - if (IS_NPC(ch) || ch->personGroup == nullptr) { - return FALSE; - } - - CHAR_DATA* leader = ch->personGroup->getLeader(); - - // если лидер умер или нет в комнате - фиг вам, а не бонусы - if (leader == nullptr || IN_ROOM(ch) != IN_ROOM(leader)) { - return FALSE; - } - - if (!leader->get_skill(SKILL_LEADERSHIP)) { - return (FALSE); - } - - percent = number(1, 101); - prob = calculate_skill(leader, SKILL_LEADERSHIP, 0); - if (percent > prob) { - return (FALSE); - } - else { - return (TRUE); - } -} - // vim: ts=4 sw=4 tw=0 noet syntax=cpp : diff --git a/src/fightsystem/fight.h b/src/fightsystem/fight.h index f129e82d4..da6e261fa 100644 --- a/src/fightsystem/fight.h +++ b/src/fightsystem/fight.h @@ -161,8 +161,6 @@ void alt_equip(CHAR_DATA *ch, int pos, int dam, int chance); void char_dam_message(int dam, CHAR_DATA *ch, CHAR_DATA *victim, bool mayflee); void test_self_hitroll(CHAR_DATA *ch); -int calc_leadership(CHAR_DATA * ch); - #endif // vim: ts=4 sw=4 tw=0 noet syntax=cpp : diff --git a/src/fightsystem/fight.penalties.cpp b/src/fightsystem/fight.penalties.cpp index 79db0bc93..e69de29bb 100644 --- a/src/fightsystem/fight.penalties.cpp +++ b/src/fightsystem/fight.penalties.cpp @@ -1,92 +0,0 @@ -#include "fight.penalties.hpp" - -#include "logger.hpp" -#include "utils.h" -#include "chars/char.hpp" - -int GroupPenaltyCalculator::get() const -{ - const bool leader_is_npc = IS_NPC(m_leader); - const bool leader_in_group = AFF_FLAGGED(m_leader, EAffectFlag::AFF_GROUP); - const bool leader_is_in_room = leader_in_group - && m_leader->in_room == IN_ROOM(m_killer); - if (!leader_is_npc - && leader_is_in_room) - { - int penalty = 0; - if (penalty_by_leader(m_leader, penalty)) - { - return penalty; - } - } - - for (auto f = m_leader->followers; f; f = f->next) - { - const bool follower_is_npc = IS_NPC(f->follower); - const bool follower_is_in_room = AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP) - && f->follower->in_room == IN_ROOM(m_killer); - - if (follower_is_npc - || !follower_is_in_room) - { - continue; - } - - int penalty = 0; - if (penalty_by_leader(f->follower, penalty)) - { - if (0 < penalty) - { - return penalty; - } - } - } - - return 0; -} - -bool GroupPenaltyCalculator::penalty_by_leader(const CHAR_DATA* player, int& penalty) const -{ - const int player_remorts = static_cast(GET_REMORT(player)); - const int player_class = static_cast(GET_CLASS(player)); - const int player_level = GET_LEVEL(player); - - if (IS_NPC(player)) - { - log("LOGIC ERROR: try to get penalty for NPC [%s], VNum: %d\n", - player->get_name().c_str(), - GET_MOB_VNUM(player)); - penalty = DEFAULT_PENALTY; - return true; - } - - if (0 > player_class - || player_class > NUM_PLAYER_CLASSES) - { - log("LOGIC ERROR: wrong player class: %d for player [%s]", - player_class, - player->get_name().c_str()); - penalty = DEFAULT_PENALTY; - return true; - } - - if (0 > player_remorts - || player_remorts > MAX_REMORT) - { - log("LOGIC ERROR: wrong number of remorts: %d for player [%s]", - player_remorts, - player->get_name().c_str()); - penalty = DEFAULT_PENALTY; - return true; - } - - bool result = false; - if (m_max_level - player_level > m_grouping[player_class][player_remorts]) - { - penalty = 50 + 2 * (m_max_level - player_level - m_grouping[player_class][player_remorts]); - result = true; - } - - return result; -} - diff --git a/src/fightsystem/fight.penalties.hpp b/src/fightsystem/fight.penalties.hpp index 736f17fb3..c2af01df2 100644 --- a/src/fightsystem/fight.penalties.hpp +++ b/src/fightsystem/fight.penalties.hpp @@ -5,28 +5,6 @@ class CHAR_DATA; // to avoid inclusion of "char.hpp" -class GroupPenaltyCalculator -{ -public: - constexpr static int DEFAULT_PENALTY = 100; - GroupPenaltyCalculator(const CHAR_DATA* killer, const CHAR_DATA* leader, const int max_level, const GroupPenalties& grouping): - m_killer(killer), - m_leader(leader), - m_max_level(max_level), - m_grouping(grouping) - { - } - - int get() const; - -private: - const CHAR_DATA* m_killer; - const CHAR_DATA* m_leader; - const int m_max_level; - const GroupPenalties& m_grouping; - - bool penalty_by_leader(const CHAR_DATA* player, int& penalty) const; -}; #endif // __FIGHT_PENALTIES_HPP__ diff --git a/src/fightsystem/fight_hit.cpp b/src/fightsystem/fight_hit.cpp index 09d78d63e..84b50e694 100644 --- a/src/fightsystem/fight_hit.cpp +++ b/src/fightsystem/fight_hit.cpp @@ -3,7 +3,7 @@ #include "logger.hpp" #include "handler.h" #include "screen.h" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "skills.h" #include "magic.h" #include "pk.h" @@ -2456,60 +2456,41 @@ void update_pk_logs(CHAR_DATA *ch, CHAR_DATA *victim) void Damage::process_death(CHAR_DATA *ch, CHAR_DATA *victim) { - CHAR_DATA *killer = NULL; - - if (IS_NPC(victim) || victim->desc) - { - if (victim == ch && IN_ROOM(victim) != NOWHERE) - { - if (spell_num == SPELL_POISON) - { - for (const auto poisoner : world[IN_ROOM(victim)]->people) - { - if (poisoner != victim - && GET_ID(poisoner) == victim->Poisoner) - { + CHAR_DATA *killer = nullptr; + + // вычисляем, чем убили и кто убивец. Но тут какая то херня :( + if (IS_NPC(victim) || victim->desc) { + if (victim == ch && IN_ROOM(victim) != NOWHERE) { + if (spell_num == SPELL_POISON) { + for (const auto poisoner : world[IN_ROOM(victim)]->people) { + if (poisoner != victim && GET_ID(poisoner) == victim->Poisoner) { killer = poisoner; } } - } - else if (msg_num == TYPE_SUFFERING) - { - for (const auto attacker : world[IN_ROOM(victim)]->people) - { - if (attacker->get_fighting() == victim) - { + } else if (msg_num == TYPE_SUFFERING) { + for (const auto attacker : world[IN_ROOM(victim)]->people) { + if (attacker->get_fighting() == victim) { killer = attacker; } } } } - - if (ch != victim) - { + if (ch != victim) { killer = ch; } } - if (killer) - { - if (AFF_FLAGGED(killer, EAffectFlag::AFF_GROUP)) - { + if (killer) { + // и чармис и хозяин в одной группе + if (IN_GROUP(killer)) + group_gain(killer, victim); + + if (AFF_FLAGGED(killer, EAffectFlag::AFF_GROUP)) { // т.к. помечен флагом AFF_GROUP - точно PC - group_gain(killer, victim); - } - else if ((AFF_FLAGGED(killer, EAffectFlag::AFF_CHARM) - || AFF_FLAGGED(killer, EAffectFlag::AFF_HELPER) - || MOB_FLAGGED(killer, MOB_PLAYER_SUMMON)) - && killer->has_master()) - // killer - зачармленный NPC с хозяином - { - // по логике надо бы сделать, что если хозяина нет в клетке, но - // кто-то из группы хозяина в клетке, то опыт накинуть согруппам, - // которые рядом с убившим моба чармисом. - if (AFF_FLAGGED(killer->get_master(), EAffectFlag::AFF_GROUP) - && IN_ROOM(killer) == IN_ROOM(killer->get_master())) - { + + } else + if ((IS_CHARMICE(killer) || MOB_FLAGGED(killer, MOB_PLAYER_SUMMON)) && killer->has_master()) { + if (IN_GROUP(killer->get_master()) && IN_ROOM(killer) == IN_ROOM(killer->get_master())) { // Хозяин - PC в группе => опыт группе group_gain(killer->get_master(), victim); } diff --git a/src/fightsystem/fight_stuff.cpp b/src/fightsystem/fight_stuff.cpp index 85fb1dd6c..676237d69 100644 --- a/src/fightsystem/fight_stuff.cpp +++ b/src/fightsystem/fight_stuff.cpp @@ -1,48 +1,47 @@ // Part of Bylins http://www.mud.ru #include "core/affect_data.h" -#include "mobact.hpp" -#include "obj.hpp" -#include "skills/flee.h" +#include "core/leveling.h" +#include "backtrace.hpp" +#include "bonus.h" +#include "chars/char.hpp" +#include "chars/char_player.hpp" #include "chars/world.characters.hpp" +#include "constants.h" +#include "corpse.hpp" +#include "db.h" +#include "dg/dg_scripts.h" #include "fight.h" -#include "fight.penalties.hpp" #include "fight_hit.hpp" -#include "chars/char.hpp" -#include "skills.h" +#include "grp/grp.main.h" #include "handler.h" -#include "db.h" -#include "room.hpp" -#include "spells.h" -#include "dg_scripts.h" -#include "corpse.hpp" #include "house.h" -#include "pk.h" -#include "stuff.hpp" -#include "sets_drop.hpp" -#include "top.h" -#include "constants.h" -#include "screen.h" +#include "logger.hpp" #include "magic.h" #include "mob_stat.hpp" -#include "logger.hpp" -#include "bonus.h" -#include "backtrace.hpp" +#include "mob_stat.hpp" +#include "mobact.hpp" +#include "obj.hpp" +#include "object.prototypes.hpp" +#include "pk.h" +#include "room.hpp" +#include "screen.h" +#include "sets_drop.hpp" +#include "skills.h" +#include "skills/flee.h" #include "spell_parser.hpp" +#include "spells.h" +#include "stuff.hpp" +#include "top.h" #include "world.objects.hpp" -#include "object.prototypes.hpp" #include "zone.table.hpp" -#include "chars/char_player.hpp" -#include "mob_stat.hpp" -#include +#include #include // extern void perform_drop_gold(CHAR_DATA * ch, int amount); -int level_exp(CHAR_DATA * ch, int chlevel); -int max_exp_gain_pc(CHAR_DATA * ch); -int max_exp_loss_pc(CHAR_DATA * ch); + void get_from_container(CHAR_DATA * ch, OBJ_DATA * cont, char *arg, int mode, int amount, bool autoloot); int slot_for_char(CHAR_DATA * ch, int i); void set_wait(CHAR_DATA * ch, int waittime, int victim_in_room); @@ -53,6 +52,11 @@ extern int max_exp_gain_npc; //интервал в секундах между восстановлением кругов после рипа extern const unsigned RECALL_SPELLS_INTERVAL; const unsigned RECALL_SPELLS_INTERVAL = 28; +// using + +using namespace ExpCalc; + + void process_mobmax(CHAR_DATA *ch, CHAR_DATA *killer) { bool leader_partner = false; @@ -331,7 +335,8 @@ bool check_tester_death(CHAR_DATA *ch, CHAR_DATA *killer) void die(CHAR_DATA *ch, CHAR_DATA *killer) { - int dec_exp = 0, e = GET_EXP(ch); + int dec_exp = 0; + long currentExp = GET_EXP(ch); if (!IS_NPC(ch) && (ch->in_room == NOWHERE)) { @@ -362,23 +367,16 @@ void die(CHAR_DATA *ch, CHAR_DATA *killer) return; } - if (IS_NPC(ch) - || !ROOM_FLAGGED(ch->in_room, ROOM_ARENA) - || RENTABLE(ch)) - { + if (IS_NPC(ch) || !ROOM_FLAGGED(ch->in_room, ROOM_ARENA) || RENTABLE(ch)) { if (!(IS_NPC(ch) || IS_IMMORTAL(ch) || GET_GOD_FLAG(ch, GF_GODSLIKE) || (killer && PRF_FLAGGED(killer, PRF_EXECUTOR))))//если убил не палач { - if (!RENTABLE(ch)) - dec_exp = (level_exp(ch, GET_LEVEL(ch) + 1) - level_exp(ch, GET_LEVEL(ch))) / (3 + MIN(3, GET_REMORT(ch) / 5)) / ch->death_player_count(); - else - dec_exp = (level_exp(ch, GET_LEVEL(ch) + 1) - level_exp(ch, GET_LEVEL(ch))) / (3 + MIN(3, GET_REMORT(ch) / 5)); - gain_exp(ch, -dec_exp); - dec_exp = e - GET_EXP(ch); - sprintf(buf, "Вы потеряли %d %s опыта.\r\n", - dec_exp, desc_count(dec_exp, WHAT_POINT)); + dec_exp = calcDeathExp(ch); + ExpCalc::gain_exp(ch, -dec_exp); + dec_exp = (int)(currentExp - GET_EXP(ch)); + sprintf(buf, "Вы потеряли %d %s опыта.\r\n", dec_exp, desc_count(dec_exp, WHAT_POINT)); send_to_char(buf, ch); } @@ -794,215 +792,6 @@ void raw_kill(CHAR_DATA *ch, CHAR_DATA *killer) } } -int get_remort_mobmax(CHAR_DATA * ch) -{ - int remort = GET_REMORT(ch); - if (remort >= 18) - return 15; - if (remort >= 14) - return 7; - if (remort >= 9) - return 4; - return 0; -} - -float get_npc_long_live_exp_bounus(int vnum) -{ - if (vnum == -1) { - return 1.0f; - } - int nowTime = time(0); - int lastKillTime = mob_stat::last_time_killed_mob(vnum); - if ( lastKillTime > 0) { - int deltaTime = nowTime - lastKillTime; - int delay = 60 * 60 * 24 * 30 ; // 30 days - float timeMultiplicator = floor(deltaTime/delay); - if (timeMultiplicator<1.0f) { - timeMultiplicator = 1.0f; - } - if (timeMultiplicator>10.0f) { - timeMultiplicator = 10.0f; - } - return timeMultiplicator; - } - return 1.0f; -} - -int get_extend_exp(int exp, CHAR_DATA * ch, CHAR_DATA * victim) -{ - int base, diff; - int koef; - - if (!IS_NPC(victim) || IS_NPC(ch)) - return (exp); - // если моб убивается первый раз, то повышаем экспу в несколько раз - // стимулируем изучение новых зон! - ch->send_to_TC(false, true, false, - "&RУ моба еще %d убийств без замакса, экспа %d, убито %d&n\r\n", - mob_proto[victim->get_rnum()].mob_specials.MaxFactor, - exp, - ch->mobmax_get(GET_MOB_VNUM(victim))); -// все равно таблица корявая, учитываются только уникальные мобы и глючит -/* - // даем увеличенную экспу за давно не убитых мобов. - // за совсем неубитых мобов не даем, что бы новые зоны не давали x10 экспу. - exp *= get_npc_long_live_exp_bounus(GET_MOB_VNUM(victim)); -*/ -/* бонусы за непопулярных мобов круче - if (ch->mobmax_get(GET_MOB_VNUM(victim)) == 0) { - // так чуть-чуть поприятней - exp *= 1.5; - exp /= std::max(1.0, 0.5 * (GET_REMORT(ch) - MAX_EXP_COEFFICIENTS_USED)); - return (exp); - } -*/ - exp += exp * (ch->add_abils.percent_exp_add) / 100.0; - for (koef = 100, base = 0, diff = ch->mobmax_get(GET_MOB_VNUM(victim)) - mob_proto[victim->get_rnum()].mob_specials.MaxFactor; - base < diff && koef > 5; base++, koef = koef * (95 - get_remort_mobmax(ch)) / 100); - // минимальный опыт при замаксе 15% от полного опыта - exp = exp * MAX(15, koef) / 100; - - // делим на реморты - exp /= std::max(1.0, 0.5 * (GET_REMORT(ch) - MAX_EXP_COEFFICIENTS_USED)); - return (exp); -} - -// When ch kills victim -void change_alignment(CHAR_DATA * ch, CHAR_DATA * victim) -{ - /* - * new alignment change algorithm: if you kill a monster with alignment A, - * you move 1/16th of the way to having alignment -A. Simple and fast. - */ - GET_ALIGNMENT(ch) += (-GET_ALIGNMENT(victim) - GET_ALIGNMENT(ch)) / 16; -} - -/*++ - Функция начисления опыта - ch - кому опыт начислять - Вызов этой функции для NPC смысла не имеет, но все равно - какие-то проверки внутри зачем то делаются ---*/ -void perform_group_gain(CHAR_DATA * ch, CHAR_DATA * victim, int members, int koef) -{ - - -// Странно, но для NPC эта функция тоже должна работать -// if (IS_NPC(ch) || !OK_GAIN_EXP(ch,victim)) - if (!OK_GAIN_EXP(ch, victim)) - { - send_to_char("Ваше деяние никто не оценил.\r\n", ch); - return; - } - - // 1. Опыт делится поровну на всех - int exp = GET_EXP(victim) / MAX(members, 1); - - if(victim->get_zone_group() > 1 && members < victim->get_zone_group()) - { - // в случае груп-зоны своего рода планка на мин кол-во человек в группе - exp = GET_EXP(victim) / victim->get_zone_group(); - } - - // 2. Учитывается коэффициент (лидерство, разность уровней) - // На мой взгляд его правильней использовать тут а не в конце процедуры, - // хотя в большинстве случаев это все равно - exp = exp * koef / 100; - - // 3. Вычисление опыта для PC и NPC - if (IS_NPC(ch)) - { - exp = MIN(max_exp_gain_npc, exp); - exp += MAX(0, (exp * MIN(4, (GET_LEVEL(victim) - GET_LEVEL(ch)))) / 8); - } - else - exp = MIN(max_exp_gain_pc(ch), get_extend_exp(exp, ch, victim)); - // 4. Последняя проверка - exp = MAX(1, exp); - if (exp > 1) - { - if (Bonus::is_bonus(Bonus::BONUS_EXP)) - { - exp *= Bonus::get_mult_bonus(); - } - - if (!IS_NPC(ch) && !ch->affected.empty()) - { - for (const auto aff : ch->affected) - { - if (aff->location == APPLY_BONUS_EXP) // скушал свиток с эксп бонусом - { - exp *= MIN(3, aff->modifier); // бонус макс тройной - } - } - } - - int long_live_bonus = floor(get_npc_long_live_exp_bounus( GET_MOB_VNUM(victim))); - if (long_live_bonus>1) { - std::string mess = ""; - switch (long_live_bonus) - { - case 2: - mess = "Редкая удача! Опыт повышен!\r\n"; - break; - case 3: - mess = "Очень редкая удача! Опыт повышен!\r\n"; - break; - case 4: - mess = "Очень-очень редкая удача! Опыт повышен!\r\n"; - break; - case 5: - mess = "Вы везунчик! Опыт повышен!\r\n"; - break; - case 6: - mess = "Ваша удача велика! Опыт повышен!\r\n"; - break; - case 7: - mess = "Ваша удача достигла небес! Опыт повышен!\r\n"; - break; - case 8: - mess = "Ваша удача коснулась луны! Опыт повышен!\r\n"; - break; - case 9: - mess = "Ваша удача затмевает солнце! Опыт повышен!\r\n"; - break; - case 10: - mess = "Ваша удача выше звезд! Опыт повышен!\r\n"; - break; - default: - mess = "Ваша удача выше звезд! Опыт повышен!\r\n"; - break; - } - send_to_char(mess.c_str(), ch); - } - - exp = MIN(max_exp_gain_pc(ch), exp); - send_to_char(ch, "Ваш опыт повысился на %d %s.\r\n", exp, desc_count(exp, WHAT_POINT)); - } - else if (exp == 1) { - send_to_char("Ваш опыт повысился всего лишь на маленькую единичку.\r\n", ch); - } - gain_exp(ch, exp); - change_alignment(ch, victim); - TopPlayer::Refresh(ch); - - if (!EXTRA_FLAGGED(victim, EXTRA_GRP_KILL_COUNT) - && !IS_NPC(ch) - && !IS_IMMORTAL(ch) - && IS_NPC(victim) - && !IS_CHARMICE(victim) - && !ROOM_FLAGGED(IN_ROOM(victim), ROOM_ARENA)) - { - mob_stat::add_mob(victim, members); - EXTRA_FLAGS(victim).set(EXTRA_GRP_KILL_COUNT); - } - else if (IS_NPC(ch) && !IS_NPC(victim) - && !ROOM_FLAGGED(IN_ROOM(victim), ROOM_ARENA)) - { - mob_stat::add_mob(ch, 0); - } -} - int grouping_koef(int player_class, int player_remort) { @@ -1012,138 +801,6 @@ int grouping_koef(int player_class, int player_remort) } - -/*++ - Функция расчитывает всякие бонусы для группы при получении опыта, - после чего вызывает функцию получения опыта для всех членов группы - Т.к. членом группы может быть только PC, то эта функция раздаст опыт только PC - - ch - обязательно член группы, из чего следует: - 1. Это не NPC - 2. Он находится в группе лидера (или сам лидер) - - Просто для PC-последователей эта функция не вызывается - ---*/ -void group_gain(CHAR_DATA * killer, CHAR_DATA * victim) -{ - int inroom_members, koef = 100, maxlevel; - struct follow_type *f; - int partner_count = 0; - int total_group_members = 1; - bool use_partner_exp = false; - - // если наем лидер, то тоже режем экспу - if (can_use_feat(killer, CYNIC_FEAT)) - { - maxlevel = 300; - } - else - { - maxlevel = GET_LEVEL(killer); - } - - auto leader = killer->get_master(); - if (nullptr == leader) - { - leader = killer; - } - - // k - подозрение на лидера группы - const bool leader_inroom = AFF_FLAGGED(leader, EAffectFlag::AFF_GROUP) - && leader->in_room == IN_ROOM(killer); - - // Количество согрупников в комнате - if (leader_inroom) - { - inroom_members = 1; - maxlevel = GET_LEVEL(leader); - } - else - { - inroom_members = 0; - } - - // Вычисляем максимальный уровень в группе - for (f = leader->followers; f; f = f->next) - { - if (AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP)) ++total_group_members; - if (AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP) - && f->follower->in_room == IN_ROOM(killer)) - { - // если в группе наем, то режим опыт всей группе - // дабы наема не выгодно было бы брать в группу - // ставим 300, чтобы вообще под ноль резало - if (can_use_feat(f->follower, CYNIC_FEAT)) - { - maxlevel = 300; - } - // просмотр членов группы в той же комнате - // член группы => PC автоматически - ++inroom_members; - maxlevel = MAX(maxlevel, GET_LEVEL(f->follower)); - if (!IS_NPC(f->follower)) - { - partner_count++; - } - } - } - - GroupPenaltyCalculator group_penalty(killer, leader, maxlevel, grouping); - koef -= group_penalty.get(); - - koef = MAX(0, koef); - - // Лидерство используется, если в комнате лидер и есть еще хоть кто-то - // из группы из PC (последователи типа лошади или чармисов не считаются) - if (koef >= 100 && leader_inroom && (inroom_members > 1) && calc_leadership(leader)) - { - koef += 20; - } - - // Раздача опыта - - // если групповой уровень зоны равняется единице - if (zone_table[world[killer->in_room]->zone].group < 2) - { - // чтобы не абьюзили на суммонах, когда в группе на самом деле больше - // двух мемберов, но лишних реколят перед непосредственным рипом - use_partner_exp = total_group_members == 2; - } - - // если лидер группы в комнате - if (leader_inroom) - { - // если у лидера группы есть способность напарник - if (can_use_feat(leader, PARTNER_FEAT) && use_partner_exp) - { - // если в группе всего двое человек - // k - лидер, и один последователь - if (partner_count == 1) - { - // и если кожф. больше или равен 100 - if (koef >= 100) - { - if (leader->get_zone_group() < 2) - { - koef += 100; - } - } - } - } - perform_group_gain(leader, victim, inroom_members, koef); - } - - for (f = leader->followers; f; f = f->next) - { - if (AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP) - && f->follower->in_room == IN_ROOM(killer)) - { - perform_group_gain(f->follower, victim, inroom_members, koef); - } - } -} - void gain_battle_exp(CHAR_DATA *ch, CHAR_DATA *victim, int dam) { // не даем получать батлу с себя по зеркалу? diff --git a/src/find.obj.id.by.vnum.cpp b/src/find.obj.id.by.vnum.cpp index 38e0ee05a..d8fb0299b 100644 --- a/src/find.obj.id.by.vnum.cpp +++ b/src/find.obj.id.by.vnum.cpp @@ -1,7 +1,7 @@ #include "find.obj.id.by.vnum.hpp" #include "logger.hpp" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "world.objects.hpp" #include "chars/char.hpp" #include "obj.hpp" diff --git a/src/glory.cpp b/src/glory.cpp index f6b2146a0..34b09e88c 100644 --- a/src/glory.cpp +++ b/src/glory.cpp @@ -24,7 +24,6 @@ #include extern void add_karma(CHAR_DATA * ch, const char * punish , const char * reason); -extern void check_max_hp(CHAR_DATA *ch); namespace Glory { diff --git a/src/glory_const.cpp b/src/glory_const.cpp index 37f5eb3ce..b328873a0 100644 --- a/src/glory_const.cpp +++ b/src/glory_const.cpp @@ -30,7 +30,6 @@ #include extern void add_karma(CHAR_DATA * ch, const char * punish , const char * reason); -extern void check_max_hp(CHAR_DATA *ch); namespace GloryConst { diff --git a/src/grp/follow.cpp b/src/grp/follow.cpp index b890a439b..657ad8026 100644 --- a/src/grp/follow.cpp +++ b/src/grp/follow.cpp @@ -45,61 +45,46 @@ bool stop_follower(CHAR_DATA * ch, int mode) } //log("[Stop follower] Remove from followers list"); - if (!ch->get_master()->followers) - { + if (!ch->get_master()->followers) { log("[Stop follower] SYSERR: Followers absent for %s (master %s).", GET_NAME(ch), GET_NAME(ch->get_master())); - } - else if (ch->get_master()->followers->follower == ch) // Head of follower-list? - { + } else if (ch->get_master()->followers->follower == ch) { k = ch->get_master()->followers; ch->get_master()->followers = k->next; - if (!ch->get_master()->followers - && !ch->get_master()->has_master()) - { - //AFF_FLAGS(ch->get_master()).unset(EAffectFlag::AFF_GROUP); + + if (!ch->get_master()->followers && !ch->get_master()->has_master()) { ch->get_master()->removeGroupFlags(); } free(k); } - else // locate follower who is not head of list - { + else { // locate follower who is not head of list for (k = ch->get_master()->followers; k->next && k->next->follower != ch; k = k->next); - if (!k->next) - { - log("[Stop follower] SYSERR: Undefined %s in %s followers list.", GET_NAME(ch), GET_NAME(ch->get_master())); - } - else - { - j = k->next; - k->next = j->next; - free(j); - } + if (!k->next) { + log("[Stop follower] SYSERR: Undefined %s in %s followers list.", GET_NAME(ch), GET_NAME(ch->get_master())); + } + else { + j = k->next; + k->next = j->next; + free(j); + } } ch->set_master(nullptr); - //AFF_FLAGS(ch).unset(EAffectFlag::AFF_GROUP); ch->removeGroupFlags(); - if (AFF_FLAGGED(ch, EAffectFlag::AFF_CHARM) - || AFF_FLAGGED(ch, EAffectFlag::AFF_HELPER) - || IS_SET(mode, SF_CHARMLOST)) - { - if (affected_by_spell(ch, SPELL_CHARM)) - { + if (IS_CHARMICE(ch)|| IS_SET(mode, SF_CHARMLOST)) { + if (affected_by_spell(ch, SPELL_CHARM)) { affect_from_char(ch, SPELL_CHARM); } EXTRACT_TIMER(ch) = 5; + AFF_FLAGS(ch).unset(EAffectFlag::AFF_CHARM); - if (ch->get_fighting()) - { + if (ch->get_fighting()) { stop_fighting(ch, TRUE); } - if (IS_NPC(ch)) - { - if (MOB_FLAGGED(ch, MOB_PLAYER_SUMMON)) - { + if (IS_NPC(ch)) { + if (MOB_FLAGGED(ch, MOB_PLAYER_SUMMON)) { act("Налетевший ветер развеял $n3, не оставив и следа.", TRUE, ch, 0, 0, TO_ROOM | TO_ARENA_LISTEN); GET_LASTROOM(ch) = GET_ROOM_VNUM(ch->in_room); perform_drop_gold(ch, ch->get_gold()); @@ -107,8 +92,7 @@ bool stop_follower(CHAR_DATA * ch, int mode) extract_char(ch, FALSE); return (TRUE); } - else if (AFF_FLAGGED(ch, EAffectFlag::AFF_HELPER)) - { + else if (AFF_FLAGGED(ch, EAffectFlag::AFF_HELPER)) { AFF_FLAGS(ch).unset(EAffectFlag::AFF_HELPER); } } diff --git a/src/grp/grouppenalties.cpp b/src/grp/grouppenalties.cpp new file mode 100644 index 000000000..c3d9f49d1 --- /dev/null +++ b/src/grp/grouppenalties.cpp @@ -0,0 +1,111 @@ +/* ************************************************************************ +* File: GroupPenalties.cpp Part of Bylins * +* Класс расчета всяких штрафов при вступлении в группу * +* * +* All rights reserved. See license.doc for complete information. * +* * +* Copyright (C) 1993, 94 by the Trustees of the Johns Hopkins University * +* CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991. * +* * +************************************************************************ */ + +#include "grp.main.h" + +#include "chars/char_player.hpp" +#include "db.h" +#include "logger.hpp" +#include "structs.h" +#include "sysdep.h" +#include "utils.h" + +/* + Берет misc/grouping, первый столбик цифр считает номерами мортов, + остальные столбики - значение макс. разрыва в уровнях для конкретного + класса. На момент написания этого в конфиге присутствует 26 строк, макс. + морт равен 50 - строки с мортами с 26 по 50 копируются с 25-мортовой строки. +*/ +int GroupPenalties::init() +{ + int clss = 0, remorts = 0, rows_assigned = 0, levels = 0, pos = 0, max_rows = MAX_REMORT+1; + + // пре-инициализация + for (remorts = 0; remorts < max_rows; remorts++) //Строк в массиве должно быть на 1 больше, чем макс. морт + { + for (clss = 0; clss < NUM_PLAYER_CLASSES; clss++) //Столбцов в массиве должно быть ровно столько же, сколько есть классов + { + m_grouping[clss][remorts] = -1; + } + } + + FILE* f = fopen(LIB_MISC "grouping", "r"); + if (!f) + { + log("Невозможно открыть файл %s", LIB_MISC "grouping"); + return 1; + } + + while (get_line(f, buf)) + { + if (!buf[0] || buf[0] == ';' || buf[0] == '\n') //Строка пустая или строка-коммент + { + continue; + } + clss = 0; + pos = 0; + while (sscanf(&buf[pos], "%d", &levels) == 1) + { + while (buf[pos] == ' ' || buf[pos] == '\t') + { + pos++; + } + if (clss == 0) //Первый проход цикла по строке + { + remorts = levels; //Номера строк + if (m_grouping[0][remorts] != -1) + { + log("Ошибка при чтении файла %s: дублирование параметров для %d ремортов", + LIB_MISC "grouping", remorts); + return 2; + } + if (remorts > MAX_REMORT || remorts < 0) + { + log("Ошибка при чтении файла %s: неверное значение количества ремортов: %d, " + "должно быть в промежутке от 0 до %d", + LIB_MISC "grouping", remorts, MAX_REMORT); + return 3; + } + } + else + { + m_grouping[clss - 1][remorts] = levels; // -1 потому что в массиве нет столбца с кол-вом мортов + } + clss++; //+Номер столбца массива + while (buf[pos] != ' ' && buf[pos] != '\t' && buf[pos] != 0) + { + pos++; //Ищем следующее число в строке конфига + } + } + if (clss != NUM_PLAYER_CLASSES+1) + { + log("Ошибка при чтении файла %s: неверный формат строки '%s', должно быть %d " + "целых чисел, прочитали %d", LIB_MISC "grouping", buf, NUM_PLAYER_CLASSES+1, clss); + return 4; + } + rows_assigned++; + } + + if (rows_assigned < max_rows) + { + for (levels = remorts; levels < max_rows; levels++) //Берем свободную переменную + { + for (clss = 0; clss < NUM_PLAYER_CLASSES; clss++) + { + m_grouping[clss][levels] = m_grouping[clss][remorts]; //Копируем последнюю строку на все морты, для которых нет строк + } + } + } + fclose(f); + return 0; +} + +GroupPenalties grouping; ///< TODO: get rid of this global variable. diff --git a/src/grp/grouppenaltycalculator.cpp b/src/grp/grouppenaltycalculator.cpp new file mode 100644 index 000000000..2d1363165 --- /dev/null +++ b/src/grp/grouppenaltycalculator.cpp @@ -0,0 +1,90 @@ +#include "grp.main.h" + +#include "logger.hpp" +#include "utils.h" + +int GroupPenaltyCalculator::get() const +{ + const bool leader_is_npc = IS_NPC(m_leader); + const bool leader_in_group = AFF_FLAGGED(m_leader, EAffectFlag::AFF_GROUP); + const bool leader_is_in_room = leader_in_group + && m_leader->in_room == IN_ROOM(m_killer); + if (!leader_is_npc + && leader_is_in_room) + { + int penalty = 0; + if (penalty_by_leader(m_leader, penalty)) + { + return penalty; + } + } + + for (auto f = m_leader->followers; f; f = f->next) + { + const bool follower_is_npc = IS_NPC(f->follower); + const bool follower_is_in_room = AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP) + && f->follower->in_room == IN_ROOM(m_killer); + + if (follower_is_npc + || !follower_is_in_room) + { + continue; + } + + int penalty = 0; + if (penalty_by_leader(f->follower, penalty)) + { + if (0 < penalty) + { + return penalty; + } + } + } + + return 0; +} + +bool GroupPenaltyCalculator::penalty_by_leader(const CHAR_DATA* player, int& penalty) const +{ + const int player_remorts = static_cast(GET_REMORT(player)); + const int player_class = static_cast(GET_CLASS(player)); + const int player_level = GET_LEVEL(player); + + if (IS_NPC(player)) + { + log("LOGIC ERROR: try to get penalty for NPC [%s], VNum: %d\n", + player->get_name().c_str(), + GET_MOB_VNUM(player)); + penalty = DEFAULT_PENALTY; + return true; + } + + if (0 > player_class + || player_class > NUM_PLAYER_CLASSES) + { + log("LOGIC ERROR: wrong player class: %d for player [%s]", + player_class, + player->get_name().c_str()); + penalty = DEFAULT_PENALTY; + return true; + } + + if (0 > player_remorts + || player_remorts > MAX_REMORT) + { + log("LOGIC ERROR: wrong number of remorts: %d for player [%s]", + player_remorts, + player->get_name().c_str()); + penalty = DEFAULT_PENALTY; + return true; + } + + bool result = false; + if (m_max_level - player_level > m_grouping[player_class][player_remorts]) + { + penalty = 50 + 2 * (m_max_level - player_level - m_grouping[player_class][player_remorts]); + result = true; + } + + return result; +} \ No newline at end of file diff --git a/src/grp/grp.group.cpp b/src/grp/grp.group.cpp index e11288a12..acb3c43a6 100644 --- a/src/grp/grp.group.cpp +++ b/src/grp/grp.group.cpp @@ -1,13 +1,33 @@ +/* ************************************************************************ +* File: grp.main.h Part of Bylins * +* Методы и функции работы с группами персонажей * +* * +* All rights reserved. See license.doc for complete information. * +* * +* Copyright (C) 1993, 94 by the Trustees of the Johns Hopkins University * +* CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991. * +* * +************************************************************************ */ + #include #include "grp.main.h" +#include "bonus.h" #include "comm.h" #include "msdp.constants.hpp" #include "screen.h" +#include "core/leveling.h" #include "magic.h" +#include "mob_stat.hpp" +#include "top.h" +#include "zone.table.hpp" + +using namespace ExpCalc; extern GroupRoster& groupRoster; +extern int max_exp_gain_npc; +extern zone_table_t& zone_table; const char *WORD_STATE[] = { "При смерти", "Оч.тяж.ран", @@ -628,13 +648,18 @@ CHAR_DATA* Group::get_random_pc_group() { } // room_rnum = 0 - ignore -cd_v Group::getMembers(rnum_t room_rnum) { +cd_v Group::getMembers(rnum_t room_rnum, bool includeCharmee) { cd_v retval; for (const auto& it : *_memberList) { auto c = (*it.second)->member; if (room_rnum == (room_rnum == 0 ? room_rnum : c->in_room) && !c->purged()) retval.push_back((*it.second)->member); } + if (includeCharmee) + for (const auto c : *_npcRoster) { + if (room_rnum == (room_rnum == 0 ? room_rnum : c->in_room) && !c->purged()) + retval.push_back(c); + } return retval; } @@ -681,3 +706,292 @@ bool same_group(CHAR_DATA * ch, CHAR_DATA * tch) return true; return false; } + + +/*++ + Функция начисления опыта + ch - кому опыт начислять + Вызов этой функции для NPC смысла не имеет, но все равно + какие-то проверки внутри зачем то делаются +--*/ +void perform_group_gain(CHAR_DATA * ch, CHAR_DATA * victim, int members, int koef) +{ + + +// Странно, но для NPC эта функция тоже должна работать +// if (IS_NPC(ch) || !OK_GAIN_EXP(ch,victim)) + if (!OK_GAIN_EXP(ch, victim)) + { + send_to_char("Ваше деяние никто не оценил.\r\n", ch); + return; + } + + // 1. Опыт делится поровну на всех + int exp = GET_EXP(victim) / MAX(members, 1); + + if(victim->get_zone_group() > 1 && members < victim->get_zone_group()) + { + // в случае груп-зоны своего рода планка на мин кол-во человек в группе + exp = GET_EXP(victim) / victim->get_zone_group(); + } + + // 2. Учитывается коэффициент (лидерство, разность уровней) + // На мой взгляд его правильней использовать тут а не в конце процедуры, + // хотя в большинстве случаев это все равно + exp = exp * koef / 100; + + // 3. Вычисление опыта для PC и NPC + if (IS_NPC(ch)) + { + exp = MIN(max_exp_gain_npc, exp); + exp += MAX(0, (exp * MIN(4, (GET_LEVEL(victim) - GET_LEVEL(ch)))) / 8); + } + else + exp = MIN(max_exp_gain_pc(ch), get_extend_exp(exp, ch, victim)); + // 4. Последняя проверка + exp = MAX(1, exp); + if (exp > 1) + { + if (Bonus::is_bonus(Bonus::BONUS_EXP)) + { + exp *= Bonus::get_mult_bonus(); + } + + if (!IS_NPC(ch) && !ch->affected.empty()) + { + for (const auto aff : ch->affected) + { + if (aff->location == APPLY_BONUS_EXP) // скушал свиток с эксп бонусом + { + exp *= MIN(3, aff->modifier); // бонус макс тройной + } + } + } + + int long_live_bonus = floor(ExpCalc::get_npc_long_live_exp_bounus( GET_MOB_VNUM(victim))); + if (long_live_bonus>1) { + std::string mess = ""; + switch (long_live_bonus) + { + case 2: + mess = "Редкая удача! Опыт повышен!\r\n"; + break; + case 3: + mess = "Очень редкая удача! Опыт повышен!\r\n"; + break; + case 4: + mess = "Очень-очень редкая удача! Опыт повышен!\r\n"; + break; + case 5: + mess = "Вы везунчик! Опыт повышен!\r\n"; + break; + case 6: + mess = "Ваша удача велика! Опыт повышен!\r\n"; + break; + case 7: + mess = "Ваша удача достигла небес! Опыт повышен!\r\n"; + break; + case 8: + mess = "Ваша удача коснулась луны! Опыт повышен!\r\n"; + break; + case 9: + mess = "Ваша удача затмевает солнце! Опыт повышен!\r\n"; + break; + case 10: + mess = "Ваша удача выше звезд! Опыт повышен!\r\n"; + break; + default: + mess = "Ваша удача выше звезд! Опыт повышен!\r\n"; + break; + } + send_to_char(mess.c_str(), ch); + } + + exp = MIN(max_exp_gain_pc(ch), exp); + send_to_char(ch, "Ваш опыт повысился на %d %s.\r\n", exp, desc_count(exp, WHAT_POINT)); + } + else if (exp == 1) { + send_to_char("Ваш опыт повысился всего лишь на маленькую единичку.\r\n", ch); + } + gain_exp(ch, exp); + GET_ALIGNMENT(ch) += (-GET_ALIGNMENT(victim) - GET_ALIGNMENT(ch)) / 16; + TopPlayer::Refresh(ch); + + if (!EXTRA_FLAGGED(victim, EXTRA_GRP_KILL_COUNT) + && !IS_NPC(ch) + && !IS_IMMORTAL(ch) + && IS_NPC(victim) + && !IS_CHARMICE(victim) + && !ROOM_FLAGGED(IN_ROOM(victim), ROOM_ARENA)) + { + mob_stat::add_mob(victim, members); + EXTRA_FLAGS(victim).set(EXTRA_GRP_KILL_COUNT); + } + else if (IS_NPC(ch) && !IS_NPC(victim) + && !ROOM_FLAGGED(IN_ROOM(victim), ROOM_ARENA)) + { + mob_stat::add_mob(ch, 0); + } +} + +/*++ + Функция расчитывает всякие бонусы для группы при получении опыта, + после чего вызывает функцию получения опыта для всех членов группы + Т.к. членом группы может быть только PC, то эта функция раздаст опыт только PC + + ch - обязательно член группы, из чего следует: + 1. Это не NPC + 2. Он находится в группе лидера (или сам лидер) + + Просто для PC-последователей эта функция не вызывается + +--*/ + +void group_gain(CHAR_DATA * killer, CHAR_DATA * victim) +{ + int inroom_members, koef = 100, maxlevel; + struct follow_type *f; + int partner_count = 0; + int total_group_members = 1; + bool use_partner_exp = false; + + // если наем лидер, то тоже режем экспу + if (can_use_feat(killer, CYNIC_FEAT)) + { + maxlevel = 300; + } + else + { + maxlevel = GET_LEVEL(killer); + } + + auto leader = killer->get_master(); + if (nullptr == leader) + { + leader = killer; + } + + // k - подозрение на лидера группы + const bool leader_inroom = AFF_FLAGGED(leader, EAffectFlag::AFF_GROUP) + && leader->in_room == IN_ROOM(killer); + + // Количество согрупников в комнате + if (leader_inroom) + { + inroom_members = 1; + maxlevel = GET_LEVEL(leader); + } + else + { + inroom_members = 0; + } + + // Вычисляем максимальный уровень в группе + for (f = leader->followers; f; f = f->next) + { + if (AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP)) ++total_group_members; + if (AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP) + && f->follower->in_room == IN_ROOM(killer)) + { + // если в группе наем, то режим опыт всей группе + // дабы наема не выгодно было бы брать в группу + // ставим 300, чтобы вообще под ноль резало + if (can_use_feat(f->follower, CYNIC_FEAT)) + { + maxlevel = 300; + } + // просмотр членов группы в той же комнате + // член группы => PC автоматически + ++inroom_members; + maxlevel = MAX(maxlevel, GET_LEVEL(f->follower)); + if (!IS_NPC(f->follower)) + { + partner_count++; + } + } + } + + GroupPenaltyCalculator group_penalty(killer, leader, maxlevel, grouping); + koef -= group_penalty.get(); + + koef = MAX(0, koef); + + // Лидерство используется, если в комнате лидер и есть еще хоть кто-то + // из группы из PC (последователи типа лошади или чармисов не считаются) + if (koef >= 100 && leader_inroom && (inroom_members > 1) && calc_leadership(leader)) + { + koef += 20; + } + + // Раздача опыта + + // если групповой уровень зоны равняется единице + if (zone_table[world[killer->in_room]->zone].group < 2) + { + // чтобы не абьюзили на суммонах, когда в группе на самом деле больше + // двух мемберов, но лишних реколят перед непосредственным рипом + use_partner_exp = total_group_members == 2; + } + + // если лидер группы в комнате + if (leader_inroom) + { + // если у лидера группы есть способность напарник + if (can_use_feat(leader, PARTNER_FEAT) && use_partner_exp) + { + // если в группе всего двое человек + // k - лидер, и один последователь + if (partner_count == 1) + { + // и если кожф. больше или равен 100 + if (koef >= 100) + { + if (leader->get_zone_group() < 2) + { + koef += 100; + } + } + } + } + perform_group_gain(leader, victim, inroom_members, koef); + } + + for (f = leader->followers; f; f = f->next) + { + if (AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP) + && f->follower->in_room == IN_ROOM(killer)) + { + perform_group_gain(f->follower, victim, inroom_members, koef); + } + } +} + + +int calc_leadership(CHAR_DATA * ch) +{ + int prob, percent; + + if (IS_NPC(ch) || ch->personGroup == nullptr) { + return FALSE; + } + + CHAR_DATA* leader = ch->personGroup->getLeader(); + + // если лидер умер или нет в комнате - фиг вам, а не бонусы + if (leader == nullptr || IN_ROOM(ch) != IN_ROOM(leader)) { + return FALSE; + } + + if (!leader->get_skill(SKILL_LEADERSHIP)) { + return (FALSE); + } + + percent = number(1, 101); + prob = calculate_skill(leader, SKILL_LEADERSHIP, 0); + if (percent > prob) { + return (FALSE); + } + else { + return (TRUE); + } +} diff --git a/src/grp/grp.main.h b/src/grp/grp.main.h index df9215170..edf8561a9 100644 --- a/src/grp/grp.main.h +++ b/src/grp/grp.main.h @@ -1,3 +1,14 @@ +/* ************************************************************************ +* File: grp.main.h Part of Bylins * +* Заголовок всех функций и классов работы с группами * +* * +* All rights reserved. See license.doc for complete information. * +* * +* Copyright (C) 1993, 94 by the Trustees of the Johns Hopkins University * +* CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991. * +* * +************************************************************************ */ + #ifndef BYLINS_GRP_MAIN_H #define BYLINS_GRP_MAIN_H @@ -19,6 +30,7 @@ void do_ungroup(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/); void do_report(CHAR_DATA *ch, char* /*argument*/, int/* cmd*/, int/* subcmd*/); int max_group_size(CHAR_DATA *ch); bool isGroupedFollower(CHAR_DATA* master, CHAR_DATA* vict); +int calc_leadership(CHAR_DATA * ch); class Group; class Request; @@ -99,7 +111,7 @@ class Group { void sendToGroup(GRP_COMM mode, const char *msg, ...); void actToGroup(CHAR_DATA* vict, GRP_COMM mode, const char *msg, ...); - cd_v getMembers(rnum_t room_rnum = 0); + cd_v getMembers(rnum_t room_rnum = 0, bool includeCharmee = false); npc_r getCharmee(rnum_t room_rnum = 0); public: // всякий унаследованный стафф @@ -107,6 +119,7 @@ class Group { // лень обвязывать, тупо переместил объект DpsSystem::GroupListType _group_dps; bool has_clan_members_in_group(CHAR_DATA *victim); + }; class Request { @@ -153,4 +166,42 @@ class GroupRoster { Request* findRequest(const char* targetPerson, const char* group, RQ_TYPE type); }; +class GroupPenalties +{ +public: + using class_penalties_t = std::array; + using penalties_t = std::array; + + int init(); + const auto& operator[](const size_t character_class) const { return m_grouping[character_class]; } + +private: + penalties_t m_grouping; +}; + +class GroupPenaltyCalculator +{ +public: + constexpr static int DEFAULT_PENALTY = 100; + + GroupPenaltyCalculator(const CHAR_DATA* killer, const CHAR_DATA* leader, const int max_level, const GroupPenalties& grouping): + m_killer(killer), + m_leader(leader), + m_max_level(max_level), + m_grouping(grouping) + { + } + + int get() const; + +private: + const CHAR_DATA* m_killer; + const CHAR_DATA* m_leader; + const int m_max_level; + const GroupPenalties& m_grouping; + + bool penalty_by_leader(const CHAR_DATA* player, int& penalty) const; +}; + +extern GroupPenalties grouping; #endif //BYLINS_GRP_MAIN_H diff --git a/src/handler.cpp b/src/handler.cpp index d2e876e78..5b776c610 100644 --- a/src/handler.cpp +++ b/src/handler.cpp @@ -25,8 +25,8 @@ #include "spells.h" #include "skills.h" #include "screen.h" -#include "dg_db_scripts.hpp" -#include "dg_scripts.h" +#include "dg/dg_db_scripts.hpp" +#include "dg/dg_scripts.h" #include "auction.h" #include "features.hpp" #include "house.h" diff --git a/src/heartbeat.cpp b/src/heartbeat.cpp index af6d9327a..2aa35e6b4 100644 --- a/src/heartbeat.cpp +++ b/src/heartbeat.cpp @@ -25,7 +25,7 @@ #include "objsave.h" #include "limits.hpp" #include "fightsystem/mobact.hpp" -#include "dg_event.h" +#include "dg/dg_event.h" #include "corpse.hpp" #include "chars/char.hpp" #include "shutdown.parameters.hpp" diff --git a/src/house_exp.cpp b/src/house_exp.cpp index ff8e8170c..35f009253 100644 --- a/src/house_exp.cpp +++ b/src/house_exp.cpp @@ -20,6 +20,28 @@ #include #include +void update_clan_exp(CHAR_DATA *ch, int gain) +{ + if (CLAN(ch) && gain != 0) + { + // экспа для уровня клана (+ только на праве, - любой, но /5) + if (gain < 0 || GET_GOD_FLAG(ch, GF_REMORT)) + { + int tmp = gain > 0 ? gain : gain / 5; + CLAN(ch)->SetClanExp(ch, tmp); + } + // экспа для топа кланов за месяц (учитываются все + и -) + CLAN(ch)->last_exp.add_temp(gain); + // экспа для топа кланов за все время (учитываются все + и -) + CLAN(ch)->AddTopExp(ch, gain); + // экспа для авто-очистки кланов (учитываются только +) + if (gain > 0) + { + CLAN(ch)->exp_history.add_exp(gain); + } + } +} + namespace { diff --git a/src/house_exp.hpp b/src/house_exp.hpp index 7f157726a..db634a7ca 100644 --- a/src/house_exp.hpp +++ b/src/house_exp.hpp @@ -13,6 +13,7 @@ #include "sysdep.h" #include "chars/char.hpp" +void update_clan_exp(CHAR_DATA *ch, int gain); void update_clan_exp(); void save_clan_exp(); diff --git a/src/interpreter.cpp b/src/interpreter.cpp index 49308b795..c7ab0b8c1 100644 --- a/src/interpreter.cpp +++ b/src/interpreter.cpp @@ -27,7 +27,7 @@ #include "craft.commands.hpp" #include "db.h" #include "depot.hpp" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "features.hpp" #include "fightsystem/assist.h" #include "fightsystem/mobact.hpp" @@ -83,6 +83,7 @@ #include "scripting.hpp" #endif #include "chars/player_races.hpp" +#include "chars/char_player.hpp" #include "birth_places.hpp" #include "help.hpp" #include "map.hpp" @@ -168,7 +169,6 @@ void medit_parse(DESCRIPTOR_DATA * d, char *arg); void trigedit_parse(DESCRIPTOR_DATA * d, char *arg); int find_social(char *name); extern int CheckProxy(DESCRIPTOR_DATA * ch); -extern void check_max_hp(CHAR_DATA *ch); // local functions int perform_dupe_check(DESCRIPTOR_DATA * d); struct alias_data *find_alias(struct alias_data *alias_list, char *str); diff --git a/src/limits.cpp b/src/limits.cpp index 2841f8cef..0d0a79ce0 100644 --- a/src/limits.cpp +++ b/src/limits.cpp @@ -23,7 +23,7 @@ #include "screen.h" #include "interpreter.h" #include "constants.h" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "house.h" #include "constants.h" #include "exchange.h" @@ -71,14 +71,11 @@ extern const unsigned RECALL_SPELLS_INTERVAL; extern int CheckProxy(DESCRIPTOR_DATA * ch); extern int check_death_ice(int room, CHAR_DATA * ch); -void decrease_level(CHAR_DATA * ch); -int max_exp_gain_pc(CHAR_DATA * ch); -int max_exp_loss_pc(CHAR_DATA * ch); int average_day_temp(void); // local functions int graf(int age, int p0, int p1, int p2, int p3, int p4, int p5, int p6); -int level_exp(CHAR_DATA * ch, int level); + void update_char_objects(CHAR_DATA * ch); // handler.cpp // Delete this, if you delete overflow fix in beat_points_update below. void die(CHAR_DATA * ch, CHAR_DATA * killer); @@ -870,186 +867,6 @@ void beat_points_update(int pulse) }); } -void update_clan_exp(CHAR_DATA *ch, int gain) -{ - if (CLAN(ch) && gain != 0) - { - // экспа для уровня клана (+ только на праве, - любой, но /5) - if (gain < 0 || GET_GOD_FLAG(ch, GF_REMORT)) - { - int tmp = gain > 0 ? gain : gain / 5; - CLAN(ch)->SetClanExp(ch, tmp); - } - // экспа для топа кланов за месяц (учитываются все + и -) - CLAN(ch)->last_exp.add_temp(gain); - // экспа для топа кланов за все время (учитываются все + и -) - CLAN(ch)->AddTopExp(ch, gain); - // экспа для авто-очистки кланов (учитываются только +) - if (gain > 0) - { - CLAN(ch)->exp_history.add_exp(gain); - } - } -} - -void gain_exp(CHAR_DATA * ch, int gain) -{ - int is_altered = FALSE; - int num_levels = 0; - char buf[128]; - - if (IS_NPC(ch)) - { - ch->set_exp(ch->get_exp() + gain); - return; - } - else - { - ch->dps_add_exp(gain); - ZoneExpStat::add(zone_table[world[ch->in_room]->zone].number, gain); - } - - if (!IS_NPC(ch) && ((GET_LEVEL(ch) < 1 || GET_LEVEL(ch) >= LVL_IMMORT))) - return; - - if (gain > 0 && GET_LEVEL(ch) < LVL_IMMORT) - { - gain = MIN(max_exp_gain_pc(ch), gain); // put a cap on the max gain per kill - ch->set_exp(ch->get_exp() + gain); - if (GET_EXP(ch) >= level_exp(ch, LVL_IMMORT)) - { - if (!GET_GOD_FLAG(ch, GF_REMORT) && GET_REMORT(ch) < MAX_REMORT) - { - if (Remort::can_remort_now(ch)) - { - send_to_char(ch, "%sПоздравляем, вы получили право на перевоплощение!%s\r\n", - CCIGRN(ch, C_NRM), CCNRM(ch, C_NRM)); - } - else - { - send_to_char(ch, - "%sПоздравляем, вы набрали максимальное количество опыта!\r\n" - "%s%s\r\n", CCIGRN(ch, C_NRM), Remort::WHERE_TO_REMORT_STR.c_str(), CCNRM(ch, C_NRM)); - } - SET_GOD_FLAG(ch, GF_REMORT); - } - } - ch->set_exp(MIN(GET_EXP(ch), level_exp(ch, LVL_IMMORT) - 1)); - while (GET_LEVEL(ch) < LVL_IMMORT && GET_EXP(ch) >= level_exp(ch, GET_LEVEL(ch) + 1)) - { - ch->set_level(ch->get_level() + 1); - num_levels++; - sprintf(buf, "%sВы достигли следующего уровня!%s\r\n", CCWHT(ch, C_NRM), CCNRM(ch, C_NRM)); - send_to_char(buf, ch); - advance_level(ch); - is_altered = TRUE; - } - - if (is_altered) - { - sprintf(buf, "%s advanced %d level%s to level %d.", - GET_NAME(ch), num_levels, num_levels == 1 ? "" : "s", GET_LEVEL(ch)); - mudlog(buf, BRF, LVL_IMPL, SYSLOG, TRUE); - } - } - else if (gain < 0 && GET_LEVEL(ch) < LVL_IMMORT) - { - gain = MAX(-max_exp_loss_pc(ch), gain); // Cap max exp lost per death - ch->set_exp(ch->get_exp() + gain); - while (GET_LEVEL(ch) > 1 && GET_EXP(ch) < level_exp(ch, GET_LEVEL(ch))) - { - ch->set_level(ch->get_level() - 1); - num_levels++; - sprintf(buf, - "%sВы потеряли уровень. Вам должно быть стыдно!%s\r\n", - CCIRED(ch, C_NRM), CCNRM(ch, C_NRM)); - send_to_char(buf, ch); - decrease_level(ch); - is_altered = TRUE; - } - if (is_altered) - { - sprintf(buf, "%s decreases %d level%s to level %d.", - GET_NAME(ch), num_levels, num_levels == 1 ? "" : "s", GET_LEVEL(ch)); - mudlog(buf, BRF, LVL_IMPL, SYSLOG, TRUE); - } - } - if ((GET_EXP(ch) < level_exp(ch, LVL_IMMORT) - 1) - && GET_GOD_FLAG(ch, GF_REMORT) - && gain - && (GET_LEVEL(ch) < LVL_IMMORT)) - { - if (Remort::can_remort_now(ch)) - { - send_to_char(ch, "%sВы потеряли право на перевоплощение!%s\r\n", - CCIRED(ch, C_NRM), CCNRM(ch, C_NRM)); - } - CLR_GOD_FLAG(ch, GF_REMORT); - } - - char_stat::add_class_exp(GET_CLASS(ch), gain); - update_clan_exp(ch, gain); -} - -// юзается исключительно в act.wizards.cpp в имм командах "advance" и "set exp". -void gain_exp_regardless(CHAR_DATA * ch, int gain) -{ - int is_altered = FALSE; - int num_levels = 0; - - ch->set_exp(ch->get_exp() + gain); - if (!IS_NPC(ch)) - { - if (gain > 0) - { - while (GET_LEVEL(ch) < LVL_IMPL && GET_EXP(ch) >= level_exp(ch, GET_LEVEL(ch) + 1)) - { - ch->set_level(ch->get_level() + 1); - num_levels++; - sprintf(buf, "%sВы достигли следующего уровня!%s\r\n", - CCWHT(ch, C_NRM), CCNRM(ch, C_NRM)); - send_to_char(buf, ch); - - advance_level(ch); - is_altered = TRUE; - } - - if (is_altered) - { - sprintf(buf, "%s advanced %d level%s to level %d.", - GET_NAME(ch), num_levels, num_levels == 1 ? "" : "s", GET_LEVEL(ch)); - mudlog(buf, BRF, LVL_IMPL, SYSLOG, TRUE); - } - } - else if (gain < 0) - { - // Pereplut: глупый участок кода. - // gain = MAX(-max_exp_loss_pc(ch), gain); // Cap max exp lost per death - // GET_EXP(ch) += gain; - // if (GET_EXP(ch) < 0) - // GET_EXP(ch) = 0; - while (GET_LEVEL(ch) > 1 && GET_EXP(ch) < level_exp(ch, GET_LEVEL(ch))) - { - ch->set_level(ch->get_level() - 1); - num_levels++; - sprintf(buf, - "%sВы потеряли уровень!%s\r\n", - CCIRED(ch, C_NRM), CCNRM(ch, C_NRM)); - send_to_char(buf, ch); - decrease_level(ch); - is_altered = TRUE; - } - if (is_altered) - { - sprintf(buf, "%s decreases %d level%s to level %d.", - GET_NAME(ch), num_levels, num_levels == 1 ? "" : "s", GET_LEVEL(ch)); - mudlog(buf, BRF, LVL_IMPL, SYSLOG, TRUE); - } - } - - } -} - void gain_condition(CHAR_DATA * ch, unsigned condition, int value) { int cond_state = GET_COND(ch, condition); diff --git a/src/magic.cpp b/src/magic.cpp index f9997e57f..13fa1a938 100644 --- a/src/magic.cpp +++ b/src/magic.cpp @@ -29,7 +29,7 @@ #include "interpreter.h" #include "screen.h" #include "constants.h" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "fightsystem/pk.h" #include "features.hpp" #include "fightsystem/fight.h" diff --git a/src/medit.cpp b/src/medit.cpp index 034137cf4..08e03adef 100644 --- a/src/medit.cpp +++ b/src/medit.cpp @@ -14,7 +14,7 @@ #include "db.h" #include "olc.h" #include "handler.h" -#include "dg_olc.h" +#include "dg/dg_olc.h" #include "constants.h" #include "features.hpp" #include "im.h" diff --git a/src/msdp.reporters.cpp b/src/msdp.reporters.cpp index 860f4dff1..3aaca2334 100644 --- a/src/msdp.reporters.cpp +++ b/src/msdp.reporters.cpp @@ -1,11 +1,12 @@ #include "msdp.reporters.hpp" -#include "structs.h" -#include "utils.h" -#include "db.h" #include "chars/char.hpp" +#include "grp/grp.main.h" +#include "db.h" #include "magic.h" #include "msdp.constants.hpp" +#include "structs.h" +#include "utils.h" #include "zone.table.hpp" namespace msdp @@ -302,36 +303,13 @@ namespace msdp */ const auto group_descriptor = std::make_shared(); const auto ch = descriptor()->character.get(); - const auto master = ch->has_master() ? ch->get_master() : ch; - append_char(group_descriptor, ch, master, true); - for (auto f = master->followers; f; f = f->next) - { - if (!AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP) - && !(MOB_FLAGGED(f->follower, MOB_PLAYER_SUMMON))) - { - continue; - } - - append_char(group_descriptor, ch, f->follower, false); - - // followers of a follower - if (!AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP)) - { - continue; - } - - for (auto ff = f->follower->followers; ff; ff = ff->next) - { - if (!(AFF_FLAGGED(ff->follower, EAffectFlag::AFF_CHARM) - || MOB_FLAGGED(ff->follower, MOB_PLAYER_SUMMON))) - { - continue; - } - - append_char(group_descriptor, ch, ff->follower, false); - } - } - + auto grp = ch->personGroup; + if (grp == nullptr) + return; + auto gl = grp->getMembers(0, true); + for (auto it : gl) { + append_char(group_descriptor, ch, it, true); + } response = std::make_shared(constants::GROUP, group_descriptor); } } diff --git a/src/named_stuff.cpp b/src/named_stuff.cpp index 0a7ed2880..229789e98 100644 --- a/src/named_stuff.cpp +++ b/src/named_stuff.cpp @@ -13,7 +13,7 @@ #include "db.h" #include "handler.h" #include "house.h" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "pugixml.hpp" #include "logger.hpp" #include "utils.h" diff --git a/src/obj.cpp b/src/obj.cpp index f82227290..cbdaa114e 100644 --- a/src/obj.cpp +++ b/src/obj.cpp @@ -9,7 +9,7 @@ #include "object.prototypes.hpp" #include "parse.hpp" #include "handler.h" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "screen.h" #include "celebrates.hpp" #include "fightsystem/pk.h" diff --git a/src/objsave.cpp b/src/objsave.cpp index 07d7b4598..3b137ba68 100644 --- a/src/objsave.cpp +++ b/src/objsave.cpp @@ -31,7 +31,7 @@ #include "named_stuff.hpp" #include "room.hpp" #include "mail.h" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "features.hpp" #include "char_obj_utils.inl" #include "structs.h" diff --git a/src/oedit.cpp b/src/oedit.cpp index a87e6aac7..7ff1f7da2 100644 --- a/src/oedit.cpp +++ b/src/oedit.cpp @@ -19,7 +19,7 @@ #include "utils.h" #include "db.h" #include "olc.h" -#include "dg_olc.h" +#include "dg/dg_olc.h" #include "im.h" #include "features.hpp" #include "depot.hpp" diff --git a/src/olc.cpp b/src/olc.cpp index a5a9b3836..ff448089b 100644 --- a/src/olc.cpp +++ b/src/olc.cpp @@ -15,7 +15,7 @@ #include "interpreter.h" #include "comm.h" #include "db.h" -#include "dg_olc.h" +#include "dg/dg_olc.h" #include "screen.h" #include "item.creation.hpp" #include "im.h" diff --git a/src/redit.cpp b/src/redit.cpp index f89a506bd..5ae9ab69e 100644 --- a/src/redit.cpp +++ b/src/redit.cpp @@ -12,7 +12,7 @@ #include "comm.h" #include "db.h" #include "olc.h" -#include "dg_olc.h" +#include "dg/dg_olc.h" #include "constants.h" #include "im.h" #include "description.h" diff --git a/src/room.cpp b/src/room.cpp index d08a9297e..e940bb5c9 100644 --- a/src/room.cpp +++ b/src/room.cpp @@ -6,7 +6,7 @@ #include "room.hpp" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" EXIT_DATA::EXIT_DATA(): keyword(nullptr), vkeyword(nullptr), diff --git a/src/room.hpp b/src/room.hpp index 61c887d7f..e25a907c5 100644 --- a/src/room.hpp +++ b/src/room.hpp @@ -5,7 +5,7 @@ #ifndef ROOM_HPP_INCLUDED #define ROOM_HPP_INCLUDED -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "obj.hpp" #include "constants.h" #include "structs.h" diff --git a/src/sets_drop.cpp b/src/sets_drop.cpp index 78f7a1317..19c02baf0 100644 --- a/src/sets_drop.cpp +++ b/src/sets_drop.cpp @@ -9,7 +9,7 @@ #include "chars/char.hpp" #include "comm.h" #include "handler.h" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "im.h" #include "room.hpp" #include "pugixml.hpp" diff --git a/src/skills.cpp b/src/skills.cpp index 2e7bab654..72977167e 100644 --- a/src/skills.cpp +++ b/src/skills.cpp @@ -17,7 +17,7 @@ #include "interpreter.h" #include "spells.h" #include "screen.h" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "constants.h" #include "im.h" #include "features.hpp" diff --git a/src/skills/flee.cpp b/src/skills/flee.cpp index 32e330172..81101ffb6 100644 --- a/src/skills/flee.cpp +++ b/src/skills/flee.cpp @@ -1,6 +1,9 @@ -#include "skills/flee.h" + #include "act.movement.hpp" +#include "core/leveling.h" +#include "skills/flee.h" #include "random.hpp" + #include void reduce_exp_after_flee(CHAR_DATA* ch, CHAR_DATA* victim, room_rnum room) @@ -9,7 +12,7 @@ void reduce_exp_after_flee(CHAR_DATA* ch, CHAR_DATA* victim, room_rnum room) return; const auto loss = MAX(1, GET_REAL_MAX_HIT(victim) - GET_HIT(victim)) * GET_LEVEL(victim); - gain_exp(ch, -loss); + ExpCalc::gain_exp(ch, -loss); } // ********************* FLEE PROCEDURE diff --git a/src/spec_procs.cpp b/src/spec_procs.cpp index 958d75ebd..eef3db29f 100644 --- a/src/spec_procs.cpp +++ b/src/spec_procs.cpp @@ -13,37 +13,38 @@ ************************************************************************ */ #include "act.movement.hpp" -#include "cmd/cmd.generic.h" -#include "obj.hpp" -#include "comm.h" -#include "interpreter.h" -#include "handler.h" -#include "db.h" -#include "spell_parser.hpp" -#include "spells.h" -#include "skills.h" -#include "screen.h" -#include "dg_scripts.h" -#include "constants.h" -#include "features.hpp" -#include "house.h" +#include "char_obj_utils.inl" #include "chars/char.hpp" #include "chars/char_player.hpp" #include "chars/mount.h" -#include "depot.hpp" #include "chars/player_races.hpp" -#include "magic.h" +#include "chars/world.characters.hpp" +#include "cmd/cmd.generic.h" +#include "comm.h" +#include "constants.h" +#include "core/leveling.h" +#include "db.h" +#include "depot.hpp" +#include "dg/dg_scripts.h" +#include "features.hpp" #include "fightsystem/fight.h" #include "fightsystem/fight_hit.hpp" -#include "char_obj_utils.inl" -#include "chars/world.characters.hpp" +#include "handler.h" +#include "house.h" +#include "interpreter.h" #include "logger.hpp" +#include "magic.h" +#include "obj.hpp" +#include "screen.h" +#include "skills.h" +#include "spell_parser.hpp" +#include "spells.h" #include "structs.h" #include "sysdep.h" #include "temp_spells.hpp" + #include #include - #include // external vars @@ -2910,7 +2911,7 @@ int dump(CHAR_DATA *ch, void* /*me*/, int cmd, char* argument) send_to_char("Боги оценили вашу жертву.\r\n", ch); act("$n оценен$y Богами.", TRUE, ch, 0, 0, TO_ROOM); if (GET_LEVEL(ch) < 3) - gain_exp(ch, value); + ExpCalc::gain_exp(ch, value); else ch->add_gold(value); } diff --git a/src/spell_parser.cpp b/src/spell_parser.cpp index 2aef49858..afe7bf468 100644 --- a/src/spell_parser.cpp +++ b/src/spell_parser.cpp @@ -26,7 +26,7 @@ #include "db.h" #include "screen.h" #include "constants.h" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "fightsystem/pk.h" #include "features.hpp" #include "im.h" diff --git a/src/spells.cpp b/src/spells.cpp index 4a0467cb8..160395a3e 100644 --- a/src/spells.cpp +++ b/src/spells.cpp @@ -28,7 +28,7 @@ #include "db.h" #include "constants.h" #include "interpreter.h" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "screen.h" #include "house.h" #include "fightsystem/pk.h" diff --git a/src/structs.cpp b/src/structs.cpp index f7430e67e..aeb9ebd6f 100644 --- a/src/structs.cpp +++ b/src/structs.cpp @@ -1,8 +1,5 @@ #include "structs.h" #include "chars/char.hpp" -#include "spells.h" -#include "utils.h" -#include "logger.hpp" #include "msdp.hpp" #include "msdp.constants.hpp" @@ -51,7 +48,7 @@ void asciiflag_conv(const char *flag, void *to) int ext_search_block(const char *arg, const char * const * const list, int exact) { - int i, j, o; + long i, j, o; if (exact) { diff --git a/src/structs.h b/src/structs.h index 60b27f83c..36ef27b0f 100644 --- a/src/structs.h +++ b/src/structs.h @@ -16,6 +16,7 @@ #define _STRUCTS_H_ #include "boards.types.hpp" +#include "ext_money.hpp" #include "sysdep.h" #include @@ -29,17 +30,17 @@ #include #include -namespace ExtMoney -{ + +namespace ExtMoney { // золотые гривны -const unsigned TORC_GOLD = 0; + const unsigned TORC_GOLD = 0; // серебряные гривны -const unsigned TORC_SILVER = 1; + const unsigned TORC_SILVER = 1; // бронзовые гривны -const unsigned TORC_BRONZE = 2; + const unsigned TORC_BRONZE = 2; // терминатор всегда в конце -const unsigned TOTAL_TYPES = 3; -} // namespace ExtMoney + const unsigned TOTAL_TYPES = 3; +} namespace currency { diff --git a/src/stuff.cpp b/src/stuff.cpp index 035ef6d57..2eff51bfd 100644 --- a/src/stuff.cpp +++ b/src/stuff.cpp @@ -15,7 +15,7 @@ #include "comm.h" #include "db.h" #include "handler.h" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "chars/char.hpp" #include "room.hpp" #include "corpse.hpp" diff --git a/src/utils.cpp b/src/utils.cpp index ea8673c4f..905d262ac 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -26,7 +26,7 @@ #include "interpreter.h" #include "constants.h" #include "im.h" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "features.hpp" #include "privilege.hpp" #include "chars/char.hpp" diff --git a/src/utils.h b/src/utils.h index 4e5a01cac..7bcedac8f 100644 --- a/src/utils.h +++ b/src/utils.h @@ -219,9 +219,6 @@ int perform_move(CHAR_DATA * ch, int dir, int following, int checkmob, CHAR_DATA int mana_gain(const CHAR_DATA * ch); int hit_gain(CHAR_DATA * ch); int move_gain(CHAR_DATA * ch); -void advance_level(CHAR_DATA * ch); -void gain_exp(CHAR_DATA * ch, int gain); -void gain_exp_regardless(CHAR_DATA * ch, int gain); void gain_condition(CHAR_DATA * ch, unsigned condition, int value); void check_idling(CHAR_DATA * ch); void point_update(void); diff --git a/src/world.objects.cpp b/src/world.objects.cpp index fb19e0dab..ae4c09215 100644 --- a/src/world.objects.cpp +++ b/src/world.objects.cpp @@ -5,7 +5,7 @@ #include "logger.hpp" #include "db.h" #include "liquid.hpp" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "utils.h" #include "global.objects.hpp" diff --git a/src/zedit.cpp b/src/zedit.cpp index e4b10a999..725d5a070 100644 --- a/src/zedit.cpp +++ b/src/zedit.cpp @@ -9,7 +9,7 @@ #include "comm.h" #include "db.h" #include "olc.h" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "chars/char.hpp" #include "room.hpp" #include "help.hpp" diff --git a/tests/char.leaders.cpp b/tests/char.leaders.cpp index 6c1c5ca24..90a8b3deb 100644 --- a/tests/char.leaders.cpp +++ b/tests/char.leaders.cpp @@ -72,6 +72,7 @@ TEST(CHAR_Leaders, Group) { test_utils::CharacterBuilder builder; builder.create_new(); + builder.add_skill(ESkill::SKILL_LEADERSHIP, 200); auto leader = builder.get(); builder.create_new("F7"); builder.add_skill(ESkill::SKILL_LEADERSHIP, 10); @@ -84,7 +85,6 @@ TEST(CHAR_Leaders, Group) { leader->add_follower(follower.get()); } - test_utils::GroupBuilder g; auto grp = g._roster->addGroup(leader.get()); grp->addFollowers(leader.get()); diff --git a/tests/fight.penalties.cpp b/tests/fight.penalties.cpp index 61a73f43a..dca86612c 100644 --- a/tests/fight.penalties.cpp +++ b/tests/fight.penalties.cpp @@ -1,4 +1,4 @@ -#include "fightsystem/fight.penalties.hpp" +#include "grp/grp.main.h" #include "char.utilities.hpp" diff --git a/tests/obj.copy.cpp b/tests/obj.copy.cpp index 41a3fd1ce..3dbfa736e 100644 --- a/tests/obj.copy.cpp +++ b/tests/obj.copy.cpp @@ -1,7 +1,7 @@ #include #include -#include +#include constexpr obj_vnum OBJECT_VNUM = 100500; constexpr obj_vnum PROTOTYPE_VNUM = 100501; diff --git a/tests/triggers.list.cpp b/tests/triggers.list.cpp index bb4d60dcf..ea9cabd87 100644 --- a/tests/triggers.list.cpp +++ b/tests/triggers.list.cpp @@ -1,6 +1,6 @@ #include -#include +#include #include class TriggersList_F : public ::testing::Test From 29256d63d48b4e2a6955930c803bc9ab98a7ddbc Mon Sep 17 00:00:00 2001 From: bikbai Date: Tue, 5 Jan 2021 14:09:27 +0300 Subject: [PATCH 16/40] =?UTF-8?q?=D0=B1=D0=BE=D0=B9=20=D0=BF=D1=80=D0=BE?= =?UTF-8?q?=D0=B4=D0=BE=D0=BB=D0=B6=D0=B0=D0=B5=D1=82=D1=81=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/fightsystem/fight_hit.cpp | 15 +--- src/fightsystem/mobact.cpp | 156 +++++++++++----------------------- src/grp/grp.group.cpp | 6 +- src/spec_procs.cpp | 46 +++------- 4 files changed, 69 insertions(+), 154 deletions(-) diff --git a/src/fightsystem/fight_hit.cpp b/src/fightsystem/fight_hit.cpp index 84b50e694..019dd09f3 100644 --- a/src/fightsystem/fight_hit.cpp +++ b/src/fightsystem/fight_hit.cpp @@ -2484,20 +2484,13 @@ void Damage::process_death(CHAR_DATA *ch, CHAR_DATA *victim) // и чармис и хозяин в одной группе if (IN_GROUP(killer)) group_gain(killer, victim); - - if (AFF_FLAGGED(killer, EAffectFlag::AFF_GROUP)) { - // т.к. помечен флагом AFF_GROUP - точно PC - - } else - if ((IS_CHARMICE(killer) || MOB_FLAGGED(killer, MOB_PLAYER_SUMMON)) && killer->has_master()) { + } else if ((IS_CHARMICE(killer) || MOB_FLAGGED(killer, MOB_PLAYER_SUMMON)) && killer->has_master()) { if (IN_GROUP(killer->get_master()) && IN_ROOM(killer) == IN_ROOM(killer->get_master())) { // Хозяин - PC в группе => опыт группе group_gain(killer->get_master(), victim); } - else if (IN_ROOM(killer) == IN_ROOM(killer->get_master())) - // Чармис и хозяин в одной комнате - // Опыт хозяину - { + else if (IN_ROOM(killer) == IN_ROOM(killer->get_master())){ + // Чармис и хозяин в одной комнате -опыт хозяину perform_group_gain(killer->get_master(), victim, 1, 100); } // else @@ -2509,7 +2502,6 @@ void Damage::process_death(CHAR_DATA *ch, CHAR_DATA *victim) // Просто NPC или PC сам по себе perform_group_gain(killer, victim, 1, 100); } - } // в сислог иммам идут только смерти в пк (без арен) // в файл пишутся все смерти чаров @@ -2519,6 +2511,7 @@ void Damage::process_death(CHAR_DATA *ch, CHAR_DATA *victim) { update_pk_logs(ch, victim); + for (const auto& ch_vict : world[ch->in_room]->people) { //Мобы все кто присутствовал при смерти игрока забывают diff --git a/src/fightsystem/mobact.cpp b/src/fightsystem/mobact.cpp index eb789c7e5..761f89456 100644 --- a/src/fightsystem/mobact.cpp +++ b/src/fightsystem/mobact.cpp @@ -1076,15 +1076,12 @@ void mobile_activity(int activity_level, int missed_pulses) character_list.foreach_on_copy([&](const CHAR_DATA::shared_ptr& ch) { - if (!IS_MOB(ch) - || !ch->in_used_zone()) - { + if (!IS_MOB(ch) || !ch->in_used_zone()) { return; } i = missed_pulses; - while (i--) - { + while (i--) { pulse_affect_update(ch.get()); } @@ -1099,12 +1096,9 @@ void mobile_activity(int activity_level, int missed_pulses) if (GET_PUNCTUAL_WAIT(ch) < 0) GET_PUNCTUAL_WAIT(ch) = 0; - if (ch->mob_specials.speed <= 0) - { + if (ch->mob_specials.speed <= 0) { activity_lev = std_lev; - } - else - { + } else { activity_lev = activity_level % (ch->mob_specials.speed RL_SEC); } @@ -1112,14 +1106,13 @@ void mobile_activity(int activity_level, int missed_pulses) // на случай вызова mobile_activity() не каждый пульс // TODO: by WorM а где-то используется это mob_specials.speed ??? - if (ch_activity - activity_lev < missed_pulses && ch_activity - activity_lev >= 0) - { + if (ch_activity - activity_lev < missed_pulses && ch_activity - activity_lev >= 0) { ch_activity = activity_lev; } + if (ch_activity != activity_lev || (was_in = ch->in_room) == NOWHERE - || GET_ROOM_VNUM(ch->in_room) % 100 == 99) - { + || GET_ROOM_VNUM(ch->in_room) % 100 == 99) { return; } @@ -1142,30 +1135,20 @@ void mobile_activity(int activity_level, int missed_pulses) } } // Extract free horses - if (AFF_FLAGGED(ch, EAffectFlag::AFF_HORSE) - && MOB_FLAGGED(ch, MOB_MOUNTING) - && !ch->has_master()) // если скакун, под седлом но нет хозяина - { + if (IS_HORSE(ch) && !ch->has_master()) { // если скакун, под седлом но нет хозяина act("Возникший как из-под земли цыган ловко вскочил на $n3 и унесся прочь.", FALSE, ch.get(), nullptr, nullptr, TO_ROOM); extract_char(ch.get(), FALSE); - return; } // Extract uncharmed mobs - if (EXTRACT_TIMER(ch) > 0) - { - if (ch->has_master()) - { + if (EXTRACT_TIMER(ch) > 0) { + if (ch->has_master()) { EXTRACT_TIMER(ch) = 0; - } - else - { + } else { EXTRACT_TIMER(ch)--; - if (!EXTRACT_TIMER(ch)) - { + if (!EXTRACT_TIMER(ch)) { extract_charmice(ch.get()); - return; } } @@ -1192,34 +1175,27 @@ void mobile_activity(int activity_level, int missed_pulses) return; } - if (GET_POS(ch) == POS_SLEEPING && GET_DEFAULT_POS(ch) > POS_SLEEPING) - { + if (GET_POS(ch) == POS_SLEEPING && GET_DEFAULT_POS(ch) > POS_SLEEPING) { GET_POS(ch) = GET_DEFAULT_POS(ch); act("$n проснул$u.", FALSE, ch.get(), 0, 0, TO_ROOM); } - if (!AWAKE(ch)) - { + if (!AWAKE(ch)) { return; } max = FALSE; bool found = false; - for (const auto vict : world[ch->in_room]->people) - { - if (ch.get() == vict) - { + for (const auto vict : world[ch->in_room]->people) { + if (ch.get() == vict) { continue; } - if (vict->get_fighting() == ch.get()) - { + if (vict->get_fighting() == ch.get()) { return; // Mob is under attack } - if (!IS_NPC(vict) - && CAN_SEE(ch, vict)) - { + if (!IS_NPC(vict) && CAN_SEE(ch, vict)) { max = TRUE; } } @@ -1233,11 +1209,8 @@ void mobile_activity(int activity_level, int missed_pulses) } // Mob return to default pos if full rested or if it is an angel - if ((GET_HIT(ch) >= GET_REAL_MAX_HIT(ch) - && GET_POS(ch) != GET_DEFAULT_POS(ch)) - || ((MOB_FLAGGED(ch, MOB_ANGEL) - || MOB_FLAGGED(ch, MOB_GHOST)) - && GET_POS(ch) != GET_DEFAULT_POS(ch))) + if ((GET_HIT(ch) >= GET_REAL_MAX_HIT(ch) && GET_POS(ch) != GET_DEFAULT_POS(ch)) + || ((MOB_FLAGGED(ch, MOB_ANGEL) || MOB_FLAGGED(ch, MOB_GHOST)) && GET_POS(ch) != GET_DEFAULT_POS(ch))) { switch (GET_DEFAULT_POS(ch)) { @@ -1259,11 +1232,9 @@ void mobile_activity(int activity_level, int missed_pulses) break; } } - // continue, if the mob is an angel + // если моб ментальная тень или ангел он не должен проявлять активность - if ((MOB_FLAGGED(ch, MOB_ANGEL)) - ||(MOB_FLAGGED(ch, MOB_GHOST))) - { + if ((MOB_FLAGGED(ch, MOB_ANGEL)) ||(MOB_FLAGGED(ch, MOB_GHOST))) { return; } @@ -1271,35 +1242,25 @@ void mobile_activity(int activity_level, int missed_pulses) do_aggressive_mob(ch.get(), FALSE); // if mob attack something - if (ch->get_fighting() - || GET_WAIT(ch) > 0) - { + if (ch->get_fighting() || GET_WAIT(ch) > 0) { return; } - // Scavenger (picking up objects) // От одного до трех предметов за раз i = number(1, 3); - while (i) - { + while (i) { npc_scavenge(ch.get()); i--; } - - if (EXTRACT_TIMER(ch) == 0) - { - //чармисы, собирающиеся уходить - не лутят! (Купала) - //Niker: LootCR// Start - //Не уверен, что рассмотрены все случаи, когда нужно снимать флаги с моба - //Реализация для лута и воровства + // лут шмота, если моб постоянный + if (EXTRACT_TIMER(ch) == 0) { int grab_stuff = FALSE; // Looting the corpses grab_stuff += npc_loot(ch.get()); grab_stuff += npc_steal(ch.get()); - - if (grab_stuff) - { + // если поднял шмотку - снимаем флаги, чтобы не спуржило + if (grab_stuff) { MOB_FLAGS(ch).unset(MOB_LIKE_DAY); //Взял из make_horse MOB_FLAGS(ch).unset(MOB_LIKE_NIGHT); MOB_FLAGS(ch).unset(MOB_LIKE_FULLMOON); @@ -1308,59 +1269,48 @@ void mobile_activity(int activity_level, int missed_pulses) MOB_FLAGS(ch).unset(MOB_LIKE_SUMMER); MOB_FLAGS(ch).unset(MOB_LIKE_AUTUMN); } - //Niker: LootCR// End } + // надеваем поднятое npc_wield(ch.get()); npc_armor(ch.get()); - if (GET_POS(ch) == POS_STANDING && NPC_FLAGGED(ch, NPC_INVIS)) - { + // возвращаем аффекты + if (GET_POS(ch) == POS_STANDING && NPC_FLAGGED(ch, NPC_INVIS)) { ch->set_affect(EAffectFlag::AFF_INVISIBLE); } - if (GET_POS(ch) == POS_STANDING && NPC_FLAGGED(ch, NPC_MOVEFLY)) - { + if (GET_POS(ch) == POS_STANDING && NPC_FLAGGED(ch, NPC_MOVEFLY)) { ch->set_affect(EAffectFlag::AFF_FLY); } - if (GET_POS(ch) == POS_STANDING && NPC_FLAGGED(ch, NPC_SNEAK)) - { - if (calculate_skill(ch.get(), SKILL_SNEAK, 0) >= number(0, 100)) - { + if (GET_POS(ch) == POS_STANDING && NPC_FLAGGED(ch, NPC_SNEAK)) { + if (calculate_skill(ch.get(), SKILL_SNEAK, 0) >= number(0, 100)) { ch->set_affect(EAffectFlag::AFF_SNEAK); } - else - { + else { ch->remove_affect(EAffectFlag::AFF_SNEAK); } affect_total(ch.get()); } - if (GET_POS(ch) == POS_STANDING && NPC_FLAGGED(ch, NPC_CAMOUFLAGE)) - { - if (calculate_skill(ch.get(), SKILL_CAMOUFLAGE, 0) >= number(0, 100)) - { + if (GET_POS(ch) == POS_STANDING && NPC_FLAGGED(ch, NPC_CAMOUFLAGE)) { + if (calculate_skill(ch.get(), SKILL_CAMOUFLAGE, 0) >= number(0, 100)) { ch->set_affect(EAffectFlag::AFF_CAMOUFLAGE); - } - else - { + } else { ch->remove_affect(EAffectFlag::AFF_CAMOUFLAGE); } - affect_total(ch.get()); } door = BFS_ERROR; - // Helpers go to some dest + // может бегать - бежит if (MOB_FLAGGED(ch, MOB_HELPER) && !MOB_FLAGGED(ch, MOB_SENTINEL) && !AFF_FLAGGED(ch, EAffectFlag::AFF_BLIND) && !ch->has_master() - && GET_POS(ch) == POS_STANDING) - { - for (found = FALSE, door = 0; door < NUM_OF_DIRS; door++) - { + && GET_POS(ch) == POS_STANDING) { + for (found = FALSE, door = 0; door < NUM_OF_DIRS; door++) { ROOM_DATA::exit_data_ptr rdata = EXIT(ch, door); if (!rdata || rdata->to_room() == NOWHERE @@ -1375,36 +1325,30 @@ void mobile_activity(int activity_level, int missed_pulses) } const auto room = world[rdata->to_room()]; - for (auto first : room->people) - { + for (auto first : room->people) { if (IS_NPC(first) && !AFF_FLAGGED(first, EAffectFlag::AFF_CHARM) && !IS_HORSE(first) && CAN_SEE(ch, first) && first->get_fighting() - && SAME_ALIGN(ch, first)) - { + && SAME_ALIGN(ch, first)) { found = TRUE; break; } } - if (found) - { + if (found) { break; } } - if (!found) - { + if (!found) { door = BFS_ERROR; } } - if (GET_DEST(ch) != NOWHERE - && GET_POS(ch) > POS_FIGHTING - && door == BFS_ERROR) - { + // вот тут самое интересное - "создаем группу?" + if (GET_DEST(ch) != NOWHERE && GET_POS(ch) > POS_FIGHTING && door == BFS_ERROR) { npc_group(ch.get()); door = npc_walk(ch.get()); } @@ -1412,14 +1356,12 @@ void mobile_activity(int activity_level, int missed_pulses) if (MEMORY(ch) && door == BFS_ERROR && GET_POS(ch) > POS_FIGHTING && ch->get_skill(SKILL_TRACK)) door = npc_track(ch.get()); - if (door == BFS_ALREADY_THERE) - { + if (door == BFS_ALREADY_THERE) { do_aggressive_mob(ch.get(), FALSE); return; } - if (door == BFS_ERROR) - { + if (door == BFS_ERROR) { door = number(0, 18); } diff --git a/src/grp/grp.group.cpp b/src/grp/grp.group.cpp index acb3c43a6..06f4c68f5 100644 --- a/src/grp/grp.group.cpp +++ b/src/grp/grp.group.cpp @@ -876,13 +876,11 @@ void group_gain(CHAR_DATA * killer, CHAR_DATA * victim) && leader->in_room == IN_ROOM(killer); // Количество согрупников в комнате - if (leader_inroom) - { + if (leader_inroom) { inroom_members = 1; maxlevel = GET_LEVEL(leader); } - else - { + else { inroom_members = 0; } diff --git a/src/spec_procs.cpp b/src/spec_procs.cpp index eef3db29f..6a55182e1 100644 --- a/src/spec_procs.cpp +++ b/src/spec_procs.cpp @@ -2758,33 +2758,26 @@ int npc_steal(CHAR_DATA * ch) void npc_group(CHAR_DATA * ch) { - CHAR_DATA *leader = NULL; + CHAR_DATA *leader = nullptr; int zone = ZONE(ch), group = GROUP(ch), members = 0; if (GET_DEST(ch) == NOWHERE || ch->in_room == NOWHERE) return; - if (ch->has_master() - && ch->in_room == IN_ROOM(ch->get_master())) - { + if (ch->has_master() && ch->in_room == IN_ROOM(ch->get_master())) { leader = ch->get_master(); } - if (!ch->has_master()) - { + if (!ch->has_master()) { leader = ch; } - if (leader - && (AFF_FLAGGED(leader, EAffectFlag::AFF_CHARM) - || GET_POS(leader) < POS_SLEEPING)) - { - leader = NULL; + if (leader && (AFF_FLAGGED(leader, EAffectFlag::AFF_CHARM) || GET_POS(leader) < POS_SLEEPING)) { + leader = nullptr; } // Find leader - for (const auto vict : world[ch->in_room]->people) - { + for (const auto vict : world[ch->in_room]->people) { if (!IS_NPC(vict) || GET_DEST(vict) != GET_DEST(ch) || zone != ZONE(vict) @@ -2797,31 +2790,24 @@ void npc_group(CHAR_DATA * ch) members++; - if (!leader - || GET_REAL_INT(vict) > GET_REAL_INT(leader)) - { + if (!leader || GET_REAL_INT(vict) > GET_REAL_INT(leader)) { leader = vict; } } - if (members <= 1) - { - if (ch->has_master()) - { + if (members <= 1) { + if (ch->has_master()) { stop_follower(ch, SF_EMPTY); } - return; } - if (leader->has_master()) - { + if (leader->has_master()) { stop_follower(leader, SF_EMPTY); } // Assign leader - for (const auto vict : world[ch->in_room]->people) - { + for (const auto vict : world[ch->in_room]->people) { if (!IS_NPC(vict) || GET_DEST(vict) != GET_DEST(ch) || zone != ZONE(vict) @@ -2832,18 +2818,14 @@ void npc_group(CHAR_DATA * ch) continue; } - if (vict == leader) - { + if (vict == leader) { AFF_FLAGS(vict).set(EAffectFlag::AFF_GROUP); continue; } - if (!vict->has_master()) - { + if (!vict->has_master()) { leader->add_follower(vict); - } - else if (vict->get_master() != leader) - { + } else if (vict->get_master() != leader) { stop_follower(vict, SF_EMPTY); leader->add_follower(vict); } From 8f802452e1aed53df7ed94486e8ac316f9bea016 Mon Sep 17 00:00:00 2001 From: bikbai Date: Tue, 5 Jan 2021 22:26:08 +0300 Subject: [PATCH 17/40] =?UTF-8?q?=D0=9F=D0=BE=D0=B1=D0=B5=D0=B4=D0=B8?= =?UTF-8?q?=D0=BB=20=D0=BE=D0=B4=D0=BD=D1=83=20=D0=B3=D0=BB=D0=BE=D0=B1?= =?UTF-8?q?=D0=B0=D0=BB=D1=8C=D0=BD=D1=83=D1=8E=20=D0=BF=D0=B5=D1=80=D0=B5?= =?UTF-8?q?=D0=BC=D0=B5=D0=BD=D0=BD=D1=83=D1=8E,=20=D1=82=D0=B5=D0=BF?= =?UTF-8?q?=D0=B5=D1=80=D1=8C=20=D1=8D=D1=82=D0=BE=20=D0=BF=D0=BE=D0=BB?= =?UTF-8?q?=D0=B5=20=D0=B3=D0=BB=D0=BE=D0=B1=D0=B0=D0=BB=D1=8C=D0=BD=D0=BE?= =?UTF-8?q?=D0=B3=D0=BE=20=D1=81=D1=82=D0=B0=D1=82=D0=B8=D1=87=D0=B5=D1=81?= =?UTF-8?q?=D0=BA=D0=BE=D0=B3=D0=BE=20=D0=BA=D0=BB=D0=B0=D1=81=D1=81=D0=B0?= =?UTF-8?q?.=20=D0=A3=D1=80=D0=B0,=20=D1=82=D0=BE=D0=B2=D0=B0=D1=80=D0=B8?= =?UTF-8?q?=D1=89=D0=B8.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/act.informative.cpp | 9 ++-- src/chars/char_player.cpp | 1 + src/cmd/hire.cpp | 1 + src/db.cpp | 2 +- src/fightsystem/fight_stuff.cpp | 8 ---- src/grp/follow.h | 1 + src/grp/grouppenalties.cpp | 11 ++++- src/grp/grp.group.cpp | 2 +- src/grp/grp.main.h | 77 +++++++++++++++++---------------- src/spells.cpp | 46 ++++++-------------- src/utils.h | 3 -- 11 files changed, 73 insertions(+), 88 deletions(-) diff --git a/src/act.informative.cpp b/src/act.informative.cpp index 557d52a62..f17523f62 100644 --- a/src/act.informative.cpp +++ b/src/act.informative.cpp @@ -77,6 +77,7 @@ using namespace ExpCalc; // extern variables extern DESCRIPTOR_DATA *descriptor_list; +extern GroupRoster& groupRoster; extern int number_of_social_commands; extern char *credits; extern char *info; @@ -3818,8 +3819,8 @@ void print_do_score_all(CHAR_DATA *ch) " || %sВы можете вступить в группу с максимальной разницей %s||\r\n" " || %sв %2d %-75s%s||\r\n", CCNRM(ch, C_NRM), CCCYN(ch, C_NRM), CCNRM(ch, C_NRM), - grouping[static_cast(GET_CLASS(ch))][static_cast(GET_REMORT(ch))], - (string(desc_count(grouping[static_cast(GET_CLASS(ch))][static_cast(GET_REMORT(ch))], WHAT_LEVEL)) + groupRoster.grouping[static_cast(GET_CLASS(ch))][static_cast(GET_REMORT(ch))], + (string(desc_count(groupRoster.grouping[static_cast(GET_CLASS(ch))][static_cast(GET_REMORT(ch))], WHAT_LEVEL)) + string(" без потерь для опыта.")).substr(0, 76).c_str(), CCCYN(ch, C_NRM)); if (RENTABLE(ch)) @@ -4087,8 +4088,8 @@ void do_score(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) { sprintf(buf + strlen(buf), "Вы можете вступить в группу с максимальной разницей в %d %s без потерь для опыта.\r\n", - grouping[static_cast(GET_CLASS(ch))][static_cast(GET_REMORT(ch))], - desc_count(grouping[static_cast(GET_CLASS(ch))][static_cast(GET_REMORT(ch))], WHAT_LEVEL)); + groupRoster.grouping[static_cast(GET_CLASS(ch))][static_cast(GET_REMORT(ch))], + desc_count(groupRoster.grouping[static_cast(GET_CLASS(ch))][static_cast(GET_REMORT(ch))], WHAT_LEVEL)); } //Напоминаем о метке, если она есть. diff --git a/src/chars/char_player.cpp b/src/chars/char_player.cpp index 97d265494..bf4efb327 100644 --- a/src/chars/char_player.cpp +++ b/src/chars/char_player.cpp @@ -2563,4 +2563,5 @@ void check_max_hp(CHAR_DATA *ch) GET_MAX_HIT(ch) = PlayerSystem::con_natural_hp(ch); } + // vim: ts=4 sw=4 tw=0 noet syntax=cpp : diff --git a/src/cmd/hire.cpp b/src/cmd/hire.cpp index 583834e34..ab6c8998f 100644 --- a/src/cmd/hire.cpp +++ b/src/cmd/hire.cpp @@ -1,5 +1,6 @@ #include "cmd.generic.h" +#include "grp/follow.h" #include "handler.h" #include diff --git a/src/db.cpp b/src/db.cpp index ddcacca63..91cf4db6f 100644 --- a/src/db.cpp +++ b/src/db.cpp @@ -2704,7 +2704,7 @@ void boot_db(void) boot_profiler.next_step("Loading grouping parameters"); log("Booting grouping parameters"); - if (grouping.init()) + if (GlobalObjects::groupRoster().grouping.init()) { exit(1); } diff --git a/src/fightsystem/fight_stuff.cpp b/src/fightsystem/fight_stuff.cpp index 676237d69..93745e432 100644 --- a/src/fightsystem/fight_stuff.cpp +++ b/src/fightsystem/fight_stuff.cpp @@ -793,14 +793,6 @@ void raw_kill(CHAR_DATA *ch, CHAR_DATA *killer) } -int grouping_koef(int player_class, int player_remort) -{ - if ((player_class >= NUM_PLAYER_CLASSES) || (player_class < 0)) - return 1; - return grouping[player_class][player_remort]; - -} - void gain_battle_exp(CHAR_DATA *ch, CHAR_DATA *victim, int dam) { // не даем получать батлу с себя по зеркалу? diff --git a/src/grp/follow.h b/src/grp/follow.h index 3afa1c2dc..aa83943c7 100644 --- a/src/grp/follow.h +++ b/src/grp/follow.h @@ -4,5 +4,6 @@ #include "chars/char.hpp" void do_follow(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/); +bool circle_follow(CHAR_DATA * ch, CHAR_DATA * victim); #endif //BYLINS_FOLLOW_H diff --git a/src/grp/grouppenalties.cpp b/src/grp/grouppenalties.cpp index c3d9f49d1..87b9159e2 100644 --- a/src/grp/grouppenalties.cpp +++ b/src/grp/grouppenalties.cpp @@ -18,12 +18,15 @@ #include "sysdep.h" #include "utils.h" +extern GroupRoster& groupRoster; + /* Берет misc/grouping, первый столбик цифр считает номерами мортов, остальные столбики - значение макс. разрыва в уровнях для конкретного класса. На момент написания этого в конфиге присутствует 26 строк, макс. морт равен 50 - строки с мортами с 26 по 50 копируются с 25-мортовой строки. */ + int GroupPenalties::init() { int clss = 0, remorts = 0, rows_assigned = 0, levels = 0, pos = 0, max_rows = MAX_REMORT+1; @@ -108,4 +111,10 @@ int GroupPenalties::init() return 0; } -GroupPenalties grouping; ///< TODO: get rid of this global variable. +int grouping_koef(int player_class, int player_remort) +{ + if ((player_class >= NUM_PLAYER_CLASSES) || (player_class < 0)) + return 1; + return groupRoster.grouping[player_class][player_remort]; + +} \ No newline at end of file diff --git a/src/grp/grp.group.cpp b/src/grp/grp.group.cpp index 06f4c68f5..ca81106d6 100644 --- a/src/grp/grp.group.cpp +++ b/src/grp/grp.group.cpp @@ -909,7 +909,7 @@ void group_gain(CHAR_DATA * killer, CHAR_DATA * victim) } } - GroupPenaltyCalculator group_penalty(killer, leader, maxlevel, grouping); + GroupPenaltyCalculator group_penalty(killer, leader, maxlevel, groupRoster.grouping); koef -= group_penalty.get(); koef = MAX(0, koef); diff --git a/src/grp/grp.main.h b/src/grp/grp.main.h index edf8561a9..d264f2b02 100644 --- a/src/grp/grp.main.h +++ b/src/grp/grp.main.h @@ -122,6 +122,44 @@ class Group { }; +// это писал йобаный наркоман +class GroupPenalties +{ +public: + using class_penalties_t = std::array; + using penalties_t = std::array; + + int init(); + const auto& operator[](const size_t character_class) const { return m_grouping[character_class]; } + +private: + penalties_t m_grouping; +}; + +class GroupPenaltyCalculator +{ +public: + constexpr static int DEFAULT_PENALTY = 100; + + GroupPenaltyCalculator(const CHAR_DATA* killer, const CHAR_DATA* leader, const int max_level, const GroupPenalties& grouping): + m_killer(killer), + m_leader(leader), + m_max_level(max_level), + m_grouping(grouping) + { + } + + int get() const; + +private: + const CHAR_DATA* m_killer; + const CHAR_DATA* m_leader; + const int m_max_level; + const GroupPenalties& m_grouping; + + bool penalty_by_leader(const CHAR_DATA* player, int& penalty) const; +}; + class Request { public: sclock_t _time; @@ -164,44 +202,9 @@ class GroupRoster { void acceptInvite(CHAR_DATA* who, char* author); void deleteRequest(Request * r); Request* findRequest(const char* targetPerson, const char* group, RQ_TYPE type); + GroupPenalties grouping; }; -class GroupPenalties -{ -public: - using class_penalties_t = std::array; - using penalties_t = std::array; - - int init(); - const auto& operator[](const size_t character_class) const { return m_grouping[character_class]; } - -private: - penalties_t m_grouping; -}; - -class GroupPenaltyCalculator -{ -public: - constexpr static int DEFAULT_PENALTY = 100; - - GroupPenaltyCalculator(const CHAR_DATA* killer, const CHAR_DATA* leader, const int max_level, const GroupPenalties& grouping): - m_killer(killer), - m_leader(leader), - m_max_level(max_level), - m_grouping(grouping) - { - } - - int get() const; - -private: - const CHAR_DATA* m_killer; - const CHAR_DATA* m_leader; - const int m_max_level; - const GroupPenalties& m_grouping; - - bool penalty_by_leader(const CHAR_DATA* player, int& penalty) const; -}; -extern GroupPenalties grouping; +//extern GroupPenalties grouping; #endif //BYLINS_GRP_MAIN_H diff --git a/src/spells.cpp b/src/spells.cpp index 160395a3e..577e371d9 100644 --- a/src/spells.cpp +++ b/src/spells.cpp @@ -14,45 +14,25 @@ #include "spells.h" -#include "skills/townportal.h" -#include "cmd/cmd.generic.h" - -#include "coredump.hpp" -#include "world.objects.hpp" -#include "object.prototypes.hpp" #include "char_obj_utils.inl" -#include "obj.hpp" -#include "comm.h" -#include "skills.h" +#include "chars/world.characters.hpp" +#include "depot.hpp" +#include "fightsystem/mobact.hpp" +#include "fightsystem/pk.h" +#include "grp/follow.h" #include "handler.h" -#include "db.h" -#include "constants.h" -#include "interpreter.h" -#include "dg/dg_scripts.h" -#include "screen.h" #include "house.h" -#include "fightsystem/pk.h" -#include "features.hpp" -#include "im.h" -#include "deathtrap.hpp" -#include "privilege.hpp" -#include "chars/char.hpp" -#include "depot.hpp" -#include "parcel.hpp" #include "liquid.hpp" -#include "modify.h" -#include "room.hpp" -#include "birth_places.hpp" -#include "obj_sets.hpp" -#include "logger.hpp" #include "magic.h" -#include "structs.h" -#include "sysdep.h" -#include "conf.h" -#include "chars/world.characters.hpp" -#include "zone.table.hpp" +#include "object.prototypes.hpp" +#include "parcel.hpp" +#include "privilege.hpp" +#include "screen.h" #include "skills/flee.h" -#include "fightsystem/mobact.hpp" +#include "skills/townportal.h" +#include "world.objects.hpp" +#include "zone.table.hpp" + #include #include diff --git a/src/utils.h b/src/utils.h index 7bcedac8f..d7d7a044c 100644 --- a/src/utils.h +++ b/src/utils.h @@ -205,9 +205,6 @@ char * CAP(char *txt); #define AtoK(c) ((ubyte)(c) < 128 ? (c) : AltToKoi[(ubyte)(c)-128]) #define AtoL(c) ((ubyte)(c) < 128 ? (c) : AltToLat[(ubyte)(c)-128]) -// in magic.cpp // -bool circle_follow(CHAR_DATA * ch, CHAR_DATA * victim); - // in act.informative.cpp // void look_at_room(CHAR_DATA * ch, int mode); From d13d73e00778d70ba0ea9033582998d33ea25a99 Mon Sep 17 00:00:00 2001 From: bikbai Date: Wed, 6 Jan 2021 19:06:56 +0300 Subject: [PATCH 18/40] =?UTF-8?q?=D0=A3=D0=BB=D1=83=D1=87=D1=88=D0=B8?= =?UTF-8?q?=D0=B7=D0=BC=D1=8B:?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit исправил и доделал замакс. перевёл проверки по коду на SAME_ROOM --- src/act.informative.cpp | 4 +- src/act.movement.cpp | 2 +- src/auction.cpp | 6 +- src/chars/char.cpp | 4 +- src/chars/char.hpp | 3 +- src/cmd/quit.cpp | 2 +- src/dg/dg_scripts.cpp | 7 +- src/dps.cpp | 9 +- src/ext_money.cpp | 11 +- src/features.cpp | 2 +- src/fightsystem/fight.cpp | 4 +- src/fightsystem/fight_hit.cpp | 10 +- src/fightsystem/fight_stuff.cpp | 135 ++++-------- src/fightsystem/mobact.cpp | 2 +- src/fightsystem/pk.cpp | 25 +-- src/grp/grouppenaltycalculator.cpp | 4 +- src/grp/grp.cmdprocessor.cpp | 4 +- src/grp/grp.group.cpp | 326 +++++++++++++---------------- src/grp/grp.main.h | 43 ++-- src/grp/grp.roster.cpp | 4 +- src/magic.cpp | 8 +- src/msdp.reporters.cpp | 2 +- src/objsave.cpp | 1 - src/spec_procs.cpp | 7 +- src/spells.cpp | 7 +- 25 files changed, 270 insertions(+), 362 deletions(-) diff --git a/src/act.informative.cpp b/src/act.informative.cpp index f17523f62..0bbb387c8 100644 --- a/src/act.informative.cpp +++ b/src/act.informative.cpp @@ -1283,7 +1283,7 @@ void list_one_char(CHAR_DATA * i, CHAR_DATA * ch, int skill_mode) if (IS_NPC(i) && i->player_data.long_descr != "" && GET_POS(i) == GET_DEFAULT_POS(i) - && ch->in_room == i->in_room + && SAME_ROOM(ch, i) && !AFF_FLAGGED(i, EAffectFlag::AFF_CHARM) && !IS_HORSE(i)) { @@ -1688,7 +1688,7 @@ void list_char_to_char(const ROOM_DATA::people_t& list, CHAR_DATA* ch) list_one_char(i, ch, 0); } else if (IS_DARK(i->in_room) - && i->in_room == ch->in_room + && SAME_ROOM(ch, i) && !CAN_SEE_IN_DARK(ch) && AFF_FLAGGED(i, EAffectFlag::AFF_INFRAVISION)) { diff --git a/src/act.movement.cpp b/src/act.movement.cpp index 94c77ab29..0e3bcd320 100644 --- a/src/act.movement.cpp +++ b/src/act.movement.cpp @@ -420,7 +420,7 @@ int legal_dir(CHAR_DATA * ch, int dir, int need_specials_check, int show_msg) // charmed if (AFF_FLAGGED(ch, EAffectFlag::AFF_CHARM) && ch->has_master() - && ch->in_room == ch->get_master()->in_room) + && SAME_ROOM(ch, ch->get_master())) { if (show_msg) { diff --git a/src/auction.cpp b/src/auction.cpp index 789398bb7..a8dd5e891 100644 --- a/src/auction.cpp +++ b/src/auction.cpp @@ -646,7 +646,7 @@ void trans_auction(int lot) return; } - if (ch->in_room == IN_ROOM(tch)) + if (SAME_ROOM(ch, tch)) { // Проверка на нахождение в одной комнате. tmpstr = "$n стоит рядом с вами."; @@ -788,9 +788,7 @@ void sell_auction(int lot) if (!check_sell(lot)) return; - if (ch->in_room != IN_ROOM(tch) - || !ROOM_FLAGGED(ch->in_room, ROOM_PEACEFUL)) - { + if (!(SAME_ROOM(ch, tch)) || !ROOM_FLAGGED(ch->in_room, ROOM_PEACEFUL)) { if (GET_LOT(lot)->tact >= MAX_AUCTION_TACT_PRESENT) { sprintf(tmpbuff, "Аукцион : лот %d(%s) снят с аукциона распорядителем торгов.", lot, obj->get_PName(0).c_str()); diff --git a/src/chars/char.cpp b/src/chars/char.cpp index f706172f5..7e7a30a91 100644 --- a/src/chars/char.cpp +++ b/src/chars/char.cpp @@ -2037,7 +2037,7 @@ void CHAR_DATA::removeGroupFlags() { AFF_FLAGS(this).unset(EAffectFlag::AFF_GROUP); PRF_FLAGS(this).unset(PRF_SKIRMISHER); if (personGroup != nullptr && IS_CHARMICE(this)) - personGroup->_removeMember(this); + personGroup->_removeMember(this->get_uid()); } void CHAR_DATA::add_follower(CHAR_DATA* ch) { @@ -2376,7 +2376,7 @@ bool CHAR_DATA::has_horse(bool same_room) const { for (f = this->followers; f; f = f->next) { if (IS_NPC(f->follower) && AFF_FLAGGED(f->follower, EAffectFlag::AFF_HORSE) - && (!same_room || this->in_room == IN_ROOM(f->follower))) { + && (!same_room || SAME_ROOM(this, f->follower))) { return true; } } diff --git a/src/chars/char.hpp b/src/chars/char.hpp index 772f58f22..6033a12a0 100644 --- a/src/chars/char.hpp +++ b/src/chars/char.hpp @@ -17,6 +17,7 @@ #include "conf.h" #include "core/affect_data.h" + #include #include @@ -756,7 +757,7 @@ class CHAR_DATA : public ProtectedCharacterData int souls; public: - bool isInSameRoom(const CHAR_DATA *ch) const {return (this->in_room == ch->in_room);}; + bool isInSameRoom(const CHAR_DATA *ch) const {return SAME_ROOM(this, ch);}; room_rnum in_room; // Location (real room number) private: diff --git a/src/cmd/quit.cpp b/src/cmd/quit.cpp index 7fa0ca172..bd9175481 100644 --- a/src/cmd/quit.cpp +++ b/src/cmd/quit.cpp @@ -57,7 +57,7 @@ void do_quit(CHAR_DATA *ch, char *argument, int/* cmd*/, int subcmd) Depot::exit_char(ch); Clan::clan_invoice(ch, false); if (ch->personGroup) - ch->personGroup->_removeMember(ch); + ch->personGroup->_removeMember(ch->get_uid()); /* * kill off all sockets connected to the same player as the one who is diff --git a/src/dg/dg_scripts.cpp b/src/dg/dg_scripts.cpp index 2561afce8..3b0be17b0 100644 --- a/src/dg/dg_scripts.cpp +++ b/src/dg/dg_scripts.cpp @@ -3084,9 +3084,10 @@ void find_replacement(void* go, SCRIPT_DATA* sc, TRIG_DATA* trig, int type, char if (!IN_GROUP(c)) { return; } - for (auto it : c->personGroup->getMembers()) { - if (it) - sprintf(str + strlen(str), "%c%ld ", UID_CHAR, GET_ID(it)); + for (auto &it : *c->personGroup) { + // мы же про одну комнату в триггере говорим, да? + if (it.second->member != nullptr && IN_ROOM(c) == IN_ROOM(it.second->member) ) + sprintf(str + strlen(str), "%c%ld ", UID_CHAR, GET_ID(it.second->member)); } } else if (!str_cmp(field, "attackers")) diff --git a/src/dps.cpp b/src/dps.cpp index 6400782b1..e01a4c6ce 100644 --- a/src/dps.cpp +++ b/src/dps.cpp @@ -201,10 +201,10 @@ unsigned tmp_total_dmg = 0; void Dps::add_tmp_group_list(CHAR_DATA *ch) { - if (!ch->personGroup) return; + if (ch == nullptr || !ch->personGroup) return; DpsSystem::GroupListType* group_dps_; group_dps_ = &ch->personGroup->_group_dps; - GroupListType::iterator it = group_dps_->find(GET_ID(ch)); + auto it = group_dps_->find(GET_ID(ch)); if (it != group_dps_->end()) { sort_node tmp_node(it->second.get_name(), it->second.get_stat(), @@ -264,9 +264,8 @@ void Dps::print_group_stats(CHAR_DATA *ch, CHAR_DATA *coder) send_to_char("\r\nСтатистика вашей группы:\r\n" "---------------------------|--------------------|----------------|-------------|\r\n", coder); - auto groupList = ch->personGroup->getMembers(); - for (auto it : groupList) { - add_tmp_group_list(it); + for (auto &it : *ch->personGroup) { + add_tmp_group_list(it.second->member); } std::string out; diff --git a/src/ext_money.cpp b/src/ext_money.cpp index 65d866c0a..8e4c10dd9 100644 --- a/src/ext_money.cpp +++ b/src/ext_money.cpp @@ -682,7 +682,7 @@ namespace ExtMoney CHAR_DATA *leader = grp != nullptr? d->character->personGroup->getLeader() : d->character.get(); int members = 1; - members = leader->personGroup->size(mob->in_room); + members = leader->personGroup->size(IN_ROOM(mob)); const int zone_lvl = zone_table[mob_index[GET_MOB_RNUM(mob)].zone].mob_level; const int drop = calc_drop_torc(zone_lvl, members); @@ -700,10 +700,11 @@ namespace ExtMoney // он был один, сваливаем if (grp == nullptr) return; - auto m_list = grp->getMembers(mob->in_room); // список живых мемберов в комнате - for (auto m : m_list) { - if (GET_GOD_FLAG(m, GF_REMORT) && mob->get_attacker(m, ATTACKER_ROUNDS) >= damager.second / 2) { - gain_torc(m, drop); + for (const auto& m : *grp) { + if (m.second->member == nullptr || IN_ROOM(mob) != IN_ROOM(m.second->member)) + return; + if (GET_GOD_FLAG(m.second->member, GF_REMORT) && mob->get_attacker(m.second->member, ATTACKER_ROUNDS) >= damager.second / 2) { + gain_torc(m.second->member, drop); } } } diff --git a/src/features.cpp b/src/features.cpp index 725e8c293..fd47a787a 100644 --- a/src/features.cpp +++ b/src/features.cpp @@ -1252,7 +1252,7 @@ void do_spell_capable(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/ && k->follower->get_master() == ch && MOB_FLAGGED(k->follower, MOB_CLONE) && !affected_by_spell(k->follower, SPELL_CAPABLE) - && ch->in_room == IN_ROOM(k->follower)) + && SAME_ROOM(ch, k->follower)) { follower = k->follower; break; diff --git a/src/fightsystem/fight.cpp b/src/fightsystem/fight.cpp index d603aaebd..e9c961651 100644 --- a/src/fightsystem/fight.cpp +++ b/src/fightsystem/fight.cpp @@ -2005,7 +2005,7 @@ void process_npc_attack(CHAR_DATA *ch) && ch->has_master() // && !IS_NPC(ch->master) && CAN_SEE(ch, ch->get_master()) - && ch->in_room == IN_ROOM(ch->get_master()) + && SAME_ROOM(ch, ch->get_master()) && AWAKE(ch) && MAY_ACT(ch) && GET_POS(ch) >= POS_FIGHTING) @@ -2290,7 +2290,7 @@ void perform_violence() auto master = ch->get_master(); if (master->desc && !master->get_fighting() - && master->in_room == ch->in_room) + && SAME_ROOM(master, ch)) { msdp_report_chars.insert(master); } diff --git a/src/fightsystem/fight_hit.cpp b/src/fightsystem/fight_hit.cpp index 019dd09f3..05e26ac0b 100644 --- a/src/fightsystem/fight_hit.cpp +++ b/src/fightsystem/fight_hit.cpp @@ -70,7 +70,7 @@ void aff_group_inspiration(CHAR_DATA *ch, EApplyLocation num_apply, int time, in k = ch; } // на лидера - if (ch->in_room == k->in_room) { + if (SAME_ROOM(ch, k)) { af.location = num_apply; af.type = SPELL_PALADINE_INSPIRATION; af.modifier = GET_REMORT(k) / 5 * 2 + modi; @@ -2405,9 +2405,9 @@ void try_angel_sacrifice(CHAR_DATA* ch, CHAR_DATA* victim) { // если виктим в группе с кем-то с ангелом - вместо смерти виктима умирает ангел if (GET_HIT(victim) <= 0 && !IS_NPC(victim) && IN_GROUP(victim)) { // ищем первого попавшегося ангела из группы в комнате - for (auto npc : *victim->personGroup->getCharmee(victim->in_room)){ - if (MOB_FLAGGED(npc, MOB_ANGEL)) { - angel = npc; + for (const auto& npc : *victim->personGroup){ + if (SAME_ROOM(npc.second->member, victim) && MOB_FLAGGED(npc.second->member, MOB_ANGEL) ){ + angel = npc.second->member; break; } } @@ -3905,7 +3905,7 @@ void hit(CHAR_DATA *ch, CHAR_DATA *victim, ESkill type, FightSystem::AttType wea return; } // Do some sanity checking, in case someone flees, etc. - if (ch->in_room != IN_ROOM(victim) || ch->in_room == NOWHERE) { + if (!SAME_ROOM(ch, victim) || ch->in_room == NOWHERE) { if (ch->get_fighting() && ch->get_fighting() == victim) { stop_fighting(ch, TRUE); } diff --git a/src/fightsystem/fight_stuff.cpp b/src/fightsystem/fight_stuff.cpp index 93745e432..8aa8cec17 100644 --- a/src/fightsystem/fight_stuff.cpp +++ b/src/fightsystem/fight_stuff.cpp @@ -57,92 +57,50 @@ const unsigned RECALL_SPELLS_INTERVAL = 28; using namespace ExpCalc; -void process_mobmax(CHAR_DATA *ch, CHAR_DATA *killer) +void process_mobmax(CHAR_DATA *mob, CHAR_DATA *killer) { - bool leader_partner = false; - int partner_feat = 0; - int total_group_members = 1; - CHAR_DATA *partner = nullptr; - - CHAR_DATA *master = nullptr; - if (IS_NPC(killer) - && (AFF_FLAGGED(killer, EAffectFlag::AFF_CHARM) - || MOB_FLAGGED(killer, MOB_ANGEL) - || MOB_FLAGGED(killer, MOB_GHOST)) - && killer->has_master()) - { - master = killer->get_master(); - } - else if (!IS_NPC(killer)) - { - master = killer; - } - - // На этот момент master - PC - if (master) - { - int cnt = 0; - if (AFF_FLAGGED(master, EAffectFlag::AFF_GROUP)) - { - - // master - член группы, переходим на лидера группы - if (master->has_master()) - { - master = master->get_master(); - } - - if (IN_ROOM(master) == IN_ROOM(killer)) - { - // лидер группы в тойже комнате, что и убивец - cnt = 1; - if (can_use_feat(master, PARTNER_FEAT)) - { - leader_partner = true; - } - } - - for (struct follow_type *f = master->followers; f; f = f->next) - { - if (AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP)) ++total_group_members; - if (AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP) - && IN_ROOM(f->follower) == IN_ROOM(killer)) - { - ++cnt; - if (leader_partner) - { - if (!IS_NPC(f->follower)) - { - partner_feat++; - partner = f->follower; - } - } - } - } - } - - // обоим замакс, если способность напарник работает - // получается замакс идет в 2 раза быстрее, чем без способности в той же группе - if (leader_partner - && partner_feat == 1 && total_group_members == 2) - { - master->mobmax_add(master, GET_MOB_VNUM(ch), 1, GET_LEVEL(ch)); - partner->mobmax_add(partner, GET_MOB_VNUM(ch), 1, GET_LEVEL(ch)); - } else { - // выберем случайным образом мембера группы для замакса - auto n = number(0, cnt); - int i = 0; - for (struct follow_type *f = master->followers; f && i < n; f = f->next) - { - if (AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP) - && IN_ROOM(f->follower) == IN_ROOM(killer)) - { - ++i; - master = f->follower; - } - } - master->mobmax_add(master, GET_MOB_VNUM(ch), 1, GET_LEVEL(ch)); - } + CHAR_DATA* leader = nullptr; // лидер, кому даём замакс, если в группе + CHAR_DATA* person = nullptr; // персонаж, кому даём замакс + bool hasPartnerFeat = false; + u_short groupMembersCount = 0; // сколько персонажей в комнате, за вычетом лидера + + // если убивец - чармис, пытаемся найти его хозяина + if (killer->has_master() + && (IS_CHARMICE(killer) || MOB_FLAGGED(killer, MOB_ANGEL) || MOB_FLAGGED(killer, MOB_GHOST))) { + // транслируем чармиса в хозяина + person = killer->get_master(); + } else { + person = killer; + } + // определяем наличие группы + if(IN_GROUP(killer)) { + leader = killer->personGroup->getLeader(); + } + + if (leader == nullptr || !can_use_feat(leader, PARTNER_FEAT) || !SAME_ROOM(leader, mob)) { + // убивец не в группе + // или лидер группы без партнёрки, или лидера нет в комнате + // и таки да, он в комнате с мобом + if (SAME_ROOM(person, mob)) + person->mobmax_add(person, GET_MOB_VNUM(mob), 1, GET_LEVEL(mob)); } + else { // всё хорошо, из группы есть минимум лидер + // смотрим сколько _живых_ игроков из группы щас в зоне, помимо лидера + // сразу пишем в вектор + std::vector membersInRoom; + for (auto& grp: *person->personGroup) { + if (grp.second->type == GM_CHARMEE || grp.second->member == nullptr || grp.second->member->purged()) + continue; + if (SAME_ROOM(grp.second->member, mob) && grp.second->member != leader) + membersInRoom.push_back(grp.second->member); + } + // лепим удвоенные замаксы лидеру и рандомному персонажу. При парном зонинге попадутся оба + if (!membersInRoom.empty()) { + leader->mobmax_add(leader, GET_MOB_VNUM(mob), 2, GET_LEVEL(mob)); + int rnd = number(0, membersInRoom.size()-1); + membersInRoom[rnd]->mobmax_add(membersInRoom[rnd], GET_MOB_VNUM(mob), 2, GET_LEVEL(mob)); + } + } } void gift_new_year(CHAR_DATA* /*vict*/) @@ -277,7 +235,7 @@ void update_leadership(CHAR_DATA *ch, CHAR_DATA *killer) && IS_NPC(killer) && AFF_FLAGGED(ch, EAffectFlag::AFF_GROUP) && ch->has_master() - && ch->in_room == IN_ROOM(ch->get_master()) + && SAME_ROOM(ch, ch->get_master()) && ch->get_master()->get_inborn_skill(SKILL_LEADERSHIP) > 1) { const auto current_skill = ch->get_master()->get_trained_skill(SKILL_LEADERSHIP); @@ -383,8 +341,7 @@ void die(CHAR_DATA *ch, CHAR_DATA *killer) // Вычисляем замакс по мобам // Решил немножко переделать, чтобы короче получилось, // кроме того, исправил ошибку с присутствием лидера в комнате - if (IS_NPC(ch) && killer) - { + if (IS_NPC(ch) && killer) { process_mobmax(ch, killer); } if (killer) @@ -569,7 +526,7 @@ void auto_loot(CHAR_DATA *ch, CHAR_DATA *killer, OBJ_DATA *corpse, int local_gol || MOB_FLAGGED(killer, MOB_GHOST)) && (corpse != NULL) && killer->has_master() - && killer->in_room == killer->get_master()->in_room + && SAME_ROOM(killer, killer->get_master()) && PRF_FLAGGED(killer->get_master(), PRF_AUTOLOOT) && can_loot(killer->get_master())) { @@ -584,7 +541,7 @@ void auto_loot(CHAR_DATA *ch, CHAR_DATA *killer, OBJ_DATA *corpse, int local_gol || MOB_FLAGGED(killer, MOB_GHOST)) && (corpse != NULL) && killer->has_master() - && killer->in_room == killer->get_master()->in_room + && SAME_ROOM(killer, killer->get_master()) && PRF_FLAGGED(killer->get_master(), PRF_AUTOMONEY) && can_loot(killer->get_master())) { diff --git a/src/fightsystem/mobact.cpp b/src/fightsystem/mobact.cpp index 761f89456..1ec4cc8db 100644 --- a/src/fightsystem/mobact.cpp +++ b/src/fightsystem/mobact.cpp @@ -512,7 +512,7 @@ bool find_master_charmice(CHAR_DATA *charmice) return true; } - if (charmice->in_room == charmice->get_master()->in_room) + if (SAME_ROOM(charmice, charmice->get_master()) { return true; } diff --git a/src/fightsystem/pk.cpp b/src/fightsystem/pk.cpp index 28a01fa9d..a3c851079 100644 --- a/src/fightsystem/pk.cpp +++ b/src/fightsystem/pk.cpp @@ -344,32 +344,19 @@ int pk_increment_revenge(CHAR_DATA * agressor, CHAR_DATA * victim) { } void pk_increment_gkill(CHAR_DATA * agressor, CHAR_DATA * victim) { - if (!AFF_FLAGGED(victim, EAffectFlag::AFF_GROUP)) { + if (!IN_GROUP(victim)){ pk_increment_kill(agressor, victim, TRUE, false); return; } - - - CHAR_DATA *leader; - struct follow_type *f; bool has_clanmember = false; if (!IS_GOD(victim)) { has_clanmember = has_clan_members_in_group(victim); } - - leader = victim->has_master() ? victim->get_master() : victim; - - if (AFF_FLAGGED(leader, EAffectFlag::AFF_GROUP) - && IN_ROOM(leader) == IN_ROOM(victim) - && pk_action_type(agressor, leader) > PK_ACTION_FIGHT) { - pk_increment_kill(agressor, leader, leader == victim, has_clanmember); - } - for (f = leader->followers; f; f = f->next) { - if (AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP) - && IN_ROOM(f->follower) == IN_ROOM(victim) - && pk_action_type(agressor, f->follower) > PK_ACTION_FIGHT) { - pk_increment_kill(agressor, f->follower, f->follower == victim, has_clanmember); - } + auto grp = victim->personGroup->getMembers(IN_ROOM(victim), false); + for (auto it : grp){ + if (pk_action_type(agressor, it) > PK_ACTION_FIGHT) { + pk_increment_kill(agressor, it, it == victim, has_clanmember); + } } } diff --git a/src/grp/grouppenaltycalculator.cpp b/src/grp/grouppenaltycalculator.cpp index 2d1363165..60ec611b4 100644 --- a/src/grp/grouppenaltycalculator.cpp +++ b/src/grp/grouppenaltycalculator.cpp @@ -7,8 +7,8 @@ int GroupPenaltyCalculator::get() const { const bool leader_is_npc = IS_NPC(m_leader); const bool leader_in_group = AFF_FLAGGED(m_leader, EAffectFlag::AFF_GROUP); - const bool leader_is_in_room = leader_in_group - && m_leader->in_room == IN_ROOM(m_killer); + const bool leader_is_in_room = leader_in_group && SAME_ROOM(m_leader, m_killer); + if (!leader_is_npc && leader_is_in_room) { diff --git a/src/grp/grp.cmdprocessor.cpp b/src/grp/grp.cmdprocessor.cpp index ee86ebc67..3d9e0f5fc 100644 --- a/src/grp/grp.cmdprocessor.cpp +++ b/src/grp/grp.cmdprocessor.cpp @@ -630,7 +630,7 @@ void do_split(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/,int cur k = ch->has_master() ? ch->get_master() : ch; if (AFF_FLAGGED(k, EAffectFlag::AFF_GROUP) - && (k->in_room == ch->in_room)) + && (SAME_ROOM(k, ch))) { num = 1; } @@ -643,7 +643,7 @@ void do_split(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/,int cur { if (AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP) && !IS_NPC(f->follower) - && IN_ROOM(f->follower) == ch->in_room) + && SAME_ROOM(f->follower) == ch->in_room) { num++; } diff --git a/src/grp/grp.group.cpp b/src/grp/grp.group.cpp index ca81106d6..9e63549b1 100644 --- a/src/grp/grp.group.cpp +++ b/src/grp/grp.group.cpp @@ -27,7 +27,7 @@ using namespace ExpCalc; extern GroupRoster& groupRoster; extern int max_exp_gain_npc; -extern zone_table_t& zone_table; + const char *WORD_STATE[] = { "При смерти", "Оч.тяж.ран", @@ -71,8 +71,6 @@ Group::Group(CHAR_DATA *leader, u_long uid){ _leaderUID = 0; // чтобы не ругался clion =) _memberCap = 0; _uid = uid; - _memberList = new std::map>; - _npcRoster = new std::unordered_set; addMember(leader, true); _setLeader(leader); } @@ -82,25 +80,25 @@ CHAR_DATA *Group::getLeader() const{ } int Group::_findMember(char *memberName) { - for (auto & it : *_memberList) { - if (isname((*it.second)->memberName, memberName)) + for (auto & it : *this) { + if (isname(it.second->memberName, memberName)) return it.first; } return 0; } CHAR_DATA* Group::_findMember(int uid) { - for (auto & it : *_memberList) { - if ((*it.second)->memberUID == uid) - return (*it.second)->member; + for (auto & it : *this) { + if (it.second->memberUID == uid) + return it.second->member; } return nullptr; } const char* Group::_getMemberName(int uid) { - for (auto & it : *_memberList) { - if ((*it.second)->memberUID == uid) - return (*it.second)->memberName.c_str(); + for (auto & it : *this) { + if (it.second->memberUID == uid) + return it.second->memberName.c_str(); } return nullptr; } @@ -120,10 +118,6 @@ u_long Group::getUid() const { return _uid; } -u_short Group::getCurrentMemberCount() const { - return (u_short)_memberList->size(); -} - int Group::_getMemberCap() const { return _memberCap; } @@ -133,7 +127,7 @@ const std::string &Group::getLeaderName() const { } bool Group::_isFull() { - if ((u_short)_memberList->size() >= (u_short)_memberCap) + if ((u_short)this->size() >= (u_short)_memberCap) return true; return false; } @@ -152,11 +146,12 @@ Group::~Group() { mudlog("~Group", BRF, LVL_IMMORT, SYSLOG, TRUE); } -char_info::char_info(int uid, CHAR_DATA *m, const std::string& name) { - memberUID = uid; - member = m; - memberName.assign(name); - expiryTime = steady_clock::now() + DEF_EXPIRY_TIME; +char_info::char_info(int uid, GM_TYPE type, CHAR_DATA *m, const std::string& name) { + this->memberUID = uid; + this->type = type; + this->member = m; + this->memberName.assign(name); + this->expiryTime = steady_clock::now() + DEF_EXPIRY_TIME; } char_info::~char_info() { @@ -168,78 +163,61 @@ void Group::addMember(CHAR_DATA *member, bool silent) { return; // в другой группе, вышвыриваем if (member->personGroup != nullptr) - member->personGroup->_removeMember(member); - if (IS_CHARMICE(member)) { - auto it = _npcRoster->find(member); - if (it == _npcRoster->end()) - _npcRoster->emplace(member); - } else { - auto it = _memberList->find(member->get_uid()); - if (it == _memberList->end()) { - // рисуем сообщение до добавления - if (!silent) { - actToGroup(member, GRP_COMM_ALL, "$N принят$A в группу."); - send_to_char(member, "Вас приняли в группу.\r\n"); - } - auto ci = new char_info(member->get_uid(), member, member->get_pc_name()); - _memberList->emplace(member->get_uid(), std::make_shared(ci)); - } else { - sprintf(buf, "Group::addMember: группа id=%lu, попытка повторного добавления персонажа с тем же uid", - getUid()); - mudlog(buf, BRF, LVL_IMMORT, SYSLOG, TRUE); - return; - } - member->personGroup = this; - auto r = groupRoster.findRequest(member->get_pc_name().c_str(), getLeaderName().c_str(), RQ_ANY); - if (r) - groupRoster.deleteRequest(r); - // добавляем чармисов группу - for (auto f = member->followers; f; f = f->next) { - auto ff = f->follower; - if (IS_CHARMICE(ff)) { - this->addMember(ff); // рекурсия! - } + member->personGroup->_removeMember(member->get_uid()); + + auto it = this->find(member->get_uid()); + if (it == this->end()) { + // рисуем сообщение до добавления, чтобы самому персонажу не показывать + if (!silent) { + actToGroup(member, GRP_COMM_ALL, "$N принят$A в группу."); + send_to_char(member, "Вас приняли в группу.\r\n"); } + auto ci = char_info(member->get_uid(), getType(member), member, member->get_pc_name()); + this->emplace(member->get_uid(), std::make_shared(ci)); + } else { + sprintf(buf, "Group::addMember: группа id=%lu, попытка повторного добавления персонажа с тем же uid", + getUid()); + mudlog(buf, BRF, LVL_IMMORT, SYSLOG, TRUE); + return; } - -} - -bool Group::_restoreMember(CHAR_DATA *member) { - if (!member) - return false; - for (auto &it: *_memberList){ - if (it.first == member->get_uid()) { - (*it.second)->member = member; - return true; + member->personGroup = this; + auto r = groupRoster.findRequest(member->get_pc_name().c_str(), getLeaderName().c_str(), RQ_ANY); + if (r) + groupRoster.deleteRequest(r); + // добавляем чармисов группу + for (auto f = member->followers; f; f = f->next) { + auto ff = f->follower; + if (IS_CHARMICE(ff)) { + this->addMember(ff); // рекурсия! } } - return false; } -bool Group::_removeMember(CHAR_DATA *member) { - if (member == nullptr || member->purged()) - return false; - if (_memberList->empty()) { +bool Group::_removeMember(int memberUID) { + if (this->empty()) { sprintf(buf, "Group::_removeMember: попытка удалить из группы при текущем индексе 0, id=%lu", getUid()); mudlog(buf, BRF, LVL_IMMORT, SYSLOG, TRUE); return false; } // удаляем персонажа из группы и ссылку на группу - auto num = _memberList->erase(member->get_uid()); - member->personGroup = nullptr; - if (num == 0){ + auto it = this->find(memberUID); + if (it == this->end()) { sprintf(buf, "Group::_removeMember: персонаж не найден, id=%lu", getUid()); mudlog(buf, BRF, LVL_IMMORT, SYSLOG, TRUE); return false; } + auto member =it->second->member; + member->personGroup = nullptr; + this->erase(memberUID); // finally remove it + // а также удаляем чармисов персонажа из групповых if (member->followers != nullptr) { for (auto f = member->followers; f; f = f->next) { auto ff = f->follower; if (IS_CHARMICE(ff)) { - auto c = _npcRoster->find(ff); - if (c != _npcRoster->end()) { - _npcRoster->erase(c); + auto c = this->find(ff->get_uid()); + if (c != this->end()) { + this->erase(c); ff->personGroup = nullptr; } } @@ -251,8 +229,8 @@ bool Group::_removeMember(CHAR_DATA *member) { int nxtLdrGrpSize = 0; // если ушел лидер - ищем нового, но после ухода предыдущего if ( member == _leader) { - for (const auto & mit : *_memberList){ - auto m = (*mit.second)->member; + for (const auto& mit : *this){ + auto m = mit.second->member; if ( m != nullptr && !m->purged() && max_group_size(m) > nxtLdrGrpSize ) { nxtLdr = m; nxtLdrGrpSize = max_group_size(m); @@ -268,34 +246,40 @@ bool Group::_removeMember(CHAR_DATA *member) { return true; } +bool Group::_restoreMember(CHAR_DATA *member) { + if (!member) + return false; + for (auto &it: *this){ + if (it.first == member->get_uid()) { + it.second->member = member; + member->personGroup = this; + return true; + } + } + return false; +} + bool Group::_isMember(int uid) { - auto it = _memberList->find(uid); - if (it == _memberList->end() ) + auto it = this->find(uid); + if (it == this->end() ) return false; return true; } void Group::_clear(bool silentMode) { - sprintf(buf, "[Group::clear()]: _memberList: %lu", _memberList->size()); + sprintf(buf, "[Group::clear()]: this: %hu", this->size()); mudlog(buf, BRF, LVL_IMMORT, SYSLOG, TRUE); CHAR_DATA* ch; // чистим игроков - for (auto & it : *_memberList) { - ch = (*it.second)->member; + for (auto & it : *this) { + ch = it.second->member; if (ch == nullptr || ch->purged()) continue; if (!silentMode) send_to_char(ch, "Ваша группа распущена.\r\n"); ch->personGroup = nullptr; } - // чистим чармисов - for (auto & it : *_npcRoster) { - it->personGroup = nullptr; - } - _npcRoster->clear(); - _memberList->clear(); - delete _memberList; - delete _npcRoster; + this->clear(); } void Group::promote(char *applicant) { @@ -304,16 +288,16 @@ void Group::promote(char *applicant) { sendToGroup(GRP_COMM_LEADER, "Нет такого персонажа."); return; } - _setLeader((*_memberList->at(memberId))->member); - sendToGroup(GRP_COMM_ALL, "Изменился лидер группы на %.", (*_memberList->at(memberId))->memberName.c_str()); - auto diff = (u_short)_memberCap - (u_short)_memberList->size(); + _setLeader(this->at(memberId)->member); + sendToGroup(GRP_COMM_ALL, "Изменился лидер группы на %.", _leaderName.c_str() ); + auto diff = (u_short)_memberCap - (u_short)this->size(); if (diff < 0) diff = 0; if (diff > 0){ u_short i = 0; - CHAR_DATA* expellList[diff]; - for (auto it = _memberList->begin(); it!= _memberList->end() || i > diff; it++){ - if ((*it->second)->member != _leader ) { - expellList[i] = (*it->second)->member; + int expellList[diff]; + for (auto it = this->begin(); it!= this->end() || i > diff; it++){ + if (it->second->member != _leader ) { + expellList[i] = it->second->member->get_uid(); ++i; } } @@ -353,13 +337,19 @@ void Group::approveRequest(const char *applicant) { void Group::expellMember(char *memberName) { auto mId = _findMember(memberName); - auto vict = (*_memberList->at(mId))->member; - if (mId != 0 && vict != nullptr) { - _removeMember(vict); + if (mId == 0) + return; + auto vict = this->at(mId)->member; // тут может быть пусто.. + if (vict != nullptr) { + _removeMember(vict->get_uid()); act("$N исключен$A из состава вашей группы.", FALSE, _leader, nullptr, vict, TO_CHAR); act("Вы исключены из группы $n1!", FALSE, _leader, nullptr, vict, TO_VICT); act("$N был$G исключен$A из группы $n1!", FALSE, _leader, nullptr, vict, TO_NOTVICT | TO_ARENA_LISTEN); + } else { + } + + } void Group::listMembers(CHAR_DATA *ch) { @@ -370,9 +360,9 @@ void Group::listMembers(CHAR_DATA *ch) { if (ch->personGroup) { leader = ch->personGroup->getLeader(); - for (auto & it : *_memberList ) + for (auto & it : *this ) { - if ((*it.second)->member == leader) + if (it.second->member == leader) continue; count++; if (count == 2){ @@ -380,7 +370,7 @@ void Group::listMembers(CHAR_DATA *ch) { sprintf(smallBuf, "Лидер: %s\r\n", ch->personGroup->getLeaderName().c_str() ); send_to_char(smallBuf, ch); } - sprintf(smallBuf, "%d. Согруппник: %s\r\n", count, (*it.second)->memberName.c_str()); + sprintf(smallBuf, "%d. Согруппник: %s\r\n", count, it.second->memberName.c_str()); send_to_char(smallBuf, ch); } if (count == 1) @@ -397,7 +387,7 @@ void Group::leaveGroup(CHAR_DATA* ch) { send_to_char(ch, "Нельзя стать еще более одиноким, чем сейчас.\r\n"); return; } - _removeMember(ch); + _removeMember(ch->get_uid()); } void Group::_printHeader(CHAR_DATA* ch, bool npc) { @@ -431,7 +421,7 @@ void Group::_printNPCLine(CHAR_DATA* ch, CHAR_DATA* npc, int header) { color_value(ch, GET_HIT(npc), GET_REAL_MAX_HIT(npc)), WORD_STATE[posi_value(GET_HIT(npc), GET_REAL_MAX_HIT(npc)) + 1], CCNRM(ch, C_NRM)); - auto ok = ch->in_room == IN_ROOM(npc); + auto ok = SAME_ROOM(ch, npc); sprintf(buf + strlen(buf), "%s%5s%s|", ok ? CCGRN(ch, C_NRM) : CCRED(ch, C_NRM), ok ? " Да " : " Нет ", CCNRM(ch, C_NRM)); @@ -473,7 +463,7 @@ void Group::_printPCLine(CHAR_DATA* ch, CHAR_DATA* pc, int header) { color_value(ch, GET_MOVE(pc), GET_REAL_MAX_MOVE(pc)), MOVE_STATE[posi_value(GET_MOVE(pc), GET_REAL_MAX_MOVE(pc)) + 1], CCNRM(ch, C_NRM)); - ok = ch->in_room == IN_ROOM(pc); + ok = SAME_ROOM(ch, pc); sprintf(buf + strlen(buf), "%s%5s%s|", ok ? CCGRN(ch, C_NRM) : CCRED(ch, C_NRM), ok ? " Да " : " Нет ", CCNRM(ch, C_NRM)); @@ -539,11 +529,13 @@ void Group::printGroup(CHAR_DATA *ch) { // печатаем группу if (ch->personGroup != nullptr ) { send_to_char("Ваша группа состоит из:\r\n", ch); - for (auto &it : *_memberList ){ - if ((*it.second)->member == nullptr || (*it.second)->member->purged()) - _printDeadLine(ch, (*it.second)->memberName.c_str(), gfound++); + for (auto &it : *this ){ + if (it.second->type == GM_CHARMEE) + continue; // чармисов скипуем и показываем ниже + if (it.second->member == nullptr || it.second->member->purged()) + _printDeadLine(ch, it.second->memberName.c_str(), gfound++); else - _printPCLine(ch, (*it.second)->member, gfound++); + _printPCLine(ch, it.second->member, gfound++); } } @@ -564,26 +556,28 @@ void Group::printGroup(CHAR_DATA *ch) { // печатаем чармисов членов группы if (PRF_FLAGGED(ch, PRF_SHOWGROUP)) { - for (auto &it : *_memberList ) { - for (auto & npc: *_npcRoster) { - // клоны отключены - if (PRF_FLAGGED(ch, PRF_NOCLONES) && - (MOB_FLAGGED(npc, MOB_CLONE) || GET_MOB_VNUM(npc) == MOB_KEEPER) && - npc->get_master() == ch /*этих раньше вывели*/) + for (auto &it : *this ) { + auto npc = it.second->member; + // на всякий пожарный + if (npc == nullptr) continue; // + // только чармисов + if (it.second->type == GM_CHAR) continue; + // клоны отключены + if (PRF_FLAGGED(ch, PRF_NOCLONES) && (MOB_FLAGGED(npc, MOB_CLONE) || GET_MOB_VNUM(npc) == MOB_KEEPER)) continue; - if (!cfound) - send_to_char("Последователи членов вашей группы:\r\n", ch); - _printNPCLine(ch, npc, cfound++); - } - + // чармисов игрока вывели ранее + if (ch == npc->get_master()) continue; + if (!cfound) + send_to_char("Последователи членов вашей группы:\r\n", ch); + _printNPCLine(ch, npc, cfound++); } } } void Group::charDataPurged(CHAR_DATA *ch) { - for (auto &it : *_memberList) { - if (ch == (*(it.second))->member) { - (*(it.second))->member = nullptr; + for (auto &it : *this) { + if (ch == it.second->member) { + it.second->member = nullptr; return; } } @@ -595,8 +589,8 @@ void Group::sendToGroup(GRP_COMM mode, const char *msg, ...) { vsnprintf(smallBuf, sizeof(smallBuf), msg, args); va_end(args); if (mode == GRP_COMM::GRP_COMM_ALL) - for (auto & it : *_memberList ) { - send_to_char((*it.second)->member, "%s%s", smallBuf, "\r\n"); + for (auto & it : *this ) { + send_to_char(it.second->member, "%s%s", smallBuf, "\r\n"); } else if (mode == GRP_COMM_LEADER) { send_to_char(_leader, "%s%s", smallBuf, "\r\n"); @@ -616,8 +610,8 @@ void Group::actToGroup(CHAR_DATA* vict, GRP_COMM mode, const char *msg, ...) { act(smallBuf, FALSE, _leader, nullptr, vict, TO_CHAR); return; } - for (auto &it : *_memberList) { - to = (*(it.second))->member; + for (auto &it : *this) { + to = it.second->member; if ( to == nullptr || to->purged()) { continue; } @@ -629,58 +623,28 @@ void Group::addFollowers(CHAR_DATA *leader) { for (auto f = leader->followers; f; f = f->next) { if (IS_NPC(f->follower)) continue; - if ((u_short)_memberList->size() < (u_short)_memberCap) + if ((u_short)this->size() < (u_short)_memberCap) this->addMember(f->follower); } } // метод может вернуть мусор :( CHAR_DATA* Group::get_random_pc_group() { - u_short rnd = number(0, (u_short)_memberList->size() - 1); + u_short rnd = number(0, (u_short)this->size() - 1); int i = 0; - for (auto it : *_memberList){ + for (const auto& it : *this){ if (i == rnd) { - return (*it.second)->member; // + return it.second->member; // } i++; } return nullptr; } -// room_rnum = 0 - ignore -cd_v Group::getMembers(rnum_t room_rnum, bool includeCharmee) { - cd_v retval; - for (const auto& it : *_memberList) { - auto c = (*it.second)->member; - if (room_rnum == (room_rnum == 0 ? room_rnum : c->in_room) && !c->purged()) - retval.push_back((*it.second)->member); - } - if (includeCharmee) - for (const auto c : *_npcRoster) { - if (room_rnum == (room_rnum == 0 ? room_rnum : c->in_room) && !c->purged()) - retval.push_back(c); - } - return retval; -} - -npc_r Group::getCharmee(rnum_t room_rnum) { - npc_r retval; - if (room_rnum == 0) - return _npcRoster; - else { - retval = new std::unordered_set(); - for (auto &it: *_npcRoster) { - if (it->in_room == room_rnum) - retval->insert(it); - } - return retval; - } -} - u_short Group::size(rnum_t room_rnum) { u_short retval = 0; - for (const auto& it : *_memberList){ - auto c = (*it.second)->member; + for (const auto& it : *this){ + auto c = it.second->member; if (room_rnum == (room_rnum == 0 ? room_rnum : c->in_room) && !c->purged() && retval < 255) retval++; } @@ -688,11 +652,11 @@ u_short Group::size(rnum_t room_rnum) { } bool Group::has_clan_members_in_group(CHAR_DATA *victim) { - for (const auto& it : *_memberList) { - auto gm = (*it.second)->member; - if (gm == nullptr) + for (const auto& it : *this) { + auto gm = it.second->member; + if (gm == nullptr || it.second->type == GM_CHARMEE) continue; - if (CLAN(gm) && gm->in_room == victim->in_room) + if (CLAN(gm) && SAME_ROOM(gm, victim)) return true; } return false; @@ -757,12 +721,10 @@ void perform_group_gain(CHAR_DATA * ch, CHAR_DATA * victim, int members, int koe exp *= Bonus::get_mult_bonus(); } - if (!IS_NPC(ch) && !ch->affected.empty()) - { - for (const auto aff : ch->affected) - { - if (aff->location == APPLY_BONUS_EXP) // скушал свиток с эксп бонусом - { + if (!IS_NPC(ch) && !ch->affected.empty()) { + for (const auto& aff : ch->affected) { + if (aff->location == APPLY_BONUS_EXP) { + // скушал свиток с эксп бонусом exp *= MIN(3, aff->modifier); // бонус макс тройной } } @@ -770,9 +732,8 @@ void perform_group_gain(CHAR_DATA * ch, CHAR_DATA * victim, int members, int koe int long_live_bonus = floor(ExpCalc::get_npc_long_live_exp_bounus( GET_MOB_VNUM(victim))); if (long_live_bonus>1) { - std::string mess = ""; - switch (long_live_bonus) - { + std::string mess; + switch (long_live_bonus) { case 2: mess = "Редкая удача! Опыт повышен!\r\n"; break; @@ -856,24 +817,21 @@ void group_gain(CHAR_DATA * killer, CHAR_DATA * victim) bool use_partner_exp = false; // если наем лидер, то тоже режем экспу - if (can_use_feat(killer, CYNIC_FEAT)) - { + if (can_use_feat(killer, CYNIC_FEAT)) { maxlevel = 300; } - else - { + else { maxlevel = GET_LEVEL(killer); } auto leader = killer->get_master(); - if (nullptr == leader) - { + if (nullptr == leader) { leader = killer; } // k - подозрение на лидера группы const bool leader_inroom = AFF_FLAGGED(leader, EAffectFlag::AFF_GROUP) - && leader->in_room == IN_ROOM(killer); + && SAME_ROOM(leader, killer); // Количество согрупников в комнате if (leader_inroom) { @@ -889,7 +847,7 @@ void group_gain(CHAR_DATA * killer, CHAR_DATA * victim) { if (AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP)) ++total_group_members; if (AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP) - && f->follower->in_room == IN_ROOM(killer)) + && SAME_ROOM(f->follower, killer)) { // если в группе наем, то режим опыт всей группе // дабы наема не выгодно было бы брать в группу @@ -957,7 +915,7 @@ void group_gain(CHAR_DATA * killer, CHAR_DATA * victim) for (f = leader->followers; f; f = f->next) { if (AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP) - && f->follower->in_room == IN_ROOM(killer)) + && SAME_ROOM(f->follower, killer)) { perform_group_gain(f->follower, victim, inroom_members, koef); } diff --git a/src/grp/grp.main.h b/src/grp/grp.main.h index d264f2b02..349203878 100644 --- a/src/grp/grp.main.h +++ b/src/grp/grp.main.h @@ -17,6 +17,7 @@ #include "structs.h" #include "dps.hpp" +enum GM_TYPE {GM_CHAR, GM_CHARMEE}; enum RQ_TYPE {RQ_GROUP, RQ_PERSON, RQ_ANY}; enum GRP_COMM {GRP_COMM_LEADER, GRP_COMM_ALL, GRP_COMM_OTHER}; enum RQ_R {RQ_R_OK, RQ_R_NO_GROUP, RQ_R_OVERFLOW, RQ_REFRESH}; @@ -32,31 +33,35 @@ int max_group_size(CHAR_DATA *ch); bool isGroupedFollower(CHAR_DATA* master, CHAR_DATA* vict); int calc_leadership(CHAR_DATA * ch); -class Group; class Request; using namespace std::chrono; -using grp_ptr = std::shared_ptr; -using rq_ptr = std::shared_ptr; -using sclock_t = time_point; -using cd_v = std::vector; -using npc_r = std::unordered_set *; -const duration DEF_EXPIRY_TIME = 600s; - -inline bool IN_GROUP(CHAR_DATA* ch) {return ch != nullptr && ch->personGroup != nullptr;} -inline bool IN_SAME_GROUP(CHAR_DATA* p1, CHAR_DATA* p2) {return IN_GROUP(p1) && IN_GROUP(p2) && p1->personGroup == p2->personGroup;} +using sclock_t = time_point; struct char_info { - char_info(int memberUid, CHAR_DATA *member, const std::string& memberName); + char_info(int memberUid, GM_TYPE type, CHAR_DATA *member, const std::string& memberName); virtual ~char_info(); int memberUID; // часть ключа + GM_TYPE type; // тип персонажа CHAR_DATA * member; // ссылка на персонажа, может быть невалидна! std::string memberName; // бэкап имени, если персонаж оффлайн sclock_t expiryTime; // время, когда запись автоматом удаляется после проверок. }; -class Group { +using grp_mt = std::map>; +using grp_ptr = std::shared_ptr; +using rq_ptr = std::shared_ptr; +using cd_v = std::vector; +using npc_r = std::unordered_set *; + +const duration DEF_EXPIRY_TIME = 600s; + +inline bool IN_GROUP(CHAR_DATA* ch) {return ch != nullptr && ch->personGroup != nullptr;} +inline bool IN_SAME_GROUP(CHAR_DATA* p1, CHAR_DATA* p2) {return IN_GROUP(p1) && IN_GROUP(p2) && p1->personGroup == p2->personGroup;} + +// класс-коллекция персонажей обоих типов что ли.. +class Group : public grp_mt { private: // ид группы в ростере u_long _uid = 0; @@ -70,7 +75,6 @@ class Group { CHAR_DATA* _leader = nullptr; public: u_long getUid() const; - u_short getCurrentMemberCount() const; const std::string &getLeaderName() const; CHAR_DATA *getLeader() const; void _clear(bool silent); @@ -84,12 +88,17 @@ class Group { const char* _getMemberName(int uid); int _findMember(char* memberName); CHAR_DATA* _findMember(int UID); - bool _removeMember(CHAR_DATA *member); + bool _removeMember(int memberUID); void charDataPurged(CHAR_DATA* ch); u_short size(rnum_t room_rnum = 0); private: - std::map> * _memberList; - npc_r _npcRoster; + GM_TYPE getType(CHAR_DATA* ch) { + if (IS_CHARMICE(ch)) + return GM_CHARMEE; + else + return GM_CHAR; + } + static void _printHeader(CHAR_DATA* ch, bool npc); static void _printDeadLine(CHAR_DATA* ch, const char* playerName, int header); static void _printNPCLine(CHAR_DATA* ch, CHAR_DATA* npc, int header); @@ -111,8 +120,6 @@ class Group { void sendToGroup(GRP_COMM mode, const char *msg, ...); void actToGroup(CHAR_DATA* vict, GRP_COMM mode, const char *msg, ...); - cd_v getMembers(rnum_t room_rnum = 0, bool includeCharmee = false); - npc_r getCharmee(rnum_t room_rnum = 0); public: // всякий унаследованный стафф CHAR_DATA* get_random_pc_group(); diff --git a/src/grp/grp.roster.cpp b/src/grp/grp.roster.cpp index b4ff56ab1..be83589d6 100644 --- a/src/grp/grp.roster.cpp +++ b/src/grp/grp.roster.cpp @@ -65,7 +65,7 @@ void GroupRoster::processGroupCommands(CHAR_DATA *ch, char *argument) { return; } if (grp != nullptr) // в группе - покидаем - grp->_removeMember(ch); + grp->_removeMember(ch->get_uid()); groupRoster.addGroup(ch); return; } else if (isname(subcmd, strLIST.c_str())) { @@ -189,7 +189,7 @@ void GroupRoster::printList(CHAR_DATA *ch) { for (auto & it : this->_groupList) { send_to_char(ch, "Группа лидера %s, кол-во участников: %hu\r\n", it.second->getLeaderName().c_str(), - it.second->getCurrentMemberCount()); + it.second->size()); } } diff --git a/src/magic.cpp b/src/magic.cpp index 13fa1a938..eecc8e494 100644 --- a/src/magic.cpp +++ b/src/magic.cpp @@ -2961,7 +2961,7 @@ int mag_affects(int level, CHAR_DATA * ch, CHAR_DATA * victim, int spellnum, int if (ch != victim && (AFF_FLAGGED(victim, EAffectFlag::AFF_SHIELD) || general_savingthrow(ch, victim, savetype, modi - GET_REAL_CON(victim) / 2))) { - if (ch->in_room == IN_ROOM(victim)) // Добавлено чтобы яд нанесенный SPELL_POISONED_FOG не спамил чару постоянно + if (SAME_ROOM(ch, victim)) // Добавлено чтобы яд нанесенный SPELL_POISONED_FOG не спамил чару постоянно send_to_char(NOEFFECT, ch); success = FALSE; break; @@ -3426,7 +3426,7 @@ int mag_affects(int level, CHAR_DATA * ch, CHAR_DATA * victim, int spellnum, int || (ch != victim && affected_by_spell(victim, SPELL_DEAFNESS))) { - if (ch->in_room == IN_ROOM(victim)) + if (SAME_ROOM(ch, victim)) send_to_char(NOEFFECT, ch); } else @@ -3940,7 +3940,7 @@ int mag_affects(int level, CHAR_DATA * ch, CHAR_DATA * victim, int spellnum, int if (IS_NPC(victim) && success){ for (i = 0; i < MAX_SPELL_AFFECTS && success; ++i) { if (AFF_FLAGGED(&mob_proto[victim->get_rnum()], static_cast(af[i].bitvector))) { - if (ch->in_room == IN_ROOM(victim)){ + if (SAME_ROOM(ch, victim)){ send_to_char(NOEFFECT, ch); } success = FALSE; @@ -3953,7 +3953,7 @@ int mag_affects(int level, CHAR_DATA * ch, CHAR_DATA * victim, int spellnum, int } // вот такой оригинальный способ запретить рекасты негативных аффектов - через флаг апдейта if ((ch != victim) && affected_by_spell(victim, spellnum) && success && (!update_spell)) { - if (ch->in_room == IN_ROOM(victim)) + if (SAME_ROOM(ch, victim)) send_to_char(NOEFFECT, ch); success = FALSE; } diff --git a/src/msdp.reporters.cpp b/src/msdp.reporters.cpp index 3aaca2334..c7ec47f70 100644 --- a/src/msdp.reporters.cpp +++ b/src/msdp.reporters.cpp @@ -198,7 +198,7 @@ namespace msdp const auto move_percents = std::to_string(posi_value(GET_MOVE(character), GET_REAL_MAX_MOVE(character)) * 10);// *10 to show percents member->add(std::make_shared("MOVE", std::make_shared(move_percents))); - const bool same_room = ch->in_room == IN_ROOM(character); + const bool same_room = SAME_ROOM(ch, character); member->add(std::make_shared("IS_HERE", std::make_shared(same_room ? "1" : "0"))); const int memory = get_mem(character); diff --git a/src/objsave.cpp b/src/objsave.cpp index 3b137ba68..95f2c4fe8 100644 --- a/src/objsave.cpp +++ b/src/objsave.cpp @@ -3438,7 +3438,6 @@ void Crash_save_all_rent(void) save_char_objects(ch.get(), RENT_FORCED, 0); log("Saving char: %s", GET_NAME(ch)); PLR_FLAGS(ch).unset(PLR_CRASH); - //AFF_FLAGS(ch.get()).unset(EAffectFlag::AFF_GROUP); (ch.get())->removeGroupFlags(); AFF_FLAGS(ch.get()).unset(EAffectFlag::AFF_HORSE); extract_char(ch.get(), FALSE); diff --git a/src/spec_procs.cpp b/src/spec_procs.cpp index 6a55182e1..fa2bd28ca 100644 --- a/src/spec_procs.cpp +++ b/src/spec_procs.cpp @@ -2192,8 +2192,7 @@ int npc_move(CHAR_DATA * ch, int dir, int/* need_specials_check*/) { return (FALSE); } - else if (ch->has_master() - && ch->in_room == IN_ROOM(ch->get_master())) + else if (ch->has_master() && SAME_ROOM(ch, IN_ROOM)) { return (FALSE); } @@ -2764,7 +2763,7 @@ void npc_group(CHAR_DATA * ch) if (GET_DEST(ch) == NOWHERE || ch->in_room == NOWHERE) return; - if (ch->has_master() && ch->in_room == IN_ROOM(ch->get_master())) { + if (ch->has_master() && SAME_ROOM(ch, ch->get_master())) { leader = ch->get_master(); } @@ -2853,7 +2852,7 @@ void npc_groupbattle(CHAR_DATA * ch) for (; k; (k = tch ? k : k->next), tch = NULL) { helper = tch ? tch : k->follower; - if (ch->in_room == IN_ROOM(helper) + if (SAME_ROOM(ch, helper) && !helper->get_fighting() && !IS_NPC(helper) && GET_POS(helper) > POS_STUNNED) diff --git a/src/spells.cpp b/src/spells.cpp index 577e371d9..7efc53a23 100644 --- a/src/spells.cpp +++ b/src/spells.cpp @@ -2361,7 +2361,7 @@ void spell_sacrifice(int/* level*/, CHAR_DATA *ch, CHAR_DATA *victim, OBJ_DATA* if (IS_NPC(f->follower) && AFF_FLAGGED(f->follower, EAffectFlag::AFF_CHARM) && MOB_FLAGGED(f->follower, MOB_CORPSE) - && ch->in_room == IN_ROOM(f->follower)) + && SAME_ROOM(ch,f->follower)) { do_sacrifice(f->follower, dam); } @@ -2596,8 +2596,9 @@ void spell_angel(int/* level*/, CHAR_DATA *ch, CHAR_DATA* /*victim*/, OBJ_DATA* GET_LIKES(mob) = 100; IS_CARRYING_W(mob) = 0; IS_CARRYING_N(mob) = 0; - - // ангел - не андед! + // тварь же человеком создана + MOB_FLAGS(mob).set(MOB_PLAYER_SUMMON); + // ангел - не андед! MOB_FLAGS(mob).set(MOB_ANGEL); MOB_FLAGS(mob).set(MOB_LIGHTBREATH); MOB_FLAGS(mob).set(MOB_PLAYER_SUMMON); From 99619d06074fa2b3a2978aa1366d5c4b8219e144 Mon Sep 17 00:00:00 2001 From: bikbai Date: Thu, 7 Jan 2021 03:20:43 +0300 Subject: [PATCH 19/40] =?UTF-8?q?=D0=BF=D1=80=D0=BE=D0=B4=D0=BE=D0=BB?= =?UTF-8?q?=D0=B6=D0=B0=D0=B5=D0=BC=20=D0=BE=D1=82=D0=BB=D0=B0=D0=B4=D0=BA?= =?UTF-8?q?=D1=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/act.informative.cpp | 13 +++- src/act.other.cpp | 128 ++++++++++++++++++--------------- src/cmd/hire.cpp | 3 + src/fightsystem/fight_hit.cpp | 2 + src/fightsystem/mobact.cpp | 7 +- src/fightsystem/pk.cpp | 10 +-- src/grp/follow.cpp | 41 ++++------- src/grp/grp.cmdprocessor.cpp | 3 +- src/grp/grp.group.cpp | 132 ++++++++++++++++++++-------------- src/grp/grp.main.h | 11 +-- src/interpreter.h | 1 + src/magic.cpp | 46 ++++++------ src/msdp.reporters.cpp | 7 +- src/spec_procs.cpp | 2 +- src/spells.cpp | 3 + src/structs.h | 2 +- 16 files changed, 226 insertions(+), 185 deletions(-) diff --git a/src/act.informative.cpp b/src/act.informative.cpp index 0bbb387c8..37f6c2f11 100644 --- a/src/act.informative.cpp +++ b/src/act.informative.cpp @@ -5922,8 +5922,11 @@ void do_toggle(CHAR_DATA *ch, char* /*argument*/, int/* cmd*/, int/* subcmd*/) " Оффтоп : %-3s \r\n" " Потеря связи : %-3s " " Ингредиенты : %-3s " - " Вспомнить : %-3u \r\n", - ONOFF(PRF_FLAGGED(ch, PRF_AUTOEXIT)), + " Вспомнить : %-3u \r\n" + " Автогрупвыход : %-3s " + " : %-3s " + " : %-3s \r\n", + ONOFF(PRF_FLAGGED(ch, PRF_AUTOEXIT)), ONOFF(PRF_FLAGGED(ch, PRF_BRIEF)), ONOFF(PRF_FLAGGED(ch, PRF_COMPACT)), YESNO(!PRF_FLAGGED(ch, PRF_NOREPEAT)), @@ -5962,7 +5965,11 @@ void do_toggle(CHAR_DATA *ch, char* /*argument*/, int/* cmd*/, int/* subcmd*/) ONOFF(PRF_FLAGGED(ch, PRF_OFFTOP_MODE)), ONOFF(PRF_FLAGGED(ch, PRF_ANTIDC_MODE)), ONOFF(PRF_FLAGGED(ch, PRF_NOINGR_MODE)), - ch->remember_get_num()); + ch->remember_get_num(), + ONOFF(PRF_FLAGGED(ch, PRF_FOLLOW_GRP_EXIT)), + "", + "" + ); send_to_char(buf, ch); if (NOTIFY_EXCH_PRICE(ch) > 0) { diff --git a/src/act.other.cpp b/src/act.other.cpp index 2d789d3a2..b441eed93 100644 --- a/src/act.other.cpp +++ b/src/act.other.cpp @@ -1213,63 +1213,66 @@ void do_display(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) #define TOG_OFF 0 #define TOG_ON 1 -const char *gen_tog_type[] = { "автовыходы", "autoexits", - "краткий", "brief", - "сжатый", "compact", - "повтор", "norepeat", - "обращения", "notell", - "кто-то", "noinvistell", - "болтать", "nogossip", - "кричать", "noshout", - "орать", "noholler", - "поздравления", "nogratz", - "аукцион", "noauction", - "базар", "exchange", - "задание", "quest", - "автозаучивание", "automem", - "нет агров", "nohassle", - "призыв", "nosummon", - "частный", "nowiz", - "флаги комнат", "roomflags", - "замедление", "slowns", - "выслеживание", "trackthru", - "супервидение", "holylight", - "кодер", "coder", - "автозавершение", "goahead", - "группа", "showgroup", - "без двойников", "noclones", - "автопомощь", "autoassist", - "автограбеж", "autoloot", - "автодележ", "autosplit", - "брать куны", "automoney", - "арена", "arena", - "ширина", "length", - "высота", "width", - "экран", "screen", - "новости", "news", - "доски", "boards", - "хранилище", "chest", - "пклист", "pklist", - "политика", "politics", - "пкформат", "pkformat", - "соклановцы", "workmate", - "оффтоп", "offtop", - "потеря связи", "disconnect", - "ингредиенты", "ingredient", - "вспомнить", "remember", - "уведомления", "notify", - "карта", "map", - "вход в зону", "enter zone", - "опечатки", "misprint", - "магщиты", "mageshields", - "автопризыв", "autonosummon", - "сдемигодам", "sdemigod", - "незрячий", "blind", - "маппер", "mapper", - "тестер", "tester", - "контроль IP", "IP control", - "\n" - }; +const char *gen_tog_type[] = + { + "автовыходы", "autoexits", + "краткий", "brief", + "сжатый", "compact", + "повтор", "norepeat", + "обращения", "notell", + "кто-то", "noinvistell", + "болтать", "nogossip", + "кричать", "noshout", + "орать", "noholler", + "поздравления", "nogratz", + "аукцион", "noauction", + "базар", "exchange", + "задание", "quest", + "автозаучивание", "automem", + "нет агров", "nohassle", + "призыв", "nosummon", + "частный", "nowiz", + "флаги комнат", "roomflags", + "замедление", "slowns", + "выслеживание", "trackthru", + "супервидение", "holylight", + "кодер", "coder", + "автозавершение", "goahead", + "группа", "showgroup", + "без двойников", "noclones", + "автопомощь", "autoassist", + "автограбеж", "autoloot", + "автодележ", "autosplit", + "брать куны", "automoney", + "арена", "arena", + "ширина", "length", + "высота", "width", + "экран", "screen", + "новости", "news", + "доски", "boards", + "хранилище", "chest", + "пклист", "pklist", + "политика", "politics", + "пкформат", "pkformat", + "соклановцы", "workmate", + "оффтоп", "offtop", + "потеря связи", "disconnect", + "ингредиенты", "ingredient", + "вспомнить", "remember", + "уведомления", "notify", + "карта", "map", + "вход в зону", "enter zone", + "опечатки", "misprint", + "магщиты", "mageshields", + "автопризыв", "autonosummon", + "сдемигодам", "sdemigod", + "незрячий", "blind", + "маппер", "mapper", + "тестер", "tester", + "контроль IP", "IP control", + "автогрупвыход", "followgroupexit", + "\n" +}; @@ -1336,7 +1339,9 @@ struct gen_tog_param_type 0, SCMD_BLIND, false}, { 0, SCMD_MAPPER, false}, { 0, SCMD_TESTER, true}, { - 0, SCMD_IPCONTROL, false} + 0, SCMD_IPCONTROL, false}, { + 0, SCMD_FOLLOW_GRP_EXIT, false} + }; void do_mode(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) @@ -1601,7 +1606,9 @@ void do_gen_tog(CHAR_DATA *ch, char *argument, int/* cmd*/, int subcmd) {"Режим вывода тестовой информации выключен.\r\n", "Режим вывода тестовой информации включен.\r\n"}, {"Режим контроля смены IP-адреса персонажа выключен.\r\n", - "Режим контроля смены IP-адреса персонажа включен.\r\n"} + "Режим контроля смены IP-адреса персонажа включен.\r\n"}, + {"После изменения порядка следования (след я) вы автоматически покинете группу.\r\n", + "Изменение порядка следования не влияет на членство в группе. Используйте команду груп покинуть.\r\n"} }; if (IS_NPC(ch)) @@ -1824,6 +1831,9 @@ void do_gen_tog(CHAR_DATA *ch, char *argument, int/* cmd*/, int subcmd) case SCMD_AUTO_NOSUMMON: result = PRF_TOG_CHK(ch, PRF_AUTO_NOSUMMON); break; + case SCMD_FOLLOW_GRP_EXIT: + result = PRF_TOG_CHK(ch, PRF_FOLLOW_GRP_EXIT); + break; default: send_to_char(ch, "Введите параметр режима полностью.\r\n"); // log("SYSERR: Unknown subcmd %d in do_gen_toggle.", subcmd); diff --git a/src/cmd/hire.cpp b/src/cmd/hire.cpp index ab6c8998f..a56dd456c 100644 --- a/src/cmd/hire.cpp +++ b/src/cmd/hire.cpp @@ -1,6 +1,7 @@ #include "cmd.generic.h" #include "grp/follow.h" +#include "grp/grp.main.h" #include "handler.h" #include @@ -261,6 +262,8 @@ void do_findhelpee(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) { AFFECT_DATA af; if (!(k && k->follower == helpee)) { ch->add_follower(helpee); + if (ch->personGroup != nullptr) + ch->personGroup->addMember(helpee, true); af.duration = pc_duration(helpee, times * TIME_KOEFF, 0, 0, 0, 0); } else { diff --git a/src/fightsystem/fight_hit.cpp b/src/fightsystem/fight_hit.cpp index 05e26ac0b..9ffb9aa36 100644 --- a/src/fightsystem/fight_hit.cpp +++ b/src/fightsystem/fight_hit.cpp @@ -2406,6 +2406,8 @@ void try_angel_sacrifice(CHAR_DATA* ch, CHAR_DATA* victim) { if (GET_HIT(victim) <= 0 && !IS_NPC(victim) && IN_GROUP(victim)) { // ищем первого попавшегося ангела из группы в комнате for (const auto& npc : *victim->personGroup){ + if (npc.second->member == nullptr) + continue; if (SAME_ROOM(npc.second->member, victim) && MOB_FLAGGED(npc.second->member, MOB_ANGEL) ){ angel = npc.second->member; break; diff --git a/src/fightsystem/mobact.cpp b/src/fightsystem/mobact.cpp index 1ec4cc8db..354a085cf 100644 --- a/src/fightsystem/mobact.cpp +++ b/src/fightsystem/mobact.cpp @@ -507,16 +507,13 @@ CHAR_DATA *find_best_stupidmob_victim(CHAR_DATA * ch, int extmode) bool find_master_charmice(CHAR_DATA *charmice) { // проверяем на спелл чарма, ищем хозяина и сравниваем румы - if (!IS_CHARMICE(charmice) || !charmice->has_master()) - { + if (!IS_CHARMICE(charmice) || !charmice->has_master()) { return true; } - if (SAME_ROOM(charmice, charmice->get_master()) - { + if (SAME_ROOM(charmice, charmice->get_master())) { return true; } - return false; } diff --git a/src/fightsystem/pk.cpp b/src/fightsystem/pk.cpp index a3c851079..bc5b421f1 100644 --- a/src/fightsystem/pk.cpp +++ b/src/fightsystem/pk.cpp @@ -352,10 +352,12 @@ void pk_increment_gkill(CHAR_DATA * agressor, CHAR_DATA * victim) { if (!IS_GOD(victim)) { has_clanmember = has_clan_members_in_group(victim); } - auto grp = victim->personGroup->getMembers(IN_ROOM(victim), false); - for (auto it : grp){ - if (pk_action_type(agressor, it) > PK_ACTION_FIGHT) { - pk_increment_kill(agressor, it, it == victim, has_clanmember); + + for (auto it : *victim->personGroup){ + if (it.second->member != nullptr && SAME_ROOM(agressor, it.second->member)) { + if (pk_action_type(agressor, it.second->member) > PK_ACTION_FIGHT) { + pk_increment_kill(agressor, it.second->member, it.second->member == victim, has_clanmember); + } } } } diff --git a/src/grp/follow.cpp b/src/grp/follow.cpp index 657ad8026..80fc8caf4 100644 --- a/src/grp/follow.cpp +++ b/src/grp/follow.cpp @@ -161,67 +161,52 @@ void do_follow(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) if (IS_NPC(ch) && AFF_FLAGGED(ch, EAffectFlag::AFF_CHARM) && ch->get_fighting()) return; - if (*smallBuf) - { - if (!str_cmp(smallBuf, "я") || !str_cmp(smallBuf, "self") || !str_cmp(smallBuf, "me")) - { - if (!ch->has_master()) - { + if (*smallBuf) { + if (!str_cmp(smallBuf, "я") || !str_cmp(smallBuf, "self") || !str_cmp(smallBuf, "me")){ + if (!ch->has_master()) { send_to_char("Но вы ведь ни за кем не следуете...\r\n", ch); } - else - { + else { stop_follower(ch, SF_EMPTY); } return; } - if (!(leader = get_char_vis(ch, smallBuf, FIND_CHAR_ROOM))) - { + if (!(leader = get_char_vis(ch, smallBuf, FIND_CHAR_ROOM))) { send_to_char(NOPERSON, ch); return; } } - else - { + else { send_to_char("За кем вы хотите следовать?\r\n", ch); return; } - if (ch->get_master() == leader) - { + if (ch->get_master() == leader){ act("Вы уже следуете за $N4.", FALSE, ch, 0, leader, TO_CHAR); return; } - if (AFF_FLAGGED(ch, EAffectFlag::AFF_CHARM) - && ch->has_master()) - { + if (AFF_FLAGGED(ch, EAffectFlag::AFF_CHARM) && ch->has_master()) { act("Но вы можете следовать только за $N4!", FALSE, ch, 0, ch->get_master(), TO_CHAR); } else // Not Charmed follow person { - if (leader == ch) - { - if (!ch->has_master()) - { + if (leader == ch) { + if (!ch->has_master()) { send_to_char("Вы уже следуете за собой.\r\n", ch); return; } stop_follower(ch, SF_EMPTY); } - else - { - if (circle_follow(ch, leader)) - { + else { + if (circle_follow(ch, leader)) { send_to_char("Так у вас целый хоровод получится.\r\n", ch); return; } - if (ch->has_master()) - { + if (ch->has_master()) { stop_follower(ch, SF_EMPTY); } - //AFF_FLAGS(ch).unset(EAffectFlag::AFF_GROUP); ch->removeGroupFlags(); for (f = ch->followers; f; f = f->next) { diff --git a/src/grp/grp.cmdprocessor.cpp b/src/grp/grp.cmdprocessor.cpp index 3d9e0f5fc..fde070c04 100644 --- a/src/grp/grp.cmdprocessor.cpp +++ b/src/grp/grp.cmdprocessor.cpp @@ -643,8 +643,7 @@ void do_split(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/,int cur { if (AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP) && !IS_NPC(f->follower) - && SAME_ROOM(f->follower) == ch->in_room) - { + && SAME_ROOM(f->follower, ch)) { num++; } } diff --git a/src/grp/grp.group.cpp b/src/grp/grp.group.cpp index 9e63549b1..ae1a983f6 100644 --- a/src/grp/grp.group.cpp +++ b/src/grp/grp.group.cpp @@ -79,18 +79,10 @@ CHAR_DATA *Group::getLeader() const{ return _leader; } -int Group::_findMember(char *memberName) { +char_info* Group::_findMember(char *memberName) { for (auto & it : *this) { - if (isname(it.second->memberName, memberName)) - return it.first; - } - return 0; -} - -CHAR_DATA* Group::_findMember(int uid) { - for (auto & it : *this) { - if (it.second->memberUID == uid) - return it.second->member; + if (isname(memberName, it.second->memberName)) + return it.second.get(); } return nullptr; } @@ -104,7 +96,7 @@ const char* Group::_getMemberName(int uid) { } void Group::_setLeader(CHAR_DATA *leader) { - _leaderUID = leader->get_uid(); + _leaderUID = _calcUID(leader); _leader = leader; _leaderName.assign(leader->get_pc_name()); _memberCap = IS_NPC(leader)? 255 : max_group_size(leader); @@ -163,17 +155,17 @@ void Group::addMember(CHAR_DATA *member, bool silent) { return; // в другой группе, вышвыриваем if (member->personGroup != nullptr) - member->personGroup->_removeMember(member->get_uid()); + _removeMember(member); - auto it = this->find(member->get_uid()); + auto it = this->find(_calcUID(member)); if (it == this->end()) { // рисуем сообщение до добавления, чтобы самому персонажу не показывать if (!silent) { actToGroup(member, GRP_COMM_ALL, "$N принят$A в группу."); send_to_char(member, "Вас приняли в группу.\r\n"); } - auto ci = char_info(member->get_uid(), getType(member), member, member->get_pc_name()); - this->emplace(member->get_uid(), std::make_shared(ci)); + auto ci = char_info(_calcUID(member), getType(member), member, member->get_pc_name()); + this->emplace(_calcUID(member), std::make_shared(ci)); } else { sprintf(buf, "Group::addMember: группа id=%lu, попытка повторного добавления персонажа с тем же uid", getUid()); @@ -188,7 +180,7 @@ void Group::addMember(CHAR_DATA *member, bool silent) { for (auto f = member->followers; f; f = f->next) { auto ff = f->follower; if (IS_CHARMICE(ff)) { - this->addMember(ff); // рекурсия! + this->addMember(ff, true); // рекурсия! } } } @@ -206,8 +198,9 @@ bool Group::_removeMember(int memberUID) { mudlog(buf, BRF, LVL_IMMORT, SYSLOG, TRUE); return false; } - auto member =it->second->member; - member->personGroup = nullptr; + auto member = it->second->member; + if (member != nullptr) + member->personGroup = nullptr; this->erase(memberUID); // finally remove it // а также удаляем чармисов персонажа из групповых @@ -215,7 +208,7 @@ bool Group::_removeMember(int memberUID) { for (auto f = member->followers; f; f = f->next) { auto ff = f->follower; if (IS_CHARMICE(ff)) { - auto c = this->find(ff->get_uid()); + auto c = this->find(_calcUID(ff)); if (c != this->end()) { this->erase(c); ff->personGroup = nullptr; @@ -224,33 +217,51 @@ bool Group::_removeMember(int memberUID) { } } - - CHAR_DATA* nxtLdr = nullptr; + CHAR_DATA* nxtLdr = _leader; int nxtLdrGrpSize = 0; // если ушел лидер - ищем нового, но после ухода предыдущего if ( member == _leader) { + nxtLdr = nullptr; for (const auto& mit : *this){ auto m = mit.second->member; if ( m != nullptr && !m->purged() && max_group_size(m) > nxtLdrGrpSize ) { - nxtLdr = m; + nxtLdr = m; // нашелся!! nxtLdrGrpSize = max_group_size(m); } } } - // никого не осталось, распускаем группу + // нового лидера не нашлось, распускаем группу if (nxtLdr == nullptr) { groupRoster.removeGroup(_uid); return false; } - _setLeader(nxtLdr); + _promote(nxtLdr); return true; } +bool Group::_removeMember(char *name) { + int uid = 0; + for (auto &it: *this){ + if (str_cmp(name, it.second->memberName) == 0) { + uid = it.first; + break; + } + } + if (uid != 0) + return _removeMember(uid); + return false; +} + +bool Group::_removeMember(CHAR_DATA *member) { + int memberUID = _calcUID(member); + return _removeMember(memberUID); +} + bool Group::_restoreMember(CHAR_DATA *member) { if (!member) return false; for (auto &it: *this){ - if (it.first == member->get_uid()) { + if (it.first == _calcUID(member)) { it.second->member = member; member->personGroup = this; return true; @@ -283,28 +294,12 @@ void Group::_clear(bool silentMode) { } void Group::promote(char *applicant) { - int memberId = _findMember(applicant); - if (memberId == 0) { + auto member = _findMember(applicant); + if (member == nullptr || member->member == nullptr) { sendToGroup(GRP_COMM_LEADER, "Нет такого персонажа."); return; } - _setLeader(this->at(memberId)->member); - sendToGroup(GRP_COMM_ALL, "Изменился лидер группы на %.", _leaderName.c_str() ); - auto diff = (u_short)_memberCap - (u_short)this->size(); - if (diff < 0) diff = 0; - if (diff > 0){ - u_short i = 0; - int expellList[diff]; - for (auto it = this->begin(); it!= this->end() || i > diff; it++){ - if (it->second->member != _leader ) { - expellList[i] = it->second->member->get_uid(); - ++i; - } - } - for (i=0; i < diff; i++){ - _removeMember(expellList[i]); - } - } + _promote(member->member); } void Group::rejectRequest(char *applicant) { @@ -336,20 +331,19 @@ void Group::approveRequest(const char *applicant) { } void Group::expellMember(char *memberName) { - auto mId = _findMember(memberName); - if (mId == 0) + auto vict = _findMember(memberName); + if (vict == nullptr) { + sendToGroup(GRP_COMM_LEADER, "Нет такого персонажа."); return; - auto vict = this->at(mId)->member; // тут может быть пусто.. - if (vict != nullptr) { - _removeMember(vict->get_uid()); + } + _removeMember(vict->memberUID); + if (vict != nullptr && vict->member != nullptr) { act("$N исключен$A из состава вашей группы.", FALSE, _leader, nullptr, vict, TO_CHAR); act("Вы исключены из группы $n1!", FALSE, _leader, nullptr, vict, TO_VICT); act("$N был$G исключен$A из группы $n1!", FALSE, _leader, nullptr, vict, TO_NOTVICT | TO_ARENA_LISTEN); } else { - + sendToGroup(GRP_COMM_LEADER, "%s был исключен из группы.'\r\n", vict->memberName.c_str()); } - - } void Group::listMembers(CHAR_DATA *ch) { @@ -387,7 +381,7 @@ void Group::leaveGroup(CHAR_DATA* ch) { send_to_char(ch, "Нельзя стать еще более одиноким, чем сейчас.\r\n"); return; } - _removeMember(ch->get_uid()); + _removeMember(_calcUID(ch)); } void Group::_printHeader(CHAR_DATA* ch, bool npc) { @@ -662,6 +656,36 @@ bool Group::has_clan_members_in_group(CHAR_DATA *victim) { return false; } +int Group::_calcUID(CHAR_DATA *ch) { + if (IS_NPC(ch)) + return ch->id; + else + return ch->get_uid(); + +} + +void Group::_promote(CHAR_DATA *member) { + _setLeader(member); + sendToGroup(GRP_COMM_ALL, "Изменился лидер группы на %.", _leaderName.c_str() ); + auto diff = (u_short)_memberCap - (u_short)this->size(); + if (diff < 0) diff = 0; + if (diff > 0){ + u_short i = 0; + int expellList[diff]; + for (auto it = this->begin(); it!= this->end() || i > diff; it++){ + if (it->second->member != _leader ) { + expellList[i] = _calcUID(it->second->member); + ++i; + } + } + for (i=0; i < diff; i++){ + _removeMember(expellList[i]); + } + } + +} + + bool same_group(CHAR_DATA * ch, CHAR_DATA * tch) { if (!ch || !tch) diff --git a/src/grp/grp.main.h b/src/grp/grp.main.h index 349203878..da86b560e 100644 --- a/src/grp/grp.main.h +++ b/src/grp/grp.main.h @@ -86,18 +86,21 @@ class Group : public grp_mt { bool _isActive(); // проверка, что в группе все персонажи онлайн bool _isMember(int uid); const char* _getMemberName(int uid); - int _findMember(char* memberName); - CHAR_DATA* _findMember(int UID); - bool _removeMember(int memberUID); + char_info* _findMember(char* memberName); + bool _removeMember(CHAR_DATA* ch); + bool _removeMember(int uid); + bool _removeMember(char *name); + void _promote(CHAR_DATA* ch); void charDataPurged(CHAR_DATA* ch); u_short size(rnum_t room_rnum = 0); private: GM_TYPE getType(CHAR_DATA* ch) { - if (IS_CHARMICE(ch)) + if (IS_NPC(ch)) return GM_CHARMEE; else return GM_CHAR; } + int _calcUID(CHAR_DATA* ch); static void _printHeader(CHAR_DATA* ch, bool npc); static void _printDeadLine(CHAR_DATA* ch, const char* playerName, int header); diff --git a/src/interpreter.h b/src/interpreter.h index 2fecdb033..bcf481dea 100644 --- a/src/interpreter.h +++ b/src/interpreter.h @@ -195,6 +195,7 @@ struct alias_data #define SCMD_MAPPER 55 #define SCMD_TESTER 56 #define SCMD_IPCONTROL 57 +#define SCMD_FOLLOW_GRP_EXIT 58 // do_wizutil #define SCMD_REROLL 0 diff --git a/src/magic.cpp b/src/magic.cpp index eecc8e494..838119ee1 100644 --- a/src/magic.cpp +++ b/src/magic.cpp @@ -15,40 +15,42 @@ #include "magic.h" #include "cmd/cmd.generic.h" -#include "core/affect_data.h" #include "action.targeting.hpp" +#include "AffectHandler.hpp" +#include "char_obj_utils.inl" +#include "chars/char.hpp" #include "chars/world.characters.hpp" -#include "world.objects.hpp" -#include "object.prototypes.hpp" -#include "obj.hpp" #include "comm.h" -#include "spells.h" -#include "skills.h" -#include "handler.h" -#include "db.h" -#include "interpreter.h" -#include "screen.h" +#include "conf.h" #include "constants.h" +#include "core/affect_data.h" +#include "corpse.hpp" +#include "db.h" +#include "deathtrap.hpp" #include "dg/dg_scripts.h" -#include "fightsystem/pk.h" #include "features.hpp" #include "fightsystem/fight.h" -#include "deathtrap.hpp" -#include "random.hpp" -#include "chars/char.hpp" -#include "poison.hpp" +#include "fightsystem/pk.h" +#include "grp/grp.main.h" +#include "handler.h" +#include "interpreter.h" +#include "logger.hpp" #include "modify.h" +#include "obj.hpp" +#include "object.prototypes.hpp" +#include "poison.hpp" +#include "random.hpp" #include "room.hpp" -#include "AffectHandler.hpp" -#include "corpse.hpp" -#include "logger.hpp" -#include "utils.h" +#include "screen.h" +#include "skills.h" +#include "spells.h" #include "structs.h" #include "sysdep.h" -#include "conf.h" -#include "char_obj_utils.inl" +#include "utils.h" +#include "world.objects.hpp" #include "zone.table.hpp" + #include #include @@ -4379,6 +4381,8 @@ int mag_summons(int level, CHAR_DATA * ch, OBJ_DATA * obj, int spellnum, int sav act(mag_summon_msgs[msg], FALSE, ch, 0, mob, TO_ROOM | TO_ARENA_LISTEN); ch->add_follower(mob); + if (ch->personGroup != nullptr) + ch->personGroup->addMember(mob, true); if (spellnum == SPELL_CLONE) { // клоны теперь кастятся все вместе // ужасно некрасиво сделано diff --git a/src/msdp.reporters.cpp b/src/msdp.reporters.cpp index c7ec47f70..b3268e7a7 100644 --- a/src/msdp.reporters.cpp +++ b/src/msdp.reporters.cpp @@ -306,9 +306,10 @@ namespace msdp auto grp = ch->personGroup; if (grp == nullptr) return; - auto gl = grp->getMembers(0, true); - for (auto it : gl) { - append_char(group_descriptor, ch, it, true); + for (auto it : *grp) { + if (it.second->member == nullptr) + continue; + append_char(group_descriptor, ch, it.second->member, true); } response = std::make_shared(constants::GROUP, group_descriptor); } diff --git a/src/spec_procs.cpp b/src/spec_procs.cpp index fa2bd28ca..9dd4cc335 100644 --- a/src/spec_procs.cpp +++ b/src/spec_procs.cpp @@ -2192,7 +2192,7 @@ int npc_move(CHAR_DATA * ch, int dir, int/* need_specials_check*/) { return (FALSE); } - else if (ch->has_master() && SAME_ROOM(ch, IN_ROOM)) + else if (ch->has_master() && SAME_ROOM(ch, ch->get_master())) { return (FALSE); } diff --git a/src/spells.cpp b/src/spells.cpp index 7efc53a23..c5940c63b 100644 --- a/src/spells.cpp +++ b/src/spells.cpp @@ -20,6 +20,7 @@ #include "fightsystem/mobact.hpp" #include "fightsystem/pk.h" #include "grp/follow.h" +#include "grp/grp.main.h" #include "handler.h" #include "house.h" #include "liquid.hpp" @@ -1088,6 +1089,8 @@ void spell_charm(int/* level*/, CHAR_DATA *ch, CHAR_DATA *victim, OBJ_DATA* /* o affect_from_char(victim, SPELL_CHARM); ch->add_follower(victim); + if (ch->personGroup != nullptr) + ch->personGroup->addMember(victim, true); AFFECT_DATA af; af.type = SPELL_CHARM; diff --git a/src/structs.h b/src/structs.h index 36ef27b0f..3602bf73b 100644 --- a/src/structs.h +++ b/src/structs.h @@ -620,7 +620,7 @@ extern const religion_names_t religion_name; #define PRF_TRIPLE_THROW (INT_TWO | 1 << 15) // готов использовать тройной бросок #define PRF_SHADOW_THROW (INT_TWO | 1 << 16) // применяет "теневой бросок" #define PRF_DISP_COOLDOWNS (INT_TWO | 1 << 17) // Показывать кулдауны скиллов в промпте -#define PRF_TELEGRAM (INT_TWO | 1 << 18) // Активирует телеграм-канал у персонажа +#define PRF_FOLLOW_GRP_EXIT (INT_TWO | 1 << 18) // Активирует телеграм-канал у персонажа // при добавлении не забываем про preference_bits[] From ec0ee3f0a97d493292b42177049e0433a0accf26 Mon Sep 17 00:00:00 2001 From: bikbai Date: Fri, 8 Jan 2021 17:46:27 +0300 Subject: [PATCH 20/40] =?UTF-8?q?=D0=90=D0=B2=D1=82=D0=BE=D1=82=D0=B5?= =?UTF-8?q?=D1=81=D1=82=D1=8B=20=D1=81=20=D0=BF=D0=B5=D1=80=D1=81=D0=BE?= =?UTF-8?q?=D0=BD=D0=B0=D0=B6=D0=B0=D0=BC=D0=B8=20=D0=B7=D0=B0=D1=80=D0=B0?= =?UTF-8?q?=D0=B1=D0=BE=D1=82=D0=B0=D0=BB=D0=B8.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chars/char.cpp | 2 +- src/chars/char_player.cpp | 2237 +++++++++++++++++---------------- src/chars/char_player.hpp | 4 +- src/cmd/quit.cpp | 2 +- src/grp/grp.cmdprocessor.cpp | 34 +- src/grp/grp.group.cpp | 45 +- src/grp/grp.main.h | 5 +- src/grp/grp.roster.cpp | 2 +- src/spells.cpp | 9 +- tests/CMakeLists.txt | 13 +- tests/char.leaders.cpp | 12 +- tests/char.utilities.cpp | 238 ++-- tests/char.utilities.hpp | 11 +- tests/data/plrs/sample.player | 136 ++ 14 files changed, 1446 insertions(+), 1304 deletions(-) create mode 100644 tests/data/plrs/sample.player diff --git a/src/chars/char.cpp b/src/chars/char.cpp index 7e7a30a91..36ec613d1 100644 --- a/src/chars/char.cpp +++ b/src/chars/char.cpp @@ -2037,7 +2037,7 @@ void CHAR_DATA::removeGroupFlags() { AFF_FLAGS(this).unset(EAffectFlag::AFF_GROUP); PRF_FLAGS(this).unset(PRF_SKIRMISHER); if (personGroup != nullptr && IS_CHARMICE(this)) - personGroup->_removeMember(this->get_uid()); + personGroup->_removeMember(this); } void CHAR_DATA::add_follower(CHAR_DATA* ch) { diff --git a/src/chars/char_player.cpp b/src/chars/char_player.cpp index bf4efb327..9d5155408 100644 --- a/src/chars/char_player.cpp +++ b/src/chars/char_player.cpp @@ -20,7 +20,6 @@ #include "comm.h" #include "core/leveling.h" #include "fightsystem/pk.h" -#include "diskio.h" #include "interpreter.h" #include "genchar.h" #include "AffectHandler.hpp" @@ -1066,14 +1065,15 @@ void Player::save_char() // * \param reboot - по дефолту = false int Player::load_char_ascii(const char *name, bool reboot, const bool find_id /*= true*/) { - int id, num = 0, num2 = 0, num3 = 0, num4 = 0, num5 = 0, num6 = 0, i; - long int lnum = 0, lnum3 = 0; - unsigned long long llnum = 0; - FBFILE *fl = NULL; - char filename[40]; - char buf[MAX_RAW_INPUT_LENGTH], line[MAX_RAW_INPUT_LENGTH], tag[6]; - char line1[MAX_RAW_INPUT_LENGTH]; - struct timed_type timed; + int id, num = 0, num2 = 0, num3 = 0, num4 = 0, num5 = 0, num6 = 0, i; + long int lnum = 0, lnum3 = 0; + unsigned long long llnum = 0; + FBFILE *fl = NULL; + char filename[40]; + char buf[MAX_RAW_INPUT_LENGTH], line[MAX_RAW_INPUT_LENGTH], tag[6]; + char line1[MAX_RAW_INPUT_LENGTH]; + struct timed_type timed; + *filename = '\0'; log("Load ascii char %s", name); if (!find_id) @@ -1095,1110 +1095,9 @@ int Player::load_char_ascii(const char *name, bool reboot, const bool find_id /* log("Can't load ascii. ID: %d; File name: \"%s\"; Current directory: \"%s\")", id, filename, getcwd(buffer, BUFFER_SIZE)); return -1; } - -/////////////////////////////////////////////////////////////////////////////// - - // первыми иним и парсим поля для ребута до поля "Rebt", если reboot на входе = 1, то на этом парс и кончается - if (!this->player_specials) - { - this->player_specials = std::make_shared(); - } - - set_level(1); - set_class(1); - set_uid(0); - set_last_logon(time(0)); - set_idnum(0); - set_exp(0); - set_remort(0); - GET_LASTIP(this)[0] = 0; - GET_EMAIL(this)[0] = 0; - PLR_FLAGS(this).from_string(""); // suspicious line: we should clear flags.. Loading from "" does not clear flags. - - bool skip_file = 0; - - do - { - if (!fbgetline(fl, line)) - { - log("SYSERROR: Wrong file ascii %d %s", id, filename); - return (-1); - } - - tag_argument(line, tag); - for (i = 0; !(line[i] == ' ' || line[i] == '\0'); i++) - { - line1[i] = line[i]; - } - line1[i] = '\0'; - num = atoi(line1); - lnum = atol(line1); - try - { - llnum = boost::lexical_cast(line1); - } - catch(boost::bad_lexical_cast &) - { - llnum = 0; - } - - - switch (*tag) - { - case 'A': - if (!strcmp(tag, "Act ")) - { - PLR_FLAGS(this).from_string(line); - } - break; - case 'C': - if (!strcmp(tag, "Clas")) - { - set_class(num); - } - break; - case 'E': - if (!strcmp(tag, "Exp ")) - { - set_exp(lnum); - } - //added by WorM 2010.08.27 лоадим мыло и айпи даже при ребуте - else if (!strcmp(tag, "EMal")) - strcpy(GET_EMAIL(this), line); - break; - case 'H': - if (!strcmp(tag, "Host")) - { - strcpy(GET_LASTIP(this), line); - } - //end by WorM - break; - case 'I': - if (!strcmp(tag, "Id ")) - { - set_idnum(lnum); - } - break; - case 'L': - if (!strcmp(tag, "LstL")) - { - set_last_logon(lnum); - } - else if (!strcmp(tag, "Levl")) - { - set_level(num); - } - break; - case 'N': - if (!strcmp(tag, "Name")) - { - set_name(line); - } - break; - case 'R': - if (!strcmp(tag, "Rebt")) - skip_file = 1; - else if (!strcmp(tag, "Rmrt")) - { - set_remort(num); - } - break; - case 'U': - if (!strcmp(tag, "UIN ")) - { - set_uid(num); - } - break; - default: - sprintf(buf, "SYSERR: Unknown tag %s in pfile %s", tag, name); - } - } - while (!skip_file); - - //added by WorM 2010.08.27 лоадим мыло и последний ip даже при считывании индексов - while((reboot) && (!*GET_EMAIL(this) || !*GET_LASTIP(this))) - { - if (!fbgetline(fl, line)) - { - log("SYSERROR: Wrong file ascii %d %s", id, filename); - return (-1); - } - - tag_argument(line, tag); - - if (!strcmp(tag, "EMal")) - strcpy(GET_EMAIL(this), line); - else if (!strcmp(tag, "Host")) - strcpy(GET_LASTIP(this), line); - } - //end by WorM - - // если с загруженными выше полями что-то хочется делать после лоада - делайте это здесь - - //Indexing experience - if his exp is lover than required for his level - set it to required - if (GET_EXP(this) < ExpCalc::level_exp(this, GET_LEVEL(this))) - { - set_exp(ExpCalc::level_exp(this, GET_LEVEL(this))); - } - - if (reboot) - { - fbclose(fl); - return id; - } - this->str_to_cities(default_str_cities); - // если происходит обычный лоад плеера, то читаем файл дальше и иним все остальные поля - -/////////////////////////////////////////////////////////////////////////////// - - - // character init - // initializations necessary to keep some things straight - - this->set_npc_name(0); - this->player_data.long_descr = ""; - - this->real_abils.Feats.reset(); - - // волхвам сетим все спеллы на рунах, остальные инит нулями - if (GET_CLASS(this) != CLASS_DRUID) - for (i = 1; i <= MAX_SPELLS; i++) - GET_SPELL_TYPE(this, i) = 0; - else - for (i = 1; i <= MAX_SPELLS; i++) - GET_SPELL_TYPE(this, i) = SPELL_RUNES; - - for (i = 1; i <= MAX_SPELLS; i++) - GET_SPELL_MEM(this, i) = 0; - this->char_specials.saved.affected_by = clear_flags; - POOFIN(this) = NULL; - POOFOUT(this) = NULL; - GET_RSKILL(this) = NULL; // рецептов не знает - this->char_specials.carry_weight = 0; - this->char_specials.carry_items = 0; - this->real_abils.armor = 100; - GET_MEM_TOTAL(this) = 0; - GET_MEM_COMPLETED(this) = 0; - MemQ_init(this); - - GET_AC(this) = 10; - GET_ALIGNMENT(this) = 0; - GET_BAD_PWS(this) = 0; - this->player_data.time.birth = time(0); - GET_KIN(this) = 0; - - this->set_str(10); - this->set_dex(10); - this->set_con(10); - this->set_int(10); - this->set_wis(10); - this ->set_cha(10); - - GET_COND(this, DRUNK) = 0; - GET_DRUNK_STATE(this) = 0; - -// Punish Init - DUMB_DURATION(this) = 0; - DUMB_REASON(this) = 0; - GET_DUMB_LEV(this) = 0; - DUMB_GODID(this) = 0; - - MUTE_DURATION(this) = 0; - MUTE_REASON(this) = 0; - GET_MUTE_LEV(this) = 0; - MUTE_GODID(this) = 0; - - HELL_DURATION(this) = 0; - HELL_REASON(this) = 0; - GET_HELL_LEV(this) = 0; - HELL_GODID(this) = 0; - - FREEZE_DURATION(this) = 0; - FREEZE_REASON(this) = 0; - GET_FREEZE_LEV(this) = 0; - FREEZE_GODID(this) = 0; - - GCURSE_DURATION(this) = 0; - GCURSE_REASON(this) = 0; - GET_GCURSE_LEV(this) = 0; - GCURSE_GODID(this) = 0; - - NAME_DURATION(this) = 0; - NAME_REASON(this) = 0; - GET_NAME_LEV(this) = 0; - NAME_GODID(this) = 0; - - UNREG_DURATION(this) = 0; - UNREG_REASON(this) = 0; - GET_UNREG_LEV(this) = 0; - UNREG_GODID(this) = 0; - -// End punish init - - GET_DR(this) = 0; - - set_gold(0, false); - set_bank(0, false); - set_ruble(0); - this->player_specials->saved.GodsLike = 0; - GET_HIT(this) = 21; - GET_MAX_HIT(this) = 21; - GET_HEIGHT(this) = 50; - GET_HR(this) = 0; - GET_COND(this, FULL) = 0; - SET_INVIS_LEV(this, 0); - this->player_data.time.logon = time(0); - GET_MOVE(this) = 44; - GET_MAX_MOVE(this) = 44; - KARMA(this) = 0; - LOGON_LIST(this).clear(); - NAME_GOD(this) = 0; - STRING_LENGTH(this) = 80; - STRING_WIDTH(this) = 30; - NAME_ID_GOD(this) = 0; - GET_OLC_ZONE(this) = 0; - this->player_data.time.played = 0; - GET_LOADROOM(this) = NOWHERE; - GET_RELIGION(this) = 1; - GET_RACE(this) = 1; - this->set_sex(ESex::SEX_NEUTRAL); - GET_COND(this, THIRST) = NORM_COND_VALUE; - GET_WEIGHT(this) = 50; - GET_WIMP_LEV(this) = 0; - PRF_FLAGS(this).from_string(""); // suspicious line: we should clear flags.. Loading from "" does not clear flags. - AFF_FLAGS(this).from_string(""); // suspicious line: we should clear flags.. Loading from "" does not clear flags. - GET_PORTALS(this) = NULL; - EXCHANGE_FILTER(this) = NULL; - clear_ignores(); - CREATE(GET_LOGS(this), 1 + LAST_LOG); - NOTIFY_EXCH_PRICE(this) = 0; - this->player_specials->saved.HiredCost = 0; - this->set_who_mana(WHO_MANA_MAX); - this->set_who_last(time(0)); - - while (fbgetline(fl, line)) - { - tag_argument(line, tag); - for (i = 0; !(line[i] == ' ' || line[i] == '\0'); i++) - { - line1[i] = line[i]; - } - line1[i] = '\0'; - num = atoi(line1); - lnum = atol(line1); - try - { - llnum = std::stoull(line1, nullptr, 10); - } - catch (const std::invalid_argument &) - { - llnum = 0; - } - catch (const std::out_of_range &) - { - llnum = 0; - } - switch (*tag) - { - case 'A': - if (!strcmp(tag, "Ac ")) - { - GET_AC(this) = num; - } - else if (!strcmp(tag, "Aff ")) - { - AFF_FLAGS(this).from_string(line); - } - else if (!strcmp(tag, "Affs")) - { - i = 0; - do - { - fbgetline(fl, line); - sscanf(line, "%d %d %d %d %d %d", &num, &num2, &num3, &num4, &num5, &num6); - if (num > 0) - { - AFFECT_DATA af; - af.type = num; - af.duration = num2; - af.modifier = num3; - af.location = static_cast(num4); - af.bitvector = num5; - af.battleflag = num6; - if (af.type == SPELL_LACKY) - { - af.handler.reset(new LackyAffectHandler()); - } - affect_to_char(this, af); - i++; - } - } while (num != 0); - /* do not load affects */ - } - else if (!strcmp(tag, "Alin")) - { - GET_ALIGNMENT(this) = num; - } - break; - - case 'B': - if (!strcmp(tag, "Badp")) - { - GET_BAD_PWS(this) = num; - } - else if (!strcmp(tag, "Bank")) - { - set_bank(lnum, false); - } - else if (!strcmp(tag, "Br01")) - set_board_date(Boards::GENERAL_BOARD, llnum); - else if (!strcmp(tag, "Br02")) - set_board_date(Boards::NEWS_BOARD, llnum); - else if (!strcmp(tag, "Br03")) - set_board_date(Boards::IDEA_BOARD, llnum); - else if (!strcmp(tag, "Br04")) - set_board_date(Boards::ERROR_BOARD, llnum); - else if (!strcmp(tag, "Br05")) - set_board_date(Boards::GODNEWS_BOARD, llnum); - else if (!strcmp(tag, "Br06")) - set_board_date(Boards::GODGENERAL_BOARD, llnum); - else if (!strcmp(tag, "Br07")) - set_board_date(Boards::GODBUILD_BOARD, llnum); - else if (!strcmp(tag, "Br08")) - set_board_date(Boards::GODCODE_BOARD, llnum); - else if (!strcmp(tag, "Br09")) - set_board_date(Boards::GODPUNISH_BOARD, llnum); - else if (!strcmp(tag, "Br10")) - set_board_date(Boards::PERS_BOARD, llnum); - else if (!strcmp(tag, "Br11")) - set_board_date(Boards::CLAN_BOARD, llnum); - else if (!strcmp(tag, "Br12")) - set_board_date(Boards::CLANNEWS_BOARD, llnum); - else if (!strcmp(tag, "Br13")) - set_board_date(Boards::NOTICE_BOARD, llnum); - else if (!strcmp(tag, "Br14")) - set_board_date(Boards::MISPRINT_BOARD, llnum); - else if (!strcmp(tag, "Br15")) - set_board_date(Boards::SUGGEST_BOARD, llnum); - else if (!strcmp(tag, "Br16")) - set_board_date(Boards::CODER_BOARD, llnum); - - else if (!strcmp(tag, "Brth")) - this->player_data.time.birth = lnum; - break; - - case 'C': - if (!strcmp(tag, "Cha ")) - this->set_cha(num); - else if ( !strcmp(tag, "Chrm") ) { - log("Load_char: Charmees loading"); - do { - fbgetline(fl, line); - sscanf(line, "%d %d %d %d %d %d", &num, &num2, &num3, &num4, &num5, &num6); - if (this->charmeeHistory.find(num) == this->charmeeHistory.end() && num != 0) { - MERCDATA md = {num2, num3, num4, num5, num6}; // num - key - this->charmeeHistory.insert( std::pair(num, md)); - log("Load_char: Charmees: vnum: %d", num); - } - } while (num != 0); - } - else if (!strcmp(tag, "Con ")) - this->set_con(num); - else if (!strcmp(tag, "CntS")) - this->reset_stats_cnt_[ResetStats::Type::MAIN_STATS] = num; - else if (!strcmp(tag, "CntR")) - this->reset_stats_cnt_[ResetStats::Type::RACE] = num; - else if (!strcmp(tag, "CntF")) - this->reset_stats_cnt_[ResetStats::Type::FEATS] = num; - else if (!strcmp(tag, "Cits")) - { - std::string buffer_cities = std::string(line); - // это на тот случай, если вдруг количество городов поменялось - if (buffer_cities.size() != ::cities.size()) - { - // если меньше - if (buffer_cities.size() < ::cities.size()) - { - const size_t b_size = buffer_cities.size(); - // то добиваем нулями - for (unsigned int i = 0; i < ::cities.size() - b_size; i++) - buffer_cities += "0"; - } - else - { - // режем строку - buffer_cities.resize(buffer_cities.size() - (buffer_cities.size() - ::cities.size())); - } - } - this->str_to_cities(std::string(buffer_cities)); - } - break; - - case 'D': - if (!strcmp(tag, "Desc")) - { - const auto ptr = fbgetstring(fl); - this->player_data.description = ptr ? ptr : ""; - } - else if (!strcmp(tag, "Disp")) - { - std::bitset tmp_flags(lnum); - disposable_flags_ = tmp_flags; - } - else if (!strcmp(tag, "Dex ")) - this->set_dex(num); - else if (!strcmp(tag, "Drnk")) - GET_COND(this, DRUNK) = num; - else if (!strcmp(tag, "DrSt")) - GET_DRUNK_STATE(this) = num; - else if (!strcmp(tag, "Drol")) - GET_DR(this) = num; - else if (!strcmp(tag, "DaiQ")) - { - - if (sscanf(line, "%d %d %ld", &num, &num2, &lnum) == 2) - { - this->add_daily_quest(num, num2); - } - else - { - this->add_daily_quest(num, num2); - this->set_time_daily_quest(num, lnum); - } - - } - break; - - case 'E': - if (!strcmp(tag, "ExFl")) - EXCHANGE_FILTER(this) = str_dup(line); - else if (!strcmp(tag, "EMal")) - strcpy(GET_EMAIL(this), line); -//29.11.09. (c) Василиса -//edited by WorM 2011.05.21 - else if (!strcmp(tag, "Expa")) - GET_EXP_ARENA(this) = llnum; - else if (!strcmp(tag, "Expm")) - GET_EXP_MOB(this) = llnum; - else if (!strcmp(tag, "Exmt")) - GET_EXP_MOBTHIS(this) = llnum; - else if (!strcmp(tag, "Expp")) - GET_EXP_PK(this) = llnum; - else if (!strcmp(tag, "Expt")) - GET_EXP_PKTHIS(this) = llnum; - else if (!strcmp(tag, "Expo")) - GET_EXP_OTHER(this) = llnum; - else if (!strcmp(tag, "Exot")) - GET_EXP_OTHERTHIS(this) = llnum; - else if (!strcmp(tag, "Expd")) - GET_EXP_DT(this) = llnum; - else if (!strcmp(tag, "Exdt")) - GET_EXP_DTTHIS(this) = llnum; -//end by WorM -//Конец правки (с) Василиса - break; - - case 'F': - // Оставлено для совместимости со старым форматом наказаний - if (!strcmp(tag, "Frez")) - GET_FREEZE_LEV(this) = num; - else if (!strcmp(tag, "Feat")) - { - do - { - fbgetline(fl, line); - sscanf(line, "%d", &num); - if (num > 0 && num < MAX_FEATS) - if (feat_info[num].classknow[(int) GET_CLASS(this)][(int) GET_KIN(this)] || PlayerRace::FeatureCheck((int)GET_KIN(this),(int)GET_RACE(this),num)) - SET_FEAT(this, num); - } - while (num != 0); - } - else if (!strcmp(tag, "FtTm")) - { - do - { - fbgetline(fl, line); - sscanf(line, "%d %d", &num, &num2); - if (num != 0) - { - timed.skill = num; - timed.time = num2; - timed_feat_to_char(this, &timed); - } - } - while (num != 0); - } - break; - - case 'G': - if (!strcmp(tag, "Gold")) - { - set_gold(lnum, false); - } - else if (!strcmp(tag, "GodD")) - GCURSE_DURATION(this) = lnum; - else if (!strcmp(tag, "GdFl")) - this->player_specials->saved.GodsLike = lnum; - // added by WorM (Видолюб) 2010.06.04 бабки потраченные на найм(возвращаются при креше) - else if (!strcmp(tag, "GldH")) - { - if(num != 0 && !IS_IMMORTAL(this) && can_use_feat(this, EMPLOYER_FEAT)) - { - this->player_specials->saved.HiredCost = num; - } - } - // end by WorM - break; - - case 'H': - if (!strcmp(tag, "Hit ")) - { - sscanf(line, "%d/%d", &num, &num2); - GET_HIT(this) = num; - GET_MAX_HIT(this) = num2; - } - else if (!strcmp(tag, "Hite")) - GET_HEIGHT(this) = num; - else if (!strcmp(tag, "Hrol")) - GET_HR(this) = num; - else if (!strcmp(tag, "Hung")) - GET_COND(this, FULL) = num; - else if (!strcmp(tag, "Hry ")) - { - if (num > cap_hryvn) - num = cap_hryvn; - this->set_hryvn(num); - } - else if (!strcmp(tag, "Host")) - strcpy(GET_LASTIP(this), line); - break; - - case 'I': - if (!strcmp(tag, "Int ")) - this->set_int(num); - else if (!strcmp(tag, "Invs")) - { - SET_INVIS_LEV(this, num); - } - else if (!strcmp(tag, "Ignr")) - { - IgnoresLoader ignores_loader(this); - ignores_loader.load_from_string(line); - } - else if (!strcmp(tag, "ICur")) - { - this->set_ice_currency(num); -// this->set_ice_currency(0); // чистка льда - } - break; - - case 'K': - if (!strcmp(tag, "Kin ")) - GET_KIN(this) = num; - else if (!strcmp(tag, "Karm")) - KARMA(this) = fbgetstring(fl); - break; - case 'L': - if (!strcmp(tag, "LogL")) - { - long lnum, lnum2; - do - { - fbgetline(fl, line); - sscanf(line, "%s %ld %ld", &buf[0], &lnum, &lnum2); - if (buf[0] != '~') - { - const logon_data cur_log = { str_dup(buf), lnum, lnum2, false }; - LOGON_LIST(this).push_back(cur_log); - } - else break; - } - while (true); - - if (!LOGON_LIST(this).empty()) - { - LOGON_LIST(this).at(0).is_first = true; - std::sort(LOGON_LIST(this).begin(), LOGON_LIST(this).end(), - [](const logon_data& a, const logon_data& b) - { - return a.lasttime < b.lasttime; - }); - } - } -// Gunner - else if (!strcmp(tag, "Logs")) - { - sscanf(line, "%d %d", &num, &num2); - if (num >= 0 && num < 1 + LAST_LOG) - GET_LOGS(this)[num] = num2; - } - else if (!strcmp(tag, "Lexc")) - this->set_last_exchange(num); - break; - - case 'M': - if (!strcmp(tag, "Mana")) - { - sscanf(line, "%d/%d", &num, &num2); - GET_MEM_COMPLETED(this) = num; - GET_MEM_TOTAL(this) = num2; - } - else if (!strcmp(tag, "Map ")) - { - std::string str(line); - std::bitset tmp(str); - map_options_.bit_list_ = tmp; - } - else if (!strcmp(tag, "Move")) - { - sscanf(line, "%d/%d", &num, &num2); - GET_MOVE(this) = num; - GET_MAX_MOVE(this) = num2; - } - else if (!strcmp(tag, "Mobs")) - { - do - { - if (!fbgetline(fl, line)) - break; - if (*line == '~') - break; - sscanf(line, "%d %d", &num, &num2); - this->mobmax_load(this, num, num2, MobMax::get_level_by_vnum(num)); - } - while (true); - } - else if (!strcmp(tag, "Mrph")) - { - morphs_load(this, std::string(line)); - } - break; - case 'N': - if (!strcmp(tag, "NmI ")) - this->player_data.PNames[0] = std::string(line); - else if (!strcmp(tag, "NmR ")) - this->player_data.PNames[1] = std::string(line); - else if (!strcmp(tag, "NmD ")) - this->player_data.PNames[2] = std::string(line); - else if (!strcmp(tag, "NmV ")) - this->player_data.PNames[3] = std::string(line); - else if (!strcmp(tag, "NmT ")) - this->player_data.PNames[4] = std::string(line); - else if (!strcmp(tag, "NmP ")) - this->player_data.PNames[5] = std::string(line); - else if (!strcmp(tag, "NamD")) - NAME_DURATION(this) = lnum; - else if (!strcmp(tag, "NamG")) - NAME_GOD(this) = num; - else if (!strcmp(tag, "NaID")) - NAME_ID_GOD(this) = lnum; - else if (!strcmp(tag, "NtfE"))//Polud мин. цена для оффлайн-оповещений - NOTIFY_EXCH_PRICE(this) = lnum; - break; - - case 'O': - if (!strcmp(tag, "Olc ")) - GET_OLC_ZONE(this) = num; - break; - - - case 'P': - if (!strcmp(tag, "Pass")) - this->set_passwd(line); - else if (!strcmp(tag, "Plyd")) - this->player_data.time.played = num; - else if (!strcmp(tag, "PfIn")) - POOFIN(this) = str_dup(line); - else if (!strcmp(tag, "PfOt")) - POOFOUT(this) = str_dup(line); - else if (!strcmp(tag, "Pref")) - { - PRF_FLAGS(this).from_string(line); - } - else if (!strcmp(tag, "Pkil")) - { - do - { - if (!fbgetline(fl, line)) - break; - if (*line == '~') - break; - if (sscanf(line, "%ld %d %d", &lnum, &num, &num2) < 3) { - num2 = 0; - }; - if (lnum < 0 || !correct_unique(lnum)) - continue; - if (num2 >= MAX_REVENGE) { - if (--num <= 0) { - continue; - } - num2 = 0; - } - struct PK_Memory_type * pk_one = NULL; - for (pk_one = this->pk_list; pk_one; pk_one = pk_one->next) - if (pk_one->unique == lnum) - break; - if (pk_one) { - log("SYSERROR: duplicate entry pkillers data for %d %s", id, filename); - continue; - } - - CREATE(pk_one, 1); - pk_one->unique = lnum; - pk_one->kill_num = num; - pk_one->revenge_num = num2; - pk_one->next = this->pk_list; - this->pk_list = pk_one; - } - while (true); - } - else if (!strcmp(tag, "Prtl")) - add_portal_to_char(this, num); - // Loads Here new punishment strings - else if (!strcmp(tag, "PMut")) - { - sscanf(line, "%ld %d %ld %[^~]", &lnum, &num2, &lnum3, &buf[0]); - MUTE_DURATION(this) = lnum; - GET_MUTE_LEV(this) = num2; - MUTE_GODID(this) = lnum3; - MUTE_REASON(this) = str_dup(buf); - } - else if (!strcmp(tag, "PHel")) - { - sscanf(line, "%ld %d %ld %[^~]", &lnum, &num2, &lnum3, &buf[0]); - HELL_DURATION(this) = lnum; - GET_HELL_LEV(this) = num2; - HELL_GODID(this) = lnum3; - HELL_REASON(this) = str_dup(buf); - } - else if (!strcmp(tag, "PDum")) - { - sscanf(line, "%ld %d %ld %[^~]", &lnum, &num2, &lnum3, &buf[0]); - DUMB_DURATION(this) = lnum; - GET_DUMB_LEV(this) = num2; - DUMB_GODID(this) = lnum3; - DUMB_REASON(this) = str_dup(buf); - } - else if (!strcmp(tag, "PNam")) - { - sscanf(line, "%ld %d %ld %[^~]", &lnum, &num2, &lnum3, &buf[0]); - NAME_DURATION(this) = lnum; - GET_NAME_LEV(this) = num2; - NAME_GODID(this) = lnum3; - NAME_REASON(this) = str_dup(buf); - } - else if (!strcmp(tag, "PFrz")) - { - sscanf(line, "%ld %d %ld %[^~]", &lnum, &num2, &lnum3, &buf[0]); - FREEZE_DURATION(this) = lnum; - GET_FREEZE_LEV(this) = num2; - FREEZE_GODID(this) = lnum3; - FREEZE_REASON(this) = str_dup(buf); - } - else if (!strcmp(tag, "PGcs")) - { - sscanf(line, "%ld %d %ld %[^~]", &lnum, &num2, &lnum3, &buf[0]); - GCURSE_DURATION(this) = lnum; - GET_GCURSE_LEV(this) = num2; - GCURSE_GODID(this) = lnum3; - GCURSE_REASON(this) = str_dup(buf); - } - else if (!strcmp(tag, "PUnr")) - { - sscanf(line, "%ld %d %ld %[^~]", &lnum, &num2, &lnum3, &buf[0]); - UNREG_DURATION(this) = lnum; - GET_UNREG_LEV(this) = num2; - UNREG_GODID(this) = lnum3; - UNREG_REASON(this) = str_dup(buf); - } - - break; - - case 'Q': - if (!strcmp(tag, "Qst ")) - { - buf[0] = '\0'; - sscanf(line, "%d %[^~]", &num, &buf[0]); - this->quested_add(this, num, buf); - } - break; - - case 'R': - if (!strcmp(tag, "Room")) - GET_LOADROOM(this) = num; -//29.11.09. (c) Василиса - else if (!strcmp(tag, "Ripa")) - GET_RIP_ARENA(this) = num; - else if (!strcmp(tag, "Ripm")) - GET_RIP_MOB(this) = num; - else if (!strcmp(tag, "Rimt")) - GET_RIP_MOBTHIS(this) = num; - else if (!strcmp(tag, "Ruble")) - this->set_ruble(num); - else if (!strcmp(tag, "Ripp")) - GET_RIP_PK(this) = num; - else if (!strcmp(tag, "Ript")) - GET_RIP_PKTHIS(this) = num; - else if (!strcmp(tag, "Ripo")) - GET_RIP_OTHER(this) = num; - else if (!strcmp(tag, "Riot")) - GET_RIP_OTHERTHIS(this) = num; - else if (!strcmp(tag, "Ripd")) - GET_RIP_DT(this) = num; - else if (!strcmp(tag, "Ridt")) - GET_RIP_DTTHIS(this) = num; -//(с) Василиса - else if (!strcmp(tag, "Rmbr")) - this->remember_set_num(num); - else if (!strcmp(tag, "Reli")) - GET_RELIGION(this) = num; - else if (!strcmp(tag, "Race")) - GET_RACE(this) = num; - else if (!strcmp(tag, "Rcps")) - { - im_rskill *last = NULL; - for (;;) - { - im_rskill *rs; - fbgetline(fl, line); - sscanf(line, "%d %d", &num, &num2); - if (num < 0) - break; - num = im_get_recipe(num); -// +newbook.patch (Alisher) - if (num < 0 || imrecipes[num].classknow[(int) GET_CLASS(this)] != KNOW_RECIPE) -// -newbook.patch (Alisher) - continue; - CREATE(rs, 1); - rs->rid = num; - rs->perc = num2; - rs->link = NULL; - if (last) - last->link = rs; - else - GET_RSKILL(this) = rs; - last = rs; - } - } - break; - - case 'S': - if (!strcmp(tag, "Size")) - GET_SIZE(this) = num; - else if (!strcmp(tag, "Sex ")) - { - this->set_sex(static_cast(num)); - } - else if (!strcmp(tag, "Skil")) - { - do - { - fbgetline(fl, line); - sscanf(line, "%d %d", &num, &num2); - if (num != 0) - { - if (skill_info[num].classknow[(int)GET_CLASS(this)][(int)GET_KIN(this)] == KNOW_SKILL) - { - this->set_skill(static_cast(num), num2); - } - } - } - while (num != 0); - } - else if (!strcmp(tag, "SkTm")) - { - do - { - fbgetline(fl, line); - sscanf(line, "%d %d", &num, &num2); - if (num != 0) - { - timed.skill = num; - timed.time = num2; - timed_to_char(this, &timed); - } - } - while (num != 0); - } - else if (!strcmp(tag, "Spel")) - { - do - { - fbgetline(fl, line); - sscanf(line, "%d %d", &num, &num2); - if (num != 0 && spell_info[num].name) - GET_SPELL_TYPE(this, num) = num2; - } - while (num != 0); - } - else if (!strcmp(tag, "SpMe")) - { - do - { - fbgetline(fl, line); - sscanf(line, "%d %d", &num, &num2); - if (num != 0) - GET_SPELL_MEM(this, num) = num2; - } - while (num != 0); - } - else if (!strcmp(tag, "SpTM")) - { - struct spell_mem_queue_item *qi_cur, ** qi = &MemQueue.queue; - while (*qi) - qi = &((*qi)->link); - do - { - fbgetline(fl, line); - sscanf(line, "%d", &num); - if (num != 0) - { - CREATE(qi_cur, 1); - *qi = qi_cur; - qi_cur->spellnum = num; - qi_cur->link = NULL; - qi = &qi_cur->link; - } - } - while (num != 0); - } - else if (!strcmp(tag, "Str ")) - this->set_str(num); - else if (!strcmp(tag, "StrL")) - STRING_LENGTH(this) = num; - else if (!strcmp(tag, "StrW")) - STRING_WIDTH(this) = num; - else if (!strcmp(tag, "St00")) - this->set_start_stat(G_STR, lnum); - else if (!strcmp(tag, "St01")) - this->set_start_stat(G_DEX, lnum); - else if (!strcmp(tag, "St02")) - this->set_start_stat(G_INT, lnum); - else if (!strcmp(tag, "St03")) - this->set_start_stat(G_WIS, lnum); - else if (!strcmp(tag, "St04")) - this->set_start_stat(G_CON, lnum); - else if (!strcmp(tag, "St05")) - this->set_start_stat(G_CHA, lnum); - break; - - case 'T': - if (!strcmp(tag, "Thir")) - GET_COND(this, THIRST) = num; - else if (!strcmp(tag, "Titl")) - GET_TITLE(this) = std::string(str_dup(line)); - else if (!strcmp(tag, "TrcG")) - set_ext_money(ExtMoney::TORC_GOLD, num, false); - else if (!strcmp(tag, "TrcS")) - set_ext_money(ExtMoney::TORC_SILVER, num, false); - else if (!strcmp(tag, "TrcB")) - set_ext_money(ExtMoney::TORC_BRONZE, num, false); - else if (!strcmp(tag, "TrcL")) { - sscanf(line, "%d %d", &num, &num2); - today_torc_.first = num; - today_torc_.second = num2; - } - else if (!strcmp(tag, "Tlgr")) { - if (lnum <= 10000000000000) { - this->player_specials->saved.telegram_id = lnum; - } else { // зачищаем остатки старой баги - this->player_specials->saved.telegram_id = 0; - } - } - else if (!strcmp(tag, "TSpl")) - { - do - { - fbgetline(fl, line); - sscanf(line, "%d %ld %ld", &num, &lnum, &lnum3); - if (num != 0 && spell_info[num].name) - { - Temporary_Spells::add_spell(this, num, lnum, lnum3); - } - } while (num != 0); - } - break; - - case 'W': - if (!strcmp(tag, "Wate")) - GET_WEIGHT(this) = num; - else if (!strcmp(tag, "Wimp")) - GET_WIMP_LEV(this) = num; - else if (!strcmp(tag, "Wis ")) - this->set_wis(num); -//29.11.09 (c) Василиса - else if (!strcmp(tag, "Wina")) - GET_WIN_ARENA(this) = num; -//конец правки (с) Василиса - else if (!strcmp(tag, "Wman")) - this->set_who_mana(num); - break; - - default: - sprintf(buf, "SYSERR: Unknown tag %s in pfile %s", tag, name); - } - } - PRF_FLAGS(this).set(PRF_COLOR_2); //всегда цвет полный - // initialization for imms - if (GET_LEVEL(this) >= LVL_IMMORT) - { - set_god_skills(this); - set_god_morphs(this); - GET_COND(this, FULL) = -1; - GET_COND(this, THIRST) = -1; - GET_COND(this, DRUNK) = -1; - GET_LOADROOM(this) = NOWHERE; - } - - setAllInbornFeatures(this); - - if (IS_GRGOD(this)) - { - for (i = 0; i <= MAX_SPELLS; i++) - GET_SPELL_TYPE(this, i) = GET_SPELL_TYPE(this, i) | - SPELL_ITEMS | SPELL_KNOW | SPELL_RUNES | SPELL_SCROLL | SPELL_POTION | SPELL_WAND; - } - else if (!IS_IMMORTAL(this)) - { - for (i = 0; i <= MAX_SPELLS; i++) - { - if (spell_info[i].slot_forc[(int) GET_CLASS(this)][(int) GET_KIN(this)] == MAX_SLOT) - REMOVE_BIT(GET_SPELL_TYPE(this, i), SPELL_KNOW | SPELL_TEMP); -// shapirus: изученное не убираем на всякий случай, но из мема выкидываем, -// если мортов мало - if (GET_REMORT(this) < MIN_CAST_REM(spell_info[i], this)) - GET_SPELL_MEM(this, i) = 0; - } - } - - /* - * If you're not poisioned and you've been away for more than an hour of - * real time, we'll set your HMV back to full - */ - if (!AFF_FLAGGED(this, EAffectFlag::AFF_POISON) && (((long)(time(0) - LAST_LOGON(this))) >= SECS_PER_REAL_HOUR)) - { - GET_HIT(this) = GET_REAL_MAX_HIT(this); - GET_MOVE(this) = GET_REAL_MAX_MOVE(this); - } - else - GET_HIT(this) = MIN(GET_HIT(this), GET_REAL_MAX_HIT(this)); - - fbclose(fl); - // здесь мы закладываемся на то, что при ребуте это все сейчас пропускается и это нормально, - // иначе в таблице crc будут пустые имена, т.к. сама плеер-таблица еще не сформирована - // и в любом случае при ребуте это все пересчитывать не нужно - FileCRC::check_crc(filename, FileCRC::PLAYER, GET_UNIQUE(this)); - - this->account = Account::get_account(GET_EMAIL(this)); - if (this->account == nullptr) - { - const auto temp_account = std::make_shared(GET_EMAIL(this)); - accounts.emplace(GET_EMAIL(this), temp_account); - this->account = temp_account; - } - this->account->add_player(GET_UNIQUE(this)); - return (id); + if (!_pfileLoad(fl, reboot, name)); + return -1; + return id; } bool Player::get_disposable_flag(int num) @@ -2556,6 +1455,1118 @@ unsigned long int Player::getTelegramId() { return this->player_specials->saved.telegram_id; } +bool Player::_pfileLoad(FBFILE *fl, bool reboot, const char* name) { + int num = 0, num2 = 0, num3 = 0, num4 = 0, num5 = 0, num6 = 0, i; + long int lnum = 0, lnum3 = 0; + unsigned long long llnum = 0; + char filename[40]; + char buf[MAX_RAW_INPUT_LENGTH], line[MAX_RAW_INPUT_LENGTH], tag[6]; + char line1[MAX_RAW_INPUT_LENGTH]; + struct timed_type timed; + /////////////////////////////////////////////////////////////////////////////// + + // первыми иним и парсим поля для ребута до поля "Rebt", если reboot на входе = 1, то на этом парс и кончается + if (!this->player_specials) + { + this->player_specials = std::make_shared(); + } + + set_level(1); + set_class(1); + set_uid(0); + set_last_logon(time(0)); + set_idnum(0); + set_exp(0); + set_remort(0); + GET_LASTIP(this)[0] = 0; + GET_EMAIL(this)[0] = 0; + PLR_FLAGS(this).from_string(""); // suspicious line: we should clear flags.. Loading from "" does not clear flags. + + bool skip_file = 0; + + do + { + if (!fbgetline(fl, line)) + { + log("SYSERROR: Wrong file ascii %d %s", id, filename); + return (-1); + } + + tag_argument(line, tag); + for (i = 0; !(line[i] == ' ' || line[i] == '\0'); i++) + { + line1[i] = line[i]; + } + line1[i] = '\0'; + num = atoi(line1); + lnum = atol(line1); + try + { + llnum = boost::lexical_cast(line1); + } + catch(boost::bad_lexical_cast &) + { + llnum = 0; + } + + + switch (*tag) + { + case 'A': + if (!strcmp(tag, "Act ")) + { + PLR_FLAGS(this).from_string(line); + } + break; + case 'C': + if (!strcmp(tag, "Clas")) + { + set_class(num); + } + break; + case 'E': + if (!strcmp(tag, "Exp ")) + { + set_exp(lnum); + } + //added by WorM 2010.08.27 лоадим мыло и айпи даже при ребуте + else if (!strcmp(tag, "EMal")) + strcpy(GET_EMAIL(this), line); + break; + case 'H': + if (!strcmp(tag, "Host")) + { + strcpy(GET_LASTIP(this), line); + } + //end by WorM + break; + case 'I': + if (!strcmp(tag, "Id ")) + { + set_idnum(lnum); + } + break; + case 'L': + if (!strcmp(tag, "LstL")) + { + set_last_logon(lnum); + } + else if (!strcmp(tag, "Levl")) + { + set_level(num); + } + break; + case 'N': + if (!strcmp(tag, "Name")) + { + set_name(line); + } + break; + case 'R': + if (!strcmp(tag, "Rebt")) + skip_file = 1; + else if (!strcmp(tag, "Rmrt")) + { + set_remort(num); + } + break; + case 'U': + if (!strcmp(tag, "UIN ")) + { + set_uid(num); + } + break; + default: + sprintf(buf, "SYSERR: Unknown tag %s in pfile %s", tag, name); + } + } + while (!skip_file); + + //added by WorM 2010.08.27 лоадим мыло и последний ip даже при считывании индексов + while((reboot) && (!*GET_EMAIL(this) || !*GET_LASTIP(this))) + { + if (!fbgetline(fl, line)) + { + log("SYSERROR: Wrong file ascii %d %s", id, filename); + return (-1); + } + + tag_argument(line, tag); + + if (!strcmp(tag, "EMal")) + strcpy(GET_EMAIL(this), line); + else if (!strcmp(tag, "Host")) + strcpy(GET_LASTIP(this), line); + } + //end by WorM + + // если с загруженными выше полями что-то хочется делать после лоада - делайте это здесь + + //Indexing experience - if his exp is lover than required for his level - set it to required + if (GET_EXP(this) < ExpCalc::level_exp(this, GET_LEVEL(this))) + { + set_exp(ExpCalc::level_exp(this, GET_LEVEL(this))); + } + + if (reboot) + { + fbclose(fl); + return id; + } + this->str_to_cities(default_str_cities); + // если происходит обычный лоад плеера, то читаем файл дальше и иним все остальные поля + +/////////////////////////////////////////////////////////////////////////////// + + + // character init + // initializations necessary to keep some things straight + + this->set_npc_name(0); + this->player_data.long_descr = ""; + + this->real_abils.Feats.reset(); + + // волхвам сетим все спеллы на рунах, остальные инит нулями + if (GET_CLASS(this) != CLASS_DRUID) + for (i = 1; i <= MAX_SPELLS; i++) + GET_SPELL_TYPE(this, i) = 0; + else + for (i = 1; i <= MAX_SPELLS; i++) + GET_SPELL_TYPE(this, i) = SPELL_RUNES; + + for (i = 1; i <= MAX_SPELLS; i++) + GET_SPELL_MEM(this, i) = 0; + this->char_specials.saved.affected_by = clear_flags; + POOFIN(this) = NULL; + POOFOUT(this) = NULL; + GET_RSKILL(this) = NULL; // рецептов не знает + this->char_specials.carry_weight = 0; + this->char_specials.carry_items = 0; + this->real_abils.armor = 100; + GET_MEM_TOTAL(this) = 0; + GET_MEM_COMPLETED(this) = 0; + MemQ_init(this); + + GET_AC(this) = 10; + GET_ALIGNMENT(this) = 0; + GET_BAD_PWS(this) = 0; + this->player_data.time.birth = time(0); + GET_KIN(this) = 0; + + this->set_str(10); + this->set_dex(10); + this->set_con(10); + this->set_int(10); + this->set_wis(10); + this ->set_cha(10); + + GET_COND(this, DRUNK) = 0; + GET_DRUNK_STATE(this) = 0; + +// Punish Init + DUMB_DURATION(this) = 0; + DUMB_REASON(this) = 0; + GET_DUMB_LEV(this) = 0; + DUMB_GODID(this) = 0; + + MUTE_DURATION(this) = 0; + MUTE_REASON(this) = 0; + GET_MUTE_LEV(this) = 0; + MUTE_GODID(this) = 0; + + HELL_DURATION(this) = 0; + HELL_REASON(this) = 0; + GET_HELL_LEV(this) = 0; + HELL_GODID(this) = 0; + + FREEZE_DURATION(this) = 0; + FREEZE_REASON(this) = 0; + GET_FREEZE_LEV(this) = 0; + FREEZE_GODID(this) = 0; + + GCURSE_DURATION(this) = 0; + GCURSE_REASON(this) = 0; + GET_GCURSE_LEV(this) = 0; + GCURSE_GODID(this) = 0; + + NAME_DURATION(this) = 0; + NAME_REASON(this) = 0; + GET_NAME_LEV(this) = 0; + NAME_GODID(this) = 0; + + UNREG_DURATION(this) = 0; + UNREG_REASON(this) = 0; + GET_UNREG_LEV(this) = 0; + UNREG_GODID(this) = 0; + +// End punish init + + GET_DR(this) = 0; + + set_gold(0, false); + set_bank(0, false); + set_ruble(0); + this->player_specials->saved.GodsLike = 0; + GET_HIT(this) = 21; + GET_MAX_HIT(this) = 21; + GET_HEIGHT(this) = 50; + GET_HR(this) = 0; + GET_COND(this, FULL) = 0; + SET_INVIS_LEV(this, 0); + this->player_data.time.logon = time(0); + GET_MOVE(this) = 44; + GET_MAX_MOVE(this) = 44; + KARMA(this) = 0; + LOGON_LIST(this).clear(); + NAME_GOD(this) = 0; + STRING_LENGTH(this) = 80; + STRING_WIDTH(this) = 30; + NAME_ID_GOD(this) = 0; + GET_OLC_ZONE(this) = 0; + this->player_data.time.played = 0; + GET_LOADROOM(this) = NOWHERE; + GET_RELIGION(this) = 1; + GET_RACE(this) = 1; + this->set_sex(ESex::SEX_NEUTRAL); + GET_COND(this, THIRST) = NORM_COND_VALUE; + GET_WEIGHT(this) = 50; + GET_WIMP_LEV(this) = 0; + PRF_FLAGS(this).from_string(""); // suspicious line: we should clear flags.. Loading from "" does not clear flags. + AFF_FLAGS(this).from_string(""); // suspicious line: we should clear flags.. Loading from "" does not clear flags. + GET_PORTALS(this) = NULL; + EXCHANGE_FILTER(this) = NULL; + clear_ignores(); + CREATE(GET_LOGS(this), 1 + LAST_LOG); + NOTIFY_EXCH_PRICE(this) = 0; + this->player_specials->saved.HiredCost = 0; + this->set_who_mana(WHO_MANA_MAX); + this->set_who_last(time(0)); + + while (fbgetline(fl, line)) + { + tag_argument(line, tag); + for (i = 0; !(line[i] == ' ' || line[i] == '\0'); i++) + { + line1[i] = line[i]; + } + line1[i] = '\0'; + num = atoi(line1); + lnum = atol(line1); + try + { + llnum = std::stoull(line1, nullptr, 10); + } + catch (const std::invalid_argument &) + { + llnum = 0; + } + catch (const std::out_of_range &) + { + llnum = 0; + } + switch (*tag) + { + case 'A': + if (!strcmp(tag, "Ac ")) + { + GET_AC(this) = num; + } + else if (!strcmp(tag, "Aff ")) + { + AFF_FLAGS(this).from_string(line); + } + else if (!strcmp(tag, "Affs")) + { + i = 0; + do + { + fbgetline(fl, line); + sscanf(line, "%d %d %d %d %d %d", &num, &num2, &num3, &num4, &num5, &num6); + if (num > 0) + { + AFFECT_DATA af; + af.type = num; + af.duration = num2; + af.modifier = num3; + af.location = static_cast(num4); + af.bitvector = num5; + af.battleflag = num6; + if (af.type == SPELL_LACKY) + { + af.handler.reset(new LackyAffectHandler()); + } + affect_to_char(this, af); + i++; + } + } while (num != 0); + /* do not load affects */ + } + else if (!strcmp(tag, "Alin")) + { + GET_ALIGNMENT(this) = num; + } + break; + + case 'B': + if (!strcmp(tag, "Badp")) + { + GET_BAD_PWS(this) = num; + } + else if (!strcmp(tag, "Bank")) + { + set_bank(lnum, false); + } + else if (!strcmp(tag, "Br01")) + set_board_date(Boards::GENERAL_BOARD, llnum); + else if (!strcmp(tag, "Br02")) + set_board_date(Boards::NEWS_BOARD, llnum); + else if (!strcmp(tag, "Br03")) + set_board_date(Boards::IDEA_BOARD, llnum); + else if (!strcmp(tag, "Br04")) + set_board_date(Boards::ERROR_BOARD, llnum); + else if (!strcmp(tag, "Br05")) + set_board_date(Boards::GODNEWS_BOARD, llnum); + else if (!strcmp(tag, "Br06")) + set_board_date(Boards::GODGENERAL_BOARD, llnum); + else if (!strcmp(tag, "Br07")) + set_board_date(Boards::GODBUILD_BOARD, llnum); + else if (!strcmp(tag, "Br08")) + set_board_date(Boards::GODCODE_BOARD, llnum); + else if (!strcmp(tag, "Br09")) + set_board_date(Boards::GODPUNISH_BOARD, llnum); + else if (!strcmp(tag, "Br10")) + set_board_date(Boards::PERS_BOARD, llnum); + else if (!strcmp(tag, "Br11")) + set_board_date(Boards::CLAN_BOARD, llnum); + else if (!strcmp(tag, "Br12")) + set_board_date(Boards::CLANNEWS_BOARD, llnum); + else if (!strcmp(tag, "Br13")) + set_board_date(Boards::NOTICE_BOARD, llnum); + else if (!strcmp(tag, "Br14")) + set_board_date(Boards::MISPRINT_BOARD, llnum); + else if (!strcmp(tag, "Br15")) + set_board_date(Boards::SUGGEST_BOARD, llnum); + else if (!strcmp(tag, "Br16")) + set_board_date(Boards::CODER_BOARD, llnum); + + else if (!strcmp(tag, "Brth")) + this->player_data.time.birth = lnum; + break; + + case 'C': + if (!strcmp(tag, "Cha ")) + this->set_cha(num); + else if ( !strcmp(tag, "Chrm") ) { + log("Load_char: Charmees loading"); + do { + fbgetline(fl, line); + sscanf(line, "%d %d %d %d %d %d", &num, &num2, &num3, &num4, &num5, &num6); + if (this->charmeeHistory.find(num) == this->charmeeHistory.end() && num != 0) { + MERCDATA md = {num2, num3, num4, num5, num6}; // num - key + this->charmeeHistory.insert( std::pair(num, md)); + log("Load_char: Charmees: vnum: %d", num); + } + } while (num != 0); + } + else if (!strcmp(tag, "Con ")) + this->set_con(num); + else if (!strcmp(tag, "CntS")) + this->reset_stats_cnt_[ResetStats::Type::MAIN_STATS] = num; + else if (!strcmp(tag, "CntR")) + this->reset_stats_cnt_[ResetStats::Type::RACE] = num; + else if (!strcmp(tag, "CntF")) + this->reset_stats_cnt_[ResetStats::Type::FEATS] = num; + else if (!strcmp(tag, "Cits")) + { + std::string buffer_cities = std::string(line); + // это на тот случай, если вдруг количество городов поменялось + if (buffer_cities.size() != ::cities.size()) + { + // если меньше + if (buffer_cities.size() < ::cities.size()) + { + const size_t b_size = buffer_cities.size(); + // то добиваем нулями + for (unsigned int i = 0; i < ::cities.size() - b_size; i++) + buffer_cities += "0"; + } + else + { + // режем строку + buffer_cities.resize(buffer_cities.size() - (buffer_cities.size() - ::cities.size())); + } + } + this->str_to_cities(std::string(buffer_cities)); + } + break; + + case 'D': + if (!strcmp(tag, "Desc")) + { + const auto ptr = fbgetstring(fl); + this->player_data.description = ptr ? ptr : ""; + } + else if (!strcmp(tag, "Disp")) + { + std::bitset tmp_flags(lnum); + disposable_flags_ = tmp_flags; + } + else if (!strcmp(tag, "Dex ")) + this->set_dex(num); + else if (!strcmp(tag, "Drnk")) + GET_COND(this, DRUNK) = num; + else if (!strcmp(tag, "DrSt")) + GET_DRUNK_STATE(this) = num; + else if (!strcmp(tag, "Drol")) + GET_DR(this) = num; + else if (!strcmp(tag, "DaiQ")) + { + + if (sscanf(line, "%d %d %ld", &num, &num2, &lnum) == 2) + { + this->add_daily_quest(num, num2); + } + else + { + this->add_daily_quest(num, num2); + this->set_time_daily_quest(num, lnum); + } + + } + break; + + case 'E': + if (!strcmp(tag, "ExFl")) + EXCHANGE_FILTER(this) = str_dup(line); + else if (!strcmp(tag, "EMal")) + strcpy(GET_EMAIL(this), line); +//29.11.09. (c) Василиса +//edited by WorM 2011.05.21 + else if (!strcmp(tag, "Expa")) + GET_EXP_ARENA(this) = llnum; + else if (!strcmp(tag, "Expm")) + GET_EXP_MOB(this) = llnum; + else if (!strcmp(tag, "Exmt")) + GET_EXP_MOBTHIS(this) = llnum; + else if (!strcmp(tag, "Expp")) + GET_EXP_PK(this) = llnum; + else if (!strcmp(tag, "Expt")) + GET_EXP_PKTHIS(this) = llnum; + else if (!strcmp(tag, "Expo")) + GET_EXP_OTHER(this) = llnum; + else if (!strcmp(tag, "Exot")) + GET_EXP_OTHERTHIS(this) = llnum; + else if (!strcmp(tag, "Expd")) + GET_EXP_DT(this) = llnum; + else if (!strcmp(tag, "Exdt")) + GET_EXP_DTTHIS(this) = llnum; +//end by WorM +//Конец правки (с) Василиса + break; + + case 'F': + // Оставлено для совместимости со старым форматом наказаний + if (!strcmp(tag, "Frez")) + GET_FREEZE_LEV(this) = num; + else if (!strcmp(tag, "Feat")) + { + do + { + fbgetline(fl, line); + sscanf(line, "%d", &num); + if (num > 0 && num < MAX_FEATS) + if (feat_info[num].classknow[(int) GET_CLASS(this)][(int) GET_KIN(this)] || PlayerRace::FeatureCheck((int)GET_KIN(this),(int)GET_RACE(this),num)) + SET_FEAT(this, num); + } + while (num != 0); + } + else if (!strcmp(tag, "FtTm")) + { + do + { + fbgetline(fl, line); + sscanf(line, "%d %d", &num, &num2); + if (num != 0) + { + timed.skill = num; + timed.time = num2; + timed_feat_to_char(this, &timed); + } + } + while (num != 0); + } + break; + + case 'G': + if (!strcmp(tag, "Gold")) + { + set_gold(lnum, false); + } + else if (!strcmp(tag, "GodD")) + GCURSE_DURATION(this) = lnum; + else if (!strcmp(tag, "GdFl")) + this->player_specials->saved.GodsLike = lnum; + // added by WorM (Видолюб) 2010.06.04 бабки потраченные на найм(возвращаются при креше) + else if (!strcmp(tag, "GldH")) + { + if(num != 0 && !IS_IMMORTAL(this) && can_use_feat(this, EMPLOYER_FEAT)) + { + this->player_specials->saved.HiredCost = num; + } + } + // end by WorM + break; + + case 'H': + if (!strcmp(tag, "Hit ")) + { + sscanf(line, "%d/%d", &num, &num2); + GET_HIT(this) = num; + GET_MAX_HIT(this) = num2; + } + else if (!strcmp(tag, "Hite")) + GET_HEIGHT(this) = num; + else if (!strcmp(tag, "Hrol")) + GET_HR(this) = num; + else if (!strcmp(tag, "Hung")) + GET_COND(this, FULL) = num; + else if (!strcmp(tag, "Hry ")) + { + if (num > cap_hryvn) + num = cap_hryvn; + this->set_hryvn(num); + } + else if (!strcmp(tag, "Host")) + strcpy(GET_LASTIP(this), line); + break; + + case 'I': + if (!strcmp(tag, "Int ")) + this->set_int(num); + else if (!strcmp(tag, "Invs")) + { + SET_INVIS_LEV(this, num); + } + else if (!strcmp(tag, "Ignr")) + { + IgnoresLoader ignores_loader(this); + ignores_loader.load_from_string(line); + } + else if (!strcmp(tag, "ICur")) + { + this->set_ice_currency(num); +// this->set_ice_currency(0); // чистка льда + } + break; + + case 'K': + if (!strcmp(tag, "Kin ")) + GET_KIN(this) = num; + else if (!strcmp(tag, "Karm")) + KARMA(this) = fbgetstring(fl); + break; + case 'L': + if (!strcmp(tag, "LogL")) + { + long lnum, lnum2; + do + { + fbgetline(fl, line); + sscanf(line, "%s %ld %ld", &buf[0], &lnum, &lnum2); + if (buf[0] != '~') + { + const logon_data cur_log = { str_dup(buf), lnum, lnum2, false }; + LOGON_LIST(this).push_back(cur_log); + } + else break; + } + while (true); + + if (!LOGON_LIST(this).empty()) + { + LOGON_LIST(this).at(0).is_first = true; + std::sort(LOGON_LIST(this).begin(), LOGON_LIST(this).end(), + [](const logon_data& a, const logon_data& b) + { + return a.lasttime < b.lasttime; + }); + } + } +// Gunner + else if (!strcmp(tag, "Logs")) + { + sscanf(line, "%d %d", &num, &num2); + if (num >= 0 && num < 1 + LAST_LOG) + GET_LOGS(this)[num] = num2; + } + else if (!strcmp(tag, "Lexc")) + this->set_last_exchange(num); + break; + + case 'M': + if (!strcmp(tag, "Mana")) + { + sscanf(line, "%d/%d", &num, &num2); + GET_MEM_COMPLETED(this) = num; + GET_MEM_TOTAL(this) = num2; + } + else if (!strcmp(tag, "Map ")) + { + std::string str(line); + std::bitset tmp(str); + map_options_.bit_list_ = tmp; + } + else if (!strcmp(tag, "Move")) + { + sscanf(line, "%d/%d", &num, &num2); + GET_MOVE(this) = num; + GET_MAX_MOVE(this) = num2; + } + else if (!strcmp(tag, "Mobs")) + { + do + { + if (!fbgetline(fl, line)) + break; + if (*line == '~') + break; + sscanf(line, "%d %d", &num, &num2); + this->mobmax_load(this, num, num2, MobMax::get_level_by_vnum(num)); + } + while (true); + } + else if (!strcmp(tag, "Mrph")) + { + morphs_load(this, std::string(line)); + } + break; + case 'N': + if (!strcmp(tag, "NmI ")) + this->player_data.PNames[0] = std::string(line); + else if (!strcmp(tag, "NmR ")) + this->player_data.PNames[1] = std::string(line); + else if (!strcmp(tag, "NmD ")) + this->player_data.PNames[2] = std::string(line); + else if (!strcmp(tag, "NmV ")) + this->player_data.PNames[3] = std::string(line); + else if (!strcmp(tag, "NmT ")) + this->player_data.PNames[4] = std::string(line); + else if (!strcmp(tag, "NmP ")) + this->player_data.PNames[5] = std::string(line); + else if (!strcmp(tag, "NamD")) + NAME_DURATION(this) = lnum; + else if (!strcmp(tag, "NamG")) + NAME_GOD(this) = num; + else if (!strcmp(tag, "NaID")) + NAME_ID_GOD(this) = lnum; + else if (!strcmp(tag, "NtfE"))//Polud мин. цена для оффлайн-оповещений + NOTIFY_EXCH_PRICE(this) = lnum; + break; + + case 'O': + if (!strcmp(tag, "Olc ")) + GET_OLC_ZONE(this) = num; + break; + + + case 'P': + if (!strcmp(tag, "Pass")) + this->set_passwd(line); + else if (!strcmp(tag, "Plyd")) + this->player_data.time.played = num; + else if (!strcmp(tag, "PfIn")) + POOFIN(this) = str_dup(line); + else if (!strcmp(tag, "PfOt")) + POOFOUT(this) = str_dup(line); + else if (!strcmp(tag, "Pref")) + { + PRF_FLAGS(this).from_string(line); + } + else if (!strcmp(tag, "Pkil")) + { + do + { + if (!fbgetline(fl, line)) + break; + if (*line == '~') + break; + if (sscanf(line, "%ld %d %d", &lnum, &num, &num2) < 3) { + num2 = 0; + }; + if (lnum < 0 || !correct_unique(lnum)) + continue; + if (num2 >= MAX_REVENGE) { + if (--num <= 0) { + continue; + } + num2 = 0; + } + struct PK_Memory_type * pk_one = NULL; + for (pk_one = this->pk_list; pk_one; pk_one = pk_one->next) + if (pk_one->unique == lnum) + break; + if (pk_one) { + log("SYSERROR: duplicate entry pkillers data for %d %s", id, filename); + continue; + } + + CREATE(pk_one, 1); + pk_one->unique = lnum; + pk_one->kill_num = num; + pk_one->revenge_num = num2; + pk_one->next = this->pk_list; + this->pk_list = pk_one; + } + while (true); + } + else if (!strcmp(tag, "Prtl")) + add_portal_to_char(this, num); + // Loads Here new punishment strings + else if (!strcmp(tag, "PMut")) + { + sscanf(line, "%ld %d %ld %[^~]", &lnum, &num2, &lnum3, &buf[0]); + MUTE_DURATION(this) = lnum; + GET_MUTE_LEV(this) = num2; + MUTE_GODID(this) = lnum3; + MUTE_REASON(this) = str_dup(buf); + } + else if (!strcmp(tag, "PHel")) + { + sscanf(line, "%ld %d %ld %[^~]", &lnum, &num2, &lnum3, &buf[0]); + HELL_DURATION(this) = lnum; + GET_HELL_LEV(this) = num2; + HELL_GODID(this) = lnum3; + HELL_REASON(this) = str_dup(buf); + } + else if (!strcmp(tag, "PDum")) + { + sscanf(line, "%ld %d %ld %[^~]", &lnum, &num2, &lnum3, &buf[0]); + DUMB_DURATION(this) = lnum; + GET_DUMB_LEV(this) = num2; + DUMB_GODID(this) = lnum3; + DUMB_REASON(this) = str_dup(buf); + } + else if (!strcmp(tag, "PNam")) + { + sscanf(line, "%ld %d %ld %[^~]", &lnum, &num2, &lnum3, &buf[0]); + NAME_DURATION(this) = lnum; + GET_NAME_LEV(this) = num2; + NAME_GODID(this) = lnum3; + NAME_REASON(this) = str_dup(buf); + } + else if (!strcmp(tag, "PFrz")) + { + sscanf(line, "%ld %d %ld %[^~]", &lnum, &num2, &lnum3, &buf[0]); + FREEZE_DURATION(this) = lnum; + GET_FREEZE_LEV(this) = num2; + FREEZE_GODID(this) = lnum3; + FREEZE_REASON(this) = str_dup(buf); + } + else if (!strcmp(tag, "PGcs")) + { + sscanf(line, "%ld %d %ld %[^~]", &lnum, &num2, &lnum3, &buf[0]); + GCURSE_DURATION(this) = lnum; + GET_GCURSE_LEV(this) = num2; + GCURSE_GODID(this) = lnum3; + GCURSE_REASON(this) = str_dup(buf); + } + else if (!strcmp(tag, "PUnr")) + { + sscanf(line, "%ld %d %ld %[^~]", &lnum, &num2, &lnum3, &buf[0]); + UNREG_DURATION(this) = lnum; + GET_UNREG_LEV(this) = num2; + UNREG_GODID(this) = lnum3; + UNREG_REASON(this) = str_dup(buf); + } + + break; + + case 'Q': + if (!strcmp(tag, "Qst ")) + { + buf[0] = '\0'; + sscanf(line, "%d %[^~]", &num, &buf[0]); + this->quested_add(this, num, buf); + } + break; + + case 'R': + if (!strcmp(tag, "Room")) + GET_LOADROOM(this) = num; +//29.11.09. (c) Василиса + else if (!strcmp(tag, "Ripa")) + GET_RIP_ARENA(this) = num; + else if (!strcmp(tag, "Ripm")) + GET_RIP_MOB(this) = num; + else if (!strcmp(tag, "Rimt")) + GET_RIP_MOBTHIS(this) = num; + else if (!strcmp(tag, "Ruble")) + this->set_ruble(num); + else if (!strcmp(tag, "Ripp")) + GET_RIP_PK(this) = num; + else if (!strcmp(tag, "Ript")) + GET_RIP_PKTHIS(this) = num; + else if (!strcmp(tag, "Ripo")) + GET_RIP_OTHER(this) = num; + else if (!strcmp(tag, "Riot")) + GET_RIP_OTHERTHIS(this) = num; + else if (!strcmp(tag, "Ripd")) + GET_RIP_DT(this) = num; + else if (!strcmp(tag, "Ridt")) + GET_RIP_DTTHIS(this) = num; +//(с) Василиса + else if (!strcmp(tag, "Rmbr")) + this->remember_set_num(num); + else if (!strcmp(tag, "Reli")) + GET_RELIGION(this) = num; + else if (!strcmp(tag, "Race")) + GET_RACE(this) = num; + else if (!strcmp(tag, "Rcps")) + { + im_rskill *last = NULL; + for (;;) + { + im_rskill *rs; + fbgetline(fl, line); + sscanf(line, "%d %d", &num, &num2); + if (num < 0) + break; + num = im_get_recipe(num); +// +newbook.patch (Alisher) + if (num < 0 || imrecipes[num].classknow[(int) GET_CLASS(this)] != KNOW_RECIPE) +// -newbook.patch (Alisher) + continue; + CREATE(rs, 1); + rs->rid = num; + rs->perc = num2; + rs->link = NULL; + if (last) + last->link = rs; + else + GET_RSKILL(this) = rs; + last = rs; + } + } + break; + + case 'S': + if (!strcmp(tag, "Size")) + GET_SIZE(this) = num; + else if (!strcmp(tag, "Sex ")) + { + this->set_sex(static_cast(num)); + } + else if (!strcmp(tag, "Skil")) + { + do + { + fbgetline(fl, line); + sscanf(line, "%d %d", &num, &num2); + if (num != 0) + { + if (skill_info[num].classknow[(int)GET_CLASS(this)][(int)GET_KIN(this)] == KNOW_SKILL) + { + this->set_skill(static_cast(num), num2); + } + } + } + while (num != 0); + } + else if (!strcmp(tag, "SkTm")) + { + do + { + fbgetline(fl, line); + sscanf(line, "%d %d", &num, &num2); + if (num != 0) + { + timed.skill = num; + timed.time = num2; + timed_to_char(this, &timed); + } + } + while (num != 0); + } + else if (!strcmp(tag, "Spel")) + { + do + { + fbgetline(fl, line); + sscanf(line, "%d %d", &num, &num2); + if (num != 0 && spell_info[num].name) + GET_SPELL_TYPE(this, num) = num2; + } + while (num != 0); + } + else if (!strcmp(tag, "SpMe")) + { + do + { + fbgetline(fl, line); + sscanf(line, "%d %d", &num, &num2); + if (num != 0) + GET_SPELL_MEM(this, num) = num2; + } + while (num != 0); + } + else if (!strcmp(tag, "SpTM")) + { + struct spell_mem_queue_item *qi_cur, ** qi = &MemQueue.queue; + while (*qi) + qi = &((*qi)->link); + do + { + fbgetline(fl, line); + sscanf(line, "%d", &num); + if (num != 0) + { + CREATE(qi_cur, 1); + *qi = qi_cur; + qi_cur->spellnum = num; + qi_cur->link = NULL; + qi = &qi_cur->link; + } + } + while (num != 0); + } + else if (!strcmp(tag, "Str ")) + this->set_str(num); + else if (!strcmp(tag, "StrL")) + STRING_LENGTH(this) = num; + else if (!strcmp(tag, "StrW")) + STRING_WIDTH(this) = num; + else if (!strcmp(tag, "St00")) + this->set_start_stat(G_STR, lnum); + else if (!strcmp(tag, "St01")) + this->set_start_stat(G_DEX, lnum); + else if (!strcmp(tag, "St02")) + this->set_start_stat(G_INT, lnum); + else if (!strcmp(tag, "St03")) + this->set_start_stat(G_WIS, lnum); + else if (!strcmp(tag, "St04")) + this->set_start_stat(G_CON, lnum); + else if (!strcmp(tag, "St05")) + this->set_start_stat(G_CHA, lnum); + break; + + case 'T': + if (!strcmp(tag, "Thir")) + GET_COND(this, THIRST) = num; + else if (!strcmp(tag, "Titl")) + GET_TITLE(this) = std::string(str_dup(line)); + else if (!strcmp(tag, "TrcG")) + set_ext_money(ExtMoney::TORC_GOLD, num, false); + else if (!strcmp(tag, "TrcS")) + set_ext_money(ExtMoney::TORC_SILVER, num, false); + else if (!strcmp(tag, "TrcB")) + set_ext_money(ExtMoney::TORC_BRONZE, num, false); + else if (!strcmp(tag, "TrcL")) { + sscanf(line, "%d %d", &num, &num2); + today_torc_.first = num; + today_torc_.second = num2; + } + else if (!strcmp(tag, "Tlgr")) { + if (lnum <= 10000000000000) { + this->player_specials->saved.telegram_id = lnum; + } else { // зачищаем остатки старой баги + this->player_specials->saved.telegram_id = 0; + } + } + else if (!strcmp(tag, "TSpl")) + { + do + { + fbgetline(fl, line); + sscanf(line, "%d %ld %ld", &num, &lnum, &lnum3); + if (num != 0 && spell_info[num].name) + { + Temporary_Spells::add_spell(this, num, lnum, lnum3); + } + } while (num != 0); + } + break; + + case 'W': + if (!strcmp(tag, "Wate")) + GET_WEIGHT(this) = num; + else if (!strcmp(tag, "Wimp")) + GET_WIMP_LEV(this) = num; + else if (!strcmp(tag, "Wis ")) + this->set_wis(num); +//29.11.09 (c) Василиса + else if (!strcmp(tag, "Wina")) + GET_WIN_ARENA(this) = num; +//конец правки (с) Василиса + else if (!strcmp(tag, "Wman")) + this->set_who_mana(num); + break; + + default: + sprintf(buf, "SYSERR: Unknown tag %s in pfile %s", tag, name); + } + } + PRF_FLAGS(this).set(PRF_COLOR_2); //всегда цвет полный + // initialization for imms + if (GET_LEVEL(this) >= LVL_IMMORT) + { + set_god_skills(this); + set_god_morphs(this); + GET_COND(this, FULL) = -1; + GET_COND(this, THIRST) = -1; + GET_COND(this, DRUNK) = -1; + GET_LOADROOM(this) = NOWHERE; + } + + setAllInbornFeatures(this); + + if (IS_GRGOD(this)) + { + for (i = 0; i <= MAX_SPELLS; i++) + GET_SPELL_TYPE(this, i) = GET_SPELL_TYPE(this, i) | + SPELL_ITEMS | SPELL_KNOW | SPELL_RUNES | SPELL_SCROLL | SPELL_POTION | SPELL_WAND; + } + else if (!IS_IMMORTAL(this)) + { + for (i = 0; i <= MAX_SPELLS; i++) + { + if (spell_info[i].slot_forc[(int) GET_CLASS(this)][(int) GET_KIN(this)] == MAX_SLOT) + REMOVE_BIT(GET_SPELL_TYPE(this, i), SPELL_KNOW | SPELL_TEMP); +// shapirus: изученное не убираем на всякий случай, но из мема выкидываем, +// если мортов мало + if (GET_REMORT(this) < MIN_CAST_REM(spell_info[i], this)) + GET_SPELL_MEM(this, i) = 0; + } + } + + /* + * If you're not poisioned and you've been away for more than an hour of + * real time, we'll set your HMV back to full + */ + if (!AFF_FLAGGED(this, EAffectFlag::AFF_POISON) && (((long)(time(0) - LAST_LOGON(this))) >= SECS_PER_REAL_HOUR)) + { + GET_HIT(this) = GET_REAL_MAX_HIT(this); + GET_MOVE(this) = GET_REAL_MAX_MOVE(this); + } + else + GET_HIT(this) = MIN(GET_HIT(this), GET_REAL_MAX_HIT(this)); + + fbclose(fl); + // здесь мы закладываемся на то, что при ребуте это все сейчас пропускается и это нормально, + // иначе в таблице crc будут пустые имена, т.к. сама плеер-таблица еще не сформирована + // и в любом случае при ребуте это все пересчитывать не нужно + FileCRC::check_crc(filename, FileCRC::PLAYER, GET_UNIQUE(this)); + + this->account = Account::get_account(GET_EMAIL(this)); + if (this->account == nullptr) + { + const auto temp_account = std::make_shared(GET_EMAIL(this)); + accounts.emplace(GET_EMAIL(this), temp_account); + this->account = temp_account; + } + this->account->add_player(GET_UNIQUE(this)); +} + // * Перерасчет максимальных родных хп персонажа. // * При входе в игру, левеле/делевеле, добавлении/удалении славы. void check_max_hp(CHAR_DATA *ch) diff --git a/src/chars/char_player.hpp b/src/chars/char_player.hpp index 7fb091f2c..eee4e137d 100644 --- a/src/chars/char_player.hpp +++ b/src/chars/char_player.hpp @@ -25,6 +25,7 @@ #include #include #include +#include // * Перерасчет максимальных родных хп персонажа. // * При входе в игру, левеле/делевеле, добавлении/удалении славы. @@ -107,6 +108,8 @@ class Player : public CHAR_DATA void save_char(); int load_char_ascii(const char *name, bool reboot = 0, const bool find_id = true); + // метод загрузки файла игрока напрямую + bool _pfileLoad(FBFILE *fl, bool reboot, const char* name); bool get_disposable_flag(int num); void set_disposable_flag(int num); @@ -174,7 +177,6 @@ class Player : public CHAR_DATA // метод выставления chat_id void setTelegramId(unsigned long chat_id) override; unsigned long int getTelegramId() override; - private: // показывает, является ли чар турнирным или нет bool arena_player = false; diff --git a/src/cmd/quit.cpp b/src/cmd/quit.cpp index bd9175481..7fa0ca172 100644 --- a/src/cmd/quit.cpp +++ b/src/cmd/quit.cpp @@ -57,7 +57,7 @@ void do_quit(CHAR_DATA *ch, char *argument, int/* cmd*/, int subcmd) Depot::exit_char(ch); Clan::clan_invoice(ch, false); if (ch->personGroup) - ch->personGroup->_removeMember(ch->get_uid()); + ch->personGroup->_removeMember(ch); /* * kill off all sockets connected to the same player as the one who is diff --git a/src/grp/grp.cmdprocessor.cpp b/src/grp/grp.cmdprocessor.cpp index fde070c04..90fa75354 100644 --- a/src/grp/grp.cmdprocessor.cpp +++ b/src/grp/grp.cmdprocessor.cpp @@ -536,11 +536,11 @@ void do_report(CHAR_DATA *ch, char* /*argument*/, int/* cmd*/, int/* subcmd*/) CHAR_DATA *k; struct follow_type *f; - if (!AFF_FLAGGED(ch, EAffectFlag::AFF_GROUP) && !AFF_FLAGGED(ch, EAffectFlag::AFF_CHARM)) - { + if (!IN_GROUP(ch) && !AFF_FLAGGED(ch, EAffectFlag::AFF_CHARM)) { send_to_char("И перед кем вы отчитываетесь?\r\n", ch); return; } + // готовим строку if (ch->is_druid()) { sprintf(buf, "%s доложил%s : %d(%d)H, %d(%d)V, %d(%d)M\r\n", @@ -552,10 +552,8 @@ void do_report(CHAR_DATA *ch, char* /*argument*/, int/* cmd*/, int/* subcmd*/) else if (AFF_FLAGGED(ch, EAffectFlag::AFF_CHARM)) { int loyalty = 0; - for (const auto& aff : ch->affected) - { - if (aff->type == SPELL_CHARM) - { + for (const auto& aff : ch->affected) { + if (aff->type == SPELL_CHARM) { loyalty = aff->duration / 2; break; } @@ -573,22 +571,22 @@ void do_report(CHAR_DATA *ch, char* /*argument*/, int/* cmd*/, int/* subcmd*/) GET_HIT(ch), GET_REAL_MAX_HIT(ch), GET_MOVE(ch), GET_REAL_MAX_MOVE(ch)); } + CAP(buf); - k = ch->has_master() ? ch->get_master() : ch; - for (f = k->followers; f; f = f->next) - { - if (AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP) - && f->follower != ch - && !AFF_FLAGGED(f->follower, EAffectFlag::AFF_DEAFNESS)) - { - send_to_char(buf, f->follower); + if (IN_GROUP(ch)) + ch->personGroup->sendToGroup(GRP_COMM_ALL, buf); + else {//чармис докладывает мастеру + k = ch->has_master() ? ch->get_master() : ch; + for (f = k->followers; f; f = f->next) { + if (!AFF_FLAGGED(f->follower, EAffectFlag::AFF_DEAFNESS) && f->follower != ch) { + send_to_char(buf, f->follower); + } + } + if (k != ch && !AFF_FLAGGED(k, EAffectFlag::AFF_DEAFNESS)) { + send_to_char(buf, k); } } - if (k != ch && !AFF_FLAGGED(k, EAffectFlag::AFF_DEAFNESS)) - { - send_to_char(buf, k); - } send_to_char("Вы доложили о состоянии всем членам вашей группы.\r\n", ch); } diff --git a/src/grp/grp.group.cpp b/src/grp/grp.group.cpp index ae1a983f6..2f282294a 100644 --- a/src/grp/grp.group.cpp +++ b/src/grp/grp.group.cpp @@ -155,7 +155,7 @@ void Group::addMember(CHAR_DATA *member, bool silent) { return; // в другой группе, вышвыриваем if (member->personGroup != nullptr) - _removeMember(member); + member->personGroup->_removeMember(member); auto it = this->find(_calcUID(member)); if (it == this->end()) { @@ -199,10 +199,14 @@ bool Group::_removeMember(int memberUID) { return false; } auto member = it->second->member; - if (member != nullptr) + if (member != nullptr) { + send_to_char(member, "Вы покинули группу.\r\n"); member->personGroup = nullptr; + } + sendToGroup(GRP_COMM_ALL, "Персонаж %s покинул группу.", it->second->memberName.c_str()); this->erase(memberUID); // finally remove it + // а также удаляем чармисов персонажа из групповых if (member->followers != nullptr) { for (auto f = member->followers; f; f = f->next) { @@ -239,19 +243,6 @@ bool Group::_removeMember(int memberUID) { return true; } -bool Group::_removeMember(char *name) { - int uid = 0; - for (auto &it: *this){ - if (str_cmp(name, it.second->memberName) == 0) { - uid = it.first; - break; - } - } - if (uid != 0) - return _removeMember(uid); - return false; -} - bool Group::_removeMember(CHAR_DATA *member) { int memberUID = _calcUID(member); return _removeMember(memberUID); @@ -336,7 +327,6 @@ void Group::expellMember(char *memberName) { sendToGroup(GRP_COMM_LEADER, "Нет такого персонажа."); return; } - _removeMember(vict->memberUID); if (vict != nullptr && vict->member != nullptr) { act("$N исключен$A из состава вашей группы.", FALSE, _leader, nullptr, vict, TO_CHAR); act("Вы исключены из группы $n1!", FALSE, _leader, nullptr, vict, TO_VICT); @@ -344,11 +334,11 @@ void Group::expellMember(char *memberName) { } else { sendToGroup(GRP_COMM_LEADER, "%s был исключен из группы.'\r\n", vict->memberName.c_str()); } + _removeMember(vict->memberUID); } void Group::listMembers(CHAR_DATA *ch) { CHAR_DATA *leader; - struct follow_type *f; int count = 1; if (ch->personGroup) @@ -510,12 +500,6 @@ void Group::_printPCLine(CHAR_DATA* ch, CHAR_DATA* pc, int header) { void Group::printGroup(CHAR_DATA *ch) { int gfound = 0, cfound = 0; - CHAR_DATA *leader; - - if (ch->personGroup) - leader = ch->personGroup->getLeader(); - else - leader = ch; if (!IS_NPC(ch)) ch->desc->msdp_report(msdp::constants::GROUP); @@ -617,14 +601,14 @@ void Group::addFollowers(CHAR_DATA *leader) { for (auto f = leader->followers; f; f = f->next) { if (IS_NPC(f->follower)) continue; - if ((u_short)this->size() < (u_short)_memberCap) + if ((u_short)this->get_size() < (u_short)_memberCap) this->addMember(f->follower); } } // метод может вернуть мусор :( CHAR_DATA* Group::get_random_pc_group() { - u_short rnd = number(0, (u_short)this->size() - 1); + u_short rnd = number(0, (u_short)this->get_size() - 1); int i = 0; for (const auto& it : *this){ if (i == rnd) { @@ -635,7 +619,7 @@ CHAR_DATA* Group::get_random_pc_group() { return nullptr; } -u_short Group::size(rnum_t room_rnum) { +u_short Group::get_size(rnum_t room_rnum) { u_short retval = 0; for (const auto& it : *this){ auto c = it.second->member; @@ -665,16 +649,19 @@ int Group::_calcUID(CHAR_DATA *ch) { } void Group::_promote(CHAR_DATA *member) { + if (_leader == member) + return; _setLeader(member); - sendToGroup(GRP_COMM_ALL, "Изменился лидер группы на %.", _leaderName.c_str() ); + sendToGroup(GRP_COMM_ALL, "Изменился лидер группы на %s.", _leaderName.c_str() ); auto diff = (u_short)_memberCap - (u_short)this->size(); - if (diff < 0) diff = 0; + if (diff < 0) diff = abs(diff); else diff = 0; if (diff > 0){ u_short i = 0; int expellList[diff]; - for (auto it = this->begin(); it!= this->end() || i > diff; it++){ + for (auto it = this->begin(); it!= this->end() || i == diff; it++){ if (it->second->member != _leader ) { expellList[i] = _calcUID(it->second->member); + sendToGroup(GRP_COMM_ALL, "В группе не хватает места, нас покинул %s!", it->second->memberName.c_str() ); ++i; } } diff --git a/src/grp/grp.main.h b/src/grp/grp.main.h index da86b560e..4630bd12d 100644 --- a/src/grp/grp.main.h +++ b/src/grp/grp.main.h @@ -88,12 +88,11 @@ class Group : public grp_mt { const char* _getMemberName(int uid); char_info* _findMember(char* memberName); bool _removeMember(CHAR_DATA* ch); - bool _removeMember(int uid); - bool _removeMember(char *name); void _promote(CHAR_DATA* ch); void charDataPurged(CHAR_DATA* ch); - u_short size(rnum_t room_rnum = 0); + u_short get_size(rnum_t room_rnum = 0); private: + bool _removeMember(int uid); GM_TYPE getType(CHAR_DATA* ch) { if (IS_NPC(ch)) return GM_CHARMEE; diff --git a/src/grp/grp.roster.cpp b/src/grp/grp.roster.cpp index be83589d6..3f3413acb 100644 --- a/src/grp/grp.roster.cpp +++ b/src/grp/grp.roster.cpp @@ -65,7 +65,7 @@ void GroupRoster::processGroupCommands(CHAR_DATA *ch, char *argument) { return; } if (grp != nullptr) // в группе - покидаем - grp->_removeMember(ch->get_uid()); + grp->_removeMember(ch); groupRoster.addGroup(ch); return; } else if (isname(subcmd, strLIST.c_str())) { diff --git a/src/spells.cpp b/src/spells.cpp index c5940c63b..b03dc327a 100644 --- a/src/spells.cpp +++ b/src/spells.cpp @@ -1066,16 +1066,13 @@ void spell_charm(int/* level*/, CHAR_DATA *ch, CHAR_DATA *victim, OBJ_DATA* /* o send_to_char("Ваша магия потерпела неудачу.\r\n", ch); else { - if (!check_charmee(ch, victim, SPELL_CHARM)) - { + if (!check_charmee(ch, victim, SPELL_CHARM)){ return; } // Левая проверка - if (victim->has_master()) - { - if (stop_follower(victim, SF_MASTERDIE)) - { + if (victim->has_master()){ + if (stop_follower(victim, SF_MASTERDIE)){ return; } } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 03f9281ce..72e2dc300 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -81,7 +81,8 @@ set(UTILITIES char.utilities.cpp) set(ADDITIONAL_FILES data/boards/changelog.sample - data/boards/news.sample) + data/boards/news.sample + data/plrs/bikbaj.player) source_group("Tests data" FILES ${ADDITIONAL_FILES}) source_group("Tests" FILES ${TESTS}) source_group("Tests helpers" FILES ${UTILITIES}) @@ -95,6 +96,8 @@ endif () add_test(all tests) +file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/data/plrs/) + add_custom_target(checks COMMAND ${CMAKE_CTEST_COMMAND} -V DEPENDS tests) add_custom_target(tests.misc.grouping @@ -112,8 +115,14 @@ add_custom_target(tests.data.boards.news_sample COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/tests/data/boards/news.sample ${CMAKE_CURRENT_BINARY_DIR}/data/boards/news.sample COMMENT "Copy tests/data/boards/news.sample for tests.") set_target_properties(tests.data.boards.news_sample PROPERTIES FOLDER "Utility targets/Tests data") +add_custom_target(tests.data.plrs + DEPENDS ${CMAKE_SOURCE_DIR}/tests/data/plrs + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/tests/data/plrs/* ${CMAKE_CURRENT_BINARY_DIR}/data/plrs/ + COMMENT "Copy tests.data.plrs for tests.") +set_target_properties(tests.data.boards.changelog_sample PROPERTIES FOLDER "Utility targets/Tests data") + -add_dependencies(tests tests.misc.grouping tests.data.boards.changelog_sample tests.data.boards.news_sample) +add_dependencies(tests tests.misc.grouping tests.data.boards.changelog_sample tests.data.boards.news_sample tests.data.plrs) add_dependencies(checks tests) # vim: set ts=4 sw=4 ai tw=0 noet syntax=cmake : diff --git a/tests/char.leaders.cpp b/tests/char.leaders.cpp index 90a8b3deb..74fbe690a 100644 --- a/tests/char.leaders.cpp +++ b/tests/char.leaders.cpp @@ -69,24 +69,28 @@ TEST(CHAR_Leaders, SimpleLoop) } TEST(CHAR_Leaders, Group) { + std::string pName; test_utils::CharacterBuilder builder; builder.create_new(); builder.add_skill(ESkill::SKILL_LEADERSHIP, 200); auto leader = builder.get(); + test_utils::GroupBuilder g; + auto grp = g._roster->addGroup(leader.get()); + builder.create_new("F7"); builder.add_skill(ESkill::SKILL_LEADERSHIP, 10); auto f7 = builder.get(); leader->add_follower(f7.get()); + grp->addFollowers(leader.get()); + for (int i = 0; i <12; i++) { - builder.create_new(); + pName = "Player" + std::to_string(i); + builder.create_new(pName); auto follower = builder.get(); leader->add_follower(follower.get()); } - - test_utils::GroupBuilder g; - auto grp = g._roster->addGroup(leader.get()); grp->addFollowers(leader.get()); EXPECT_EQ(12, leader->personGroup->size()); grp->promote("F7"); diff --git a/tests/char.utilities.cpp b/tests/char.utilities.cpp index c85e0e56d..b049d9a9e 100644 --- a/tests/char.utilities.cpp +++ b/tests/char.utilities.cpp @@ -6,152 +6,150 @@ #include "chars/player_races.hpp" #include #include +#include "diskio.h" namespace test_utils { -void CharacterBuilder::create_new() -{ - const auto result = std::make_shared(); - result->player_specials = std::make_shared(); - result->char_specials.saved.act = clear_flags; - result->char_specials.saved.affected_by = clear_flags; - result->set_class(CLASS_DRUID); - result->set_level(1); - m_result = result; -} + void CharacterBuilder::create_new() + { + const auto result = std::make_shared(); + char filename[40] = "data/plrs/sample.player"; + FBFILE *fl = NULL; + fl = fbopen(filename, FB_READ); + result->_pfileLoad(fl, false, "test"); + m_result = result; + sprintf(smallBuf, "Player-%d",m_uid); + m_result->set_pc_name(smallBuf); + m_result->set_uid(m_uid); + ++m_uid; + _store.push_back(m_result); + } -void CharacterBuilder::create_new_with_class(const short player_class) -{ - create_new(); - set_class(player_class); -} + void CharacterBuilder::create_new_with_class(const short player_class) + { + create_new(); + set_class(player_class); + } -void CharacterBuilder::create_character_with_one_removable_affect() -{ - create_new(); - add_poison(); -} + void CharacterBuilder::create_character_with_one_removable_affect() + { + create_new(); + add_poison(); + } -void CharacterBuilder::create_character_with_two_removable_affects() -{ - create_character_with_one_removable_affect(); - add_sleep(); -} + void CharacterBuilder::create_character_with_two_removable_affects() + { + create_character_with_one_removable_affect(); + add_sleep(); + } -void CharacterBuilder::create_character_with_two_removable_and_two_not_removable_affects() -{ - create_character_with_two_removable_affects(); - add_detect_invis(); - add_detect_align(); -} + void CharacterBuilder::create_character_with_two_removable_and_two_not_removable_affects() + { + create_character_with_two_removable_affects(); + add_detect_invis(); + add_detect_align(); + } -void CharacterBuilder::add_poison() -{ - check_character_existance(); - - AFFECT_DATA poison; - poison.type = SPELL_POISON; - poison.modifier = 0; - poison.location = APPLY_STR; - poison.duration = pc_duration(m_result.get(), 10 * 2, 0, 0, 0, 0); - poison.bitvector = to_underlying(EAffectFlag::AFF_POISON); - poison.battleflag = AF_SAME_TIME; - affect_join(m_result.get(), poison, false, false, false, false); -} + void CharacterBuilder::add_poison() + { + check_character_existance(); + + AFFECT_DATA poison; + poison.type = SPELL_POISON; + poison.modifier = 0; + poison.location = APPLY_STR; + poison.duration = pc_duration(m_result.get(), 10 * 2, 0, 0, 0, 0); + poison.bitvector = to_underlying(EAffectFlag::AFF_POISON); + poison.battleflag = AF_SAME_TIME; + affect_join(m_result.get(), poison, false, false, false, false); + } -void CharacterBuilder::add_sleep() -{ - check_character_existance(); - - AFFECT_DATA sleep; - sleep.type = SPELL_SLEEP; - sleep.modifier = 0; - sleep.location = APPLY_AC; - sleep.duration = pc_duration(m_result.get(), 10 * 2, 0, 0, 0, 0); - sleep.bitvector = to_underlying(EAffectFlag::AFF_SLEEP); - sleep.battleflag = AF_SAME_TIME; - affect_join(m_result.get(), sleep, false, false, false, false); -} + void CharacterBuilder::add_sleep() + { + check_character_existance(); + + AFFECT_DATA sleep; + sleep.type = SPELL_SLEEP; + sleep.modifier = 0; + sleep.location = APPLY_AC; + sleep.duration = pc_duration(m_result.get(), 10 * 2, 0, 0, 0, 0); + sleep.bitvector = to_underlying(EAffectFlag::AFF_SLEEP); + sleep.battleflag = AF_SAME_TIME; + affect_join(m_result.get(), sleep, false, false, false, false); + } -void CharacterBuilder::add_detect_invis() -{ - check_character_existance(); - - AFFECT_DATA detect_invis; - detect_invis.type = SPELL_DETECT_INVIS; - detect_invis.modifier = 0; - detect_invis.location = APPLY_AC; - detect_invis.duration = pc_duration(m_result.get(), 10 * 2, 0, 0, 0, 0); - detect_invis.bitvector = to_underlying(EAffectFlag::AFF_DETECT_INVIS); - detect_invis.battleflag = AF_SAME_TIME; - affect_join(m_result.get(), detect_invis, false, false, false, false); -} + void CharacterBuilder::add_detect_invis() + { + check_character_existance(); + + AFFECT_DATA detect_invis; + detect_invis.type = SPELL_DETECT_INVIS; + detect_invis.modifier = 0; + detect_invis.location = APPLY_AC; + detect_invis.duration = pc_duration(m_result.get(), 10 * 2, 0, 0, 0, 0); + detect_invis.bitvector = to_underlying(EAffectFlag::AFF_DETECT_INVIS); + detect_invis.battleflag = AF_SAME_TIME; + affect_join(m_result.get(), detect_invis, false, false, false, false); + } -void CharacterBuilder::add_detect_align() -{ - check_character_existance(); - - AFFECT_DATA detect_align; - detect_align.type = SPELL_DETECT_ALIGN; - detect_align.modifier = 0; - detect_align.location = APPLY_AC; - detect_align.duration = pc_duration(m_result.get(), 10 * 2, 0, 0, 0, 0); - detect_align.bitvector = to_underlying(EAffectFlag::AFF_DETECT_ALIGN); - detect_align.battleflag = AF_SAME_TIME; - affect_join(m_result.get(), detect_align, false, false, false, false); -} + void CharacterBuilder::add_detect_align() + { + check_character_existance(); + + AFFECT_DATA detect_align; + detect_align.type = SPELL_DETECT_ALIGN; + detect_align.modifier = 0; + detect_align.location = APPLY_AC; + detect_align.duration = pc_duration(m_result.get(), 10 * 2, 0, 0, 0, 0); + detect_align.bitvector = to_underlying(EAffectFlag::AFF_DETECT_ALIGN); + detect_align.battleflag = AF_SAME_TIME; + affect_join(m_result.get(), detect_align, false, false, false, false); + } -void CharacterBuilder::set_level(const int level) -{ - m_result->set_level(level); -} + void CharacterBuilder::set_level(const int level) + { + m_result->set_level(level); + } -void CharacterBuilder::set_class(const short player_class) -{ - m_result->set_class(player_class); -} + void CharacterBuilder::set_class(const short player_class) + { + m_result->set_class(player_class); + } -void CharacterBuilder::make_group(CharacterBuilder& character_builder) -{ - check_character_existance(); - check_character_existance(character_builder.get()); + void CharacterBuilder::make_group(CharacterBuilder& character_builder) + { + check_character_existance(); + check_character_existance(character_builder.get()); - auto character = character_builder.get(); + auto character = character_builder.get(); - m_result->add_follower_silently(character.get()); + m_result->add_follower_silently(character.get()); - perform_group(m_result.get(), m_result.get()); - perform_group(m_result.get(), character.get()); -} + perform_group(m_result.get(), m_result.get()); + perform_group(m_result.get(), character.get()); + } -void CharacterBuilder::check_character_existance() const -{ - check_character_existance(m_result); -} + void CharacterBuilder::check_character_existance() const + { + check_character_existance(m_result); + } -void CharacterBuilder::check_character_existance(result_t character) -{ - if (!character) - { - throw std::runtime_error("Character wasn't created."); - } -} + void CharacterBuilder::check_character_existance(result_t character) + { + if (!character) + { + throw std::runtime_error("Character wasn't created."); + } + } void CharacterBuilder::add_skill(ESkill skill, short value) { m_result->set_skill(skill, value); } - void CharacterBuilder::load_player(u_short idx) { - getcwd(buf, 3000); - Player* t_vict = new Player(); - if (load_char(_names[idx].c_str(), t_vict) == -1) - throw std::runtime_error("Character wasn't created."); - m_result = std::shared_ptr(t_vict); - } - - void CharacterBuilder::create_new(char *name) { + void CharacterBuilder::create_new(std::string name) { create_new(); m_result->set_pc_name(name); + m_result->set_passwd(name); } GroupBuilder::GroupBuilder() { diff --git a/tests/char.utilities.hpp b/tests/char.utilities.hpp index 49ac07b41..f5d1c5742 100644 --- a/tests/char.utilities.hpp +++ b/tests/char.utilities.hpp @@ -12,8 +12,10 @@ namespace test_utils using character_t = CHAR_DATA; using result_t = character_t::shared_ptr; - void create_new(); - void create_new(char* name); + CharacterBuilder() {m_uid = 1;} + + void create_new(); + void create_new(std::string name); void load_player(u_short idx); void create_new_with_class(const short player_class); void create_character_with_one_removable_affect(); @@ -36,10 +38,9 @@ namespace test_utils void check_character_existance() const; static void check_character_existance(result_t character); - + int m_uid = 1; result_t m_result; - const std::string _names[13] = {"Бикбай", "Первый", "Второй", "третий", "четвертый", "пятый", "шестой", - "седьмой", "восьмой", "девятый", "десятый", "одиннадцатый", "двеннадцатый"}; + std::vector _store; }; class GroupBuilder { diff --git a/tests/data/plrs/sample.player b/tests/data/plrs/sample.player new file mode 100644 index 000000000..a6787d68b --- /dev/null +++ b/tests/data/plrs/sample.player @@ -0,0 +1,136 @@ +Name: Бикбай +Levl: 34 +Clas: 1 +UIN : 1009969084 +LstL: 1610053558 +Host: 192.168.204.1 +Id : 1 +Exp : 2000000000 +Act : C0D0 +EMal: bikbai@mail +Rebt: следующие далее поля при перезагрузке не парсятся + +NmI : Бикбай +NmR : Бикбая +NmD : Бикбаю +NmV : Бикбая +NmT : Бикбаем +NmP : Бикбаи +Pass: 12345 +Sex : 1 мужчина +Kin : 0 Русич +Brth: 1601408138 Tue Sep 29 22:35:38 2020 + +Plyd: 366111 +Last: 1610053558 Fri Jan 8 00:05:58 2021 + +Hite: 178 +Wate: 130 +Size: 50 +Alin: 0 +Aff : k0 +Str : 25 +Int : 25 +Wis : 25 +Dex : 25 +Con : 25 +Cha : 25 +Cits: 1 +Rcps: +-1 -1 +Hrol: 0 +Drol: 0 +Ac : 100 +Hry : 0 +Hit : 630/630 +Mana: 0/0 +Move: 82/82 +Gold: 0 +Bank: 0 +ICur: 0 +Ruble: 0 +Wimp: 0 +Frez: 0 +Invs: 0 +Room: 0 +Lexc: -252316953 +Badp: 0 +Br01: 1601408097 +Br02: 1601408097 +Br03: 1601408097 +Br04: 1601408097 +Br05: 1601408097 +Br06: 1601408097 +Br07: 1601408097 +Br08: 1601408097 +Br09: 1601408097 +Br10: 1601408097 +Br11: 1601408097 +Br12: 1601408097 +Br13: 1601408097 +Br14: 1601408097 +Br15: 1601408097 +Br16: 1601408097 +St00: 11 +St01: 10 +St02: 24 +St03: 23 +St04: 12 +St05: 15 +Reli: 1 Христианин +Race: 2 Кривич +DrSt: 0 +Olc : 0 +Pref: e0f0g0o0u0w0x0c2 +LogL: +192.168.204.1 224 1610053558 +~ +GdFl: 0 +NamG: 1034 +NaID: -1 +StrL: 80 +StrW: 30 +NtfE: 0 +Logs: 0 992872272 +Logs: 1 22041 +Logs: 2 34478 +Logs: 3 0 +Logs: 4 49 +Disp: 0 +Ripa: 0 +Wina: 0 +Expa: 0 +Ripm: 0 +Expm: 0 +Ripd: 0 +Expd: 0 +Ripo: 0 +Expo: 0 +Ripp: 0 +Expp: 0 +Rimt: 0 +Exmt: 0 +Ridt: 0 +Exdt: 0 +Riot: 0 +Exot: 0 +Ript: 0 +Expt: 0 +Wman: 6000 +Mobs: +~ +Pkil: +~ +Mrph: #MORPH_FALCON#MORPH_LYNX#MORPH_PIKE#MORPH_WOLF +Map : 000000000000000000000 +TrcG: 0 +TrcS: 0 +TrcB: 0 +TrcL: 0 0 +Chrm: +4010 1 0 0 1 1 +4019 1 0 0 1 1 +4020 6 0 0 1 1 +4021 18 0 0 1 1 +0 0 0 0 0 0 +Tlgr: 0 From ab96610c9b4719613787ccf97f90b70a466a27e9 Mon Sep 17 00:00:00 2001 From: bikbai Date: Fri, 8 Jan 2021 17:46:27 +0300 Subject: [PATCH 21/40] =?UTF-8?q?=D0=90=D0=B2=D1=82=D0=BE=D1=82=D0=B5?= =?UTF-8?q?=D1=81=D1=82=D1=8B=20=D1=81=20=D0=BF=D0=B5=D1=80=D1=81=D0=BE?= =?UTF-8?q?=D0=BD=D0=B0=D0=B6=D0=B0=D0=BC=D0=B8=20=D0=B7=D0=B0=D1=80=D0=B0?= =?UTF-8?q?=D0=B1=D0=BE=D1=82=D0=B0=D0=BB=D0=B8.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chars/char.cpp | 2 +- src/chars/char_player.cpp | 2237 +++++++++++++++++---------------- src/chars/char_player.hpp | 4 +- src/cmd/quit.cpp | 2 +- src/ext_money.cpp | 2 +- src/grp/grp.cmdprocessor.cpp | 34 +- src/grp/grp.group.cpp | 49 +- src/grp/grp.main.h | 5 +- src/grp/grp.roster.cpp | 2 +- src/spells.cpp | 9 +- tests/CMakeLists.txt | 13 +- tests/char.leaders.cpp | 15 +- tests/char.utilities.cpp | 242 ++-- tests/char.utilities.hpp | 11 +- tests/data/plrs/sample.player | 136 ++ 15 files changed, 1456 insertions(+), 1307 deletions(-) create mode 100644 tests/data/plrs/sample.player diff --git a/src/chars/char.cpp b/src/chars/char.cpp index 7e7a30a91..36ec613d1 100644 --- a/src/chars/char.cpp +++ b/src/chars/char.cpp @@ -2037,7 +2037,7 @@ void CHAR_DATA::removeGroupFlags() { AFF_FLAGS(this).unset(EAffectFlag::AFF_GROUP); PRF_FLAGS(this).unset(PRF_SKIRMISHER); if (personGroup != nullptr && IS_CHARMICE(this)) - personGroup->_removeMember(this->get_uid()); + personGroup->_removeMember(this); } void CHAR_DATA::add_follower(CHAR_DATA* ch) { diff --git a/src/chars/char_player.cpp b/src/chars/char_player.cpp index bf4efb327..9d5155408 100644 --- a/src/chars/char_player.cpp +++ b/src/chars/char_player.cpp @@ -20,7 +20,6 @@ #include "comm.h" #include "core/leveling.h" #include "fightsystem/pk.h" -#include "diskio.h" #include "interpreter.h" #include "genchar.h" #include "AffectHandler.hpp" @@ -1066,14 +1065,15 @@ void Player::save_char() // * \param reboot - по дефолту = false int Player::load_char_ascii(const char *name, bool reboot, const bool find_id /*= true*/) { - int id, num = 0, num2 = 0, num3 = 0, num4 = 0, num5 = 0, num6 = 0, i; - long int lnum = 0, lnum3 = 0; - unsigned long long llnum = 0; - FBFILE *fl = NULL; - char filename[40]; - char buf[MAX_RAW_INPUT_LENGTH], line[MAX_RAW_INPUT_LENGTH], tag[6]; - char line1[MAX_RAW_INPUT_LENGTH]; - struct timed_type timed; + int id, num = 0, num2 = 0, num3 = 0, num4 = 0, num5 = 0, num6 = 0, i; + long int lnum = 0, lnum3 = 0; + unsigned long long llnum = 0; + FBFILE *fl = NULL; + char filename[40]; + char buf[MAX_RAW_INPUT_LENGTH], line[MAX_RAW_INPUT_LENGTH], tag[6]; + char line1[MAX_RAW_INPUT_LENGTH]; + struct timed_type timed; + *filename = '\0'; log("Load ascii char %s", name); if (!find_id) @@ -1095,1110 +1095,9 @@ int Player::load_char_ascii(const char *name, bool reboot, const bool find_id /* log("Can't load ascii. ID: %d; File name: \"%s\"; Current directory: \"%s\")", id, filename, getcwd(buffer, BUFFER_SIZE)); return -1; } - -/////////////////////////////////////////////////////////////////////////////// - - // первыми иним и парсим поля для ребута до поля "Rebt", если reboot на входе = 1, то на этом парс и кончается - if (!this->player_specials) - { - this->player_specials = std::make_shared(); - } - - set_level(1); - set_class(1); - set_uid(0); - set_last_logon(time(0)); - set_idnum(0); - set_exp(0); - set_remort(0); - GET_LASTIP(this)[0] = 0; - GET_EMAIL(this)[0] = 0; - PLR_FLAGS(this).from_string(""); // suspicious line: we should clear flags.. Loading from "" does not clear flags. - - bool skip_file = 0; - - do - { - if (!fbgetline(fl, line)) - { - log("SYSERROR: Wrong file ascii %d %s", id, filename); - return (-1); - } - - tag_argument(line, tag); - for (i = 0; !(line[i] == ' ' || line[i] == '\0'); i++) - { - line1[i] = line[i]; - } - line1[i] = '\0'; - num = atoi(line1); - lnum = atol(line1); - try - { - llnum = boost::lexical_cast(line1); - } - catch(boost::bad_lexical_cast &) - { - llnum = 0; - } - - - switch (*tag) - { - case 'A': - if (!strcmp(tag, "Act ")) - { - PLR_FLAGS(this).from_string(line); - } - break; - case 'C': - if (!strcmp(tag, "Clas")) - { - set_class(num); - } - break; - case 'E': - if (!strcmp(tag, "Exp ")) - { - set_exp(lnum); - } - //added by WorM 2010.08.27 лоадим мыло и айпи даже при ребуте - else if (!strcmp(tag, "EMal")) - strcpy(GET_EMAIL(this), line); - break; - case 'H': - if (!strcmp(tag, "Host")) - { - strcpy(GET_LASTIP(this), line); - } - //end by WorM - break; - case 'I': - if (!strcmp(tag, "Id ")) - { - set_idnum(lnum); - } - break; - case 'L': - if (!strcmp(tag, "LstL")) - { - set_last_logon(lnum); - } - else if (!strcmp(tag, "Levl")) - { - set_level(num); - } - break; - case 'N': - if (!strcmp(tag, "Name")) - { - set_name(line); - } - break; - case 'R': - if (!strcmp(tag, "Rebt")) - skip_file = 1; - else if (!strcmp(tag, "Rmrt")) - { - set_remort(num); - } - break; - case 'U': - if (!strcmp(tag, "UIN ")) - { - set_uid(num); - } - break; - default: - sprintf(buf, "SYSERR: Unknown tag %s in pfile %s", tag, name); - } - } - while (!skip_file); - - //added by WorM 2010.08.27 лоадим мыло и последний ip даже при считывании индексов - while((reboot) && (!*GET_EMAIL(this) || !*GET_LASTIP(this))) - { - if (!fbgetline(fl, line)) - { - log("SYSERROR: Wrong file ascii %d %s", id, filename); - return (-1); - } - - tag_argument(line, tag); - - if (!strcmp(tag, "EMal")) - strcpy(GET_EMAIL(this), line); - else if (!strcmp(tag, "Host")) - strcpy(GET_LASTIP(this), line); - } - //end by WorM - - // если с загруженными выше полями что-то хочется делать после лоада - делайте это здесь - - //Indexing experience - if his exp is lover than required for his level - set it to required - if (GET_EXP(this) < ExpCalc::level_exp(this, GET_LEVEL(this))) - { - set_exp(ExpCalc::level_exp(this, GET_LEVEL(this))); - } - - if (reboot) - { - fbclose(fl); - return id; - } - this->str_to_cities(default_str_cities); - // если происходит обычный лоад плеера, то читаем файл дальше и иним все остальные поля - -/////////////////////////////////////////////////////////////////////////////// - - - // character init - // initializations necessary to keep some things straight - - this->set_npc_name(0); - this->player_data.long_descr = ""; - - this->real_abils.Feats.reset(); - - // волхвам сетим все спеллы на рунах, остальные инит нулями - if (GET_CLASS(this) != CLASS_DRUID) - for (i = 1; i <= MAX_SPELLS; i++) - GET_SPELL_TYPE(this, i) = 0; - else - for (i = 1; i <= MAX_SPELLS; i++) - GET_SPELL_TYPE(this, i) = SPELL_RUNES; - - for (i = 1; i <= MAX_SPELLS; i++) - GET_SPELL_MEM(this, i) = 0; - this->char_specials.saved.affected_by = clear_flags; - POOFIN(this) = NULL; - POOFOUT(this) = NULL; - GET_RSKILL(this) = NULL; // рецептов не знает - this->char_specials.carry_weight = 0; - this->char_specials.carry_items = 0; - this->real_abils.armor = 100; - GET_MEM_TOTAL(this) = 0; - GET_MEM_COMPLETED(this) = 0; - MemQ_init(this); - - GET_AC(this) = 10; - GET_ALIGNMENT(this) = 0; - GET_BAD_PWS(this) = 0; - this->player_data.time.birth = time(0); - GET_KIN(this) = 0; - - this->set_str(10); - this->set_dex(10); - this->set_con(10); - this->set_int(10); - this->set_wis(10); - this ->set_cha(10); - - GET_COND(this, DRUNK) = 0; - GET_DRUNK_STATE(this) = 0; - -// Punish Init - DUMB_DURATION(this) = 0; - DUMB_REASON(this) = 0; - GET_DUMB_LEV(this) = 0; - DUMB_GODID(this) = 0; - - MUTE_DURATION(this) = 0; - MUTE_REASON(this) = 0; - GET_MUTE_LEV(this) = 0; - MUTE_GODID(this) = 0; - - HELL_DURATION(this) = 0; - HELL_REASON(this) = 0; - GET_HELL_LEV(this) = 0; - HELL_GODID(this) = 0; - - FREEZE_DURATION(this) = 0; - FREEZE_REASON(this) = 0; - GET_FREEZE_LEV(this) = 0; - FREEZE_GODID(this) = 0; - - GCURSE_DURATION(this) = 0; - GCURSE_REASON(this) = 0; - GET_GCURSE_LEV(this) = 0; - GCURSE_GODID(this) = 0; - - NAME_DURATION(this) = 0; - NAME_REASON(this) = 0; - GET_NAME_LEV(this) = 0; - NAME_GODID(this) = 0; - - UNREG_DURATION(this) = 0; - UNREG_REASON(this) = 0; - GET_UNREG_LEV(this) = 0; - UNREG_GODID(this) = 0; - -// End punish init - - GET_DR(this) = 0; - - set_gold(0, false); - set_bank(0, false); - set_ruble(0); - this->player_specials->saved.GodsLike = 0; - GET_HIT(this) = 21; - GET_MAX_HIT(this) = 21; - GET_HEIGHT(this) = 50; - GET_HR(this) = 0; - GET_COND(this, FULL) = 0; - SET_INVIS_LEV(this, 0); - this->player_data.time.logon = time(0); - GET_MOVE(this) = 44; - GET_MAX_MOVE(this) = 44; - KARMA(this) = 0; - LOGON_LIST(this).clear(); - NAME_GOD(this) = 0; - STRING_LENGTH(this) = 80; - STRING_WIDTH(this) = 30; - NAME_ID_GOD(this) = 0; - GET_OLC_ZONE(this) = 0; - this->player_data.time.played = 0; - GET_LOADROOM(this) = NOWHERE; - GET_RELIGION(this) = 1; - GET_RACE(this) = 1; - this->set_sex(ESex::SEX_NEUTRAL); - GET_COND(this, THIRST) = NORM_COND_VALUE; - GET_WEIGHT(this) = 50; - GET_WIMP_LEV(this) = 0; - PRF_FLAGS(this).from_string(""); // suspicious line: we should clear flags.. Loading from "" does not clear flags. - AFF_FLAGS(this).from_string(""); // suspicious line: we should clear flags.. Loading from "" does not clear flags. - GET_PORTALS(this) = NULL; - EXCHANGE_FILTER(this) = NULL; - clear_ignores(); - CREATE(GET_LOGS(this), 1 + LAST_LOG); - NOTIFY_EXCH_PRICE(this) = 0; - this->player_specials->saved.HiredCost = 0; - this->set_who_mana(WHO_MANA_MAX); - this->set_who_last(time(0)); - - while (fbgetline(fl, line)) - { - tag_argument(line, tag); - for (i = 0; !(line[i] == ' ' || line[i] == '\0'); i++) - { - line1[i] = line[i]; - } - line1[i] = '\0'; - num = atoi(line1); - lnum = atol(line1); - try - { - llnum = std::stoull(line1, nullptr, 10); - } - catch (const std::invalid_argument &) - { - llnum = 0; - } - catch (const std::out_of_range &) - { - llnum = 0; - } - switch (*tag) - { - case 'A': - if (!strcmp(tag, "Ac ")) - { - GET_AC(this) = num; - } - else if (!strcmp(tag, "Aff ")) - { - AFF_FLAGS(this).from_string(line); - } - else if (!strcmp(tag, "Affs")) - { - i = 0; - do - { - fbgetline(fl, line); - sscanf(line, "%d %d %d %d %d %d", &num, &num2, &num3, &num4, &num5, &num6); - if (num > 0) - { - AFFECT_DATA af; - af.type = num; - af.duration = num2; - af.modifier = num3; - af.location = static_cast(num4); - af.bitvector = num5; - af.battleflag = num6; - if (af.type == SPELL_LACKY) - { - af.handler.reset(new LackyAffectHandler()); - } - affect_to_char(this, af); - i++; - } - } while (num != 0); - /* do not load affects */ - } - else if (!strcmp(tag, "Alin")) - { - GET_ALIGNMENT(this) = num; - } - break; - - case 'B': - if (!strcmp(tag, "Badp")) - { - GET_BAD_PWS(this) = num; - } - else if (!strcmp(tag, "Bank")) - { - set_bank(lnum, false); - } - else if (!strcmp(tag, "Br01")) - set_board_date(Boards::GENERAL_BOARD, llnum); - else if (!strcmp(tag, "Br02")) - set_board_date(Boards::NEWS_BOARD, llnum); - else if (!strcmp(tag, "Br03")) - set_board_date(Boards::IDEA_BOARD, llnum); - else if (!strcmp(tag, "Br04")) - set_board_date(Boards::ERROR_BOARD, llnum); - else if (!strcmp(tag, "Br05")) - set_board_date(Boards::GODNEWS_BOARD, llnum); - else if (!strcmp(tag, "Br06")) - set_board_date(Boards::GODGENERAL_BOARD, llnum); - else if (!strcmp(tag, "Br07")) - set_board_date(Boards::GODBUILD_BOARD, llnum); - else if (!strcmp(tag, "Br08")) - set_board_date(Boards::GODCODE_BOARD, llnum); - else if (!strcmp(tag, "Br09")) - set_board_date(Boards::GODPUNISH_BOARD, llnum); - else if (!strcmp(tag, "Br10")) - set_board_date(Boards::PERS_BOARD, llnum); - else if (!strcmp(tag, "Br11")) - set_board_date(Boards::CLAN_BOARD, llnum); - else if (!strcmp(tag, "Br12")) - set_board_date(Boards::CLANNEWS_BOARD, llnum); - else if (!strcmp(tag, "Br13")) - set_board_date(Boards::NOTICE_BOARD, llnum); - else if (!strcmp(tag, "Br14")) - set_board_date(Boards::MISPRINT_BOARD, llnum); - else if (!strcmp(tag, "Br15")) - set_board_date(Boards::SUGGEST_BOARD, llnum); - else if (!strcmp(tag, "Br16")) - set_board_date(Boards::CODER_BOARD, llnum); - - else if (!strcmp(tag, "Brth")) - this->player_data.time.birth = lnum; - break; - - case 'C': - if (!strcmp(tag, "Cha ")) - this->set_cha(num); - else if ( !strcmp(tag, "Chrm") ) { - log("Load_char: Charmees loading"); - do { - fbgetline(fl, line); - sscanf(line, "%d %d %d %d %d %d", &num, &num2, &num3, &num4, &num5, &num6); - if (this->charmeeHistory.find(num) == this->charmeeHistory.end() && num != 0) { - MERCDATA md = {num2, num3, num4, num5, num6}; // num - key - this->charmeeHistory.insert( std::pair(num, md)); - log("Load_char: Charmees: vnum: %d", num); - } - } while (num != 0); - } - else if (!strcmp(tag, "Con ")) - this->set_con(num); - else if (!strcmp(tag, "CntS")) - this->reset_stats_cnt_[ResetStats::Type::MAIN_STATS] = num; - else if (!strcmp(tag, "CntR")) - this->reset_stats_cnt_[ResetStats::Type::RACE] = num; - else if (!strcmp(tag, "CntF")) - this->reset_stats_cnt_[ResetStats::Type::FEATS] = num; - else if (!strcmp(tag, "Cits")) - { - std::string buffer_cities = std::string(line); - // это на тот случай, если вдруг количество городов поменялось - if (buffer_cities.size() != ::cities.size()) - { - // если меньше - if (buffer_cities.size() < ::cities.size()) - { - const size_t b_size = buffer_cities.size(); - // то добиваем нулями - for (unsigned int i = 0; i < ::cities.size() - b_size; i++) - buffer_cities += "0"; - } - else - { - // режем строку - buffer_cities.resize(buffer_cities.size() - (buffer_cities.size() - ::cities.size())); - } - } - this->str_to_cities(std::string(buffer_cities)); - } - break; - - case 'D': - if (!strcmp(tag, "Desc")) - { - const auto ptr = fbgetstring(fl); - this->player_data.description = ptr ? ptr : ""; - } - else if (!strcmp(tag, "Disp")) - { - std::bitset tmp_flags(lnum); - disposable_flags_ = tmp_flags; - } - else if (!strcmp(tag, "Dex ")) - this->set_dex(num); - else if (!strcmp(tag, "Drnk")) - GET_COND(this, DRUNK) = num; - else if (!strcmp(tag, "DrSt")) - GET_DRUNK_STATE(this) = num; - else if (!strcmp(tag, "Drol")) - GET_DR(this) = num; - else if (!strcmp(tag, "DaiQ")) - { - - if (sscanf(line, "%d %d %ld", &num, &num2, &lnum) == 2) - { - this->add_daily_quest(num, num2); - } - else - { - this->add_daily_quest(num, num2); - this->set_time_daily_quest(num, lnum); - } - - } - break; - - case 'E': - if (!strcmp(tag, "ExFl")) - EXCHANGE_FILTER(this) = str_dup(line); - else if (!strcmp(tag, "EMal")) - strcpy(GET_EMAIL(this), line); -//29.11.09. (c) Василиса -//edited by WorM 2011.05.21 - else if (!strcmp(tag, "Expa")) - GET_EXP_ARENA(this) = llnum; - else if (!strcmp(tag, "Expm")) - GET_EXP_MOB(this) = llnum; - else if (!strcmp(tag, "Exmt")) - GET_EXP_MOBTHIS(this) = llnum; - else if (!strcmp(tag, "Expp")) - GET_EXP_PK(this) = llnum; - else if (!strcmp(tag, "Expt")) - GET_EXP_PKTHIS(this) = llnum; - else if (!strcmp(tag, "Expo")) - GET_EXP_OTHER(this) = llnum; - else if (!strcmp(tag, "Exot")) - GET_EXP_OTHERTHIS(this) = llnum; - else if (!strcmp(tag, "Expd")) - GET_EXP_DT(this) = llnum; - else if (!strcmp(tag, "Exdt")) - GET_EXP_DTTHIS(this) = llnum; -//end by WorM -//Конец правки (с) Василиса - break; - - case 'F': - // Оставлено для совместимости со старым форматом наказаний - if (!strcmp(tag, "Frez")) - GET_FREEZE_LEV(this) = num; - else if (!strcmp(tag, "Feat")) - { - do - { - fbgetline(fl, line); - sscanf(line, "%d", &num); - if (num > 0 && num < MAX_FEATS) - if (feat_info[num].classknow[(int) GET_CLASS(this)][(int) GET_KIN(this)] || PlayerRace::FeatureCheck((int)GET_KIN(this),(int)GET_RACE(this),num)) - SET_FEAT(this, num); - } - while (num != 0); - } - else if (!strcmp(tag, "FtTm")) - { - do - { - fbgetline(fl, line); - sscanf(line, "%d %d", &num, &num2); - if (num != 0) - { - timed.skill = num; - timed.time = num2; - timed_feat_to_char(this, &timed); - } - } - while (num != 0); - } - break; - - case 'G': - if (!strcmp(tag, "Gold")) - { - set_gold(lnum, false); - } - else if (!strcmp(tag, "GodD")) - GCURSE_DURATION(this) = lnum; - else if (!strcmp(tag, "GdFl")) - this->player_specials->saved.GodsLike = lnum; - // added by WorM (Видолюб) 2010.06.04 бабки потраченные на найм(возвращаются при креше) - else if (!strcmp(tag, "GldH")) - { - if(num != 0 && !IS_IMMORTAL(this) && can_use_feat(this, EMPLOYER_FEAT)) - { - this->player_specials->saved.HiredCost = num; - } - } - // end by WorM - break; - - case 'H': - if (!strcmp(tag, "Hit ")) - { - sscanf(line, "%d/%d", &num, &num2); - GET_HIT(this) = num; - GET_MAX_HIT(this) = num2; - } - else if (!strcmp(tag, "Hite")) - GET_HEIGHT(this) = num; - else if (!strcmp(tag, "Hrol")) - GET_HR(this) = num; - else if (!strcmp(tag, "Hung")) - GET_COND(this, FULL) = num; - else if (!strcmp(tag, "Hry ")) - { - if (num > cap_hryvn) - num = cap_hryvn; - this->set_hryvn(num); - } - else if (!strcmp(tag, "Host")) - strcpy(GET_LASTIP(this), line); - break; - - case 'I': - if (!strcmp(tag, "Int ")) - this->set_int(num); - else if (!strcmp(tag, "Invs")) - { - SET_INVIS_LEV(this, num); - } - else if (!strcmp(tag, "Ignr")) - { - IgnoresLoader ignores_loader(this); - ignores_loader.load_from_string(line); - } - else if (!strcmp(tag, "ICur")) - { - this->set_ice_currency(num); -// this->set_ice_currency(0); // чистка льда - } - break; - - case 'K': - if (!strcmp(tag, "Kin ")) - GET_KIN(this) = num; - else if (!strcmp(tag, "Karm")) - KARMA(this) = fbgetstring(fl); - break; - case 'L': - if (!strcmp(tag, "LogL")) - { - long lnum, lnum2; - do - { - fbgetline(fl, line); - sscanf(line, "%s %ld %ld", &buf[0], &lnum, &lnum2); - if (buf[0] != '~') - { - const logon_data cur_log = { str_dup(buf), lnum, lnum2, false }; - LOGON_LIST(this).push_back(cur_log); - } - else break; - } - while (true); - - if (!LOGON_LIST(this).empty()) - { - LOGON_LIST(this).at(0).is_first = true; - std::sort(LOGON_LIST(this).begin(), LOGON_LIST(this).end(), - [](const logon_data& a, const logon_data& b) - { - return a.lasttime < b.lasttime; - }); - } - } -// Gunner - else if (!strcmp(tag, "Logs")) - { - sscanf(line, "%d %d", &num, &num2); - if (num >= 0 && num < 1 + LAST_LOG) - GET_LOGS(this)[num] = num2; - } - else if (!strcmp(tag, "Lexc")) - this->set_last_exchange(num); - break; - - case 'M': - if (!strcmp(tag, "Mana")) - { - sscanf(line, "%d/%d", &num, &num2); - GET_MEM_COMPLETED(this) = num; - GET_MEM_TOTAL(this) = num2; - } - else if (!strcmp(tag, "Map ")) - { - std::string str(line); - std::bitset tmp(str); - map_options_.bit_list_ = tmp; - } - else if (!strcmp(tag, "Move")) - { - sscanf(line, "%d/%d", &num, &num2); - GET_MOVE(this) = num; - GET_MAX_MOVE(this) = num2; - } - else if (!strcmp(tag, "Mobs")) - { - do - { - if (!fbgetline(fl, line)) - break; - if (*line == '~') - break; - sscanf(line, "%d %d", &num, &num2); - this->mobmax_load(this, num, num2, MobMax::get_level_by_vnum(num)); - } - while (true); - } - else if (!strcmp(tag, "Mrph")) - { - morphs_load(this, std::string(line)); - } - break; - case 'N': - if (!strcmp(tag, "NmI ")) - this->player_data.PNames[0] = std::string(line); - else if (!strcmp(tag, "NmR ")) - this->player_data.PNames[1] = std::string(line); - else if (!strcmp(tag, "NmD ")) - this->player_data.PNames[2] = std::string(line); - else if (!strcmp(tag, "NmV ")) - this->player_data.PNames[3] = std::string(line); - else if (!strcmp(tag, "NmT ")) - this->player_data.PNames[4] = std::string(line); - else if (!strcmp(tag, "NmP ")) - this->player_data.PNames[5] = std::string(line); - else if (!strcmp(tag, "NamD")) - NAME_DURATION(this) = lnum; - else if (!strcmp(tag, "NamG")) - NAME_GOD(this) = num; - else if (!strcmp(tag, "NaID")) - NAME_ID_GOD(this) = lnum; - else if (!strcmp(tag, "NtfE"))//Polud мин. цена для оффлайн-оповещений - NOTIFY_EXCH_PRICE(this) = lnum; - break; - - case 'O': - if (!strcmp(tag, "Olc ")) - GET_OLC_ZONE(this) = num; - break; - - - case 'P': - if (!strcmp(tag, "Pass")) - this->set_passwd(line); - else if (!strcmp(tag, "Plyd")) - this->player_data.time.played = num; - else if (!strcmp(tag, "PfIn")) - POOFIN(this) = str_dup(line); - else if (!strcmp(tag, "PfOt")) - POOFOUT(this) = str_dup(line); - else if (!strcmp(tag, "Pref")) - { - PRF_FLAGS(this).from_string(line); - } - else if (!strcmp(tag, "Pkil")) - { - do - { - if (!fbgetline(fl, line)) - break; - if (*line == '~') - break; - if (sscanf(line, "%ld %d %d", &lnum, &num, &num2) < 3) { - num2 = 0; - }; - if (lnum < 0 || !correct_unique(lnum)) - continue; - if (num2 >= MAX_REVENGE) { - if (--num <= 0) { - continue; - } - num2 = 0; - } - struct PK_Memory_type * pk_one = NULL; - for (pk_one = this->pk_list; pk_one; pk_one = pk_one->next) - if (pk_one->unique == lnum) - break; - if (pk_one) { - log("SYSERROR: duplicate entry pkillers data for %d %s", id, filename); - continue; - } - - CREATE(pk_one, 1); - pk_one->unique = lnum; - pk_one->kill_num = num; - pk_one->revenge_num = num2; - pk_one->next = this->pk_list; - this->pk_list = pk_one; - } - while (true); - } - else if (!strcmp(tag, "Prtl")) - add_portal_to_char(this, num); - // Loads Here new punishment strings - else if (!strcmp(tag, "PMut")) - { - sscanf(line, "%ld %d %ld %[^~]", &lnum, &num2, &lnum3, &buf[0]); - MUTE_DURATION(this) = lnum; - GET_MUTE_LEV(this) = num2; - MUTE_GODID(this) = lnum3; - MUTE_REASON(this) = str_dup(buf); - } - else if (!strcmp(tag, "PHel")) - { - sscanf(line, "%ld %d %ld %[^~]", &lnum, &num2, &lnum3, &buf[0]); - HELL_DURATION(this) = lnum; - GET_HELL_LEV(this) = num2; - HELL_GODID(this) = lnum3; - HELL_REASON(this) = str_dup(buf); - } - else if (!strcmp(tag, "PDum")) - { - sscanf(line, "%ld %d %ld %[^~]", &lnum, &num2, &lnum3, &buf[0]); - DUMB_DURATION(this) = lnum; - GET_DUMB_LEV(this) = num2; - DUMB_GODID(this) = lnum3; - DUMB_REASON(this) = str_dup(buf); - } - else if (!strcmp(tag, "PNam")) - { - sscanf(line, "%ld %d %ld %[^~]", &lnum, &num2, &lnum3, &buf[0]); - NAME_DURATION(this) = lnum; - GET_NAME_LEV(this) = num2; - NAME_GODID(this) = lnum3; - NAME_REASON(this) = str_dup(buf); - } - else if (!strcmp(tag, "PFrz")) - { - sscanf(line, "%ld %d %ld %[^~]", &lnum, &num2, &lnum3, &buf[0]); - FREEZE_DURATION(this) = lnum; - GET_FREEZE_LEV(this) = num2; - FREEZE_GODID(this) = lnum3; - FREEZE_REASON(this) = str_dup(buf); - } - else if (!strcmp(tag, "PGcs")) - { - sscanf(line, "%ld %d %ld %[^~]", &lnum, &num2, &lnum3, &buf[0]); - GCURSE_DURATION(this) = lnum; - GET_GCURSE_LEV(this) = num2; - GCURSE_GODID(this) = lnum3; - GCURSE_REASON(this) = str_dup(buf); - } - else if (!strcmp(tag, "PUnr")) - { - sscanf(line, "%ld %d %ld %[^~]", &lnum, &num2, &lnum3, &buf[0]); - UNREG_DURATION(this) = lnum; - GET_UNREG_LEV(this) = num2; - UNREG_GODID(this) = lnum3; - UNREG_REASON(this) = str_dup(buf); - } - - break; - - case 'Q': - if (!strcmp(tag, "Qst ")) - { - buf[0] = '\0'; - sscanf(line, "%d %[^~]", &num, &buf[0]); - this->quested_add(this, num, buf); - } - break; - - case 'R': - if (!strcmp(tag, "Room")) - GET_LOADROOM(this) = num; -//29.11.09. (c) Василиса - else if (!strcmp(tag, "Ripa")) - GET_RIP_ARENA(this) = num; - else if (!strcmp(tag, "Ripm")) - GET_RIP_MOB(this) = num; - else if (!strcmp(tag, "Rimt")) - GET_RIP_MOBTHIS(this) = num; - else if (!strcmp(tag, "Ruble")) - this->set_ruble(num); - else if (!strcmp(tag, "Ripp")) - GET_RIP_PK(this) = num; - else if (!strcmp(tag, "Ript")) - GET_RIP_PKTHIS(this) = num; - else if (!strcmp(tag, "Ripo")) - GET_RIP_OTHER(this) = num; - else if (!strcmp(tag, "Riot")) - GET_RIP_OTHERTHIS(this) = num; - else if (!strcmp(tag, "Ripd")) - GET_RIP_DT(this) = num; - else if (!strcmp(tag, "Ridt")) - GET_RIP_DTTHIS(this) = num; -//(с) Василиса - else if (!strcmp(tag, "Rmbr")) - this->remember_set_num(num); - else if (!strcmp(tag, "Reli")) - GET_RELIGION(this) = num; - else if (!strcmp(tag, "Race")) - GET_RACE(this) = num; - else if (!strcmp(tag, "Rcps")) - { - im_rskill *last = NULL; - for (;;) - { - im_rskill *rs; - fbgetline(fl, line); - sscanf(line, "%d %d", &num, &num2); - if (num < 0) - break; - num = im_get_recipe(num); -// +newbook.patch (Alisher) - if (num < 0 || imrecipes[num].classknow[(int) GET_CLASS(this)] != KNOW_RECIPE) -// -newbook.patch (Alisher) - continue; - CREATE(rs, 1); - rs->rid = num; - rs->perc = num2; - rs->link = NULL; - if (last) - last->link = rs; - else - GET_RSKILL(this) = rs; - last = rs; - } - } - break; - - case 'S': - if (!strcmp(tag, "Size")) - GET_SIZE(this) = num; - else if (!strcmp(tag, "Sex ")) - { - this->set_sex(static_cast(num)); - } - else if (!strcmp(tag, "Skil")) - { - do - { - fbgetline(fl, line); - sscanf(line, "%d %d", &num, &num2); - if (num != 0) - { - if (skill_info[num].classknow[(int)GET_CLASS(this)][(int)GET_KIN(this)] == KNOW_SKILL) - { - this->set_skill(static_cast(num), num2); - } - } - } - while (num != 0); - } - else if (!strcmp(tag, "SkTm")) - { - do - { - fbgetline(fl, line); - sscanf(line, "%d %d", &num, &num2); - if (num != 0) - { - timed.skill = num; - timed.time = num2; - timed_to_char(this, &timed); - } - } - while (num != 0); - } - else if (!strcmp(tag, "Spel")) - { - do - { - fbgetline(fl, line); - sscanf(line, "%d %d", &num, &num2); - if (num != 0 && spell_info[num].name) - GET_SPELL_TYPE(this, num) = num2; - } - while (num != 0); - } - else if (!strcmp(tag, "SpMe")) - { - do - { - fbgetline(fl, line); - sscanf(line, "%d %d", &num, &num2); - if (num != 0) - GET_SPELL_MEM(this, num) = num2; - } - while (num != 0); - } - else if (!strcmp(tag, "SpTM")) - { - struct spell_mem_queue_item *qi_cur, ** qi = &MemQueue.queue; - while (*qi) - qi = &((*qi)->link); - do - { - fbgetline(fl, line); - sscanf(line, "%d", &num); - if (num != 0) - { - CREATE(qi_cur, 1); - *qi = qi_cur; - qi_cur->spellnum = num; - qi_cur->link = NULL; - qi = &qi_cur->link; - } - } - while (num != 0); - } - else if (!strcmp(tag, "Str ")) - this->set_str(num); - else if (!strcmp(tag, "StrL")) - STRING_LENGTH(this) = num; - else if (!strcmp(tag, "StrW")) - STRING_WIDTH(this) = num; - else if (!strcmp(tag, "St00")) - this->set_start_stat(G_STR, lnum); - else if (!strcmp(tag, "St01")) - this->set_start_stat(G_DEX, lnum); - else if (!strcmp(tag, "St02")) - this->set_start_stat(G_INT, lnum); - else if (!strcmp(tag, "St03")) - this->set_start_stat(G_WIS, lnum); - else if (!strcmp(tag, "St04")) - this->set_start_stat(G_CON, lnum); - else if (!strcmp(tag, "St05")) - this->set_start_stat(G_CHA, lnum); - break; - - case 'T': - if (!strcmp(tag, "Thir")) - GET_COND(this, THIRST) = num; - else if (!strcmp(tag, "Titl")) - GET_TITLE(this) = std::string(str_dup(line)); - else if (!strcmp(tag, "TrcG")) - set_ext_money(ExtMoney::TORC_GOLD, num, false); - else if (!strcmp(tag, "TrcS")) - set_ext_money(ExtMoney::TORC_SILVER, num, false); - else if (!strcmp(tag, "TrcB")) - set_ext_money(ExtMoney::TORC_BRONZE, num, false); - else if (!strcmp(tag, "TrcL")) { - sscanf(line, "%d %d", &num, &num2); - today_torc_.first = num; - today_torc_.second = num2; - } - else if (!strcmp(tag, "Tlgr")) { - if (lnum <= 10000000000000) { - this->player_specials->saved.telegram_id = lnum; - } else { // зачищаем остатки старой баги - this->player_specials->saved.telegram_id = 0; - } - } - else if (!strcmp(tag, "TSpl")) - { - do - { - fbgetline(fl, line); - sscanf(line, "%d %ld %ld", &num, &lnum, &lnum3); - if (num != 0 && spell_info[num].name) - { - Temporary_Spells::add_spell(this, num, lnum, lnum3); - } - } while (num != 0); - } - break; - - case 'W': - if (!strcmp(tag, "Wate")) - GET_WEIGHT(this) = num; - else if (!strcmp(tag, "Wimp")) - GET_WIMP_LEV(this) = num; - else if (!strcmp(tag, "Wis ")) - this->set_wis(num); -//29.11.09 (c) Василиса - else if (!strcmp(tag, "Wina")) - GET_WIN_ARENA(this) = num; -//конец правки (с) Василиса - else if (!strcmp(tag, "Wman")) - this->set_who_mana(num); - break; - - default: - sprintf(buf, "SYSERR: Unknown tag %s in pfile %s", tag, name); - } - } - PRF_FLAGS(this).set(PRF_COLOR_2); //всегда цвет полный - // initialization for imms - if (GET_LEVEL(this) >= LVL_IMMORT) - { - set_god_skills(this); - set_god_morphs(this); - GET_COND(this, FULL) = -1; - GET_COND(this, THIRST) = -1; - GET_COND(this, DRUNK) = -1; - GET_LOADROOM(this) = NOWHERE; - } - - setAllInbornFeatures(this); - - if (IS_GRGOD(this)) - { - for (i = 0; i <= MAX_SPELLS; i++) - GET_SPELL_TYPE(this, i) = GET_SPELL_TYPE(this, i) | - SPELL_ITEMS | SPELL_KNOW | SPELL_RUNES | SPELL_SCROLL | SPELL_POTION | SPELL_WAND; - } - else if (!IS_IMMORTAL(this)) - { - for (i = 0; i <= MAX_SPELLS; i++) - { - if (spell_info[i].slot_forc[(int) GET_CLASS(this)][(int) GET_KIN(this)] == MAX_SLOT) - REMOVE_BIT(GET_SPELL_TYPE(this, i), SPELL_KNOW | SPELL_TEMP); -// shapirus: изученное не убираем на всякий случай, но из мема выкидываем, -// если мортов мало - if (GET_REMORT(this) < MIN_CAST_REM(spell_info[i], this)) - GET_SPELL_MEM(this, i) = 0; - } - } - - /* - * If you're not poisioned and you've been away for more than an hour of - * real time, we'll set your HMV back to full - */ - if (!AFF_FLAGGED(this, EAffectFlag::AFF_POISON) && (((long)(time(0) - LAST_LOGON(this))) >= SECS_PER_REAL_HOUR)) - { - GET_HIT(this) = GET_REAL_MAX_HIT(this); - GET_MOVE(this) = GET_REAL_MAX_MOVE(this); - } - else - GET_HIT(this) = MIN(GET_HIT(this), GET_REAL_MAX_HIT(this)); - - fbclose(fl); - // здесь мы закладываемся на то, что при ребуте это все сейчас пропускается и это нормально, - // иначе в таблице crc будут пустые имена, т.к. сама плеер-таблица еще не сформирована - // и в любом случае при ребуте это все пересчитывать не нужно - FileCRC::check_crc(filename, FileCRC::PLAYER, GET_UNIQUE(this)); - - this->account = Account::get_account(GET_EMAIL(this)); - if (this->account == nullptr) - { - const auto temp_account = std::make_shared(GET_EMAIL(this)); - accounts.emplace(GET_EMAIL(this), temp_account); - this->account = temp_account; - } - this->account->add_player(GET_UNIQUE(this)); - return (id); + if (!_pfileLoad(fl, reboot, name)); + return -1; + return id; } bool Player::get_disposable_flag(int num) @@ -2556,6 +1455,1118 @@ unsigned long int Player::getTelegramId() { return this->player_specials->saved.telegram_id; } +bool Player::_pfileLoad(FBFILE *fl, bool reboot, const char* name) { + int num = 0, num2 = 0, num3 = 0, num4 = 0, num5 = 0, num6 = 0, i; + long int lnum = 0, lnum3 = 0; + unsigned long long llnum = 0; + char filename[40]; + char buf[MAX_RAW_INPUT_LENGTH], line[MAX_RAW_INPUT_LENGTH], tag[6]; + char line1[MAX_RAW_INPUT_LENGTH]; + struct timed_type timed; + /////////////////////////////////////////////////////////////////////////////// + + // первыми иним и парсим поля для ребута до поля "Rebt", если reboot на входе = 1, то на этом парс и кончается + if (!this->player_specials) + { + this->player_specials = std::make_shared(); + } + + set_level(1); + set_class(1); + set_uid(0); + set_last_logon(time(0)); + set_idnum(0); + set_exp(0); + set_remort(0); + GET_LASTIP(this)[0] = 0; + GET_EMAIL(this)[0] = 0; + PLR_FLAGS(this).from_string(""); // suspicious line: we should clear flags.. Loading from "" does not clear flags. + + bool skip_file = 0; + + do + { + if (!fbgetline(fl, line)) + { + log("SYSERROR: Wrong file ascii %d %s", id, filename); + return (-1); + } + + tag_argument(line, tag); + for (i = 0; !(line[i] == ' ' || line[i] == '\0'); i++) + { + line1[i] = line[i]; + } + line1[i] = '\0'; + num = atoi(line1); + lnum = atol(line1); + try + { + llnum = boost::lexical_cast(line1); + } + catch(boost::bad_lexical_cast &) + { + llnum = 0; + } + + + switch (*tag) + { + case 'A': + if (!strcmp(tag, "Act ")) + { + PLR_FLAGS(this).from_string(line); + } + break; + case 'C': + if (!strcmp(tag, "Clas")) + { + set_class(num); + } + break; + case 'E': + if (!strcmp(tag, "Exp ")) + { + set_exp(lnum); + } + //added by WorM 2010.08.27 лоадим мыло и айпи даже при ребуте + else if (!strcmp(tag, "EMal")) + strcpy(GET_EMAIL(this), line); + break; + case 'H': + if (!strcmp(tag, "Host")) + { + strcpy(GET_LASTIP(this), line); + } + //end by WorM + break; + case 'I': + if (!strcmp(tag, "Id ")) + { + set_idnum(lnum); + } + break; + case 'L': + if (!strcmp(tag, "LstL")) + { + set_last_logon(lnum); + } + else if (!strcmp(tag, "Levl")) + { + set_level(num); + } + break; + case 'N': + if (!strcmp(tag, "Name")) + { + set_name(line); + } + break; + case 'R': + if (!strcmp(tag, "Rebt")) + skip_file = 1; + else if (!strcmp(tag, "Rmrt")) + { + set_remort(num); + } + break; + case 'U': + if (!strcmp(tag, "UIN ")) + { + set_uid(num); + } + break; + default: + sprintf(buf, "SYSERR: Unknown tag %s in pfile %s", tag, name); + } + } + while (!skip_file); + + //added by WorM 2010.08.27 лоадим мыло и последний ip даже при считывании индексов + while((reboot) && (!*GET_EMAIL(this) || !*GET_LASTIP(this))) + { + if (!fbgetline(fl, line)) + { + log("SYSERROR: Wrong file ascii %d %s", id, filename); + return (-1); + } + + tag_argument(line, tag); + + if (!strcmp(tag, "EMal")) + strcpy(GET_EMAIL(this), line); + else if (!strcmp(tag, "Host")) + strcpy(GET_LASTIP(this), line); + } + //end by WorM + + // если с загруженными выше полями что-то хочется делать после лоада - делайте это здесь + + //Indexing experience - if his exp is lover than required for his level - set it to required + if (GET_EXP(this) < ExpCalc::level_exp(this, GET_LEVEL(this))) + { + set_exp(ExpCalc::level_exp(this, GET_LEVEL(this))); + } + + if (reboot) + { + fbclose(fl); + return id; + } + this->str_to_cities(default_str_cities); + // если происходит обычный лоад плеера, то читаем файл дальше и иним все остальные поля + +/////////////////////////////////////////////////////////////////////////////// + + + // character init + // initializations necessary to keep some things straight + + this->set_npc_name(0); + this->player_data.long_descr = ""; + + this->real_abils.Feats.reset(); + + // волхвам сетим все спеллы на рунах, остальные инит нулями + if (GET_CLASS(this) != CLASS_DRUID) + for (i = 1; i <= MAX_SPELLS; i++) + GET_SPELL_TYPE(this, i) = 0; + else + for (i = 1; i <= MAX_SPELLS; i++) + GET_SPELL_TYPE(this, i) = SPELL_RUNES; + + for (i = 1; i <= MAX_SPELLS; i++) + GET_SPELL_MEM(this, i) = 0; + this->char_specials.saved.affected_by = clear_flags; + POOFIN(this) = NULL; + POOFOUT(this) = NULL; + GET_RSKILL(this) = NULL; // рецептов не знает + this->char_specials.carry_weight = 0; + this->char_specials.carry_items = 0; + this->real_abils.armor = 100; + GET_MEM_TOTAL(this) = 0; + GET_MEM_COMPLETED(this) = 0; + MemQ_init(this); + + GET_AC(this) = 10; + GET_ALIGNMENT(this) = 0; + GET_BAD_PWS(this) = 0; + this->player_data.time.birth = time(0); + GET_KIN(this) = 0; + + this->set_str(10); + this->set_dex(10); + this->set_con(10); + this->set_int(10); + this->set_wis(10); + this ->set_cha(10); + + GET_COND(this, DRUNK) = 0; + GET_DRUNK_STATE(this) = 0; + +// Punish Init + DUMB_DURATION(this) = 0; + DUMB_REASON(this) = 0; + GET_DUMB_LEV(this) = 0; + DUMB_GODID(this) = 0; + + MUTE_DURATION(this) = 0; + MUTE_REASON(this) = 0; + GET_MUTE_LEV(this) = 0; + MUTE_GODID(this) = 0; + + HELL_DURATION(this) = 0; + HELL_REASON(this) = 0; + GET_HELL_LEV(this) = 0; + HELL_GODID(this) = 0; + + FREEZE_DURATION(this) = 0; + FREEZE_REASON(this) = 0; + GET_FREEZE_LEV(this) = 0; + FREEZE_GODID(this) = 0; + + GCURSE_DURATION(this) = 0; + GCURSE_REASON(this) = 0; + GET_GCURSE_LEV(this) = 0; + GCURSE_GODID(this) = 0; + + NAME_DURATION(this) = 0; + NAME_REASON(this) = 0; + GET_NAME_LEV(this) = 0; + NAME_GODID(this) = 0; + + UNREG_DURATION(this) = 0; + UNREG_REASON(this) = 0; + GET_UNREG_LEV(this) = 0; + UNREG_GODID(this) = 0; + +// End punish init + + GET_DR(this) = 0; + + set_gold(0, false); + set_bank(0, false); + set_ruble(0); + this->player_specials->saved.GodsLike = 0; + GET_HIT(this) = 21; + GET_MAX_HIT(this) = 21; + GET_HEIGHT(this) = 50; + GET_HR(this) = 0; + GET_COND(this, FULL) = 0; + SET_INVIS_LEV(this, 0); + this->player_data.time.logon = time(0); + GET_MOVE(this) = 44; + GET_MAX_MOVE(this) = 44; + KARMA(this) = 0; + LOGON_LIST(this).clear(); + NAME_GOD(this) = 0; + STRING_LENGTH(this) = 80; + STRING_WIDTH(this) = 30; + NAME_ID_GOD(this) = 0; + GET_OLC_ZONE(this) = 0; + this->player_data.time.played = 0; + GET_LOADROOM(this) = NOWHERE; + GET_RELIGION(this) = 1; + GET_RACE(this) = 1; + this->set_sex(ESex::SEX_NEUTRAL); + GET_COND(this, THIRST) = NORM_COND_VALUE; + GET_WEIGHT(this) = 50; + GET_WIMP_LEV(this) = 0; + PRF_FLAGS(this).from_string(""); // suspicious line: we should clear flags.. Loading from "" does not clear flags. + AFF_FLAGS(this).from_string(""); // suspicious line: we should clear flags.. Loading from "" does not clear flags. + GET_PORTALS(this) = NULL; + EXCHANGE_FILTER(this) = NULL; + clear_ignores(); + CREATE(GET_LOGS(this), 1 + LAST_LOG); + NOTIFY_EXCH_PRICE(this) = 0; + this->player_specials->saved.HiredCost = 0; + this->set_who_mana(WHO_MANA_MAX); + this->set_who_last(time(0)); + + while (fbgetline(fl, line)) + { + tag_argument(line, tag); + for (i = 0; !(line[i] == ' ' || line[i] == '\0'); i++) + { + line1[i] = line[i]; + } + line1[i] = '\0'; + num = atoi(line1); + lnum = atol(line1); + try + { + llnum = std::stoull(line1, nullptr, 10); + } + catch (const std::invalid_argument &) + { + llnum = 0; + } + catch (const std::out_of_range &) + { + llnum = 0; + } + switch (*tag) + { + case 'A': + if (!strcmp(tag, "Ac ")) + { + GET_AC(this) = num; + } + else if (!strcmp(tag, "Aff ")) + { + AFF_FLAGS(this).from_string(line); + } + else if (!strcmp(tag, "Affs")) + { + i = 0; + do + { + fbgetline(fl, line); + sscanf(line, "%d %d %d %d %d %d", &num, &num2, &num3, &num4, &num5, &num6); + if (num > 0) + { + AFFECT_DATA af; + af.type = num; + af.duration = num2; + af.modifier = num3; + af.location = static_cast(num4); + af.bitvector = num5; + af.battleflag = num6; + if (af.type == SPELL_LACKY) + { + af.handler.reset(new LackyAffectHandler()); + } + affect_to_char(this, af); + i++; + } + } while (num != 0); + /* do not load affects */ + } + else if (!strcmp(tag, "Alin")) + { + GET_ALIGNMENT(this) = num; + } + break; + + case 'B': + if (!strcmp(tag, "Badp")) + { + GET_BAD_PWS(this) = num; + } + else if (!strcmp(tag, "Bank")) + { + set_bank(lnum, false); + } + else if (!strcmp(tag, "Br01")) + set_board_date(Boards::GENERAL_BOARD, llnum); + else if (!strcmp(tag, "Br02")) + set_board_date(Boards::NEWS_BOARD, llnum); + else if (!strcmp(tag, "Br03")) + set_board_date(Boards::IDEA_BOARD, llnum); + else if (!strcmp(tag, "Br04")) + set_board_date(Boards::ERROR_BOARD, llnum); + else if (!strcmp(tag, "Br05")) + set_board_date(Boards::GODNEWS_BOARD, llnum); + else if (!strcmp(tag, "Br06")) + set_board_date(Boards::GODGENERAL_BOARD, llnum); + else if (!strcmp(tag, "Br07")) + set_board_date(Boards::GODBUILD_BOARD, llnum); + else if (!strcmp(tag, "Br08")) + set_board_date(Boards::GODCODE_BOARD, llnum); + else if (!strcmp(tag, "Br09")) + set_board_date(Boards::GODPUNISH_BOARD, llnum); + else if (!strcmp(tag, "Br10")) + set_board_date(Boards::PERS_BOARD, llnum); + else if (!strcmp(tag, "Br11")) + set_board_date(Boards::CLAN_BOARD, llnum); + else if (!strcmp(tag, "Br12")) + set_board_date(Boards::CLANNEWS_BOARD, llnum); + else if (!strcmp(tag, "Br13")) + set_board_date(Boards::NOTICE_BOARD, llnum); + else if (!strcmp(tag, "Br14")) + set_board_date(Boards::MISPRINT_BOARD, llnum); + else if (!strcmp(tag, "Br15")) + set_board_date(Boards::SUGGEST_BOARD, llnum); + else if (!strcmp(tag, "Br16")) + set_board_date(Boards::CODER_BOARD, llnum); + + else if (!strcmp(tag, "Brth")) + this->player_data.time.birth = lnum; + break; + + case 'C': + if (!strcmp(tag, "Cha ")) + this->set_cha(num); + else if ( !strcmp(tag, "Chrm") ) { + log("Load_char: Charmees loading"); + do { + fbgetline(fl, line); + sscanf(line, "%d %d %d %d %d %d", &num, &num2, &num3, &num4, &num5, &num6); + if (this->charmeeHistory.find(num) == this->charmeeHistory.end() && num != 0) { + MERCDATA md = {num2, num3, num4, num5, num6}; // num - key + this->charmeeHistory.insert( std::pair(num, md)); + log("Load_char: Charmees: vnum: %d", num); + } + } while (num != 0); + } + else if (!strcmp(tag, "Con ")) + this->set_con(num); + else if (!strcmp(tag, "CntS")) + this->reset_stats_cnt_[ResetStats::Type::MAIN_STATS] = num; + else if (!strcmp(tag, "CntR")) + this->reset_stats_cnt_[ResetStats::Type::RACE] = num; + else if (!strcmp(tag, "CntF")) + this->reset_stats_cnt_[ResetStats::Type::FEATS] = num; + else if (!strcmp(tag, "Cits")) + { + std::string buffer_cities = std::string(line); + // это на тот случай, если вдруг количество городов поменялось + if (buffer_cities.size() != ::cities.size()) + { + // если меньше + if (buffer_cities.size() < ::cities.size()) + { + const size_t b_size = buffer_cities.size(); + // то добиваем нулями + for (unsigned int i = 0; i < ::cities.size() - b_size; i++) + buffer_cities += "0"; + } + else + { + // режем строку + buffer_cities.resize(buffer_cities.size() - (buffer_cities.size() - ::cities.size())); + } + } + this->str_to_cities(std::string(buffer_cities)); + } + break; + + case 'D': + if (!strcmp(tag, "Desc")) + { + const auto ptr = fbgetstring(fl); + this->player_data.description = ptr ? ptr : ""; + } + else if (!strcmp(tag, "Disp")) + { + std::bitset tmp_flags(lnum); + disposable_flags_ = tmp_flags; + } + else if (!strcmp(tag, "Dex ")) + this->set_dex(num); + else if (!strcmp(tag, "Drnk")) + GET_COND(this, DRUNK) = num; + else if (!strcmp(tag, "DrSt")) + GET_DRUNK_STATE(this) = num; + else if (!strcmp(tag, "Drol")) + GET_DR(this) = num; + else if (!strcmp(tag, "DaiQ")) + { + + if (sscanf(line, "%d %d %ld", &num, &num2, &lnum) == 2) + { + this->add_daily_quest(num, num2); + } + else + { + this->add_daily_quest(num, num2); + this->set_time_daily_quest(num, lnum); + } + + } + break; + + case 'E': + if (!strcmp(tag, "ExFl")) + EXCHANGE_FILTER(this) = str_dup(line); + else if (!strcmp(tag, "EMal")) + strcpy(GET_EMAIL(this), line); +//29.11.09. (c) Василиса +//edited by WorM 2011.05.21 + else if (!strcmp(tag, "Expa")) + GET_EXP_ARENA(this) = llnum; + else if (!strcmp(tag, "Expm")) + GET_EXP_MOB(this) = llnum; + else if (!strcmp(tag, "Exmt")) + GET_EXP_MOBTHIS(this) = llnum; + else if (!strcmp(tag, "Expp")) + GET_EXP_PK(this) = llnum; + else if (!strcmp(tag, "Expt")) + GET_EXP_PKTHIS(this) = llnum; + else if (!strcmp(tag, "Expo")) + GET_EXP_OTHER(this) = llnum; + else if (!strcmp(tag, "Exot")) + GET_EXP_OTHERTHIS(this) = llnum; + else if (!strcmp(tag, "Expd")) + GET_EXP_DT(this) = llnum; + else if (!strcmp(tag, "Exdt")) + GET_EXP_DTTHIS(this) = llnum; +//end by WorM +//Конец правки (с) Василиса + break; + + case 'F': + // Оставлено для совместимости со старым форматом наказаний + if (!strcmp(tag, "Frez")) + GET_FREEZE_LEV(this) = num; + else if (!strcmp(tag, "Feat")) + { + do + { + fbgetline(fl, line); + sscanf(line, "%d", &num); + if (num > 0 && num < MAX_FEATS) + if (feat_info[num].classknow[(int) GET_CLASS(this)][(int) GET_KIN(this)] || PlayerRace::FeatureCheck((int)GET_KIN(this),(int)GET_RACE(this),num)) + SET_FEAT(this, num); + } + while (num != 0); + } + else if (!strcmp(tag, "FtTm")) + { + do + { + fbgetline(fl, line); + sscanf(line, "%d %d", &num, &num2); + if (num != 0) + { + timed.skill = num; + timed.time = num2; + timed_feat_to_char(this, &timed); + } + } + while (num != 0); + } + break; + + case 'G': + if (!strcmp(tag, "Gold")) + { + set_gold(lnum, false); + } + else if (!strcmp(tag, "GodD")) + GCURSE_DURATION(this) = lnum; + else if (!strcmp(tag, "GdFl")) + this->player_specials->saved.GodsLike = lnum; + // added by WorM (Видолюб) 2010.06.04 бабки потраченные на найм(возвращаются при креше) + else if (!strcmp(tag, "GldH")) + { + if(num != 0 && !IS_IMMORTAL(this) && can_use_feat(this, EMPLOYER_FEAT)) + { + this->player_specials->saved.HiredCost = num; + } + } + // end by WorM + break; + + case 'H': + if (!strcmp(tag, "Hit ")) + { + sscanf(line, "%d/%d", &num, &num2); + GET_HIT(this) = num; + GET_MAX_HIT(this) = num2; + } + else if (!strcmp(tag, "Hite")) + GET_HEIGHT(this) = num; + else if (!strcmp(tag, "Hrol")) + GET_HR(this) = num; + else if (!strcmp(tag, "Hung")) + GET_COND(this, FULL) = num; + else if (!strcmp(tag, "Hry ")) + { + if (num > cap_hryvn) + num = cap_hryvn; + this->set_hryvn(num); + } + else if (!strcmp(tag, "Host")) + strcpy(GET_LASTIP(this), line); + break; + + case 'I': + if (!strcmp(tag, "Int ")) + this->set_int(num); + else if (!strcmp(tag, "Invs")) + { + SET_INVIS_LEV(this, num); + } + else if (!strcmp(tag, "Ignr")) + { + IgnoresLoader ignores_loader(this); + ignores_loader.load_from_string(line); + } + else if (!strcmp(tag, "ICur")) + { + this->set_ice_currency(num); +// this->set_ice_currency(0); // чистка льда + } + break; + + case 'K': + if (!strcmp(tag, "Kin ")) + GET_KIN(this) = num; + else if (!strcmp(tag, "Karm")) + KARMA(this) = fbgetstring(fl); + break; + case 'L': + if (!strcmp(tag, "LogL")) + { + long lnum, lnum2; + do + { + fbgetline(fl, line); + sscanf(line, "%s %ld %ld", &buf[0], &lnum, &lnum2); + if (buf[0] != '~') + { + const logon_data cur_log = { str_dup(buf), lnum, lnum2, false }; + LOGON_LIST(this).push_back(cur_log); + } + else break; + } + while (true); + + if (!LOGON_LIST(this).empty()) + { + LOGON_LIST(this).at(0).is_first = true; + std::sort(LOGON_LIST(this).begin(), LOGON_LIST(this).end(), + [](const logon_data& a, const logon_data& b) + { + return a.lasttime < b.lasttime; + }); + } + } +// Gunner + else if (!strcmp(tag, "Logs")) + { + sscanf(line, "%d %d", &num, &num2); + if (num >= 0 && num < 1 + LAST_LOG) + GET_LOGS(this)[num] = num2; + } + else if (!strcmp(tag, "Lexc")) + this->set_last_exchange(num); + break; + + case 'M': + if (!strcmp(tag, "Mana")) + { + sscanf(line, "%d/%d", &num, &num2); + GET_MEM_COMPLETED(this) = num; + GET_MEM_TOTAL(this) = num2; + } + else if (!strcmp(tag, "Map ")) + { + std::string str(line); + std::bitset tmp(str); + map_options_.bit_list_ = tmp; + } + else if (!strcmp(tag, "Move")) + { + sscanf(line, "%d/%d", &num, &num2); + GET_MOVE(this) = num; + GET_MAX_MOVE(this) = num2; + } + else if (!strcmp(tag, "Mobs")) + { + do + { + if (!fbgetline(fl, line)) + break; + if (*line == '~') + break; + sscanf(line, "%d %d", &num, &num2); + this->mobmax_load(this, num, num2, MobMax::get_level_by_vnum(num)); + } + while (true); + } + else if (!strcmp(tag, "Mrph")) + { + morphs_load(this, std::string(line)); + } + break; + case 'N': + if (!strcmp(tag, "NmI ")) + this->player_data.PNames[0] = std::string(line); + else if (!strcmp(tag, "NmR ")) + this->player_data.PNames[1] = std::string(line); + else if (!strcmp(tag, "NmD ")) + this->player_data.PNames[2] = std::string(line); + else if (!strcmp(tag, "NmV ")) + this->player_data.PNames[3] = std::string(line); + else if (!strcmp(tag, "NmT ")) + this->player_data.PNames[4] = std::string(line); + else if (!strcmp(tag, "NmP ")) + this->player_data.PNames[5] = std::string(line); + else if (!strcmp(tag, "NamD")) + NAME_DURATION(this) = lnum; + else if (!strcmp(tag, "NamG")) + NAME_GOD(this) = num; + else if (!strcmp(tag, "NaID")) + NAME_ID_GOD(this) = lnum; + else if (!strcmp(tag, "NtfE"))//Polud мин. цена для оффлайн-оповещений + NOTIFY_EXCH_PRICE(this) = lnum; + break; + + case 'O': + if (!strcmp(tag, "Olc ")) + GET_OLC_ZONE(this) = num; + break; + + + case 'P': + if (!strcmp(tag, "Pass")) + this->set_passwd(line); + else if (!strcmp(tag, "Plyd")) + this->player_data.time.played = num; + else if (!strcmp(tag, "PfIn")) + POOFIN(this) = str_dup(line); + else if (!strcmp(tag, "PfOt")) + POOFOUT(this) = str_dup(line); + else if (!strcmp(tag, "Pref")) + { + PRF_FLAGS(this).from_string(line); + } + else if (!strcmp(tag, "Pkil")) + { + do + { + if (!fbgetline(fl, line)) + break; + if (*line == '~') + break; + if (sscanf(line, "%ld %d %d", &lnum, &num, &num2) < 3) { + num2 = 0; + }; + if (lnum < 0 || !correct_unique(lnum)) + continue; + if (num2 >= MAX_REVENGE) { + if (--num <= 0) { + continue; + } + num2 = 0; + } + struct PK_Memory_type * pk_one = NULL; + for (pk_one = this->pk_list; pk_one; pk_one = pk_one->next) + if (pk_one->unique == lnum) + break; + if (pk_one) { + log("SYSERROR: duplicate entry pkillers data for %d %s", id, filename); + continue; + } + + CREATE(pk_one, 1); + pk_one->unique = lnum; + pk_one->kill_num = num; + pk_one->revenge_num = num2; + pk_one->next = this->pk_list; + this->pk_list = pk_one; + } + while (true); + } + else if (!strcmp(tag, "Prtl")) + add_portal_to_char(this, num); + // Loads Here new punishment strings + else if (!strcmp(tag, "PMut")) + { + sscanf(line, "%ld %d %ld %[^~]", &lnum, &num2, &lnum3, &buf[0]); + MUTE_DURATION(this) = lnum; + GET_MUTE_LEV(this) = num2; + MUTE_GODID(this) = lnum3; + MUTE_REASON(this) = str_dup(buf); + } + else if (!strcmp(tag, "PHel")) + { + sscanf(line, "%ld %d %ld %[^~]", &lnum, &num2, &lnum3, &buf[0]); + HELL_DURATION(this) = lnum; + GET_HELL_LEV(this) = num2; + HELL_GODID(this) = lnum3; + HELL_REASON(this) = str_dup(buf); + } + else if (!strcmp(tag, "PDum")) + { + sscanf(line, "%ld %d %ld %[^~]", &lnum, &num2, &lnum3, &buf[0]); + DUMB_DURATION(this) = lnum; + GET_DUMB_LEV(this) = num2; + DUMB_GODID(this) = lnum3; + DUMB_REASON(this) = str_dup(buf); + } + else if (!strcmp(tag, "PNam")) + { + sscanf(line, "%ld %d %ld %[^~]", &lnum, &num2, &lnum3, &buf[0]); + NAME_DURATION(this) = lnum; + GET_NAME_LEV(this) = num2; + NAME_GODID(this) = lnum3; + NAME_REASON(this) = str_dup(buf); + } + else if (!strcmp(tag, "PFrz")) + { + sscanf(line, "%ld %d %ld %[^~]", &lnum, &num2, &lnum3, &buf[0]); + FREEZE_DURATION(this) = lnum; + GET_FREEZE_LEV(this) = num2; + FREEZE_GODID(this) = lnum3; + FREEZE_REASON(this) = str_dup(buf); + } + else if (!strcmp(tag, "PGcs")) + { + sscanf(line, "%ld %d %ld %[^~]", &lnum, &num2, &lnum3, &buf[0]); + GCURSE_DURATION(this) = lnum; + GET_GCURSE_LEV(this) = num2; + GCURSE_GODID(this) = lnum3; + GCURSE_REASON(this) = str_dup(buf); + } + else if (!strcmp(tag, "PUnr")) + { + sscanf(line, "%ld %d %ld %[^~]", &lnum, &num2, &lnum3, &buf[0]); + UNREG_DURATION(this) = lnum; + GET_UNREG_LEV(this) = num2; + UNREG_GODID(this) = lnum3; + UNREG_REASON(this) = str_dup(buf); + } + + break; + + case 'Q': + if (!strcmp(tag, "Qst ")) + { + buf[0] = '\0'; + sscanf(line, "%d %[^~]", &num, &buf[0]); + this->quested_add(this, num, buf); + } + break; + + case 'R': + if (!strcmp(tag, "Room")) + GET_LOADROOM(this) = num; +//29.11.09. (c) Василиса + else if (!strcmp(tag, "Ripa")) + GET_RIP_ARENA(this) = num; + else if (!strcmp(tag, "Ripm")) + GET_RIP_MOB(this) = num; + else if (!strcmp(tag, "Rimt")) + GET_RIP_MOBTHIS(this) = num; + else if (!strcmp(tag, "Ruble")) + this->set_ruble(num); + else if (!strcmp(tag, "Ripp")) + GET_RIP_PK(this) = num; + else if (!strcmp(tag, "Ript")) + GET_RIP_PKTHIS(this) = num; + else if (!strcmp(tag, "Ripo")) + GET_RIP_OTHER(this) = num; + else if (!strcmp(tag, "Riot")) + GET_RIP_OTHERTHIS(this) = num; + else if (!strcmp(tag, "Ripd")) + GET_RIP_DT(this) = num; + else if (!strcmp(tag, "Ridt")) + GET_RIP_DTTHIS(this) = num; +//(с) Василиса + else if (!strcmp(tag, "Rmbr")) + this->remember_set_num(num); + else if (!strcmp(tag, "Reli")) + GET_RELIGION(this) = num; + else if (!strcmp(tag, "Race")) + GET_RACE(this) = num; + else if (!strcmp(tag, "Rcps")) + { + im_rskill *last = NULL; + for (;;) + { + im_rskill *rs; + fbgetline(fl, line); + sscanf(line, "%d %d", &num, &num2); + if (num < 0) + break; + num = im_get_recipe(num); +// +newbook.patch (Alisher) + if (num < 0 || imrecipes[num].classknow[(int) GET_CLASS(this)] != KNOW_RECIPE) +// -newbook.patch (Alisher) + continue; + CREATE(rs, 1); + rs->rid = num; + rs->perc = num2; + rs->link = NULL; + if (last) + last->link = rs; + else + GET_RSKILL(this) = rs; + last = rs; + } + } + break; + + case 'S': + if (!strcmp(tag, "Size")) + GET_SIZE(this) = num; + else if (!strcmp(tag, "Sex ")) + { + this->set_sex(static_cast(num)); + } + else if (!strcmp(tag, "Skil")) + { + do + { + fbgetline(fl, line); + sscanf(line, "%d %d", &num, &num2); + if (num != 0) + { + if (skill_info[num].classknow[(int)GET_CLASS(this)][(int)GET_KIN(this)] == KNOW_SKILL) + { + this->set_skill(static_cast(num), num2); + } + } + } + while (num != 0); + } + else if (!strcmp(tag, "SkTm")) + { + do + { + fbgetline(fl, line); + sscanf(line, "%d %d", &num, &num2); + if (num != 0) + { + timed.skill = num; + timed.time = num2; + timed_to_char(this, &timed); + } + } + while (num != 0); + } + else if (!strcmp(tag, "Spel")) + { + do + { + fbgetline(fl, line); + sscanf(line, "%d %d", &num, &num2); + if (num != 0 && spell_info[num].name) + GET_SPELL_TYPE(this, num) = num2; + } + while (num != 0); + } + else if (!strcmp(tag, "SpMe")) + { + do + { + fbgetline(fl, line); + sscanf(line, "%d %d", &num, &num2); + if (num != 0) + GET_SPELL_MEM(this, num) = num2; + } + while (num != 0); + } + else if (!strcmp(tag, "SpTM")) + { + struct spell_mem_queue_item *qi_cur, ** qi = &MemQueue.queue; + while (*qi) + qi = &((*qi)->link); + do + { + fbgetline(fl, line); + sscanf(line, "%d", &num); + if (num != 0) + { + CREATE(qi_cur, 1); + *qi = qi_cur; + qi_cur->spellnum = num; + qi_cur->link = NULL; + qi = &qi_cur->link; + } + } + while (num != 0); + } + else if (!strcmp(tag, "Str ")) + this->set_str(num); + else if (!strcmp(tag, "StrL")) + STRING_LENGTH(this) = num; + else if (!strcmp(tag, "StrW")) + STRING_WIDTH(this) = num; + else if (!strcmp(tag, "St00")) + this->set_start_stat(G_STR, lnum); + else if (!strcmp(tag, "St01")) + this->set_start_stat(G_DEX, lnum); + else if (!strcmp(tag, "St02")) + this->set_start_stat(G_INT, lnum); + else if (!strcmp(tag, "St03")) + this->set_start_stat(G_WIS, lnum); + else if (!strcmp(tag, "St04")) + this->set_start_stat(G_CON, lnum); + else if (!strcmp(tag, "St05")) + this->set_start_stat(G_CHA, lnum); + break; + + case 'T': + if (!strcmp(tag, "Thir")) + GET_COND(this, THIRST) = num; + else if (!strcmp(tag, "Titl")) + GET_TITLE(this) = std::string(str_dup(line)); + else if (!strcmp(tag, "TrcG")) + set_ext_money(ExtMoney::TORC_GOLD, num, false); + else if (!strcmp(tag, "TrcS")) + set_ext_money(ExtMoney::TORC_SILVER, num, false); + else if (!strcmp(tag, "TrcB")) + set_ext_money(ExtMoney::TORC_BRONZE, num, false); + else if (!strcmp(tag, "TrcL")) { + sscanf(line, "%d %d", &num, &num2); + today_torc_.first = num; + today_torc_.second = num2; + } + else if (!strcmp(tag, "Tlgr")) { + if (lnum <= 10000000000000) { + this->player_specials->saved.telegram_id = lnum; + } else { // зачищаем остатки старой баги + this->player_specials->saved.telegram_id = 0; + } + } + else if (!strcmp(tag, "TSpl")) + { + do + { + fbgetline(fl, line); + sscanf(line, "%d %ld %ld", &num, &lnum, &lnum3); + if (num != 0 && spell_info[num].name) + { + Temporary_Spells::add_spell(this, num, lnum, lnum3); + } + } while (num != 0); + } + break; + + case 'W': + if (!strcmp(tag, "Wate")) + GET_WEIGHT(this) = num; + else if (!strcmp(tag, "Wimp")) + GET_WIMP_LEV(this) = num; + else if (!strcmp(tag, "Wis ")) + this->set_wis(num); +//29.11.09 (c) Василиса + else if (!strcmp(tag, "Wina")) + GET_WIN_ARENA(this) = num; +//конец правки (с) Василиса + else if (!strcmp(tag, "Wman")) + this->set_who_mana(num); + break; + + default: + sprintf(buf, "SYSERR: Unknown tag %s in pfile %s", tag, name); + } + } + PRF_FLAGS(this).set(PRF_COLOR_2); //всегда цвет полный + // initialization for imms + if (GET_LEVEL(this) >= LVL_IMMORT) + { + set_god_skills(this); + set_god_morphs(this); + GET_COND(this, FULL) = -1; + GET_COND(this, THIRST) = -1; + GET_COND(this, DRUNK) = -1; + GET_LOADROOM(this) = NOWHERE; + } + + setAllInbornFeatures(this); + + if (IS_GRGOD(this)) + { + for (i = 0; i <= MAX_SPELLS; i++) + GET_SPELL_TYPE(this, i) = GET_SPELL_TYPE(this, i) | + SPELL_ITEMS | SPELL_KNOW | SPELL_RUNES | SPELL_SCROLL | SPELL_POTION | SPELL_WAND; + } + else if (!IS_IMMORTAL(this)) + { + for (i = 0; i <= MAX_SPELLS; i++) + { + if (spell_info[i].slot_forc[(int) GET_CLASS(this)][(int) GET_KIN(this)] == MAX_SLOT) + REMOVE_BIT(GET_SPELL_TYPE(this, i), SPELL_KNOW | SPELL_TEMP); +// shapirus: изученное не убираем на всякий случай, но из мема выкидываем, +// если мортов мало + if (GET_REMORT(this) < MIN_CAST_REM(spell_info[i], this)) + GET_SPELL_MEM(this, i) = 0; + } + } + + /* + * If you're not poisioned and you've been away for more than an hour of + * real time, we'll set your HMV back to full + */ + if (!AFF_FLAGGED(this, EAffectFlag::AFF_POISON) && (((long)(time(0) - LAST_LOGON(this))) >= SECS_PER_REAL_HOUR)) + { + GET_HIT(this) = GET_REAL_MAX_HIT(this); + GET_MOVE(this) = GET_REAL_MAX_MOVE(this); + } + else + GET_HIT(this) = MIN(GET_HIT(this), GET_REAL_MAX_HIT(this)); + + fbclose(fl); + // здесь мы закладываемся на то, что при ребуте это все сейчас пропускается и это нормально, + // иначе в таблице crc будут пустые имена, т.к. сама плеер-таблица еще не сформирована + // и в любом случае при ребуте это все пересчитывать не нужно + FileCRC::check_crc(filename, FileCRC::PLAYER, GET_UNIQUE(this)); + + this->account = Account::get_account(GET_EMAIL(this)); + if (this->account == nullptr) + { + const auto temp_account = std::make_shared(GET_EMAIL(this)); + accounts.emplace(GET_EMAIL(this), temp_account); + this->account = temp_account; + } + this->account->add_player(GET_UNIQUE(this)); +} + // * Перерасчет максимальных родных хп персонажа. // * При входе в игру, левеле/делевеле, добавлении/удалении славы. void check_max_hp(CHAR_DATA *ch) diff --git a/src/chars/char_player.hpp b/src/chars/char_player.hpp index 7fb091f2c..eee4e137d 100644 --- a/src/chars/char_player.hpp +++ b/src/chars/char_player.hpp @@ -25,6 +25,7 @@ #include #include #include +#include // * Перерасчет максимальных родных хп персонажа. // * При входе в игру, левеле/делевеле, добавлении/удалении славы. @@ -107,6 +108,8 @@ class Player : public CHAR_DATA void save_char(); int load_char_ascii(const char *name, bool reboot = 0, const bool find_id = true); + // метод загрузки файла игрока напрямую + bool _pfileLoad(FBFILE *fl, bool reboot, const char* name); bool get_disposable_flag(int num); void set_disposable_flag(int num); @@ -174,7 +177,6 @@ class Player : public CHAR_DATA // метод выставления chat_id void setTelegramId(unsigned long chat_id) override; unsigned long int getTelegramId() override; - private: // показывает, является ли чар турнирным или нет bool arena_player = false; diff --git a/src/cmd/quit.cpp b/src/cmd/quit.cpp index bd9175481..7fa0ca172 100644 --- a/src/cmd/quit.cpp +++ b/src/cmd/quit.cpp @@ -57,7 +57,7 @@ void do_quit(CHAR_DATA *ch, char *argument, int/* cmd*/, int subcmd) Depot::exit_char(ch); Clan::clan_invoice(ch, false); if (ch->personGroup) - ch->personGroup->_removeMember(ch->get_uid()); + ch->personGroup->_removeMember(ch); /* * kill off all sockets connected to the same player as the one who is diff --git a/src/ext_money.cpp b/src/ext_money.cpp index 8e4c10dd9..7b1034f2d 100644 --- a/src/ext_money.cpp +++ b/src/ext_money.cpp @@ -682,7 +682,7 @@ namespace ExtMoney CHAR_DATA *leader = grp != nullptr? d->character->personGroup->getLeader() : d->character.get(); int members = 1; - members = leader->personGroup->size(IN_ROOM(mob)); + members = leader->personGroup->get_size(IN_ROOM(mob)); const int zone_lvl = zone_table[mob_index[GET_MOB_RNUM(mob)].zone].mob_level; const int drop = calc_drop_torc(zone_lvl, members); diff --git a/src/grp/grp.cmdprocessor.cpp b/src/grp/grp.cmdprocessor.cpp index fde070c04..90fa75354 100644 --- a/src/grp/grp.cmdprocessor.cpp +++ b/src/grp/grp.cmdprocessor.cpp @@ -536,11 +536,11 @@ void do_report(CHAR_DATA *ch, char* /*argument*/, int/* cmd*/, int/* subcmd*/) CHAR_DATA *k; struct follow_type *f; - if (!AFF_FLAGGED(ch, EAffectFlag::AFF_GROUP) && !AFF_FLAGGED(ch, EAffectFlag::AFF_CHARM)) - { + if (!IN_GROUP(ch) && !AFF_FLAGGED(ch, EAffectFlag::AFF_CHARM)) { send_to_char("И перед кем вы отчитываетесь?\r\n", ch); return; } + // готовим строку if (ch->is_druid()) { sprintf(buf, "%s доложил%s : %d(%d)H, %d(%d)V, %d(%d)M\r\n", @@ -552,10 +552,8 @@ void do_report(CHAR_DATA *ch, char* /*argument*/, int/* cmd*/, int/* subcmd*/) else if (AFF_FLAGGED(ch, EAffectFlag::AFF_CHARM)) { int loyalty = 0; - for (const auto& aff : ch->affected) - { - if (aff->type == SPELL_CHARM) - { + for (const auto& aff : ch->affected) { + if (aff->type == SPELL_CHARM) { loyalty = aff->duration / 2; break; } @@ -573,22 +571,22 @@ void do_report(CHAR_DATA *ch, char* /*argument*/, int/* cmd*/, int/* subcmd*/) GET_HIT(ch), GET_REAL_MAX_HIT(ch), GET_MOVE(ch), GET_REAL_MAX_MOVE(ch)); } + CAP(buf); - k = ch->has_master() ? ch->get_master() : ch; - for (f = k->followers; f; f = f->next) - { - if (AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP) - && f->follower != ch - && !AFF_FLAGGED(f->follower, EAffectFlag::AFF_DEAFNESS)) - { - send_to_char(buf, f->follower); + if (IN_GROUP(ch)) + ch->personGroup->sendToGroup(GRP_COMM_ALL, buf); + else {//чармис докладывает мастеру + k = ch->has_master() ? ch->get_master() : ch; + for (f = k->followers; f; f = f->next) { + if (!AFF_FLAGGED(f->follower, EAffectFlag::AFF_DEAFNESS) && f->follower != ch) { + send_to_char(buf, f->follower); + } + } + if (k != ch && !AFF_FLAGGED(k, EAffectFlag::AFF_DEAFNESS)) { + send_to_char(buf, k); } } - if (k != ch && !AFF_FLAGGED(k, EAffectFlag::AFF_DEAFNESS)) - { - send_to_char(buf, k); - } send_to_char("Вы доложили о состоянии всем членам вашей группы.\r\n", ch); } diff --git a/src/grp/grp.group.cpp b/src/grp/grp.group.cpp index ae1a983f6..4526cf0a9 100644 --- a/src/grp/grp.group.cpp +++ b/src/grp/grp.group.cpp @@ -155,7 +155,7 @@ void Group::addMember(CHAR_DATA *member, bool silent) { return; // в другой группе, вышвыриваем if (member->personGroup != nullptr) - _removeMember(member); + member->personGroup->_removeMember(member); auto it = this->find(_calcUID(member)); if (it == this->end()) { @@ -199,10 +199,14 @@ bool Group::_removeMember(int memberUID) { return false; } auto member = it->second->member; - if (member != nullptr) + if (member != nullptr) { + send_to_char(member, "Вы покинули группу.\r\n"); member->personGroup = nullptr; + } + sendToGroup(GRP_COMM_ALL, "Персонаж %s покинул группу.", it->second->memberName.c_str()); this->erase(memberUID); // finally remove it + // а также удаляем чармисов персонажа из групповых if (member->followers != nullptr) { for (auto f = member->followers; f; f = f->next) { @@ -239,19 +243,6 @@ bool Group::_removeMember(int memberUID) { return true; } -bool Group::_removeMember(char *name) { - int uid = 0; - for (auto &it: *this){ - if (str_cmp(name, it.second->memberName) == 0) { - uid = it.first; - break; - } - } - if (uid != 0) - return _removeMember(uid); - return false; -} - bool Group::_removeMember(CHAR_DATA *member) { int memberUID = _calcUID(member); return _removeMember(memberUID); @@ -278,7 +269,7 @@ bool Group::_isMember(int uid) { } void Group::_clear(bool silentMode) { - sprintf(buf, "[Group::clear()]: this: %hu", this->size()); + sprintf(buf, "[Group::clear()]: this: %lu", this->size()); mudlog(buf, BRF, LVL_IMMORT, SYSLOG, TRUE); CHAR_DATA* ch; // чистим игроков @@ -336,7 +327,6 @@ void Group::expellMember(char *memberName) { sendToGroup(GRP_COMM_LEADER, "Нет такого персонажа."); return; } - _removeMember(vict->memberUID); if (vict != nullptr && vict->member != nullptr) { act("$N исключен$A из состава вашей группы.", FALSE, _leader, nullptr, vict, TO_CHAR); act("Вы исключены из группы $n1!", FALSE, _leader, nullptr, vict, TO_VICT); @@ -344,11 +334,11 @@ void Group::expellMember(char *memberName) { } else { sendToGroup(GRP_COMM_LEADER, "%s был исключен из группы.'\r\n", vict->memberName.c_str()); } + _removeMember(vict->memberUID); } void Group::listMembers(CHAR_DATA *ch) { CHAR_DATA *leader; - struct follow_type *f; int count = 1; if (ch->personGroup) @@ -510,12 +500,6 @@ void Group::_printPCLine(CHAR_DATA* ch, CHAR_DATA* pc, int header) { void Group::printGroup(CHAR_DATA *ch) { int gfound = 0, cfound = 0; - CHAR_DATA *leader; - - if (ch->personGroup) - leader = ch->personGroup->getLeader(); - else - leader = ch; if (!IS_NPC(ch)) ch->desc->msdp_report(msdp::constants::GROUP); @@ -617,14 +601,14 @@ void Group::addFollowers(CHAR_DATA *leader) { for (auto f = leader->followers; f; f = f->next) { if (IS_NPC(f->follower)) continue; - if ((u_short)this->size() < (u_short)_memberCap) + if ((u_short)this->get_size() < (u_short)_memberCap) this->addMember(f->follower); } } // метод может вернуть мусор :( CHAR_DATA* Group::get_random_pc_group() { - u_short rnd = number(0, (u_short)this->size() - 1); + u_short rnd = number(0, (u_short)this->get_size() - 1); int i = 0; for (const auto& it : *this){ if (i == rnd) { @@ -635,10 +619,12 @@ CHAR_DATA* Group::get_random_pc_group() { return nullptr; } -u_short Group::size(rnum_t room_rnum) { +u_short Group::get_size(rnum_t room_rnum) { u_short retval = 0; for (const auto& it : *this){ auto c = it.second->member; + if (c == nullptr) + continue; if (room_rnum == (room_rnum == 0 ? room_rnum : c->in_room) && !c->purged() && retval < 255) retval++; } @@ -665,16 +651,19 @@ int Group::_calcUID(CHAR_DATA *ch) { } void Group::_promote(CHAR_DATA *member) { + if (_leader == member) + return; _setLeader(member); - sendToGroup(GRP_COMM_ALL, "Изменился лидер группы на %.", _leaderName.c_str() ); + sendToGroup(GRP_COMM_ALL, "Изменился лидер группы на %s.", _leaderName.c_str() ); auto diff = (u_short)_memberCap - (u_short)this->size(); - if (diff < 0) diff = 0; + if (diff < 0) diff = abs(diff); else diff = 0; if (diff > 0){ u_short i = 0; int expellList[diff]; - for (auto it = this->begin(); it!= this->end() || i > diff; it++){ + for (auto it = this->begin(); it != this->end() && i < diff; it++){ if (it->second->member != _leader ) { expellList[i] = _calcUID(it->second->member); + sendToGroup(GRP_COMM_ALL, "В группе не хватает места, нас покинул %s!", it->second->memberName.c_str() ); ++i; } } diff --git a/src/grp/grp.main.h b/src/grp/grp.main.h index da86b560e..4630bd12d 100644 --- a/src/grp/grp.main.h +++ b/src/grp/grp.main.h @@ -88,12 +88,11 @@ class Group : public grp_mt { const char* _getMemberName(int uid); char_info* _findMember(char* memberName); bool _removeMember(CHAR_DATA* ch); - bool _removeMember(int uid); - bool _removeMember(char *name); void _promote(CHAR_DATA* ch); void charDataPurged(CHAR_DATA* ch); - u_short size(rnum_t room_rnum = 0); + u_short get_size(rnum_t room_rnum = 0); private: + bool _removeMember(int uid); GM_TYPE getType(CHAR_DATA* ch) { if (IS_NPC(ch)) return GM_CHARMEE; diff --git a/src/grp/grp.roster.cpp b/src/grp/grp.roster.cpp index be83589d6..3f3413acb 100644 --- a/src/grp/grp.roster.cpp +++ b/src/grp/grp.roster.cpp @@ -65,7 +65,7 @@ void GroupRoster::processGroupCommands(CHAR_DATA *ch, char *argument) { return; } if (grp != nullptr) // в группе - покидаем - grp->_removeMember(ch->get_uid()); + grp->_removeMember(ch); groupRoster.addGroup(ch); return; } else if (isname(subcmd, strLIST.c_str())) { diff --git a/src/spells.cpp b/src/spells.cpp index c5940c63b..b03dc327a 100644 --- a/src/spells.cpp +++ b/src/spells.cpp @@ -1066,16 +1066,13 @@ void spell_charm(int/* level*/, CHAR_DATA *ch, CHAR_DATA *victim, OBJ_DATA* /* o send_to_char("Ваша магия потерпела неудачу.\r\n", ch); else { - if (!check_charmee(ch, victim, SPELL_CHARM)) - { + if (!check_charmee(ch, victim, SPELL_CHARM)){ return; } // Левая проверка - if (victim->has_master()) - { - if (stop_follower(victim, SF_MASTERDIE)) - { + if (victim->has_master()){ + if (stop_follower(victim, SF_MASTERDIE)){ return; } } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 03f9281ce..72e2dc300 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -81,7 +81,8 @@ set(UTILITIES char.utilities.cpp) set(ADDITIONAL_FILES data/boards/changelog.sample - data/boards/news.sample) + data/boards/news.sample + data/plrs/bikbaj.player) source_group("Tests data" FILES ${ADDITIONAL_FILES}) source_group("Tests" FILES ${TESTS}) source_group("Tests helpers" FILES ${UTILITIES}) @@ -95,6 +96,8 @@ endif () add_test(all tests) +file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/data/plrs/) + add_custom_target(checks COMMAND ${CMAKE_CTEST_COMMAND} -V DEPENDS tests) add_custom_target(tests.misc.grouping @@ -112,8 +115,14 @@ add_custom_target(tests.data.boards.news_sample COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/tests/data/boards/news.sample ${CMAKE_CURRENT_BINARY_DIR}/data/boards/news.sample COMMENT "Copy tests/data/boards/news.sample for tests.") set_target_properties(tests.data.boards.news_sample PROPERTIES FOLDER "Utility targets/Tests data") +add_custom_target(tests.data.plrs + DEPENDS ${CMAKE_SOURCE_DIR}/tests/data/plrs + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/tests/data/plrs/* ${CMAKE_CURRENT_BINARY_DIR}/data/plrs/ + COMMENT "Copy tests.data.plrs for tests.") +set_target_properties(tests.data.boards.changelog_sample PROPERTIES FOLDER "Utility targets/Tests data") + -add_dependencies(tests tests.misc.grouping tests.data.boards.changelog_sample tests.data.boards.news_sample) +add_dependencies(tests tests.misc.grouping tests.data.boards.changelog_sample tests.data.boards.news_sample tests.data.plrs) add_dependencies(checks tests) # vim: set ts=4 sw=4 ai tw=0 noet syntax=cmake : diff --git a/tests/char.leaders.cpp b/tests/char.leaders.cpp index 90a8b3deb..8ff22f2b3 100644 --- a/tests/char.leaders.cpp +++ b/tests/char.leaders.cpp @@ -69,26 +69,31 @@ TEST(CHAR_Leaders, SimpleLoop) } TEST(CHAR_Leaders, Group) { + std::string pName; test_utils::CharacterBuilder builder; builder.create_new(); builder.add_skill(ESkill::SKILL_LEADERSHIP, 200); auto leader = builder.get(); + test_utils::GroupBuilder g; + auto grp = g._roster->addGroup(leader.get()); + builder.create_new("F7"); builder.add_skill(ESkill::SKILL_LEADERSHIP, 10); auto f7 = builder.get(); leader->add_follower(f7.get()); + grp->addFollowers(leader.get()); + for (int i = 0; i <12; i++) { - builder.create_new(); + pName = "Player" + std::to_string(i); + builder.create_new(pName); auto follower = builder.get(); leader->add_follower(follower.get()); } - - test_utils::GroupBuilder g; - auto grp = g._roster->addGroup(leader.get()); grp->addFollowers(leader.get()); - EXPECT_EQ(12, leader->personGroup->size()); + EXPECT_EQ(0, leader->personGroup->get_size(100)); + EXPECT_EQ(11, leader->personGroup->size()); grp->promote("F7"); EXPECT_EQ(7, leader->personGroup->size()); diff --git a/tests/char.utilities.cpp b/tests/char.utilities.cpp index c85e0e56d..2e908eeab 100644 --- a/tests/char.utilities.cpp +++ b/tests/char.utilities.cpp @@ -1,157 +1,159 @@ #include "char.utilities.hpp" +#include "db.h" #include #include "chars/char.hpp" #include "chars/char_player.hpp" #include "chars/player_races.hpp" #include #include +#include "diskio.h" -namespace test_utils -{ -void CharacterBuilder::create_new() -{ - const auto result = std::make_shared(); - result->player_specials = std::make_shared(); - result->char_specials.saved.act = clear_flags; - result->char_specials.saved.affected_by = clear_flags; - result->set_class(CLASS_DRUID); - result->set_level(1); - m_result = result; -} -void CharacterBuilder::create_new_with_class(const short player_class) -{ - create_new(); - set_class(player_class); -} -void CharacterBuilder::create_character_with_one_removable_affect() +namespace test_utils { - create_new(); - add_poison(); -} + void CharacterBuilder::create_new() + { + const auto result = std::make_shared(); + char filename[40] = "data/plrs/sample.player"; + FBFILE *fl = NULL; + fl = fbopen(filename, FB_READ); + result->_pfileLoad(fl, false, "test"); + m_result = result; + sprintf(smallBuf, "Player-%d",m_uid); + m_result->set_pc_name(smallBuf); + m_result->set_uid(m_uid); + ++m_uid; + char_to_room(this->get(), NOWHERE); + _store.push_back(m_result); + } -void CharacterBuilder::create_character_with_two_removable_affects() -{ - create_character_with_one_removable_affect(); - add_sleep(); -} + void CharacterBuilder::create_new_with_class(const short player_class) + { + create_new(); + set_class(player_class); + } -void CharacterBuilder::create_character_with_two_removable_and_two_not_removable_affects() -{ - create_character_with_two_removable_affects(); - add_detect_invis(); - add_detect_align(); -} + void CharacterBuilder::create_character_with_one_removable_affect() + { + create_new(); + add_poison(); + } -void CharacterBuilder::add_poison() -{ - check_character_existance(); - - AFFECT_DATA poison; - poison.type = SPELL_POISON; - poison.modifier = 0; - poison.location = APPLY_STR; - poison.duration = pc_duration(m_result.get(), 10 * 2, 0, 0, 0, 0); - poison.bitvector = to_underlying(EAffectFlag::AFF_POISON); - poison.battleflag = AF_SAME_TIME; - affect_join(m_result.get(), poison, false, false, false, false); -} + void CharacterBuilder::create_character_with_two_removable_affects() + { + create_character_with_one_removable_affect(); + add_sleep(); + } -void CharacterBuilder::add_sleep() -{ - check_character_existance(); - - AFFECT_DATA sleep; - sleep.type = SPELL_SLEEP; - sleep.modifier = 0; - sleep.location = APPLY_AC; - sleep.duration = pc_duration(m_result.get(), 10 * 2, 0, 0, 0, 0); - sleep.bitvector = to_underlying(EAffectFlag::AFF_SLEEP); - sleep.battleflag = AF_SAME_TIME; - affect_join(m_result.get(), sleep, false, false, false, false); -} + void CharacterBuilder::create_character_with_two_removable_and_two_not_removable_affects() + { + create_character_with_two_removable_affects(); + add_detect_invis(); + add_detect_align(); + } -void CharacterBuilder::add_detect_invis() -{ - check_character_existance(); - - AFFECT_DATA detect_invis; - detect_invis.type = SPELL_DETECT_INVIS; - detect_invis.modifier = 0; - detect_invis.location = APPLY_AC; - detect_invis.duration = pc_duration(m_result.get(), 10 * 2, 0, 0, 0, 0); - detect_invis.bitvector = to_underlying(EAffectFlag::AFF_DETECT_INVIS); - detect_invis.battleflag = AF_SAME_TIME; - affect_join(m_result.get(), detect_invis, false, false, false, false); -} + void CharacterBuilder::add_poison() + { + check_character_existance(); + + AFFECT_DATA poison; + poison.type = SPELL_POISON; + poison.modifier = 0; + poison.location = APPLY_STR; + poison.duration = pc_duration(m_result.get(), 10 * 2, 0, 0, 0, 0); + poison.bitvector = to_underlying(EAffectFlag::AFF_POISON); + poison.battleflag = AF_SAME_TIME; + affect_join(m_result.get(), poison, false, false, false, false); + } -void CharacterBuilder::add_detect_align() -{ - check_character_existance(); - - AFFECT_DATA detect_align; - detect_align.type = SPELL_DETECT_ALIGN; - detect_align.modifier = 0; - detect_align.location = APPLY_AC; - detect_align.duration = pc_duration(m_result.get(), 10 * 2, 0, 0, 0, 0); - detect_align.bitvector = to_underlying(EAffectFlag::AFF_DETECT_ALIGN); - detect_align.battleflag = AF_SAME_TIME; - affect_join(m_result.get(), detect_align, false, false, false, false); -} + void CharacterBuilder::add_sleep() + { + check_character_existance(); + + AFFECT_DATA sleep; + sleep.type = SPELL_SLEEP; + sleep.modifier = 0; + sleep.location = APPLY_AC; + sleep.duration = pc_duration(m_result.get(), 10 * 2, 0, 0, 0, 0); + sleep.bitvector = to_underlying(EAffectFlag::AFF_SLEEP); + sleep.battleflag = AF_SAME_TIME; + affect_join(m_result.get(), sleep, false, false, false, false); + } -void CharacterBuilder::set_level(const int level) -{ - m_result->set_level(level); -} + void CharacterBuilder::add_detect_invis() + { + check_character_existance(); + + AFFECT_DATA detect_invis; + detect_invis.type = SPELL_DETECT_INVIS; + detect_invis.modifier = 0; + detect_invis.location = APPLY_AC; + detect_invis.duration = pc_duration(m_result.get(), 10 * 2, 0, 0, 0, 0); + detect_invis.bitvector = to_underlying(EAffectFlag::AFF_DETECT_INVIS); + detect_invis.battleflag = AF_SAME_TIME; + affect_join(m_result.get(), detect_invis, false, false, false, false); + } -void CharacterBuilder::set_class(const short player_class) -{ - m_result->set_class(player_class); -} + void CharacterBuilder::add_detect_align() + { + check_character_existance(); + + AFFECT_DATA detect_align; + detect_align.type = SPELL_DETECT_ALIGN; + detect_align.modifier = 0; + detect_align.location = APPLY_AC; + detect_align.duration = pc_duration(m_result.get(), 10 * 2, 0, 0, 0, 0); + detect_align.bitvector = to_underlying(EAffectFlag::AFF_DETECT_ALIGN); + detect_align.battleflag = AF_SAME_TIME; + affect_join(m_result.get(), detect_align, false, false, false, false); + } -void CharacterBuilder::make_group(CharacterBuilder& character_builder) -{ - check_character_existance(); - check_character_existance(character_builder.get()); + void CharacterBuilder::set_level(const int level) + { + m_result->set_level(level); + } - auto character = character_builder.get(); + void CharacterBuilder::set_class(const short player_class) + { + m_result->set_class(player_class); + } - m_result->add_follower_silently(character.get()); + void CharacterBuilder::make_group(CharacterBuilder& character_builder) + { + check_character_existance(); + check_character_existance(character_builder.get()); - perform_group(m_result.get(), m_result.get()); - perform_group(m_result.get(), character.get()); -} + auto character = character_builder.get(); -void CharacterBuilder::check_character_existance() const -{ - check_character_existance(m_result); -} + m_result->add_follower_silently(character.get()); -void CharacterBuilder::check_character_existance(result_t character) -{ - if (!character) - { - throw std::runtime_error("Character wasn't created."); - } -} + perform_group(m_result.get(), m_result.get()); + perform_group(m_result.get(), character.get()); + } - void CharacterBuilder::add_skill(ESkill skill, short value) { - m_result->set_skill(skill, value); + void CharacterBuilder::check_character_existance() const + { + check_character_existance(m_result); } - void CharacterBuilder::load_player(u_short idx) { - getcwd(buf, 3000); - Player* t_vict = new Player(); - if (load_char(_names[idx].c_str(), t_vict) == -1) + void CharacterBuilder::check_character_existance(result_t character) + { + if (!character) + { throw std::runtime_error("Character wasn't created."); - m_result = std::shared_ptr(t_vict); + } + } + + void CharacterBuilder::add_skill(ESkill skill, short value) { + m_result->set_skill(skill, value); } - void CharacterBuilder::create_new(char *name) { + void CharacterBuilder::create_new(std::string name) { create_new(); m_result->set_pc_name(name); + m_result->set_passwd(name); } GroupBuilder::GroupBuilder() { diff --git a/tests/char.utilities.hpp b/tests/char.utilities.hpp index 49ac07b41..f5d1c5742 100644 --- a/tests/char.utilities.hpp +++ b/tests/char.utilities.hpp @@ -12,8 +12,10 @@ namespace test_utils using character_t = CHAR_DATA; using result_t = character_t::shared_ptr; - void create_new(); - void create_new(char* name); + CharacterBuilder() {m_uid = 1;} + + void create_new(); + void create_new(std::string name); void load_player(u_short idx); void create_new_with_class(const short player_class); void create_character_with_one_removable_affect(); @@ -36,10 +38,9 @@ namespace test_utils void check_character_existance() const; static void check_character_existance(result_t character); - + int m_uid = 1; result_t m_result; - const std::string _names[13] = {"Бикбай", "Первый", "Второй", "третий", "четвертый", "пятый", "шестой", - "седьмой", "восьмой", "девятый", "десятый", "одиннадцатый", "двеннадцатый"}; + std::vector _store; }; class GroupBuilder { diff --git a/tests/data/plrs/sample.player b/tests/data/plrs/sample.player new file mode 100644 index 000000000..a6787d68b --- /dev/null +++ b/tests/data/plrs/sample.player @@ -0,0 +1,136 @@ +Name: Бикбай +Levl: 34 +Clas: 1 +UIN : 1009969084 +LstL: 1610053558 +Host: 192.168.204.1 +Id : 1 +Exp : 2000000000 +Act : C0D0 +EMal: bikbai@mail +Rebt: следующие далее поля при перезагрузке не парсятся + +NmI : Бикбай +NmR : Бикбая +NmD : Бикбаю +NmV : Бикбая +NmT : Бикбаем +NmP : Бикбаи +Pass: 12345 +Sex : 1 мужчина +Kin : 0 Русич +Brth: 1601408138 Tue Sep 29 22:35:38 2020 + +Plyd: 366111 +Last: 1610053558 Fri Jan 8 00:05:58 2021 + +Hite: 178 +Wate: 130 +Size: 50 +Alin: 0 +Aff : k0 +Str : 25 +Int : 25 +Wis : 25 +Dex : 25 +Con : 25 +Cha : 25 +Cits: 1 +Rcps: +-1 -1 +Hrol: 0 +Drol: 0 +Ac : 100 +Hry : 0 +Hit : 630/630 +Mana: 0/0 +Move: 82/82 +Gold: 0 +Bank: 0 +ICur: 0 +Ruble: 0 +Wimp: 0 +Frez: 0 +Invs: 0 +Room: 0 +Lexc: -252316953 +Badp: 0 +Br01: 1601408097 +Br02: 1601408097 +Br03: 1601408097 +Br04: 1601408097 +Br05: 1601408097 +Br06: 1601408097 +Br07: 1601408097 +Br08: 1601408097 +Br09: 1601408097 +Br10: 1601408097 +Br11: 1601408097 +Br12: 1601408097 +Br13: 1601408097 +Br14: 1601408097 +Br15: 1601408097 +Br16: 1601408097 +St00: 11 +St01: 10 +St02: 24 +St03: 23 +St04: 12 +St05: 15 +Reli: 1 Христианин +Race: 2 Кривич +DrSt: 0 +Olc : 0 +Pref: e0f0g0o0u0w0x0c2 +LogL: +192.168.204.1 224 1610053558 +~ +GdFl: 0 +NamG: 1034 +NaID: -1 +StrL: 80 +StrW: 30 +NtfE: 0 +Logs: 0 992872272 +Logs: 1 22041 +Logs: 2 34478 +Logs: 3 0 +Logs: 4 49 +Disp: 0 +Ripa: 0 +Wina: 0 +Expa: 0 +Ripm: 0 +Expm: 0 +Ripd: 0 +Expd: 0 +Ripo: 0 +Expo: 0 +Ripp: 0 +Expp: 0 +Rimt: 0 +Exmt: 0 +Ridt: 0 +Exdt: 0 +Riot: 0 +Exot: 0 +Ript: 0 +Expt: 0 +Wman: 6000 +Mobs: +~ +Pkil: +~ +Mrph: #MORPH_FALCON#MORPH_LYNX#MORPH_PIKE#MORPH_WOLF +Map : 000000000000000000000 +TrcG: 0 +TrcS: 0 +TrcB: 0 +TrcL: 0 0 +Chrm: +4010 1 0 0 1 1 +4019 1 0 0 1 1 +4020 6 0 0 1 1 +4021 18 0 0 1 1 +0 0 0 0 0 0 +Tlgr: 0 From 7dd96d40feed0a8938e70e372bee398b58b1aef2 Mon Sep 17 00:00:00 2001 From: bikbai Date: Sat, 16 Jan 2021 22:07:21 +0300 Subject: [PATCH 22/40] =?UTF-8?q?=D0=9F=D1=80=D0=BE=D0=BC=D0=B5=D0=B6?= =?UTF-8?q?=D1=83=D1=82=D0=BE=D1=87=D0=BD=D1=8B=D0=B9=20=D1=87=D0=B5=D0=BA?= =?UTF-8?q?=D0=B8=D0=BD,=20=D0=BD=D0=B5=20=D0=BA=D0=BE=D0=BC=D0=BF=D0=B8?= =?UTF-8?q?=D0=BB=D0=B8=D1=80=D1=83=D0=B5=D1=82=D1=81=D1=8F.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + CMakeLists.txt | 4 +- src/act.other.hpp | 2 - src/action.targeting.cpp | 2 +- src/chars/char.cpp | 19 +- src/chars/char.hpp | 1 - src/chars/char_player.cpp | 640 +++++++++++++---------------- src/chars/char_player.hpp | 4 +- src/cmd.imm/act.wizard.cpp | 3 +- src/core/leveling.cpp | 125 +++++- src/core/leveling.h | 1 + src/db.cpp | 2 +- src/fightsystem/common.cpp | 6 +- src/fightsystem/fight_hit.cpp | 177 +++----- src/fightsystem/fight_hit.hpp | 1 - src/fightsystem/fight_stuff.cpp | 56 +-- src/fightsystem/mobact.cpp | 2 +- src/fightsystem/start.fight.cpp | 2 +- src/grp/follow.cpp | 1 - src/grp/grouppenaltycalculator.cpp | 90 ---- src/grp/grp.cmdprocessor.cpp | 445 ++------------------ src/grp/grp.group.cpp | 365 +++++----------- src/grp/grp.main.h | 87 ++-- src/grp/grp.roster.cpp | 22 +- src/handler.cpp | 1 - src/interpreter.cpp | 3 +- src/skills/chopoff.cpp | 2 +- src/skills/strangle.cpp | 2 +- src/structs.h | 2 +- src/utils.cpp | 16 +- src/utils.h | 4 +- tests/CMakeLists.txt | 2 +- tests/char.utilities.cpp | 4 +- 33 files changed, 742 insertions(+), 1352 deletions(-) delete mode 100644 src/grp/grouppenaltycalculator.cpp diff --git a/.gitignore b/.gitignore index 234b94fd3..494c27cdb 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ libcircle.library.a lib/ tests/CTestTestfile.cmake cmake-build-release/ +/cmake-build-tdebug/ diff --git a/CMakeLists.txt b/CMakeLists.txt index ac032fa2b..2e5c66db0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -212,7 +212,7 @@ set(SOURCES src/zedit.cpp src/zone.table.cpp src/core/leveling.cpp - src/grp/grouppenalties.cpp src/grp/grouppenaltycalculator.cpp) + src/grp/grouppenalties.cpp) set(HEADERS src/core/leveling.h @@ -754,7 +754,7 @@ endif () if (CMAKE_HOST_UNIX) if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang") set(CMAKE_CXX_FLAGS_RELEASE "-ggdb3 -O0 -Wall -Wextra") - set(CMAKE_CXX_FLAGS_DEBUG "-ggdb3 -O0 -Wall -Wextra -D_GLIBCXX_DEBUG -D_GLIBXX_DEBUG_PEDANTIC ${ASAN_FLAGS} ${DEBUG_CRYPT} ${TESTBUILD_DEFINITIONS} -DLOG_AUTOFLUSH") + set(CMAKE_CXX_FLAGS_DEBUG "-ggdb3 -O0 -Wall -Wextra -D_GLIBCXX_DEBUG -D_GLIBXX_DEBUG_PEDANTIC ${TESTBUILD_DEFINITIONS} ${ASAN_FLAGS} ${DEBUG_CRYPT} -DLOG_AUTOFLUSH") set(CMAKE_CXX_FLAGS_TEST " -Og -Wall -Wextra ${TESTBUILD_DEFINITIONS} -DLOG_AUTOFLUSH") set(CMAKE_CXX_FLAGS_FASTTEST " -Ofast -Wall -Wextra ${TESTBUILD_DEFINITIONS}") diff --git a/src/act.other.hpp b/src/act.other.hpp index b5aac084b..09e4e72be 100644 --- a/src/act.other.hpp +++ b/src/act.other.hpp @@ -3,8 +3,6 @@ class CHAR_DATA; // to avoid inclusion of "char.hpp" -int perform_group(CHAR_DATA * ch, CHAR_DATA * vict); - #endif // __ACT_OTHER_HPP__ // vim: ts=4 sw=4 tw=0 noet syntax=cpp : diff --git a/src/action.targeting.cpp b/src/action.targeting.cpp index c93ac1d24..0d09ccf44 100644 --- a/src/action.targeting.cpp +++ b/src/action.targeting.cpp @@ -34,7 +34,7 @@ namespace ActionTargeting { const FilterType emptyFilter; const FilterType isNotCorrectTarget = [](CHAR_DATA* actor, CHAR_DATA* target) { - return (!HERE(target) || IN_ROOM(target) == NOWHERE || !actor->isInSameRoom(target)); + return (!HERE(target) || IN_ROOM(target) == NOWHERE || !SAME_ROOM(actor, target)); }; const FilterType isCorrectFriend = [](CHAR_DATA *actor, CHAR_DATA *target) { diff --git a/src/chars/char.cpp b/src/chars/char.cpp index 36ec613d1..76f24747e 100644 --- a/src/chars/char.cpp +++ b/src/chars/char.cpp @@ -41,6 +41,8 @@ #include "grp/grp.main.h" +extern GroupRoster& groupRoster; + std::string PlayerI::empty_const_str; MapSystem::Options PlayerI::empty_map_options; @@ -613,6 +615,7 @@ void CHAR_DATA::purge() // чистим указатель в групе if (this->personGroup != nullptr) this->personGroup->charDataPurged(this); + groupRoster.charDataPurged(this); } // * Скилл с учетом всех плюсов и минусов от шмоток/яда. @@ -969,16 +972,12 @@ bool AWAKE(const CHAR_DATA* ch) bool OK_GAIN_EXP(const CHAR_DATA* ch, const CHAR_DATA* victim) { - return !NAME_BAD(ch) - && (NAME_FINE(ch) - || !(GET_LEVEL(ch) == NAME_LEVEL)) - && !ROOM_FLAGGED(ch->in_room, ROOM_ARENA) - && IS_NPC(victim) - && (GET_EXP(victim) > 0) - && (!IS_NPC(victim) - || !IS_NPC(ch) - || AFF_FLAGGED(ch, EAffectFlag::AFF_CHARM)) - && !IS_HORSE(victim); + return !NAME_BAD(ch) + && (NAME_FINE(ch) || !(GET_LEVEL(ch) == NAME_LEVEL)) + && !ROOM_FLAGGED(ch->in_room, ROOM_ARENA) + && IS_NPC(victim) + && (GET_EXP(victim) > 0) + && !IS_CHARMICE(victim); } bool IS_MALE(const CHAR_DATA* ch) diff --git a/src/chars/char.hpp b/src/chars/char.hpp index 6033a12a0..8b0cc56b1 100644 --- a/src/chars/char.hpp +++ b/src/chars/char.hpp @@ -757,7 +757,6 @@ class CHAR_DATA : public ProtectedCharacterData int souls; public: - bool isInSameRoom(const CHAR_DATA *ch) const {return SAME_ROOM(this, ch);}; room_rnum in_room; // Location (real room number) private: diff --git a/src/chars/char_player.cpp b/src/chars/char_player.cpp index 9d5155408..9a7174325 100644 --- a/src/chars/char_player.cpp +++ b/src/chars/char_player.cpp @@ -1057,49 +1057,6 @@ void Player::save_char() #undef NO_EXTRANEOUS_TRIGGERS -// на счет reboot: используется только при старте мада в вызовах из entrycount -// при включенном флаге файл читается только до поля Rebt, все остальные поля пропускаются -// поэтому при каких-то изменениях в entrycount, must_be_deleted и TopPlayer::Refresh следует -// убедиться, что изменный код работает с действительно проинициализированными полями персонажа -// на данный момент это: PLR_FLAGS, GET_CLASS, GET_EXP, GET_IDNUM, LAST_LOGON, GET_LEVEL, GET_NAME, GET_REMORT, GET_UNIQUE, GET_EMAIL -// * \param reboot - по дефолту = false -int Player::load_char_ascii(const char *name, bool reboot, const bool find_id /*= true*/) -{ - int id, num = 0, num2 = 0, num3 = 0, num4 = 0, num5 = 0, num6 = 0, i; - long int lnum = 0, lnum3 = 0; - unsigned long long llnum = 0; - FBFILE *fl = NULL; - char filename[40]; - char buf[MAX_RAW_INPUT_LENGTH], line[MAX_RAW_INPUT_LENGTH], tag[6]; - char line1[MAX_RAW_INPUT_LENGTH]; - struct timed_type timed; - - *filename = '\0'; - log("Load ascii char %s", name); - if (!find_id) - { - id = 1; - } - else - { - id = find_name(name); - } - - bool result = id >= 0; - result = result && get_filename(name, filename, PLAYERS_FILE); - result = result && (fl = fbopen(filename, FB_READ)); - if (!result) - { - const std::size_t BUFFER_SIZE = 1024; - char buffer[BUFFER_SIZE]; - log("Can't load ascii. ID: %d; File name: \"%s\"; Current directory: \"%s\")", id, filename, getcwd(buffer, BUFFER_SIZE)); - return -1; - } - if (!_pfileLoad(fl, reboot, name)); - return -1; - return id; -} - bool Player::get_disposable_flag(int num) { if (num < 0 || num >= DIS_TOTAL_NUM) @@ -1455,7 +1412,132 @@ unsigned long int Player::getTelegramId() { return this->player_specials->saved.telegram_id; } -bool Player::_pfileLoad(FBFILE *fl, bool reboot, const char* name) { +void Player::initPlayerFields() {// character init +// initializations necessary to keep some things straight + int i = 0; + set_npc_name(0); + player_data.long_descr = ""; + + real_abils.Feats.reset(); + + // волхвам сетим все спеллы на рунах, остальные инит нулями + if (GET_CLASS(this) != CLASS_DRUID) + for (i = 1; i <= MAX_SPELLS; i++) + GET_SPELL_TYPE(this, i) = 0; + else + for (i = 1; i <= MAX_SPELLS; i++) + GET_SPELL_TYPE(this, i) = SPELL_RUNES; + + for (i = 1; i <= MAX_SPELLS; i++) + GET_SPELL_MEM(this, i) = 0; + char_specials.saved.affected_by = clear_flags; + POOFIN(this) = NULL; + POOFOUT(this) = NULL; + GET_RSKILL(this) = NULL; // рецептов не знает + char_specials.carry_weight = 0; + char_specials.carry_items = 0; + real_abils.armor = 100; + GET_MEM_TOTAL(this) = 0; + GET_MEM_COMPLETED(this) = 0; + MemQ_init(this); + + GET_AC(this) = 10; + GET_ALIGNMENT(this) = 0; + GET_BAD_PWS(this) = 0; + player_data.time.birth = time(0); + GET_KIN(this) = 0; + + set_str(10); + set_dex(10); + set_con(10); + set_int(10); + set_wis(10); + set_cha(10); + + GET_COND(this, DRUNK) = 0; + GET_DRUNK_STATE(this) = 0; + +// Punish Init + DUMB_DURATION(this) = 0; + DUMB_REASON(this) = 0; + GET_DUMB_LEV(this) = 0; + DUMB_GODID(this) = 0; + + MUTE_DURATION(this) = 0; + MUTE_REASON(this) = 0; + GET_MUTE_LEV(this) = 0; + MUTE_GODID(this) = 0; + + HELL_DURATION(this) = 0; + HELL_REASON(this) = 0; + GET_HELL_LEV(this) = 0; + HELL_GODID(this) = 0; + + FREEZE_DURATION(this) = 0; + FREEZE_REASON(this) = 0; + GET_FREEZE_LEV(this) = 0; + FREEZE_GODID(this) = 0; + + GCURSE_DURATION(this) = 0; + GCURSE_REASON(this) = 0; + GET_GCURSE_LEV(this) = 0; + GCURSE_GODID(this) = 0; + + NAME_DURATION(this) = 0; + NAME_REASON(this) = 0; + GET_NAME_LEV(this) = 0; + NAME_GODID(this) = 0; + + UNREG_DURATION(this) = 0; + UNREG_REASON(this) = 0; + GET_UNREG_LEV(this) = 0; + UNREG_GODID(this) = 0; + +// End punish init + + GET_DR(this) = 0; + + set_gold(0, false); + set_bank(0, false); + set_ruble(0); + player_specials->saved.GodsLike = 0; + GET_HIT(this) = 21; + GET_MAX_HIT(this) = 21; + GET_HEIGHT(this) = 50; + GET_HR(this) = 0; + GET_COND(this, FULL) = 0; + SET_INVIS_LEV(this, 0); + player_data.time.logon = time(0); + GET_MOVE(this) = 44; + GET_MAX_MOVE(this) = 44; + KARMA(this) = 0; + LOGON_LIST(this).clear(); + NAME_GOD(this) = 0; + STRING_LENGTH(this) = 80; + STRING_WIDTH(this) = 30; + NAME_ID_GOD(this) = 0; + GET_OLC_ZONE(this) = 0; + player_data.time.played = 0; + GET_LOADROOM(this) = NOWHERE; + GET_RELIGION(this) = 1; + GET_RACE(this) = 1; + set_sex(ESex::SEX_NEUTRAL); + GET_COND(this, THIRST) = NORM_COND_VALUE; + GET_WEIGHT(this) = 50; + GET_WIMP_LEV(this) = 0; + PRF_FLAGS(this).from_string(""); // suspicious line: we should clear flags.. Loading from "" does not clear flags. + AFF_FLAGS(this).from_string(""); // suspicious line: we should clear flags.. Loading from "" does not clear flags. + GET_PORTALS(this) = NULL; + EXCHANGE_FILTER(this) = NULL; + clear_ignores(); + CREATE(GET_LOGS(this), 1 + LAST_LOG); + NOTIFY_EXCH_PRICE(this) = 0; + player_specials->saved.HiredCost = 0; + set_who_mana(WHO_MANA_MAX); + set_who_last(time(0)); +} + +int Player::_pfileLoad(FBFILE *fl, bool reboot, const char* name) { int num = 0, num2 = 0, num3 = 0, num4 = 0, num5 = 0, num6 = 0, i; long int lnum = 0, lnum3 = 0; unsigned long long llnum = 0; @@ -1486,83 +1568,81 @@ bool Player::_pfileLoad(FBFILE *fl, bool reboot, const char* name) { do { - if (!fbgetline(fl, line)) - { + if (!fbgetline(fl, line)) { log("SYSERROR: Wrong file ascii %d %s", id, filename); return (-1); } tag_argument(line, tag); - for (i = 0; !(line[i] == ' ' || line[i] == '\0'); i++) - { + for (i = 0; !(line[i] == ' ' || line[i] == '\0'); i++) { line1[i] = line[i]; } line1[i] = '\0'; num = atoi(line1); lnum = atol(line1); - try - { + try { llnum = boost::lexical_cast(line1); } - catch(boost::bad_lexical_cast &) - { + catch(boost::bad_lexical_cast &) { llnum = 0; } - - - switch (*tag) - { - case 'A': - if (!strcmp(tag, "Act ")) + // reboot header + switch (*tag) { + case 'N': { + if (!strcmp(tag, "Name")) { - PLR_FLAGS(this).from_string(line); + set_name(line); } break; - case 'C': - if (!strcmp(tag, "Clas")) + } + case 'L': { + if (!strcmp(tag, "LstL")) { - set_class(num); + set_last_logon(lnum); } - break; - case 'E': - if (!strcmp(tag, "Exp ")) + else if (!strcmp(tag, "Levl")) { - set_exp(lnum); + set_level(num); } - //added by WorM 2010.08.27 лоадим мыло и айпи даже при ребуте - else if (!strcmp(tag, "EMal")) - strcpy(GET_EMAIL(this), line); break; - case 'H': - if (!strcmp(tag, "Host")) - { + } + case 'C': { + if (!strcmp(tag, "Clas")) + set_class(num); + break; + } + case 'U': { + if (!strcmp(tag, "UIN ")) { + set_uid(num); + } + break; + } + case 'H': { + if (!strcmp(tag, "Host")) { strcpy(GET_LASTIP(this), line); } - //end by WorM break; - case 'I': - if (!strcmp(tag, "Id ")) - { + } + case 'I': { + if (!strcmp(tag, "Id ")) { set_idnum(lnum); } break; - case 'L': - if (!strcmp(tag, "LstL")) - { - set_last_logon(lnum); - } - else if (!strcmp(tag, "Levl")) - { - set_level(num); + } + case 'E': { + if (!strcmp(tag, "Exp ")) { + set_exp(lnum); } + else if (!strcmp(tag, "EMal")) + strcpy(GET_EMAIL(this), line); break; - case 'N': - if (!strcmp(tag, "Name")) - { - set_name(line); - } + } + case 'A': { + if (!strcmp(tag, "Act ")) + PLR_FLAGS(this).from_string(line); break; - case 'R': + } + case 'R': { if (!strcmp(tag, "Rebt")) skip_file = 1; else if (!strcmp(tag, "Rmrt")) @@ -1570,19 +1650,13 @@ bool Player::_pfileLoad(FBFILE *fl, bool reboot, const char* name) { set_remort(num); } break; - case 'U': - if (!strcmp(tag, "UIN ")) - { - set_uid(num); - } - break; + } default: sprintf(buf, "SYSERR: Unknown tag %s in pfile %s", tag, name); } } while (!skip_file); - //added by WorM 2010.08.27 лоадим мыло и последний ip даже при считывании индексов while((reboot) && (!*GET_EMAIL(this) || !*GET_LASTIP(this))) { if (!fbgetline(fl, line)) @@ -1598,18 +1672,14 @@ bool Player::_pfileLoad(FBFILE *fl, bool reboot, const char* name) { else if (!strcmp(tag, "Host")) strcpy(GET_LASTIP(this), line); } - //end by WorM - // если с загруженными выше полями что-то хочется делать после лоада - делайте это здесь - //Indexing experience - if his exp is lover than required for his level - set it to required - if (GET_EXP(this) < ExpCalc::level_exp(this, GET_LEVEL(this))) - { + //Indexing experience - if his exp is lower than required for his level - set it to required + if (GET_EXP(this) < ExpCalc::level_exp(this, GET_LEVEL(this))) { set_exp(ExpCalc::level_exp(this, GET_LEVEL(this))); } - if (reboot) - { + if (reboot) { fbclose(fl); return id; } @@ -1617,157 +1687,29 @@ bool Player::_pfileLoad(FBFILE *fl, bool reboot, const char* name) { // если происходит обычный лоад плеера, то читаем файл дальше и иним все остальные поля /////////////////////////////////////////////////////////////////////////////// - - - // character init - // initializations necessary to keep some things straight - - this->set_npc_name(0); - this->player_data.long_descr = ""; - - this->real_abils.Feats.reset(); - - // волхвам сетим все спеллы на рунах, остальные инит нулями - if (GET_CLASS(this) != CLASS_DRUID) - for (i = 1; i <= MAX_SPELLS; i++) - GET_SPELL_TYPE(this, i) = 0; - else - for (i = 1; i <= MAX_SPELLS; i++) - GET_SPELL_TYPE(this, i) = SPELL_RUNES; - - for (i = 1; i <= MAX_SPELLS; i++) - GET_SPELL_MEM(this, i) = 0; - this->char_specials.saved.affected_by = clear_flags; - POOFIN(this) = NULL; - POOFOUT(this) = NULL; - GET_RSKILL(this) = NULL; // рецептов не знает - this->char_specials.carry_weight = 0; - this->char_specials.carry_items = 0; - this->real_abils.armor = 100; - GET_MEM_TOTAL(this) = 0; - GET_MEM_COMPLETED(this) = 0; - MemQ_init(this); - - GET_AC(this) = 10; - GET_ALIGNMENT(this) = 0; - GET_BAD_PWS(this) = 0; - this->player_data.time.birth = time(0); - GET_KIN(this) = 0; - - this->set_str(10); - this->set_dex(10); - this->set_con(10); - this->set_int(10); - this->set_wis(10); - this ->set_cha(10); - - GET_COND(this, DRUNK) = 0; - GET_DRUNK_STATE(this) = 0; - -// Punish Init - DUMB_DURATION(this) = 0; - DUMB_REASON(this) = 0; - GET_DUMB_LEV(this) = 0; - DUMB_GODID(this) = 0; - - MUTE_DURATION(this) = 0; - MUTE_REASON(this) = 0; - GET_MUTE_LEV(this) = 0; - MUTE_GODID(this) = 0; - - HELL_DURATION(this) = 0; - HELL_REASON(this) = 0; - GET_HELL_LEV(this) = 0; - HELL_GODID(this) = 0; - - FREEZE_DURATION(this) = 0; - FREEZE_REASON(this) = 0; - GET_FREEZE_LEV(this) = 0; - FREEZE_GODID(this) = 0; - - GCURSE_DURATION(this) = 0; - GCURSE_REASON(this) = 0; - GET_GCURSE_LEV(this) = 0; - GCURSE_GODID(this) = 0; - - NAME_DURATION(this) = 0; - NAME_REASON(this) = 0; - GET_NAME_LEV(this) = 0; - NAME_GODID(this) = 0; - - UNREG_DURATION(this) = 0; - UNREG_REASON(this) = 0; - GET_UNREG_LEV(this) = 0; - UNREG_GODID(this) = 0; - -// End punish init - - GET_DR(this) = 0; - - set_gold(0, false); - set_bank(0, false); - set_ruble(0); - this->player_specials->saved.GodsLike = 0; - GET_HIT(this) = 21; - GET_MAX_HIT(this) = 21; - GET_HEIGHT(this) = 50; - GET_HR(this) = 0; - GET_COND(this, FULL) = 0; - SET_INVIS_LEV(this, 0); - this->player_data.time.logon = time(0); - GET_MOVE(this) = 44; - GET_MAX_MOVE(this) = 44; - KARMA(this) = 0; - LOGON_LIST(this).clear(); - NAME_GOD(this) = 0; - STRING_LENGTH(this) = 80; - STRING_WIDTH(this) = 30; - NAME_ID_GOD(this) = 0; - GET_OLC_ZONE(this) = 0; - this->player_data.time.played = 0; - GET_LOADROOM(this) = NOWHERE; - GET_RELIGION(this) = 1; - GET_RACE(this) = 1; - this->set_sex(ESex::SEX_NEUTRAL); - GET_COND(this, THIRST) = NORM_COND_VALUE; - GET_WEIGHT(this) = 50; - GET_WIMP_LEV(this) = 0; - PRF_FLAGS(this).from_string(""); // suspicious line: we should clear flags.. Loading from "" does not clear flags. - AFF_FLAGS(this).from_string(""); // suspicious line: we should clear flags.. Loading from "" does not clear flags. - GET_PORTALS(this) = NULL; - EXCHANGE_FILTER(this) = NULL; - clear_ignores(); - CREATE(GET_LOGS(this), 1 + LAST_LOG); - NOTIFY_EXCH_PRICE(this) = 0; - this->player_specials->saved.HiredCost = 0; - this->set_who_mana(WHO_MANA_MAX); - this->set_who_last(time(0)); + initPlayerFields(); while (fbgetline(fl, line)) { tag_argument(line, tag); - for (i = 0; !(line[i] == ' ' || line[i] == '\0'); i++) - { + for (i = 0; !(line[i] == ' ' || line[i] == '\0'); i++){ line1[i] = line[i]; } line1[i] = '\0'; num = atoi(line1); lnum = atol(line1); - try - { + try { llnum = std::stoull(line1, nullptr, 10); } - catch (const std::invalid_argument &) - { + catch (const std::invalid_argument &) { llnum = 0; } - catch (const std::out_of_range &) - { + catch (const std::out_of_range &) { llnum = 0; } switch (*tag) { - case 'A': + case 'A':{ if (!strcmp(tag, "Ac ")) { GET_AC(this) = num; @@ -1807,8 +1749,8 @@ bool Player::_pfileLoad(FBFILE *fl, bool reboot, const char* name) { GET_ALIGNMENT(this) = num; } break; - - case 'B': + } + case 'B': { if (!strcmp(tag, "Badp")) { GET_BAD_PWS(this) = num; @@ -1853,8 +1795,8 @@ bool Player::_pfileLoad(FBFILE *fl, bool reboot, const char* name) { else if (!strcmp(tag, "Brth")) this->player_data.time.birth = lnum; break; - - case 'C': + } + case 'C':{ if (!strcmp(tag, "Cha ")) this->set_cha(num); else if ( !strcmp(tag, "Chrm") ) { @@ -1900,8 +1842,8 @@ bool Player::_pfileLoad(FBFILE *fl, bool reboot, const char* name) { this->str_to_cities(std::string(buffer_cities)); } break; - - case 'D': + } + case 'D': { if (!strcmp(tag, "Desc")) { const auto ptr = fbgetstring(fl); @@ -1935,14 +1877,12 @@ bool Player::_pfileLoad(FBFILE *fl, bool reboot, const char* name) { } break; - - case 'E': + } + case 'E': { if (!strcmp(tag, "ExFl")) EXCHANGE_FILTER(this) = str_dup(line); else if (!strcmp(tag, "EMal")) strcpy(GET_EMAIL(this), line); -//29.11.09. (c) Василиса -//edited by WorM 2011.05.21 else if (!strcmp(tag, "Expa")) GET_EXP_ARENA(this) = llnum; else if (!strcmp(tag, "Expm")) @@ -1961,11 +1901,9 @@ bool Player::_pfileLoad(FBFILE *fl, bool reboot, const char* name) { GET_EXP_DT(this) = llnum; else if (!strcmp(tag, "Exdt")) GET_EXP_DTTHIS(this) = llnum; -//end by WorM -//Конец правки (с) Василиса break; - - case 'F': + } + case 'F': { // Оставлено для совместимости со старым форматом наказаний if (!strcmp(tag, "Frez")) GET_FREEZE_LEV(this) = num; @@ -1997,8 +1935,8 @@ bool Player::_pfileLoad(FBFILE *fl, bool reboot, const char* name) { while (num != 0); } break; - - case 'G': + } + case 'G': { if (!strcmp(tag, "Gold")) { set_gold(lnum, false); @@ -2017,8 +1955,8 @@ bool Player::_pfileLoad(FBFILE *fl, bool reboot, const char* name) { } // end by WorM break; - - case 'H': + } + case 'H': { if (!strcmp(tag, "Hit ")) { sscanf(line, "%d/%d", &num, &num2); @@ -2040,8 +1978,8 @@ bool Player::_pfileLoad(FBFILE *fl, bool reboot, const char* name) { else if (!strcmp(tag, "Host")) strcpy(GET_LASTIP(this), line); break; - - case 'I': + } + case 'I': { if (!strcmp(tag, "Int ")) this->set_int(num); else if (!strcmp(tag, "Invs")) @@ -2059,14 +1997,15 @@ bool Player::_pfileLoad(FBFILE *fl, bool reboot, const char* name) { // this->set_ice_currency(0); // чистка льда } break; - - case 'K': + } + case 'K': { if (!strcmp(tag, "Kin ")) GET_KIN(this) = num; else if (!strcmp(tag, "Karm")) KARMA(this) = fbgetstring(fl); break; - case 'L': + } + case 'L': { if (!strcmp(tag, "LogL")) { long lnum, lnum2; @@ -2093,7 +2032,6 @@ bool Player::_pfileLoad(FBFILE *fl, bool reboot, const char* name) { }); } } -// Gunner else if (!strcmp(tag, "Logs")) { sscanf(line, "%d %d", &num, &num2); @@ -2103,8 +2041,8 @@ bool Player::_pfileLoad(FBFILE *fl, bool reboot, const char* name) { else if (!strcmp(tag, "Lexc")) this->set_last_exchange(num); break; - - case 'M': + } + case 'M': { if (!strcmp(tag, "Mana")) { sscanf(line, "%d/%d", &num, &num2); @@ -2141,7 +2079,8 @@ bool Player::_pfileLoad(FBFILE *fl, bool reboot, const char* name) { morphs_load(this, std::string(line)); } break; - case 'N': + } + case 'N': { if (!strcmp(tag, "NmI ")) this->player_data.PNames[0] = std::string(line); else if (!strcmp(tag, "NmR ")) @@ -2163,14 +2102,13 @@ bool Player::_pfileLoad(FBFILE *fl, bool reboot, const char* name) { else if (!strcmp(tag, "NtfE"))//Polud мин. цена для оффлайн-оповещений NOTIFY_EXCH_PRICE(this) = lnum; break; - - case 'O': + } + case 'O': { if (!strcmp(tag, "Olc ")) GET_OLC_ZONE(this) = num; break; - - - case 'P': + } + case 'P': { if (!strcmp(tag, "Pass")) this->set_passwd(line); else if (!strcmp(tag, "Plyd")) @@ -2281,8 +2219,8 @@ bool Player::_pfileLoad(FBFILE *fl, bool reboot, const char* name) { } break; - - case 'Q': + } + case 'Q': { if (!strcmp(tag, "Qst ")) { buf[0] = '\0'; @@ -2290,11 +2228,10 @@ bool Player::_pfileLoad(FBFILE *fl, bool reboot, const char* name) { this->quested_add(this, num, buf); } break; - - case 'R': + } + case 'R': { if (!strcmp(tag, "Room")) GET_LOADROOM(this) = num; -//29.11.09. (c) Василиса else if (!strcmp(tag, "Ripa")) GET_RIP_ARENA(this) = num; else if (!strcmp(tag, "Ripm")) @@ -2315,29 +2252,24 @@ bool Player::_pfileLoad(FBFILE *fl, bool reboot, const char* name) { GET_RIP_DT(this) = num; else if (!strcmp(tag, "Ridt")) GET_RIP_DTTHIS(this) = num; -//(с) Василиса else if (!strcmp(tag, "Rmbr")) this->remember_set_num(num); else if (!strcmp(tag, "Reli")) GET_RELIGION(this) = num; else if (!strcmp(tag, "Race")) GET_RACE(this) = num; - else if (!strcmp(tag, "Rcps")) - { + else if (!strcmp(tag, "Rcps")) { im_rskill *last = NULL; - for (;;) - { + for (;;) { im_rskill *rs; fbgetline(fl, line); sscanf(line, "%d %d", &num, &num2); if (num < 0) break; num = im_get_recipe(num); -// +newbook.patch (Alisher) if (num < 0 || imrecipes[num].classknow[(int) GET_CLASS(this)] != KNOW_RECIPE) -// -newbook.patch (Alisher) continue; - CREATE(rs, 1); + CREATE(rs, 1); rs->rid = num; rs->perc = num2; rs->link = NULL; @@ -2349,88 +2281,62 @@ bool Player::_pfileLoad(FBFILE *fl, bool reboot, const char* name) { } } break; - - case 'S': + } + case 'S': { if (!strcmp(tag, "Size")) GET_SIZE(this) = num; - else if (!strcmp(tag, "Sex ")) - { + else if (!strcmp(tag, "Sex ")) { this->set_sex(static_cast(num)); - } - else if (!strcmp(tag, "Skil")) - { - do - { + } else if (!strcmp(tag, "Skil")) { + do { fbgetline(fl, line); sscanf(line, "%d %d", &num, &num2); - if (num != 0) - { - if (skill_info[num].classknow[(int)GET_CLASS(this)][(int)GET_KIN(this)] == KNOW_SKILL) - { + if (num != 0) { + if (skill_info[num].classknow[(int) GET_CLASS(this)][(int) GET_KIN(this)] == KNOW_SKILL) { this->set_skill(static_cast(num), num2); } } - } - while (num != 0); - } - else if (!strcmp(tag, "SkTm")) - { - do - { + } while (num != 0); + } else if (!strcmp(tag, "SkTm")) { + do { fbgetline(fl, line); sscanf(line, "%d %d", &num, &num2); - if (num != 0) - { + if (num != 0) { timed.skill = num; timed.time = num2; timed_to_char(this, &timed); } - } - while (num != 0); - } - else if (!strcmp(tag, "Spel")) - { - do - { + } while (num != 0); + } else if (!strcmp(tag, "Spel")) { + do { fbgetline(fl, line); sscanf(line, "%d %d", &num, &num2); if (num != 0 && spell_info[num].name) GET_SPELL_TYPE(this, num) = num2; - } - while (num != 0); - } - else if (!strcmp(tag, "SpMe")) - { - do - { + } while (num != 0); + } else if (!strcmp(tag, "SpMe")) { + do { fbgetline(fl, line); sscanf(line, "%d %d", &num, &num2); if (num != 0) GET_SPELL_MEM(this, num) = num2; - } - while (num != 0); - } - else if (!strcmp(tag, "SpTM")) - { - struct spell_mem_queue_item *qi_cur, ** qi = &MemQueue.queue; + } while (num != 0); + } else if (!strcmp(tag, "SpTM")) { + struct spell_mem_queue_item *qi_cur, **qi = &MemQueue.queue; while (*qi) qi = &((*qi)->link); - do - { + do { fbgetline(fl, line); sscanf(line, "%d", &num); - if (num != 0) - { + if (num != 0) { CREATE(qi_cur, 1); *qi = qi_cur; qi_cur->spellnum = num; qi_cur->link = NULL; qi = &qi_cur->link; } - } - while (num != 0); - } - else if (!strcmp(tag, "Str ")) + } while (num != 0); + } else if (!strcmp(tag, "Str ")) this->set_str(num); else if (!strcmp(tag, "StrL")) STRING_LENGTH(this) = num; @@ -2449,8 +2355,8 @@ bool Player::_pfileLoad(FBFILE *fl, bool reboot, const char* name) { else if (!strcmp(tag, "St05")) this->set_start_stat(G_CHA, lnum); break; - - case 'T': + } + case 'T': { if (!strcmp(tag, "Thir")) GET_COND(this, THIRST) = num; else if (!strcmp(tag, "Titl")) @@ -2465,43 +2371,36 @@ bool Player::_pfileLoad(FBFILE *fl, bool reboot, const char* name) { sscanf(line, "%d %d", &num, &num2); today_torc_.first = num; today_torc_.second = num2; - } - else if (!strcmp(tag, "Tlgr")) { + } else if (!strcmp(tag, "Tlgr")) { if (lnum <= 10000000000000) { this->player_specials->saved.telegram_id = lnum; } else { // зачищаем остатки старой баги this->player_specials->saved.telegram_id = 0; } - } - else if (!strcmp(tag, "TSpl")) - { - do - { + } else if (!strcmp(tag, "TSpl")) { + do { fbgetline(fl, line); sscanf(line, "%d %ld %ld", &num, &lnum, &lnum3); - if (num != 0 && spell_info[num].name) - { + if (num != 0 && spell_info[num].name) { Temporary_Spells::add_spell(this, num, lnum, lnum3); } } while (num != 0); } break; - - case 'W': + } + case 'W': { if (!strcmp(tag, "Wate")) GET_WEIGHT(this) = num; else if (!strcmp(tag, "Wimp")) GET_WIMP_LEV(this) = num; else if (!strcmp(tag, "Wis ")) this->set_wis(num); -//29.11.09 (c) Василиса else if (!strcmp(tag, "Wina")) GET_WIN_ARENA(this) = num; -//конец правки (с) Василиса else if (!strcmp(tag, "Wman")) this->set_who_mana(num); break; - + } default: sprintf(buf, "SYSERR: Unknown tag %s in pfile %s", tag, name); } @@ -2532,8 +2431,6 @@ bool Player::_pfileLoad(FBFILE *fl, bool reboot, const char* name) { { if (spell_info[i].slot_forc[(int) GET_CLASS(this)][(int) GET_KIN(this)] == MAX_SLOT) REMOVE_BIT(GET_SPELL_TYPE(this, i), SPELL_KNOW | SPELL_TEMP); -// shapirus: изученное не убираем на всякий случай, но из мема выкидываем, -// если мортов мало if (GET_REMORT(this) < MIN_CAST_REM(spell_info[i], this)) GET_SPELL_MEM(this, i) = 0; } @@ -2565,8 +2462,49 @@ bool Player::_pfileLoad(FBFILE *fl, bool reboot, const char* name) { this->account = temp_account; } this->account->add_player(GET_UNIQUE(this)); + return id; +} + +// на счет reboot: используется только при старте мада в вызовах из entrycount +// при включенном флаге файл читается только до поля Rebt, все остальные поля пропускаются +// поэтому при каких-то изменениях в entrycount, must_be_deleted и TopPlayer::Refresh следует +// убедиться, что изменный код работает с действительно проинициализированными полями персонажа +// на данный момент это: PLR_FLAGS, GET_CLASS, GET_EXP, GET_IDNUM, LAST_LOGON, GET_LEVEL, GET_NAME, GET_REMORT, GET_UNIQUE, GET_EMAIL +// * \param reboot - по дефолту = false +int Player::load_char_ascii(const char *name, bool reboot, const bool find_id /*= true*/) +{ + int id, num = 0, num2 = 0, num3 = 0, num4 = 0, num5 = 0, num6 = 0, i; + long int lnum = 0, lnum3 = 0; + unsigned long long llnum = 0; + FBFILE *fl = NULL; + char filename[40]; + char buf[MAX_RAW_INPUT_LENGTH], line[MAX_RAW_INPUT_LENGTH], tag[6]; + char line1[MAX_RAW_INPUT_LENGTH]; + struct timed_type timed; + + *filename = '\0'; + log("Load ascii char %s", name); + if (!find_id) { + id = 1; + } + else { + id = find_name(name); + } + + bool result = id >= 0; + result = result && get_filename(name, filename, PLAYERS_FILE); + result = result && (fl = fbopen(filename, FB_READ)); + if (!result) + { + const std::size_t BUFFER_SIZE = 1024; + char buffer[BUFFER_SIZE]; + log("Can't load ascii. ID: %d; File name: \"%s\"; Current directory: \"%s\")", id, filename, getcwd(buffer, BUFFER_SIZE)); + return -1; + } + return _pfileLoad(fl, reboot, name); } + // * Перерасчет максимальных родных хп персонажа. // * При входе в игру, левеле/делевеле, добавлении/удалении славы. void check_max_hp(CHAR_DATA *ch) diff --git a/src/chars/char_player.hpp b/src/chars/char_player.hpp index eee4e137d..9c31ba79e 100644 --- a/src/chars/char_player.hpp +++ b/src/chars/char_player.hpp @@ -109,7 +109,7 @@ class Player : public CHAR_DATA void save_char(); int load_char_ascii(const char *name, bool reboot = 0, const bool find_id = true); // метод загрузки файла игрока напрямую - bool _pfileLoad(FBFILE *fl, bool reboot, const char* name); + int _pfileLoad(FBFILE *fl, bool reboot, const char* name); bool get_disposable_flag(int num); void set_disposable_flag(int num); @@ -247,6 +247,8 @@ class Player : public CHAR_DATA std::shared_ptr account; //перечень чармисов, доступных с команды наемник std::map charmeeHistory; + + void initPlayerFields(); }; namespace PlayerSystem diff --git a/src/cmd.imm/act.wizard.cpp b/src/cmd.imm/act.wizard.cpp index 02df535ab..6401c4627 100644 --- a/src/cmd.imm/act.wizard.cpp +++ b/src/cmd.imm/act.wizard.cpp @@ -4131,8 +4131,7 @@ void do_advance(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) imm_log("%s has advanced %s to level %d (from %d)", GET_NAME(ch), GET_NAME(victim), newlevel, oldlevel); } - ExpCalc::gain_exp_regardless(victim, ExpCalc::level_exp(victim, newlevel) - - GET_EXP(victim)); + ExpCalc::gain_exp_regardless(victim, ExpCalc::level_exp(victim, newlevel) - GET_EXP(victim)); victim->save_char(); } diff --git a/src/core/leveling.cpp b/src/core/leveling.cpp index 2be1e6c26..747315d6b 100644 --- a/src/core/leveling.cpp +++ b/src/core/leveling.cpp @@ -1,7 +1,7 @@ #include "leveling.h" +#include "bonus.h" #include "chars/char_player.hpp" -#include "class.hpp" #include "exchange.h" #include "ext_money.hpp" #include "house_exp.hpp" @@ -13,6 +13,7 @@ #include "zone.table.hpp" extern double exp_coefficients[]; +extern int max_exp_gain_npc; const char* Remort::CONFIG_FILE = LIB_MISC"remort.xml"; int get_remort_mobmax(CHAR_DATA * ch) @@ -799,6 +800,128 @@ namespace ExpCalc { else return (level_exp(ch, GET_LEVEL(ch) + 1) - level_exp(ch, GET_LEVEL(ch))) / (3 + MIN(3, GET_REMORT(ch) / 5)); } +/*++ + Функция начисления опыта + ch - кому опыт начислять + Вызов этой функции для NPC смысла не имеет, но все равно + какие-то проверки внутри зачем то делаются +--*/ + void increaseExperience(CHAR_DATA * ch, CHAR_DATA * victim, int members, int koef) + { + +// Странно, но для NPC эта функция тоже должна работать +// if (IS_NPC(ch) || !OK_GAIN_EXP(ch,victim)) + if (!OK_GAIN_EXP(ch, victim)) + { + send_to_char("Ваше деяние никто не оценил.\r\n", ch); + return; + } + + // 1. Опыт делится поровну на всех + int exp = GET_EXP(victim) / MAX(members, 1); + + if(victim->get_zone_group() > 1 && members < victim->get_zone_group()) + { + // в случае груп-зоны своего рода планка на мин кол-во человек в группе + exp = GET_EXP(victim) / victim->get_zone_group(); + } + + // 2. Учитывается коэффициент (лидерство, разность уровней) + // На мой взгляд его правильней использовать тут а не в конце процедуры, + // хотя в большинстве случаев это все равно + exp = exp * koef / 100; + + // 3. Вычисление опыта для PC и NPC + if (IS_NPC(ch)) + { + exp = MIN(max_exp_gain_npc, exp); + exp += MAX(0, (exp * MIN(4, (GET_LEVEL(victim) - GET_LEVEL(ch)))) / 8); + } + else + exp = MIN(max_exp_gain_pc(ch), get_extend_exp(exp, ch, victim)); + // 4. Последняя проверка + exp = MAX(1, exp); + if (exp > 1) + { + if (Bonus::is_bonus(Bonus::BONUS_EXP)) + { + exp *= Bonus::get_mult_bonus(); + } + + if (!IS_NPC(ch) && !ch->affected.empty()) { + for (const auto& aff : ch->affected) { + if (aff->location == APPLY_BONUS_EXP) { + // скушал свиток с эксп бонусом + exp *= MIN(3, aff->modifier); // бонус макс тройной + } + } + } + + int long_live_bonus = floor(ExpCalc::get_npc_long_live_exp_bounus( GET_MOB_VNUM(victim))); + if (long_live_bonus>1) { + std::string mess; + switch (long_live_bonus) { + case 2: + mess = "Редкая удача! Опыт повышен!\r\n"; + break; + case 3: + mess = "Очень редкая удача! Опыт повышен!\r\n"; + break; + case 4: + mess = "Очень-очень редкая удача! Опыт повышен!\r\n"; + break; + case 5: + mess = "Вы везунчик! Опыт повышен!\r\n"; + break; + case 6: + mess = "Ваша удача велика! Опыт повышен!\r\n"; + break; + case 7: + mess = "Ваша удача достигла небес! Опыт повышен!\r\n"; + break; + case 8: + mess = "Ваша удача коснулась луны! Опыт повышен!\r\n"; + break; + case 9: + mess = "Ваша удача затмевает солнце! Опыт повышен!\r\n"; + break; + case 10: + mess = "Ваша удача выше звезд! Опыт повышен!\r\n"; + break; + default: + mess = "Ваша удача выше звезд! Опыт повышен!\r\n"; + break; + } + send_to_char(mess.c_str(), ch); + } + + exp = MIN(max_exp_gain_pc(ch), exp); + send_to_char(ch, "Ваш опыт повысился на %d %s.\r\n", exp, desc_count(exp, WHAT_POINT)); + } + else if (exp == 1) { + send_to_char("Ваш опыт повысился всего лишь на маленькую единичку.\r\n", ch); + } + gain_exp(ch, exp); + GET_ALIGNMENT(ch) += (-GET_ALIGNMENT(victim) - GET_ALIGNMENT(ch)) / 16; + TopPlayer::Refresh(ch); + + if (!EXTRA_FLAGGED(victim, EXTRA_GRP_KILL_COUNT) + && !IS_NPC(ch) + && !IS_IMMORTAL(ch) + && IS_NPC(victim) + && !IS_CHARMICE(victim) + && !ROOM_FLAGGED(IN_ROOM(victim), ROOM_ARENA)) + { + mob_stat::add_mob(victim, members); + EXTRA_FLAGS(victim).set(EXTRA_GRP_KILL_COUNT); + } + else if (IS_NPC(ch) && !IS_NPC(victim) + && !ROOM_FLAGGED(IN_ROOM(victim), ROOM_ARENA)) + { + mob_stat::add_mob(ch, 0); + } + } + } namespace Remort diff --git a/src/core/leveling.h b/src/core/leveling.h index e64580ce9..0dab08f24 100644 --- a/src/core/leveling.h +++ b/src/core/leveling.h @@ -19,6 +19,7 @@ namespace ExpCalc { int level_exp(CHAR_DATA * ch, int level); int calcDeathExp(CHAR_DATA* ch); void advance_level(CHAR_DATA * ch); + void increaseExperience(CHAR_DATA * ch, CHAR_DATA * victim, int members, int koef); } namespace Remort diff --git a/src/db.cpp b/src/db.cpp index 91cf4db6f..62bd3acea 100644 --- a/src/db.cpp +++ b/src/db.cpp @@ -5415,7 +5415,7 @@ long get_ptable_by_name(const char *name) for (std::size_t i = 0; i < player_table.size(); i++) { const char* pname = player_table[i].name(); - if (!str_cmp(pname, arg)) + if (!str_cmp(pname, smallBuf)) { return static_cast(i); } diff --git a/src/fightsystem/common.cpp b/src/fightsystem/common.cpp index a09e769b1..29674b09d 100644 --- a/src/fightsystem/common.cpp +++ b/src/fightsystem/common.cpp @@ -39,7 +39,7 @@ int isHaveNoExtraAttack(CHAR_DATA * ch) { } void set_wait(CHAR_DATA * ch, int waittime, int victim_in_room) { - if (!WAITLESS(ch) && (!victim_in_room || (ch->get_fighting() && ch->isInSameRoom(ch->get_fighting())))) { + if (!WAITLESS(ch) && (!victim_in_room || (ch->get_fighting() && SAME_ROOM(ch, ch->get_fighting())))) { WAIT_STATE(ch, waittime * PULSE_VIOLENCE); } } @@ -51,7 +51,7 @@ void setSkillCooldown(CHAR_DATA* ch, ESkill skill, int cooldownInPulses) { } void setSkillCooldownInFight(CHAR_DATA* ch, ESkill skill, int cooldownInPulses) { - if (ch->get_fighting() && ch->isInSameRoom(ch->get_fighting())) { + if (ch->get_fighting() && SAME_ROOM(ch,ch->get_fighting())) { setSkillCooldown(ch, skill, cooldownInPulses); } else { WAIT_STATE(ch, PULSE_VIOLENCE/6); @@ -62,7 +62,7 @@ CHAR_DATA* findVictim(CHAR_DATA* ch, char* argument) { one_argument(argument, arg); CHAR_DATA* victim = get_char_vis(ch, arg, FIND_CHAR_ROOM); if (!victim) { - if (!*arg && ch->get_fighting() && ch->isInSameRoom(ch->get_fighting())) { + if (!*arg && ch->get_fighting() && SAME_ROOM(ch, ch->get_fighting())) { victim = ch->get_fighting(); } } diff --git a/src/fightsystem/fight_hit.cpp b/src/fightsystem/fight_hit.cpp index 9ffb9aa36..8171167b4 100644 --- a/src/fightsystem/fight_hit.cpp +++ b/src/fightsystem/fight_hit.cpp @@ -1,20 +1,20 @@ #include "fight_hit.hpp" -#include "logger.hpp" -#include "handler.h" -#include "screen.h" -#include "dg/dg_scripts.h" -#include "skills.h" -#include "magic.h" -#include "pk.h" +#include "bonus.h" #include "dps.hpp" #include "fight.h" -#include "house_exp.hpp" -#include "poison.hpp" -#include "bonus.h" -#include "mobact.hpp" #include "fightsystem/common.h" #include "grp/grp.main.h" +#include "handler.h" +#include "house_exp.hpp" +#include "core/leveling.h" +#include "logger.hpp" +#include "magic.h" +#include "mobact.hpp" +#include "pk.h" +#include "poison.hpp" +#include "screen.h" +#include "skills.h" // extern int extra_aco(int class_num, int level); @@ -22,7 +22,6 @@ void alt_equip(CHAR_DATA * ch, int pos, int dam, int chance); int thaco(int class_num, int level); void npc_groupbattle(CHAR_DATA * ch); void set_wait(CHAR_DATA * ch, int waittime, int victim_in_room); -void go_autoassist(CHAR_DATA * ch); int armor_class_limit(CHAR_DATA * ch) { if (IS_CHARMICE(ch)) { @@ -58,46 +57,6 @@ int armor_class_limit(CHAR_DATA * ch) { return -300; } -void aff_group_inspiration(CHAR_DATA *ch, EApplyLocation num_apply, int time, int modi) { - CHAR_DATA *k; - AFFECT_DATA af; - send_to_char(ch, "&YВаш точный удар воодушевил вас, придав новых сил!&n\r\n"); - struct follow_type *f; - if (ch->has_master()){ - k = ch->get_master(); - } - else { - k = ch; - } -// на лидера - if (SAME_ROOM(ch, k)) { - af.location = num_apply; - af.type = SPELL_PALADINE_INSPIRATION; - af.modifier = GET_REMORT(k) / 5 * 2 + modi; - af.battleflag = AF_BATTLEDEC | AF_PULSEDEC; - af.duration = pc_duration(k, time, 0, 0, 0, 0); - affect_join(k, af, FALSE, FALSE, FALSE, FALSE); - if (k != ch) - send_to_char(k, "&YТочный удар %s воодушевил вас, придав новых сил!\r\n&n", GET_PAD(ch, 1)); - } -// на группу - for (f = k->followers; f; f = f->next) { - if (!AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP)) { - continue; - } - if (ch->in_room != f->follower->in_room) - continue; - af.location = num_apply; - af.type = SPELL_PALADINE_INSPIRATION; - af.modifier = GET_REMORT(ch) / 5 * 2 + modi; - af.battleflag = AF_BATTLEDEC | AF_PULSEDEC; - af.duration = pc_duration(f->follower, time, 0, 0 , 0, 0); - affect_join(f->follower, af, FALSE, FALSE, FALSE, FALSE); - if (ch != f->follower) - send_to_char(f->follower, "&YТочный удар %s воодушевил вас, придав новых сил!\r\n&n", GET_PAD(ch, 1)); - } -} - void aff_random_pc_inspiration(CHAR_DATA *ch, EApplyLocation num_apply, int time, int modi) { CHAR_DATA *target; AFFECT_DATA af; @@ -112,28 +71,6 @@ void aff_random_pc_inspiration(CHAR_DATA *ch, EApplyLocation num_apply, int time send_to_char(target, "&YТочный удар %s воодушевил вас, придав новых сил!&n\r\n", GET_PAD(ch,1)); } -void inspiration(CHAR_DATA *ch) { - byte num = number(1,4); -// CHAR_DATA * target = get_random_pc_group(ch); - - switch (num){ - case 1: - aff_group_inspiration(ch, APPLY_PERCENT_DAM, 5, GET_REMORT(ch)); - break; - case 2: - aff_group_inspiration(ch, APPLY_CAST_SUCCESS, 3, GET_REMORT(ch)); - break; - case 3: - aff_group_inspiration(ch, APPLY_MANAREG, 10, GET_REMORT(ch) * 5); - break; - case 4: - aff_group_inspiration(ch, APPLY_HITROLL, 0, 0); // вывод мессаги - call_magic(ch, ch, nullptr, nullptr, SPELL_GROUP_HEAL, GET_LEVEL(ch)); - break; - default: - break; - } -} int compute_armor_class(CHAR_DATA * ch) { @@ -2460,83 +2397,71 @@ void Damage::process_death(CHAR_DATA *ch, CHAR_DATA *victim) { CHAR_DATA *killer = nullptr; - // вычисляем, чем убили и кто убивец. Но тут какая то херня :( - if (IS_NPC(victim) || victim->desc) { + // если не умер сам, или отравили - убивец сразу известен + if (ch != victim) { + killer = ch; + // определяем убивца, если отравили или сам помер, от ран + } else if (IS_NPC(victim) || victim->desc) { + // когда умирает от яда - киллер равно виктим, киллера извлекаем из свойств отравы if (victim == ch && IN_ROOM(victim) != NOWHERE) { if (spell_num == SPELL_POISON) { for (const auto poisoner : world[IN_ROOM(victim)]->people) { if (poisoner != victim && GET_ID(poisoner) == victim->Poisoner) { killer = poisoner; + break; } } + // киллер - первый попавшийся убивец, хотя надо бы по дамагу =) } else if (msg_num == TYPE_SUFFERING) { for (const auto attacker : world[IN_ROOM(victim)]->people) { if (attacker->get_fighting() == victim) { killer = attacker; + break; } } } } - if (ch != victim) { - killer = ch; - } } if (killer) { - // и чармис и хозяин в одной группе - if (IN_GROUP(killer)) - group_gain(killer, victim); - } else if ((IS_CHARMICE(killer) || MOB_FLAGGED(killer, MOB_PLAYER_SUMMON)) && killer->has_master()) { - if (IN_GROUP(killer->get_master()) && IN_ROOM(killer) == IN_ROOM(killer->get_master())) { - // Хозяин - PC в группе => опыт группе - group_gain(killer->get_master(), victim); - } - else if (IN_ROOM(killer) == IN_ROOM(killer->get_master())){ - // Чармис и хозяин в одной комнате -опыт хозяину - perform_group_gain(killer->get_master(), victim, 1, 100); - } - // else - // А хозяина то рядом не оказалось, все чармису - убрано - // нефиг абьюзить чарм perform_group_gain( killer, victim, 1, 100 ); - } - else - { - // Просто NPC или PC сам по себе - perform_group_gain(killer, victim, 1, 100); - } + if (IN_GROUP(killer)) { + // и чармис и хозяин обязательно одной группе, доп проверок не надо + killer->personGroup->gainExp(victim); + } + else + if ((IS_CHARMICE(killer) || MOB_FLAGGED(killer, MOB_PLAYER_SUMMON)) && killer->has_master() + && SAME_ROOM(killer->get_master(), killer)) { + // был убит чармисом, хозяин рядышком - ему экспу + ExpCalc::increaseExperience(killer->get_master(), victim, 1, 100); + } else { + // Просто NPC или PC сам по себе + ExpCalc::increaseExperience(killer, victim, 1, 100); + } + } // в сислог иммам идут только смерти в пк (без арен) // в файл пишутся все смерти чаров - // если чар убит палачем то тоже не спамим + // если чар убит палачом то тоже не спамим - if (!IS_NPC(victim) && !(killer && PRF_FLAGGED(killer, PRF_EXECUTOR))) - { + if (!IS_NPC(victim) && !(killer && PRF_FLAGGED(killer, PRF_EXECUTOR))) { update_pk_logs(ch, victim); - - for (const auto& ch_vict : world[ch->in_room]->people) - { - //Мобы все кто присутствовал при смерти игрока забывают - if (IS_IMMORTAL(ch_vict)) - continue; - if (!HERE(ch_vict)) - continue; - if (!IS_NPC(ch_vict)) - continue; - if (MOB_FLAGGED(ch_vict, MOB_MEMORY)) - { - mobForget(ch_vict, victim); - } - } - - } - - if (killer) - { - ch = killer; + for (const auto& ch_vict : world[ch->in_room]->people) + { + //Мобы все кто присутствовал при смерти игрока забывают + if (IS_IMMORTAL(ch_vict)) + continue; + if (!HERE(ch_vict)) + continue; + if (!IS_NPC(ch_vict)) + continue; + if (MOB_FLAGGED(ch_vict, MOB_MEMORY)) { + mobForget(ch_vict, victim); + } + } } - die(victim, ch); + die(victim, killer); } void stopFollowOnAggro(CHAR_DATA* aggressor, CHAR_DATA* victim) @@ -4213,7 +4138,7 @@ void hit(CHAR_DATA *ch, CHAR_DATA *victim, ESkill type, FightSystem::AttType wea PUNCTUAL_WAIT_STATE(ch, 2 * PULSE_VIOLENCE); } } - inspiration(ch); +// inspiration(ch); } } diff --git a/src/fightsystem/fight_hit.hpp b/src/fightsystem/fight_hit.hpp index 307dc3b1f..cbb2ea479 100644 --- a/src/fightsystem/fight_hit.hpp +++ b/src/fightsystem/fight_hit.hpp @@ -92,7 +92,6 @@ struct HitData void set_battle_pos(CHAR_DATA * ch); void gain_battle_exp(CHAR_DATA *ch, CHAR_DATA *victim, int dam); -void perform_group_gain(CHAR_DATA * ch, CHAR_DATA * victim, int members, int koef); void group_gain(CHAR_DATA * ch, CHAR_DATA * victim); char *replace_string(const char *str, const char *weapon_singular, const char *weapon_plural); diff --git a/src/fightsystem/fight_stuff.cpp b/src/fightsystem/fight_stuff.cpp index 8aa8cec17..bf2aaa270 100644 --- a/src/fightsystem/fight_stuff.cpp +++ b/src/fightsystem/fight_stuff.cpp @@ -47,7 +47,7 @@ int slot_for_char(CHAR_DATA * ch, int i); void set_wait(CHAR_DATA * ch, int waittime, int victim_in_room); extern int material_value[]; -extern int max_exp_gain_npc; + //интервал в секундах между восстановлением кругов после рипа extern const unsigned RECALL_SPELLS_INTERVAL; @@ -201,46 +201,26 @@ void update_die_counts(CHAR_DATA *ch, CHAR_DATA *killer, int dec_exp) //end by WorM //конец правки (с) Василиса -void update_leadership(CHAR_DATA *ch, CHAR_DATA *killer) +void update_leadership(CHAR_DATA *victim, CHAR_DATA *killer) { - // train LEADERSHIP - if (IS_NPC(ch) && killer) // Убили моба - { - if (!IS_NPC(killer) // Убил загрупленный чар - && AFF_FLAGGED(killer, EAffectFlag::AFF_GROUP) - && killer->has_master() - && killer->get_master()->get_skill(SKILL_LEADERSHIP) > 0 - && IN_ROOM(killer) == IN_ROOM(killer->get_master())) - { - improve_skill(killer->get_master(), SKILL_LEADERSHIP, number(0, 1), ch); - } - else if (IS_NPC(killer) // Убил чармис загрупленного чара - && IS_CHARMICE(killer) - && killer->has_master() - && AFF_FLAGGED(killer->get_master(), EAffectFlag::AFF_GROUP)) - { - if (killer->get_master()->has_master() // Владелец чармиса НЕ лидер - && killer->get_master()->get_master()->get_skill(SKILL_LEADERSHIP) > 0 - && IN_ROOM(killer) == IN_ROOM(killer->get_master()) - && IN_ROOM(killer) == IN_ROOM(killer->get_master()->get_master())) - { - improve_skill(killer->get_master()->get_master(), SKILL_LEADERSHIP, number(0, 1), ch); - } - } + // не в группе - выходим + if (!killer || !IN_GROUP(killer)) + return; + auto leader = killer->personGroup->getLeader(); + // лидера нет с нами + if (leader == nullptr || leader->purged() || !SAME_ROOM(leader, killer)) { + return; + } + // если убили чюжого моба не не-чармиса + if (IS_NPC(victim) && !IS_CHARMICE(victim)) { + improve_skill(leader, SKILL_LEADERSHIP, number(0, 1), victim); } - - // decrease LEADERSHIP - if (!IS_NPC(ch) // Член группы убит мобом - && killer - && IS_NPC(killer) - && AFF_FLAGGED(ch, EAffectFlag::AFF_GROUP) - && ch->has_master() - && SAME_ROOM(ch, ch->get_master()) - && ch->get_master()->get_inborn_skill(SKILL_LEADERSHIP) > 1) - { - const auto current_skill = ch->get_master()->get_trained_skill(SKILL_LEADERSHIP); - ch->get_master()->set_skill(SKILL_LEADERSHIP, current_skill - 1); + // если убили персонажа из группы + if (!IS_NPC(victim) && IN_SAME_GROUP(leader, victim)) { + const auto current_skill = victim->get_master()->get_trained_skill(SKILL_LEADERSHIP); + victim->get_master()->set_skill(SKILL_LEADERSHIP, current_skill - 1); } + } bool check_tester_death(CHAR_DATA *ch, CHAR_DATA *killer) diff --git a/src/fightsystem/mobact.cpp b/src/fightsystem/mobact.cpp index 354a085cf..c67df8446 100644 --- a/src/fightsystem/mobact.cpp +++ b/src/fightsystem/mobact.cpp @@ -312,7 +312,7 @@ CHAR_DATA* selectVictimDependingOnGroupFormation(CHAR_DATA *assaulter, CHAR_DATA if (initialVictim->has_master()) { leader = initialVictim->get_master(); } - if (!assaulter->isInSameRoom(leader)) { + if (!SAME_ROOM(assaulter, leader)) { return initialVictim; } diff --git a/src/fightsystem/start.fight.cpp b/src/fightsystem/start.fight.cpp index 70d8588be..ce8ce664a 100644 --- a/src/fightsystem/start.fight.cpp +++ b/src/fightsystem/start.fight.cpp @@ -75,7 +75,7 @@ int set_hit(CHAR_DATA * ch, CHAR_DATA * victim) { && !IS_NPC(victim->get_master())) { if (MOB_FLAGGED(victim, MOB_CLONE)) { mobRemember(ch, victim->get_master()); - } else if (ch->isInSameRoom(victim->get_master()) && CAN_SEE(ch, victim->get_master())) { + } else if (SAME_ROOM(ch, victim->get_master()) && CAN_SEE(ch, victim->get_master())) { mobRemember(ch, victim->get_master()); } } diff --git a/src/grp/follow.cpp b/src/grp/follow.cpp index 80fc8caf4..0afc3e1d3 100644 --- a/src/grp/follow.cpp +++ b/src/grp/follow.cpp @@ -12,7 +12,6 @@ void perform_drop_gold(CHAR_DATA * ch, int amount); // This will NOT do if a character quits/dies!! // // При возврате 1 использовать ch нельзя, т.к. прошли через extract_char // TODO: по всем вызовам не проходил, может еще где-то коряво вызывается, кроме передачи скакунов -- Krodo -// при персонаже на входе - пуржить не должно полюбому, если начнет, как минимум в change_leader будут глюки bool stop_follower(CHAR_DATA * ch, int mode) { struct follow_type *j, *k; diff --git a/src/grp/grouppenaltycalculator.cpp b/src/grp/grouppenaltycalculator.cpp deleted file mode 100644 index 60ec611b4..000000000 --- a/src/grp/grouppenaltycalculator.cpp +++ /dev/null @@ -1,90 +0,0 @@ -#include "grp.main.h" - -#include "logger.hpp" -#include "utils.h" - -int GroupPenaltyCalculator::get() const -{ - const bool leader_is_npc = IS_NPC(m_leader); - const bool leader_in_group = AFF_FLAGGED(m_leader, EAffectFlag::AFF_GROUP); - const bool leader_is_in_room = leader_in_group && SAME_ROOM(m_leader, m_killer); - - if (!leader_is_npc - && leader_is_in_room) - { - int penalty = 0; - if (penalty_by_leader(m_leader, penalty)) - { - return penalty; - } - } - - for (auto f = m_leader->followers; f; f = f->next) - { - const bool follower_is_npc = IS_NPC(f->follower); - const bool follower_is_in_room = AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP) - && f->follower->in_room == IN_ROOM(m_killer); - - if (follower_is_npc - || !follower_is_in_room) - { - continue; - } - - int penalty = 0; - if (penalty_by_leader(f->follower, penalty)) - { - if (0 < penalty) - { - return penalty; - } - } - } - - return 0; -} - -bool GroupPenaltyCalculator::penalty_by_leader(const CHAR_DATA* player, int& penalty) const -{ - const int player_remorts = static_cast(GET_REMORT(player)); - const int player_class = static_cast(GET_CLASS(player)); - const int player_level = GET_LEVEL(player); - - if (IS_NPC(player)) - { - log("LOGIC ERROR: try to get penalty for NPC [%s], VNum: %d\n", - player->get_name().c_str(), - GET_MOB_VNUM(player)); - penalty = DEFAULT_PENALTY; - return true; - } - - if (0 > player_class - || player_class > NUM_PLAYER_CLASSES) - { - log("LOGIC ERROR: wrong player class: %d for player [%s]", - player_class, - player->get_name().c_str()); - penalty = DEFAULT_PENALTY; - return true; - } - - if (0 > player_remorts - || player_remorts > MAX_REMORT) - { - log("LOGIC ERROR: wrong number of remorts: %d for player [%s]", - player_remorts, - player->get_name().c_str()); - penalty = DEFAULT_PENALTY; - return true; - } - - bool result = false; - if (m_max_level - player_level > m_grouping[player_class][player_remorts]) - { - penalty = 50 + 2 * (m_max_level - player_level - m_grouping[player_class][player_remorts]); - result = true; - } - - return result; -} \ No newline at end of file diff --git a/src/grp/grp.cmdprocessor.cpp b/src/grp/grp.cmdprocessor.cpp index 90fa75354..891ccf884 100644 --- a/src/grp/grp.cmdprocessor.cpp +++ b/src/grp/grp.cmdprocessor.cpp @@ -9,6 +9,8 @@ #include "msdp.constants.hpp" #include "magic.h" #include "remember.hpp" +#include "grp.main.h" + extern GroupRoster& groupRoster; @@ -64,38 +66,6 @@ bool is_group_member(CHAR_DATA *ch, CHAR_DATA *vict) } } -void print_list_group(CHAR_DATA *ch) -{ - CHAR_DATA *k; - struct follow_type *f; - int count = 1; - k = (ch->has_master() ? ch->get_master() : ch); - if (AFF_FLAGGED(ch, EAffectFlag::AFF_GROUP)) - { - send_to_char("Ваша группа состоит из:\r\n", ch); - if (AFF_FLAGGED(k, EAffectFlag::AFF_GROUP)) - { - sprintf(buf1, "Лидер: %s\r\n", GET_NAME(k)); - send_to_char(buf1, ch); - } - - for (f = k->followers; f; f = f->next) - { - if (!AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP)) - { - continue; - } - sprintf(buf1, "%d. Согруппник: %s\r\n", count, GET_NAME(f->follower)); - send_to_char(buf1, ch); - count++; - } - } - else - { - send_to_char("Но вы же не член (в лучшем смысле этого слова) группы!\r\n", ch); - } -} - int perform_group(CHAR_DATA * ch, CHAR_DATA * vict) { if (AFF_FLAGGED(vict, EAffectFlag::AFF_GROUP) @@ -110,9 +80,7 @@ int perform_group(CHAR_DATA * ch, CHAR_DATA * vict) AFF_FLAGS(vict).set(EAffectFlag::AFF_GROUP); if (ch != vict) { - act("$N принят$A в члены вашего кружка (тьфу-ты, группы :).", FALSE, ch, 0, vict, TO_CHAR); - act("Вы приняты в группу $n1.", FALSE, ch, 0, vict, TO_VICT); - act("$N принят$A в группу $n1.", FALSE, ch, 0, vict, TO_NOTVICT | TO_ARENA_LISTEN); + } return (TRUE); } @@ -125,409 +93,71 @@ int max_group_size(CHAR_DATA *ch) return MAX_GROUPED_FOLLOWERS + (int) VPOSI((ch->get_skill(SKILL_LEADERSHIP) - 80) / 5, 0, 4) + bonus_commander; } -/** -* Смена лидера группы на персонажа с макс лидеркой. -* Сам лидер при этом остается в группе, если он живой. -* \param vict - новый лидер, если смена происходит по команде 'гр лидер имя', -* старый лидер соответственно группится обратно, если нулевой, то считаем, что -* произошла смерть старого лидера и новый выбирается по наибольшей лидерке. -*/ -void change_leader(CHAR_DATA *ch, CHAR_DATA *vict) -{ - if (IS_NPC(ch) - || ch->has_master() - || !ch->followers) - { - return; - } - - CHAR_DATA *leader = vict; - if (!leader) - { - // лидер умер, ищем согрупника с максимальным скиллом лидерки - for (struct follow_type *l = ch->followers; l; l = l->next) - { - if (!is_group_member(ch, l->follower)) - continue; - if (!leader) - leader = l->follower; - else if (l->follower->get_skill(SKILL_LEADERSHIP) > leader->get_skill(SKILL_LEADERSHIP)) - leader = l->follower; - } - } - - if (!leader) - { - return; - } - - // для реследования используем стандартные функции - std::vector temp_list; - for (struct follow_type *n = 0, *l = ch->followers; l; l = n) - { - n = l->next; - if (!is_group_member(ch, l->follower)) - { - continue; - } - else - { - CHAR_DATA *temp_vict = l->follower; - if (temp_vict->has_master() - && stop_follower(temp_vict, SF_SILENCE)) - { - continue; - } - - if (temp_vict != leader) - { - temp_list.push_back(temp_vict); - } - } - } - - // вся эта фигня только для того, чтобы при реследовании пройтись по списку в обратном - // направлении и сохранить относительный порядок следования в группе - if (!temp_list.empty()) - { - for (std::vector::reverse_iterator it = temp_list.rbegin(); it != temp_list.rend(); ++it) - { - leader->add_follower_silently(*it); - } - } - - // бывшего лидера последним закидываем обратно в группу, если он живой - if (vict) - { - // флаг группы надо снять, иначе при регрупе не будет писаться о старом лидере - //AFF_FLAGS(ch).unset(EAffectFlag::AFF_GROUP); - ch->removeGroupFlags(); - leader->add_follower_silently(ch); - } - - if (!leader->followers) - { - return; - } - - ch->dps_copy(leader); - perform_group(leader, leader); - int followers = 0; - for (struct follow_type *f = leader->followers; f; f = f->next) - { - if (followers < max_group_size(leader)) - { - if (perform_group(leader, f->follower)) - ++followers; - } - else - { - send_to_char("Вы больше никого не можете принять в группу.\r\n", ch); - return; - } - } -} - -void do_group(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) -{ - CHAR_DATA *vict; - struct follow_type *f; - int found, f_number; - - argument = one_argument(argument, buf); - - if (!*buf) - { -// print_group(ch); - return; - } - - if (isname(buf, "список")) - { - print_list_group(ch); - return; - } - - if (GET_POS(ch) < POS_RESTING) - { - send_to_char("Трудно управлять группой в таком состоянии.\r\n", ch); - return; - } - - if (ch->has_master()) - { - act("Вы не можете управлять группой. Вы еще не ведущий.", FALSE, ch, 0, 0, TO_CHAR); - return; - } - - if (!ch->followers) - { - send_to_char("За вами никто не следует.\r\n", ch); - return; - } - - - -// вычисляем количество последователей - for (f_number = 0, f = ch->followers; f; f = f->next) - { - if (AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP)) - { - f_number++; - } - } - - if (!str_cmp(buf, "all") - || !str_cmp(buf, "все")) - { - perform_group(ch, ch); - for (found = 0, f = ch->followers; f; f = f->next) - { - if ((f_number + found) >= max_group_size(ch)) - { - send_to_char("Вы больше никого не можете принять в группу.\r\n", ch); - return; - } - found += perform_group(ch, f->follower); - } - - if (!found) - { - send_to_char("Все, кто за вами следуют, уже включены в вашу группу.\r\n", ch); - } - - return; - } - else if (!str_cmp(buf, "leader") || !str_cmp(buf, "лидер")) - { - vict = get_player_vis(ch, argument, FIND_CHAR_WORLD); - // added by WorM (Видолюб) Если найден клон и его хозяин персонаж - // а то чото как-то глючно Двойник %1 не является членом вашей группы. - if (vict - && IS_NPC(vict) - && MOB_FLAGGED(vict, MOB_CLONE) - && AFF_FLAGGED(vict, EAffectFlag::AFF_CHARM) - && vict->has_master() - && !IS_NPC(vict->get_master())) - { - if (CAN_SEE(ch, vict->get_master())) - { - vict = vict->get_master(); - } - else - { - vict = NULL; - } - } - - // end by WorM - if (!vict) - { - send_to_char("Нет такого персонажа.\r\n", ch); - return; - } - else if (vict == ch) - { - send_to_char("Вы и так лидер группы...\r\n", ch); - return; - } - else if (!AFF_FLAGGED(vict, EAffectFlag::AFF_GROUP) - || vict->get_master() != ch) - { - send_to_char(ch, "%s не является членом вашей группы.\r\n", GET_NAME(vict)); - return; - } - change_leader(ch, vict); - return; - } - - if (!(vict = get_char_vis(ch, buf, FIND_CHAR_ROOM))) - { - send_to_char(NOPERSON, ch); - } - else if ((vict->get_master() != ch) && (vict != ch)) - { - act("$N2 нужно следовать за вами, чтобы стать членом вашей группы.", FALSE, ch, 0, vict, TO_CHAR); - } - else - { - if (!AFF_FLAGGED(vict, EAffectFlag::AFF_GROUP)) - { - if (AFF_FLAGGED(vict, EAffectFlag::AFF_CHARM) || MOB_FLAGGED(vict, MOB_ANGEL) || MOB_FLAGGED(vict, MOB_GHOST) || IS_HORSE(vict)) - { - send_to_char("Только равноправные персонажи могут быть включены в группу.\r\n", ch); - send_to_char("Только равноправные персонажи могут быть включены в группу.\r\n", vict); - }; - if (f_number >= max_group_size(ch)) - { - send_to_char("Вы больше никого не можете принять в группу.\r\n", ch); - return; - } - perform_group(ch, ch); - perform_group(ch, vict); - } - else if (ch != vict) - { - act("$N исключен$A из состава вашей группы.", FALSE, ch, 0, vict, TO_CHAR); - act("Вы исключены из группы $n1!", FALSE, ch, 0, vict, TO_VICT); - act("$N был$G исключен$A из группы $n1!", FALSE, ch, 0, vict, TO_NOTVICT | TO_ARENA_LISTEN); - //AFF_FLAGS(vict).unset(EAffectFlag::AFF_GROUP); - vict->removeGroupFlags(); - } - } -} - void do_group2(CHAR_DATA *ch, char *argument, int, int){ groupRoster.processGroupCommands(ch, argument); } void do_ungroup(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) { - struct follow_type *f, *next_fol; - CHAR_DATA *tch; - - one_argument(argument, buf); - - if (ch->has_master() - || !(AFF_FLAGGED(ch, EAffectFlag::AFF_GROUP))) - { - send_to_char("Вы же не лидер группы!\r\n", ch); - return; - } - - if (!*buf) - { - sprintf(buf2, "Вы исключены из группы %s.\r\n", GET_PAD(ch, 1)); - for (f = ch->followers; f; f = next_fol) - { - next_fol = f->next; - if (AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP)) - { - //AFF_FLAGS(f->follower).unset(EAffectFlag::AFF_GROUP); - f->follower->removeGroupFlags(); - send_to_char(buf2, f->follower); - if (!AFF_FLAGGED(f->follower, EAffectFlag::AFF_CHARM) - && !(IS_NPC(f->follower) - && AFF_FLAGGED(f->follower, EAffectFlag::AFF_HORSE))) - { - stop_follower(f->follower, SF_EMPTY); - } - } - } - AFF_FLAGS(ch).unset(EAffectFlag::AFF_GROUP); - ch->removeGroupFlags(); - send_to_char("Вы распустили группу.\r\n", ch); - return; - } - for (f = ch->followers; f; f = next_fol) - { - next_fol = f->next; - tch = f->follower; - if (isname(buf, tch->get_pc_name()) - && !AFF_FLAGGED(tch, EAffectFlag::AFF_CHARM) - && !IS_HORSE(tch)) - { - //AFF_FLAGS(tch).unset(EAffectFlag::AFF_GROUP); - tch->removeGroupFlags(); - act("$N более не член вашей группы.", FALSE, ch, 0, tch, TO_CHAR); - act("Вы исключены из группы $n1!", FALSE, ch, 0, tch, TO_VICT); - act("$N был$G изгнан$A из группы $n1!", FALSE, ch, 0, tch, TO_NOTVICT | TO_ARENA_LISTEN); - stop_follower(tch, SF_EMPTY); - return; - } - } - send_to_char("Этот игрок не входит в состав вашей группы.\r\n", ch); - return; + groupRoster.processGroupCommands(ch, argument); } + void do_gsay(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) { - CHAR_DATA *k; - struct follow_type *f; + CHAR_DATA* gc; if (AFF_FLAGGED(ch, EAffectFlag::AFF_SILENCE) - || AFF_FLAGGED(ch, EAffectFlag::AFF_STRANGLED)) - { + || AFF_FLAGGED(ch, EAffectFlag::AFF_STRANGLED)) { send_to_char("Вы немы, как рыба об лед.\r\n", ch); return; } - if (!IS_NPC(ch) && PLR_FLAGGED(ch, PLR_DUMB)) - { + if (!IS_NPC(ch) && PLR_FLAGGED(ch, PLR_DUMB)) { send_to_char("Вам запрещено обращаться к другим игрокам!\r\n", ch); return; } skip_spaces(&argument); - if (!AFF_FLAGGED(ch, EAffectFlag::AFF_GROUP)) - { + if (!IN_GROUP(ch)) { send_to_char("Вы не являетесь членом группы!\r\n", ch); return; } - - if (!*argument) - { + if (!*argument) { send_to_char("О чем вы хотите сообщить своей группе?\r\n", ch); + return; } - else - { - if (ch->has_master()) - { - k = ch->get_master(); - } - else - { - k = ch; - } - sprintf(buf, "$n сообщил$g группе : '%s'", argument); + auto group = ch->personGroup; + sprintf(smallBuf, "$n сообщил$g группе : '%s'", argument); - if (AFF_FLAGGED(k, EAffectFlag::AFF_GROUP) - && k != ch - && !ignores(k, ch, IGNORE_GROUP)) - { - act(buf, FALSE, ch, 0, k, TO_VICT | TO_SLEEP | CHECK_DEAF); - // added by WorM групптелы 2010.10.13 - if(!AFF_FLAGGED(k, EAffectFlag::AFF_DEAFNESS) - && GET_POS(k) > POS_DEAD) - { - sprintf(buf1, "%s сообщил%s группе : '%s'\r\n", tell_can_see(ch, k) ? GET_NAME(ch) : "Кто-то", GET_CH_VIS_SUF_1(ch, k), argument); - k->remember_add(buf1, Remember::ALL); - k->remember_add(buf1, Remember::GROUP); - } - //end by WorM - } - for (f = k->followers; f; f = f->next) - { - if (AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP) - && (f->follower != ch) - && !ignores(f->follower, ch, IGNORE_GROUP)) - { - act(buf, FALSE, ch, 0, f->follower, TO_VICT | TO_SLEEP | CHECK_DEAF); + for (auto it : *group) { + gc = it.second->member; + if (gc == nullptr || it.second->type == GM_CHARMEE || ignores(gc, ch, IGNORE_GROUP)) + continue; + if (gc == ch) { + if (!PRF_FLAGGED(ch, PRF_NOREPEAT)) { + sprintf(buf, "Вы сообщили группе : '%s'\r\n", argument); + send_to_char(buf, ch); // added by WorM групптелы 2010.10.13 - if (!AFF_FLAGGED(f->follower, EAffectFlag::AFF_DEAFNESS) - && GET_POS(f->follower) > POS_DEAD) - { - sprintf(buf1, "%s сообщил%s группе : '%s'\r\n", tell_can_see(ch, f->follower) ? GET_NAME(ch) : "Кто-то", GET_CH_VIS_SUF_1(ch, f->follower), argument); - f->follower->remember_add(buf1, Remember::ALL); - f->follower->remember_add(buf1, Remember::GROUP); - } - //end by WorM + ch->remember_add(buf, Remember::ALL); + ch->remember_add(buf, Remember::GROUP); + } else { + send_to_char(OK, ch); } - } + continue; - if (PRF_FLAGGED(ch, PRF_NOREPEAT)) - send_to_char(OK, ch); - else + } + act(smallBuf, FALSE, ch, 0, gc, TO_VICT | TO_SLEEP | CHECK_DEAF); + if (!AFF_FLAGGED(gc, EAffectFlag::AFF_DEAFNESS) + && GET_POS(gc) > POS_DEAD) { - sprintf(buf, "Вы сообщили группе : '%s'\r\n", argument); - send_to_char(buf, ch); - // added by WorM групптелы 2010.10.13 - ch->remember_add(buf, Remember::ALL); - ch->remember_add(buf, Remember::GROUP); - //end by WorM + sprintf(buf, "%s сообщил%s группе : '%s'\r\n", tell_can_see(ch, gc) ? GET_NAME(ch) : "Кто-то", GET_CH_VIS_SUF_1(ch, gc), argument); + gc->remember_add(buf, Remember::ALL); + gc->remember_add(buf, Remember::GROUP); } } + } @@ -574,7 +204,7 @@ void do_report(CHAR_DATA *ch, char* /*argument*/, int/* cmd*/, int/* subcmd*/) CAP(buf); if (IN_GROUP(ch)) - ch->personGroup->sendToGroup(GRP_COMM_ALL, buf); + ch->personGroup->actToGroup(nullptr, nullptr, grpActMode(GC_LEADER | GC_REST), buf); else {//чармис докладывает мастеру k = ch->has_master() ? ch->get_master() : ch; for (f = k->followers; f; f = f->next) { @@ -730,3 +360,8 @@ void do_split(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/,int cur void do_split(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) { do_split(ch,argument,0,0,0); } + +grpActMode::grpActMode(grpActMode::GRP_COMM val) { + this->set(); + +} diff --git a/src/grp/grp.group.cpp b/src/grp/grp.group.cpp index 4526cf0a9..f50a27e4a 100644 --- a/src/grp/grp.group.cpp +++ b/src/grp/grp.group.cpp @@ -159,10 +159,10 @@ void Group::addMember(CHAR_DATA *member, bool silent) { auto it = this->find(_calcUID(member)); if (it == this->end()) { - // рисуем сообщение до добавления, чтобы самому персонажу не показывать if (!silent) { - actToGroup(member, GRP_COMM_ALL, "$N принят$A в группу."); - send_to_char(member, "Вас приняли в группу.\r\n"); + actToGroup(nullptr, member, grpActMode(GC_LEADER), "$N принят$A в члены вашего кружка (тьфу-ты, группы :)."); + actToGroup(member, _leader, grpActMode(GC_CHAR) , "Вы приняты в группу $N1."); + actToGroup(member, _leader, grpActMode(GC_REST) , "$n принят$a в группу $N1."); } auto ci = char_info(_calcUID(member), getType(member), member, member->get_pc_name()); this->emplace(_calcUID(member), std::make_shared(ci)); @@ -173,6 +173,9 @@ void Group::addMember(CHAR_DATA *member, bool silent) { return; } member->personGroup = this; + if (!IS_NPC(member) && _maxPlayerLevel < GET_LEVEL(member)) + _maxPlayerLevel = GET_LEVEL(member); + auto r = groupRoster.findRequest(member->get_pc_name().c_str(), getLeaderName().c_str(), RQ_ANY); if (r) groupRoster.deleteRequest(r); @@ -203,9 +206,18 @@ bool Group::_removeMember(int memberUID) { send_to_char(member, "Вы покинули группу.\r\n"); member->personGroup = nullptr; } - sendToGroup(GRP_COMM_ALL, "Персонаж %s покинул группу.", it->second->memberName.c_str()); + actToGroup(nullptr, it->second->member, grpActMode(GC_ROOM) , "%n покинул группу."); this->erase(memberUID); // finally remove it - + // пересчитываем макс.уровень + if ( GET_LEVEL(member) == _maxPlayerLevel) { + _maxPlayerLevel = 1; + for (auto m: *this) { + if (m.second->member == nullptr || m.second->type == GM_CHARMEE) + continue; + if (GET_LEVEL(m.second->member) > _maxPlayerLevel) + _maxPlayerLevel = GET_LEVEL(m.second->member); + } + } // а также удаляем чармисов персонажа из групповых if (member->followers != nullptr) { @@ -287,7 +299,7 @@ void Group::_clear(bool silentMode) { void Group::promote(char *applicant) { auto member = _findMember(applicant); if (member == nullptr || member->member == nullptr) { - sendToGroup(GRP_COMM_LEADER, "Нет такого персонажа."); + actToGroup(_leader, nullptr, grpActMode(GC_LEADER), "Нет такого персонажа."); return; } _promote(member->member); @@ -324,15 +336,15 @@ void Group::approveRequest(const char *applicant) { void Group::expellMember(char *memberName) { auto vict = _findMember(memberName); if (vict == nullptr) { - sendToGroup(GRP_COMM_LEADER, "Нет такого персонажа."); + actToGroup(_leader, nullptr, grpActMode(GC_LEADER), "Нет такого персонажа."); return; } if (vict != nullptr && vict->member != nullptr) { - act("$N исключен$A из состава вашей группы.", FALSE, _leader, nullptr, vict, TO_CHAR); - act("Вы исключены из группы $n1!", FALSE, _leader, nullptr, vict, TO_VICT); - act("$N был$G исключен$A из группы $n1!", FALSE, _leader, nullptr, vict, TO_NOTVICT | TO_ARENA_LISTEN); + act("$N исключен$A из состава вашей группы.", FALSE, _leader, nullptr, vict->member, TO_CHAR); + act("Вы исключены из группы $n1!", FALSE, _leader, nullptr, vict->member, TO_VICT); + act("$N был$G исключен$A из группы $n1!", FALSE, _leader, nullptr, vict->member, TO_NOTVICT | TO_ARENA_LISTEN); } else { - sendToGroup(GRP_COMM_LEADER, "%s был исключен из группы.'\r\n", vict->memberName.c_str()); + actToGroup(nullptr, nullptr, grpActMode(GC_ROOM), "%s был исключен из группы.'\r\n", vict->memberName.c_str()); } _removeMember(vict->memberUID); } @@ -561,6 +573,7 @@ void Group::charDataPurged(CHAR_DATA *ch) { } } +/* void Group::sendToGroup(GRP_COMM mode, const char *msg, ...) { va_list args; va_start(args, msg); @@ -574,27 +587,28 @@ void Group::sendToGroup(GRP_COMM mode, const char *msg, ...) { send_to_char(_leader, "%s%s", smallBuf, "\r\n"); } } +*/ -// надстройка над act, пока только про персонажей -void Group::actToGroup(CHAR_DATA* vict, GRP_COMM mode, const char *msg, ...) { - if (vict == nullptr) - return; +// надстройка над act, регулируется набором флагов grpActMode + GRP_COMM +// vict передается в аргумент функции act +void Group::actToGroup(CHAR_DATA* ch, CHAR_DATA* vict, grpActMode mode, const char *msg, ...) { va_list args; va_start(args, msg); vsnprintf(smallBuf, sizeof(smallBuf), msg, args); va_end(args); CHAR_DATA* to; - if (mode == GRP_COMM_LEADER) { - act(smallBuf, FALSE, _leader, nullptr, vict, TO_CHAR); - return; - } for (auto &it : *this) { to = it.second->member; if ( to == nullptr || to->purged()) { continue; } - act(smallBuf, FALSE, to, nullptr, vict, TO_CHAR); + if ((mode.test(GRP_COMM::GC_LEADER) && to == _leader) || + (mode.test(GRP_COMM::GC_CHAR) && to == ch) || + (mode.test(GRP_COMM::GC_REST) && to != ch && to != _leader)) + act(smallBuf, TRUE, to, nullptr, vict, TO_CHAR); } + if (mode.test(GRP_COMM::GC_ROOM)) + act(smallBuf, TRUE, ch, nullptr, nullptr, TO_ROOM | TO_ARENA_LISTEN); } void Group::addFollowers(CHAR_DATA *leader) { @@ -654,7 +668,7 @@ void Group::_promote(CHAR_DATA *member) { if (_leader == member) return; _setLeader(member); - sendToGroup(GRP_COMM_ALL, "Изменился лидер группы на %s.", _leaderName.c_str() ); + actToGroup(nullptr, nullptr, grpActMode(GC_LEADER | GC_REST), "Изменился лидер группы на %s.", _leaderName.c_str()); auto diff = (u_short)_memberCap - (u_short)this->size(); if (diff < 0) diff = abs(diff); else diff = 0; if (diff > 0){ @@ -663,7 +677,7 @@ void Group::_promote(CHAR_DATA *member) { for (auto it = this->begin(); it != this->end() && i < diff; it++){ if (it->second->member != _leader ) { expellList[i] = _calcUID(it->second->member); - sendToGroup(GRP_COMM_ALL, "В группе не хватает места, нас покинул %s!", it->second->memberName.c_str() ); + actToGroup(nullptr, nullptr, grpActMode(GC_LEADER | GC_REST), "В группе не хватает места, нас покинул %s!", it->second->memberName.c_str()); ++i; } } @@ -685,128 +699,6 @@ bool same_group(CHAR_DATA * ch, CHAR_DATA * tch) } -/*++ - Функция начисления опыта - ch - кому опыт начислять - Вызов этой функции для NPC смысла не имеет, но все равно - какие-то проверки внутри зачем то делаются ---*/ -void perform_group_gain(CHAR_DATA * ch, CHAR_DATA * victim, int members, int koef) -{ - - -// Странно, но для NPC эта функция тоже должна работать -// if (IS_NPC(ch) || !OK_GAIN_EXP(ch,victim)) - if (!OK_GAIN_EXP(ch, victim)) - { - send_to_char("Ваше деяние никто не оценил.\r\n", ch); - return; - } - - // 1. Опыт делится поровну на всех - int exp = GET_EXP(victim) / MAX(members, 1); - - if(victim->get_zone_group() > 1 && members < victim->get_zone_group()) - { - // в случае груп-зоны своего рода планка на мин кол-во человек в группе - exp = GET_EXP(victim) / victim->get_zone_group(); - } - - // 2. Учитывается коэффициент (лидерство, разность уровней) - // На мой взгляд его правильней использовать тут а не в конце процедуры, - // хотя в большинстве случаев это все равно - exp = exp * koef / 100; - - // 3. Вычисление опыта для PC и NPC - if (IS_NPC(ch)) - { - exp = MIN(max_exp_gain_npc, exp); - exp += MAX(0, (exp * MIN(4, (GET_LEVEL(victim) - GET_LEVEL(ch)))) / 8); - } - else - exp = MIN(max_exp_gain_pc(ch), get_extend_exp(exp, ch, victim)); - // 4. Последняя проверка - exp = MAX(1, exp); - if (exp > 1) - { - if (Bonus::is_bonus(Bonus::BONUS_EXP)) - { - exp *= Bonus::get_mult_bonus(); - } - - if (!IS_NPC(ch) && !ch->affected.empty()) { - for (const auto& aff : ch->affected) { - if (aff->location == APPLY_BONUS_EXP) { - // скушал свиток с эксп бонусом - exp *= MIN(3, aff->modifier); // бонус макс тройной - } - } - } - - int long_live_bonus = floor(ExpCalc::get_npc_long_live_exp_bounus( GET_MOB_VNUM(victim))); - if (long_live_bonus>1) { - std::string mess; - switch (long_live_bonus) { - case 2: - mess = "Редкая удача! Опыт повышен!\r\n"; - break; - case 3: - mess = "Очень редкая удача! Опыт повышен!\r\n"; - break; - case 4: - mess = "Очень-очень редкая удача! Опыт повышен!\r\n"; - break; - case 5: - mess = "Вы везунчик! Опыт повышен!\r\n"; - break; - case 6: - mess = "Ваша удача велика! Опыт повышен!\r\n"; - break; - case 7: - mess = "Ваша удача достигла небес! Опыт повышен!\r\n"; - break; - case 8: - mess = "Ваша удача коснулась луны! Опыт повышен!\r\n"; - break; - case 9: - mess = "Ваша удача затмевает солнце! Опыт повышен!\r\n"; - break; - case 10: - mess = "Ваша удача выше звезд! Опыт повышен!\r\n"; - break; - default: - mess = "Ваша удача выше звезд! Опыт повышен!\r\n"; - break; - } - send_to_char(mess.c_str(), ch); - } - - exp = MIN(max_exp_gain_pc(ch), exp); - send_to_char(ch, "Ваш опыт повысился на %d %s.\r\n", exp, desc_count(exp, WHAT_POINT)); - } - else if (exp == 1) { - send_to_char("Ваш опыт повысился всего лишь на маленькую единичку.\r\n", ch); - } - gain_exp(ch, exp); - GET_ALIGNMENT(ch) += (-GET_ALIGNMENT(victim) - GET_ALIGNMENT(ch)) / 16; - TopPlayer::Refresh(ch); - - if (!EXTRA_FLAGGED(victim, EXTRA_GRP_KILL_COUNT) - && !IS_NPC(ch) - && !IS_IMMORTAL(ch) - && IS_NPC(victim) - && !IS_CHARMICE(victim) - && !ROOM_FLAGGED(IN_ROOM(victim), ROOM_ARENA)) - { - mob_stat::add_mob(victim, members); - EXTRA_FLAGS(victim).set(EXTRA_GRP_KILL_COUNT); - } - else if (IS_NPC(ch) && !IS_NPC(victim) - && !ROOM_FLAGGED(IN_ROOM(victim), ROOM_ARENA)) - { - mob_stat::add_mob(ch, 0); - } -} /*++ Функция расчитывает всякие бонусы для группы при получении опыта, @@ -821,146 +713,109 @@ void perform_group_gain(CHAR_DATA * ch, CHAR_DATA * victim, int members, int koe --*/ -void group_gain(CHAR_DATA * killer, CHAR_DATA * victim) -{ - int inroom_members, koef = 100, maxlevel; - struct follow_type *f; - int partner_count = 0; - int total_group_members = 1; - bool use_partner_exp = false; - - // если наем лидер, то тоже режем экспу - if (can_use_feat(killer, CYNIC_FEAT)) { - maxlevel = 300; - } - else { - maxlevel = GET_LEVEL(killer); - } - - auto leader = killer->get_master(); - if (nullptr == leader) { - leader = killer; - } +void Group::gainExp(CHAR_DATA * victim) { + int inroom_members = 0; + u_short expMultiplicator = 0; + u_short expDivider = 1; + CHAR_DATA* ch; - // k - подозрение на лидера группы - const bool leader_inroom = AFF_FLAGGED(leader, EAffectFlag::AFF_GROUP) - && SAME_ROOM(leader, killer); + // на всякий пожарный проверим + if (!victim) + return; - // Количество согрупников в комнате - if (leader_inroom) { - inroom_members = 1; - maxlevel = GET_LEVEL(leader); - } - else { - inroom_members = 0; - } + // есть ли в комнате живой лидер + const bool leader_inroom = SAME_ROOM(_leader, victim); - // Вычисляем максимальный уровень в группе - for (f = leader->followers; f; f = f->next) - { - if (AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP)) ++total_group_members; - if (AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP) - && SAME_ROOM(f->follower, killer)) - { - // если в группе наем, то режим опыт всей группе - // дабы наема не выгодно было бы брать в группу - // ставим 300, чтобы вообще под ноль резало - if (can_use_feat(f->follower, CYNIC_FEAT)) - { - maxlevel = 300; - } - // просмотр членов группы в той же комнате - // член группы => PC автоматически + // прежде чем раздать опыт, считаем количество персонажей в клетке + for (auto it: *this) { + ch = it.second->member; + if (ch != nullptr && SAME_ROOM(ch, victim) && it.second->type == GM_CHAR) { ++inroom_members; - maxlevel = MAX(maxlevel, GET_LEVEL(f->follower)); - if (!IS_NPC(f->follower)) - { - partner_count++; - } } } + // есть партнерка и персонажей двое и зона негрупповая + if (leader_inroom and inroom_members == 2 and can_use_feat(_leader, PARTNER_FEAT) and _leader->get_zone_group() <2) + expDivider = 1; + else + expDivider = inroom_members; - GroupPenaltyCalculator group_penalty(killer, leader, maxlevel, groupRoster.grouping); - koef -= group_penalty.get(); - - koef = MAX(0, koef); - - // Лидерство используется, если в комнате лидер и есть еще хоть кто-то - // из группы из PC (последователи типа лошади или чармисов не считаются) - if (koef >= 100 && leader_inroom && (inroom_members > 1) && calc_leadership(leader)) - { - koef += 20; - } - - // Раздача опыта - - // если групповой уровень зоны равняется единице - if (zone_table[world[killer->in_room]->zone].group < 2) - { - // чтобы не абьюзили на суммонах, когда в группе на самом деле больше - // двух мемберов, но лишних реколят перед непосредственным рипом - use_partner_exp = total_group_members == 2; - } - - // если лидер группы в комнате - if (leader_inroom) - { - // если у лидера группы есть способность напарник - if (can_use_feat(leader, PARTNER_FEAT) && use_partner_exp) - { - // если в группе всего двое человек - // k - лидер, и один последователь - if (partner_count == 1) - { - // и если кожф. больше или равен 100 - if (koef >= 100) - { - if (leader->get_zone_group() < 2) - { - koef += 100; - } - } - } - } - perform_group_gain(leader, victim, inroom_members, koef); - } - - for (f = leader->followers; f; f = f->next) - { - if (AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP) - && SAME_ROOM(f->follower, killer)) - { - perform_group_gain(f->follower, victim, inroom_members, koef); + // погнали раздавать опыт + for (auto m: *this) { + ch = m.second->member; + if (ch != nullptr && SAME_ROOM(ch, victim) && m.second->type == GM_CHAR) { + expMultiplicator = calcExpMultiplicator(ch); // цифра процента } + // если прокнула лидерка, то добавляем еще 20 + if (leader_inroom && calc_leadership(_leader) and inroom_members > 1) + expMultiplicator += 20; + increaseExperience(ch, victim, expDivider, expMultiplicator); } } - -int calc_leadership(CHAR_DATA * ch) +bool calc_leadership(CHAR_DATA * ch) { int prob, percent; if (IS_NPC(ch) || ch->personGroup == nullptr) { - return FALSE; + return false; } CHAR_DATA* leader = ch->personGroup->getLeader(); // если лидер умер или нет в комнате - фиг вам, а не бонусы - if (leader == nullptr || IN_ROOM(ch) != IN_ROOM(leader)) { - return FALSE; + if (!SAME_ROOM(ch, leader)) { + return false; } if (!leader->get_skill(SKILL_LEADERSHIP)) { - return (FALSE); + return false; } percent = number(1, 101); prob = calculate_skill(leader, SKILL_LEADERSHIP, 0); if (percent > prob) { - return (FALSE); + return false; } else { - return (TRUE); + return true; } } + + +u_short Group::calcExpMultiplicator(const CHAR_DATA* player) +{ + const int player_remorts = static_cast(GET_REMORT(player)); + const int player_class = static_cast(GET_CLASS(player)); + const int player_level = GET_LEVEL(player); + auto m_grouping = groupRoster.grouping; + short result = DEFAULT_100; + + if (IS_NPC(player)) + { + log("LOGIC ERROR: try to get penalty for NPC [%s], VNum: %d\n", + player->get_name().c_str(), + GET_MOB_VNUM(player)); + return result; + } + + if (0 > player_class || player_class > NUM_PLAYER_CLASSES) { + log("LOGIC ERROR: wrong player class: %d for player [%s]", + player_class, + player->get_name().c_str()); + return result; + + } + + if (0 > player_remorts || player_remorts > MAX_REMORT){ + log("LOGIC ERROR: wrong number of remorts: %d for player [%s]", + player_remorts, + player->get_name().c_str()); + return result; + } + + if (_maxPlayerLevel - player_level > m_grouping[player_class][player_remorts]) { + return MAX(1, DEFAULT_100 - 3 * (_maxPlayerLevel - player_level)); + } + return DEFAULT_100; +} + diff --git a/src/grp/grp.main.h b/src/grp/grp.main.h index 4630bd12d..7c9673697 100644 --- a/src/grp/grp.main.h +++ b/src/grp/grp.main.h @@ -19,19 +19,28 @@ enum GM_TYPE {GM_CHAR, GM_CHARMEE}; enum RQ_TYPE {RQ_GROUP, RQ_PERSON, RQ_ANY}; -enum GRP_COMM {GRP_COMM_LEADER, GRP_COMM_ALL, GRP_COMM_OTHER}; + +class grpActMode:std::bitset<4> { +public: + enum GRP_COMM : short { + GC_LEADER = 0, // только лидеру + GC_CHAR = 1, // только персонажу + GC_REST = 2, // всем остальным + GC_ROOM = 3 // эхо в комнату + }; + grpActMode(GRP_COMM val); +}; enum RQ_R {RQ_R_OK, RQ_R_NO_GROUP, RQ_R_OVERFLOW, RQ_REFRESH}; enum INV_R {INV_R_OK, INV_R_NO_PERSON, INV_R_BUSY, INV_R_REFRESH}; void do_grequest(CHAR_DATA *ch, char *argument, int, int); -void change_leader(CHAR_DATA *ch, CHAR_DATA *vict); -void do_group(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/); void do_group2(CHAR_DATA *ch, char *argument, int, int); void do_ungroup(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/); void do_report(CHAR_DATA *ch, char* /*argument*/, int/* cmd*/, int/* subcmd*/); int max_group_size(CHAR_DATA *ch); bool isGroupedFollower(CHAR_DATA* master, CHAR_DATA* vict); -int calc_leadership(CHAR_DATA * ch); +// возвращает true, если рядом с персонажем лидер группы и прокнул скилл +bool calc_leadership(CHAR_DATA * ch); class Request; @@ -49,6 +58,10 @@ struct char_info { sclock_t expiryTime; // время, когда запись автоматом удаляется после проверок. }; +struct PenaltyCalcData { + int penalty; +}; + using grp_mt = std::map>; using grp_ptr = std::shared_ptr; using rq_ptr = std::shared_ptr; @@ -60,6 +73,21 @@ const duration DEF_EXPIRY_TIME = 600s; inline bool IN_GROUP(CHAR_DATA* ch) {return ch != nullptr && ch->personGroup != nullptr;} inline bool IN_SAME_GROUP(CHAR_DATA* p1, CHAR_DATA* p2) {return IN_GROUP(p1) && IN_GROUP(p2) && p1->personGroup == p2->personGroup;} +// класс, хранящий обвязку штрафов по экспе для группы +// наркоман писал, не иначе +class GroupPenalties +{ +public: + using class_penalties_t = std::array; + using penalties_t = std::array; + + int init(); + const auto& operator[](const size_t player_class) const { return m_grouping[player_class]; } +private: + penalties_t m_grouping; +}; + + // класс-коллекция персонажей обоих типов что ли.. class Group : public grp_mt { private: @@ -73,6 +101,8 @@ class Group : public grp_mt { std::string _leaderName; // ссылка на персонажа, АХТУНГ! Может меняться и быть невалидным CHAR_DATA* _leader = nullptr; + // макс.уровень игрока в группе, для расчета штрафа + u_short _maxPlayerLevel = 1; public: u_long getUid() const; const std::string &getLeaderName() const; @@ -107,6 +137,8 @@ class Group : public grp_mt { static void _printPCLine(CHAR_DATA* ch, CHAR_DATA* pc, int header); bool _sameGroup(CHAR_DATA * ch, CHAR_DATA * vict); public: + constexpr static int DEFAULT_100 = 100; + void addFollowers(CHAR_DATA* leader); void addMember(CHAR_DATA *member, bool silent = false); void expellMember(char* memberName); @@ -120,53 +152,17 @@ class Group : public grp_mt { void rejectRequest(char *applicant); void leaveGroup(CHAR_DATA* vict); - void sendToGroup(GRP_COMM mode, const char *msg, ...); - void actToGroup(CHAR_DATA* vict, GRP_COMM mode, const char *msg, ...); +// void sendToGroup(GRP_COMM mode, const char *msg, ...); + void actToGroup(CHAR_DATA* ch, CHAR_DATA* vict, grpActMode mode, const char *msg, ...); + u_short calcExpMultiplicator(const CHAR_DATA* player); public: // всякий унаследованный стафф CHAR_DATA* get_random_pc_group(); // лень обвязывать, тупо переместил объект DpsSystem::GroupListType _group_dps; bool has_clan_members_in_group(CHAR_DATA *victim); - -}; - -// это писал йобаный наркоман -class GroupPenalties -{ -public: - using class_penalties_t = std::array; - using penalties_t = std::array; - - int init(); - const auto& operator[](const size_t character_class) const { return m_grouping[character_class]; } - -private: - penalties_t m_grouping; -}; - -class GroupPenaltyCalculator -{ -public: - constexpr static int DEFAULT_PENALTY = 100; - - GroupPenaltyCalculator(const CHAR_DATA* killer, const CHAR_DATA* leader, const int max_level, const GroupPenalties& grouping): - m_killer(killer), - m_leader(leader), - m_max_level(max_level), - m_grouping(grouping) - { - } - - int get() const; - -private: - const CHAR_DATA* m_killer; - const CHAR_DATA* m_leader; - const int m_max_level; - const GroupPenalties& m_grouping; - - bool penalty_by_leader(const CHAR_DATA* player, int& penalty) const; + // группа получает экспу за убивство + void gainExp(CHAR_DATA * victim); }; class Request { @@ -185,6 +181,7 @@ class GroupRoster { // properties public: GroupRoster(); + void charDataPurged(CHAR_DATA* ch); // очистка заявок при пурже персонажа void restorePlayerGroup(CHAR_DATA* ch); // возвращает игрока в группу после смерти void processGroupCommands(CHAR_DATA *ch, char *argument); void printList(CHAR_DATA *ch); diff --git a/src/grp/grp.roster.cpp b/src/grp/grp.roster.cpp index 3f3413acb..c5717d004 100644 --- a/src/grp/grp.roster.cpp +++ b/src/grp/grp.roster.cpp @@ -19,7 +19,7 @@ void GroupRoster::restorePlayerGroup(CHAR_DATA *ch) { return; if (!grp->_restoreMember(ch)) return; - grp->actToGroup(ch, GRP_COMM_ALL, "$N заново присоединил$A к вашей группе."); + grp->actToGroup(nullptr, ch,grpActMode(GC_LEADER | GC_REST), "$N заново присоединил$A к вашей группе."); } void GroupRoster::processGroupCommands(CHAR_DATA *ch, char *argument) { @@ -187,7 +187,7 @@ void GroupRoster::printList(CHAR_DATA *ch) { size_t cnt = this->_groupList.size(); send_to_char(ch, "Текущее количество групп в мире: %lu\r\n", cnt); for (auto & it : this->_groupList) { - send_to_char(ch, "Группа лидера %s, кол-во участников: %hu\r\n", + send_to_char(ch, "Группа лидера %s, кол-во участников: %lu\r\n", it.second->getLeaderName().c_str(), it.second->size()); } @@ -293,7 +293,7 @@ void GroupRoster::makeRequest(CHAR_DATA *author, char* target) { return; case RQ_R::RQ_R_OK: send_to_char("Заявка на вступление в группу отправлена.\r\n", author); - grp->actToGroup(author, GRP_COMM_LEADER, "Получена заявка от $N1 на вступление в группу.\r\n"); + grp->actToGroup(nullptr, author, grpActMode(GC_LEADER), "Получена заявка от $N1 на вступление в группу.\r\n"); break; case RQ_R::RQ_REFRESH: send_to_char("Заявка успешно продлена.\r\n", author); @@ -377,3 +377,19 @@ void GroupRoster::acceptInvite(CHAR_DATA* who, char* author) { void GroupRoster::runTests(CHAR_DATA *leader) { } + +// удаление заявок при пурже персонажа +void GroupRoster::charDataPurged(CHAR_DATA *ch) { + if (IS_NPC(ch)) + return; + for (auto r = _requestList.begin(); r != _requestList.end();){ + if (r->get()->_applicant == ch) { + _requestList.erase(r); + } else { + ++r; + } + + + + } +} diff --git a/src/handler.cpp b/src/handler.cpp index 5b776c610..a0a6bbfb5 100644 --- a/src/handler.cpp +++ b/src/handler.cpp @@ -78,7 +78,6 @@ void do_entergame(DESCRIPTOR_DATA * d); void do_return(CHAR_DATA *ch, char *argument, int cmd, int subcmd); extern std::vector cities; extern int global_uid; -extern void change_leader(CHAR_DATA *ch, CHAR_DATA *vict); extern char *find_exdesc(char *word, const EXTRA_DESCR_DATA::shared_ptr& list); extern void setSkillCooldown(CHAR_DATA* ch, ESkill skill, int cooldownInPulses); diff --git a/src/interpreter.cpp b/src/interpreter.cpp index c7ab0b8c1..487e1f10f 100644 --- a/src/interpreter.cpp +++ b/src/interpreter.cpp @@ -870,10 +870,9 @@ cpp_extern const struct command_info cmd_info[] = {"gossip", POS_RESTING, do_gen_comm, 0, SCMD_GOSSIP, -1}, {"goto", POS_SLEEPING, do_goto, LVL_GOD, 0, 0}, {"grab", POS_RESTING, do_grab, 0, 0, 500}, - {"group", POS_RESTING, do_group, 1, 0, 500}, + {"group", POS_RESTING, do_group2, 1, 0, 500}, {"gsay", POS_SLEEPING, do_gsay, 0, 0, -1}, {"gtell", POS_SLEEPING, do_gsay, 0, 0, -1}, - {"group2", POS_RESTING, do_group2, 1, 0, 500}, {"grequest", POS_RESTING, do_grequest, 1, 0, 500}, {"handbook", POS_DEAD, do_gen_ps, LVL_IMMORT, SCMD_HANDBOOK, 0}, {"hcontrol", POS_DEAD, DoHcontrol, LVL_GRGOD, 0, 0}, diff --git a/src/skills/chopoff.cpp b/src/skills/chopoff.cpp index 919dbf220..15cee1346 100644 --- a/src/skills/chopoff.cpp +++ b/src/skills/chopoff.cpp @@ -87,7 +87,7 @@ void go_chopoff(CHAR_DATA * ch, CHAR_DATA * vict) { act("$n ловко подсек$q $N3, уронив $S на землю.", TRUE, ch, 0, vict, TO_NOTVICT | TO_ARENA_LISTEN); set_wait(vict, 3, FALSE); - if (ch->isInSameRoom(vict)) { + if (SAME_ROOM(ch, vict)) { GET_POS(vict) = POS_SITTING; } diff --git a/src/skills/strangle.cpp b/src/skills/strangle.cpp index 8a659c112..75340bc5b 100644 --- a/src/skills/strangle.cpp +++ b/src/skills/strangle.cpp @@ -68,7 +68,7 @@ void go_strangle(CHAR_DATA * ch, CHAR_DATA * vict) { act("Рванув на себя, $N стащил$G $n3 на землю.", FALSE, vict, nullptr, ch, TO_NOTVICT | TO_ARENA_LISTEN); vict->drop_from_horse(); } - if (ch->get_skill(SKILL_CHOPOFF) && ch->isInSameRoom(vict)) { + if (ch->get_skill(SKILL_CHOPOFF) && SAME_ROOM(ch, vict)) { go_chopoff(ch, vict); } //set_wait(ch, 2, TRUE); diff --git a/src/structs.h b/src/structs.h index 3602bf73b..3d570649f 100644 --- a/src/structs.h +++ b/src/structs.h @@ -620,7 +620,7 @@ extern const religion_names_t religion_name; #define PRF_TRIPLE_THROW (INT_TWO | 1 << 15) // готов использовать тройной бросок #define PRF_SHADOW_THROW (INT_TWO | 1 << 16) // применяет "теневой бросок" #define PRF_DISP_COOLDOWNS (INT_TWO | 1 << 17) // Показывать кулдауны скиллов в промпте -#define PRF_FOLLOW_GRP_EXIT (INT_TWO | 1 << 18) // Активирует телеграм-канал у персонажа +#define PRF_FOLLOW_GRP_EXIT (INT_TWO | 1 << 18) // Активирует автоматику выхода из группы при смене следования // при добавлении не забываем про preference_bits[] diff --git a/src/utils.cpp b/src/utils.cpp index 905d262ac..1651812dd 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -3963,5 +3963,19 @@ bool tell_can_see(CHAR_DATA *ch, CHAR_DATA *vict) { return false; } -} +}; + +bool SAME_ROOM(CHAR_DATA *ch, CHAR_DATA *tch) { + if (ch == nullptr || tch == nullptr) + return false; + return IN_ROOM(ch) == IN_ROOM(tch); +}; + +bool SAME_ROOM(const CHAR_DATA *ch, const CHAR_DATA *tch) { + if (ch == nullptr || tch == nullptr) + return false; + return IN_ROOM(ch) == IN_ROOM(tch); +}; + + // vim: ts=4 sw=4 tw=0 noet syntax=cpp : diff --git a/src/utils.h b/src/utils.h index d7d7a044c..ea1e723c1 100644 --- a/src/utils.h +++ b/src/utils.h @@ -564,7 +564,6 @@ inline void TOGGLE_BIT(T& var, const uint32_t bit) #define GET_TITLE(ch) ((ch)->player_data.title) #define GET_LEVEL(ch) ((ch)->get_level()) #define GET_MAX_MANA(ch) (mana[MIN(50, GET_REAL_WIS(ch))]) -#define SAME_ROOM(ch, tch) (IN_ROOM(ch) == IN_ROOM(tch)) #define GET_MANA_COST(ch,spellnum) mag_manacost(ch,spellnum) #define GET_MANA_STORED(ch) ((ch)->MemQueue.stored) #define GET_MEM_COMPLETED(ch) ((ch)->MemQueue.stored) @@ -1777,6 +1776,9 @@ class StreamFlagsHolder bool tell_can_see(CHAR_DATA *ch, CHAR_DATA *vict); +bool SAME_ROOM(CHAR_DATA *ch, CHAR_DATA *tch); +bool SAME_ROOM(const CHAR_DATA *ch, const CHAR_DATA *tch); + #endif // _UTILS_H_ // vim: ts=4 sw=4 tw=0 noet syntax=cpp : diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 72e2dc300..53158ca4e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -82,7 +82,7 @@ set(UTILITIES set(ADDITIONAL_FILES data/boards/changelog.sample data/boards/news.sample - data/plrs/bikbaj.player) + data/plrs/sample.player) source_group("Tests data" FILES ${ADDITIONAL_FILES}) source_group("Tests" FILES ${TESTS}) source_group("Tests helpers" FILES ${UTILITIES}) diff --git a/tests/char.utilities.cpp b/tests/char.utilities.cpp index e6fd809d9..69763e1d3 100644 --- a/tests/char.utilities.cpp +++ b/tests/char.utilities.cpp @@ -130,8 +130,8 @@ namespace test_utils m_result->add_follower_silently(character.get()); - perform_group(m_result.get(), m_result.get()); - perform_group(m_result.get(), character.get()); +// perform_group(m_result.get(), m_result.get()); +// perform_group(m_result.get(), character.get()); } void CharacterBuilder::check_character_existance() const From be609e60037d7b5d560494ed5aab685f3d139dbe Mon Sep 17 00:00:00 2001 From: Bikbai Date: Sun, 17 Jan 2021 00:12:13 +0300 Subject: [PATCH 23/40] =?UTF-8?q?=D0=94=D0=BE=D0=B4=D0=B5=D0=BB=D0=B0?= =?UTF-8?q?=D0=BB=20=D1=81=D0=BE=D0=BE=D0=B1=D1=89=D0=B5=D0=BD=D0=B8=D1=8F?= =?UTF-8?q?=20=D0=B8=20=D0=BF=D0=B5=D1=80=D0=B5=D0=B4=D0=B5=D0=BB=D0=B0?= =?UTF-8?q?=D0=BB=20=D0=B2=D1=81=D0=B5=20=D0=BE=D0=B1=D1=80=D0=B0=D0=B1?= =?UTF-8?q?=D0=BE=D1=82=D1=87=D0=B8=D0=BA=D0=B8=20=D0=B0=D1=84=D1=84=D0=B5?= =?UTF-8?q?=D0=BA=D1=82=D0=B0=20AFF=5FGROUP.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Остались группы мобов и режим выхода из группы, если сменил следование (след я -> выход) --- src/comm.cpp | 2 + src/grp/grp.cmdprocessor.cpp | 118 ++++++++--------------------------- src/grp/grp.group.cpp | 51 ++++++++------- src/grp/grp.main.h | 23 +++---- src/grp/grp.roster.cpp | 4 +- src/interpreter.cpp | 4 ++ 6 files changed, 76 insertions(+), 126 deletions(-) diff --git a/src/comm.cpp b/src/comm.cpp index e10a25b70..c86e2f322 100644 --- a/src/comm.cpp +++ b/src/comm.cpp @@ -3421,6 +3421,8 @@ void close_socket(DESCRIPTOR_DATA * d, int direct) if (STATE(d) == CON_PLAYING || STATE(d) == CON_DISCONNECT) { act("$n потерял$g связь.", TRUE, d->character.get(), 0, 0, TO_ROOM | TO_ARENA_LISTEN); + if (IN_GROUP(d->character.get())) + d->character->personGroup->actToGroup(nullptr, d->character.get(), GC_LEADER | GC_REST, "$n потерял$g связь."); if (d->character->get_fighting() && PRF_FLAGGED(d->character, PRF_ANTIDC_MODE)) { snprintf(buf2, sizeof(buf2), "зачитать свиток.возврата"); diff --git a/src/grp/grp.cmdprocessor.cpp b/src/grp/grp.cmdprocessor.cpp index 891ccf884..647e6dbce 100644 --- a/src/grp/grp.cmdprocessor.cpp +++ b/src/grp/grp.cmdprocessor.cpp @@ -54,36 +54,6 @@ void do_grequest(CHAR_DATA *ch, char *argument, int, int){ } -bool is_group_member(CHAR_DATA *ch, CHAR_DATA *vict) -{ - if (IS_NPC(vict) || !AFF_FLAGGED(vict, EAffectFlag::AFF_GROUP) || vict->get_master() != ch) - { - return false; - } - else - { - return true; - } -} - -int perform_group(CHAR_DATA * ch, CHAR_DATA * vict) -{ - if (AFF_FLAGGED(vict, EAffectFlag::AFF_GROUP) - || AFF_FLAGGED(vict, EAffectFlag::AFF_CHARM) - || MOB_FLAGGED(vict, MOB_ANGEL) - || MOB_FLAGGED(vict, MOB_GHOST) - || IS_HORSE(vict)) - { - return (FALSE); - } - - AFF_FLAGS(vict).set(EAffectFlag::AFF_GROUP); - if (ch != vict) - { - - } - return (TRUE); -} int max_group_size(CHAR_DATA *ch) { @@ -204,7 +174,7 @@ void do_report(CHAR_DATA *ch, char* /*argument*/, int/* cmd*/, int/* subcmd*/) CAP(buf); if (IN_GROUP(ch)) - ch->personGroup->actToGroup(nullptr, nullptr, grpActMode(GC_LEADER | GC_REST), buf); + ch->personGroup->actToGroup(nullptr, nullptr, GC_LEADER | GC_REST, buf); else {//чармис докладывает мастеру k = ch->has_master() ? ch->get_master() : ch; for (f = k->followers; f; f = f->next) { @@ -223,12 +193,18 @@ void do_report(CHAR_DATA *ch, char* /*argument*/, int/* cmd*/, int/* subcmd*/) void do_split(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/,int currency) { int amount, num, share, rest; - CHAR_DATA *k; - struct follow_type *f; + CHAR_DATA* m; + if (IS_NPC(ch)) return; + if (!IN_GROUP(ch)){ + send_to_char("Увы, но вам не с кем делиться.\r\n", ch); + return; + } + auto grp = ch->personGroup; + one_argument(argument, buf); int what_currency; @@ -244,49 +220,29 @@ void do_split(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/,int cur if (is_number(buf)) { amount = atoi(buf); - if (amount <= 0) - { + if (amount <= 0) { send_to_char("И как вы это планируете сделать?\r\n", ch); return; } - if (amount > ch->get_gold() && currency == currency::GOLD) - { + if (amount > ch->get_gold() && currency == currency::GOLD) { send_to_char("И где бы взять вам столько денег?.\r\n", ch); return; } - k = ch->has_master() ? ch->get_master() : ch; - if (AFF_FLAGGED(k, EAffectFlag::AFF_GROUP) - && (SAME_ROOM(k, ch))) - { - num = 1; - } - else - { - num = 0; - } - - for (f = k->followers; f; f = f->next) - { - if (AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP) - && !IS_NPC(f->follower) - && SAME_ROOM(f->follower, ch)) { + for (auto it : *grp) { + if (it.second->type == GM_CHAR &&SAME_ROOM(it.second->member, ch) ) num++; - } } - if (num && AFF_FLAGGED(ch, EAffectFlag::AFF_GROUP)) - { + if (num) { share = amount / num; rest = amount % num; } - else - { + else { send_to_char("С кем вы хотите разделить это добро?\r\n", ch); return; } - //MONEY_HACK switch (currency) { case currency::ICE : @@ -299,41 +255,23 @@ void do_split(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/,int cur sprintf(buf, "%s разделил%s %d %s; вам досталось %d.\r\n", GET_NAME(ch), GET_CH_SUF_1(ch), amount, desc_count(amount, what_currency), share); - if (AFF_FLAGGED(k, EAffectFlag::AFF_GROUP) && IN_ROOM(k) == ch->in_room && !IS_NPC(k) && k != ch) - { - send_to_char(buf, k); - switch (currency) - { - case currency::ICE : - { - k->add_ice_currency(share); + + for (auto it : *grp) { + m = it.second->member; + if (it.second->type != GM_CHAR || !SAME_ROOM(m, ch) || m == ch) + continue; + send_to_char(buf, m); + switch (currency) { + case currency::ICE : { + m->add_ice_currency(share); break; } - case currency::GOLD : - { - k->add_gold(share, true, true); + case currency::GOLD : { + m->add_gold(share, true, true); break; } } } - for (f = k->followers; f; f = f->next) - { - if (AFF_FLAGGED(f->follower, EAffectFlag::AFF_GROUP) - && !IS_NPC(f->follower) - && IN_ROOM(f->follower) == ch->in_room - && f->follower != ch) - { - send_to_char(buf, f->follower); - switch (currency) { - case currency::ICE : - f->follower->add_ice_currency(share); - break; - case currency::GOLD : - f->follower->add_gold(share, true, true); - break; - } - } - } sprintf(buf, "Вы разделили %d %s на %d - по %d каждому.\r\n", amount, desc_count(amount, what_currency), num, share); if (rest) @@ -361,7 +299,3 @@ void do_split(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) { do_split(ch,argument,0,0,0); } -grpActMode::grpActMode(grpActMode::GRP_COMM val) { - this->set(); - -} diff --git a/src/grp/grp.group.cpp b/src/grp/grp.group.cpp index f50a27e4a..0fbf19b79 100644 --- a/src/grp/grp.group.cpp +++ b/src/grp/grp.group.cpp @@ -159,13 +159,15 @@ void Group::addMember(CHAR_DATA *member, bool silent) { auto it = this->find(_calcUID(member)); if (it == this->end()) { - if (!silent) { - actToGroup(nullptr, member, grpActMode(GC_LEADER), "$N принят$A в члены вашего кружка (тьфу-ты, группы :)."); - actToGroup(member, _leader, grpActMode(GC_CHAR) , "Вы приняты в группу $N1."); - actToGroup(member, _leader, grpActMode(GC_REST) , "$n принят$a в группу $N1."); - } auto ci = char_info(_calcUID(member), getType(member), member, member->get_pc_name()); this->emplace(_calcUID(member), std::make_shared(ci)); + if (!IS_NPC(member)) + _pcCount++; + if (!silent) { + actToGroup(nullptr, member, GC_LEADER, "$N принят$A в члены вашего кружка (тьфу-ты, группы :)."); + actToGroup(member, _leader, GC_CHAR , "Вы приняты в группу $N1."); + actToGroup(nullptr, member, GC_REST , "$N принят$A в группу."); + } } else { sprintf(buf, "Group::addMember: группа id=%lu, попытка повторного добавления персонажа с тем же uid", getUid()); @@ -206,7 +208,9 @@ bool Group::_removeMember(int memberUID) { send_to_char(member, "Вы покинули группу.\r\n"); member->personGroup = nullptr; } - actToGroup(nullptr, it->second->member, grpActMode(GC_ROOM) , "%n покинул группу."); + actToGroup(nullptr, it->second->member, GC_ROOM , "%n покинул$g группу."); + if (it->second->type == GM_CHAR) + _pcCount--; this->erase(memberUID); // finally remove it // пересчитываем макс.уровень if ( GET_LEVEL(member) == _maxPlayerLevel) { @@ -299,7 +303,7 @@ void Group::_clear(bool silentMode) { void Group::promote(char *applicant) { auto member = _findMember(applicant); if (member == nullptr || member->member == nullptr) { - actToGroup(_leader, nullptr, grpActMode(GC_LEADER), "Нет такого персонажа."); + actToGroup(_leader, nullptr, GC_LEADER, "Нет такого персонажа."); return; } _promote(member->member); @@ -336,7 +340,7 @@ void Group::approveRequest(const char *applicant) { void Group::expellMember(char *memberName) { auto vict = _findMember(memberName); if (vict == nullptr) { - actToGroup(_leader, nullptr, grpActMode(GC_LEADER), "Нет такого персонажа."); + actToGroup(_leader, nullptr, GC_LEADER, "Нет такого персонажа."); return; } if (vict != nullptr && vict->member != nullptr) { @@ -344,7 +348,7 @@ void Group::expellMember(char *memberName) { act("Вы исключены из группы $n1!", FALSE, _leader, nullptr, vict->member, TO_VICT); act("$N был$G исключен$A из группы $n1!", FALSE, _leader, nullptr, vict->member, TO_NOTVICT | TO_ARENA_LISTEN); } else { - actToGroup(nullptr, nullptr, grpActMode(GC_ROOM), "%s был исключен из группы.'\r\n", vict->memberName.c_str()); + actToGroup(nullptr, nullptr, GC_ROOM, "%s был исключен из группы.'\r\n", vict->memberName.c_str()); } _removeMember(vict->memberUID); } @@ -352,17 +356,21 @@ void Group::expellMember(char *memberName) { void Group::listMembers(CHAR_DATA *ch) { CHAR_DATA *leader; int count = 1; + u_short inRoomCount = 0; if (ch->personGroup) { leader = ch->personGroup->getLeader(); - for (auto & it : *this ) - { + for (const auto rm : world[ch->in_room]->people) { + if (IN_SAME_GROUP(ch, rm)) + inRoomCount++; + } + for (auto & it : *this ) { if (it.second->member == leader) continue; count++; if (count == 2){ - send_to_char("Ваша группа состоит из:\r\n", ch); + send_to_char( ch, "Ваша группа [%d/%d/%d]:\r\n", _pcCount, _getMemberCap(), inRoomCount ); sprintf(smallBuf, "Лидер: %s\r\n", ch->personGroup->getLeaderName().c_str() ); send_to_char(smallBuf, ch); } @@ -522,7 +530,7 @@ void Group::printGroup(CHAR_DATA *ch) { for (auto &it : *this ){ if (it.second->type == GM_CHARMEE) continue; // чармисов скипуем и показываем ниже - if (it.second->member == nullptr || it.second->member->purged()) + if (it.second->member == nullptr || it.second->member->purged() || it.second->member->desc == NULL) _printDeadLine(ch, it.second->memberName.c_str(), gfound++); else _printPCLine(ch, it.second->member, gfound++); @@ -590,8 +598,9 @@ void Group::sendToGroup(GRP_COMM mode, const char *msg, ...) { */ // надстройка над act, регулируется набором флагов grpActMode + GRP_COMM +// ch указывается для указания цели в режиме GC_CHAR и GC_ROOM // vict передается в аргумент функции act -void Group::actToGroup(CHAR_DATA* ch, CHAR_DATA* vict, grpActMode mode, const char *msg, ...) { +void Group::actToGroup(CHAR_DATA* ch, CHAR_DATA* vict, int mode, const char *msg, ...) { va_list args; va_start(args, msg); vsnprintf(smallBuf, sizeof(smallBuf), msg, args); @@ -602,13 +611,13 @@ void Group::actToGroup(CHAR_DATA* ch, CHAR_DATA* vict, grpActMode mode, const ch if ( to == nullptr || to->purged()) { continue; } - if ((mode.test(GRP_COMM::GC_LEADER) && to == _leader) || - (mode.test(GRP_COMM::GC_CHAR) && to == ch) || - (mode.test(GRP_COMM::GC_REST) && to != ch && to != _leader)) + if ( (mode & GC_LEADER && to == _leader) || + (mode & GC_CHAR && to == ch) || + (mode & GC_REST && to != vict && to != _leader)) act(smallBuf, TRUE, to, nullptr, vict, TO_CHAR); } - if (mode.test(GRP_COMM::GC_ROOM)) - act(smallBuf, TRUE, ch, nullptr, nullptr, TO_ROOM | TO_ARENA_LISTEN); + if (mode & GC_ROOM) + act(smallBuf, TRUE, ch, nullptr, vict, TO_ROOM | TO_ARENA_LISTEN); } void Group::addFollowers(CHAR_DATA *leader) { @@ -668,7 +677,7 @@ void Group::_promote(CHAR_DATA *member) { if (_leader == member) return; _setLeader(member); - actToGroup(nullptr, nullptr, grpActMode(GC_LEADER | GC_REST), "Изменился лидер группы на %s.", _leaderName.c_str()); + actToGroup(nullptr, nullptr, GC_LEADER | GC_REST, "Изменился лидер группы на %s.", _leaderName.c_str()); auto diff = (u_short)_memberCap - (u_short)this->size(); if (diff < 0) diff = abs(diff); else diff = 0; if (diff > 0){ @@ -677,7 +686,7 @@ void Group::_promote(CHAR_DATA *member) { for (auto it = this->begin(); it != this->end() && i < diff; it++){ if (it->second->member != _leader ) { expellList[i] = _calcUID(it->second->member); - actToGroup(nullptr, nullptr, grpActMode(GC_LEADER | GC_REST), "В группе не хватает места, нас покинул %s!", it->second->memberName.c_str()); + actToGroup(nullptr, nullptr, GC_LEADER | GC_REST, "В группе не хватает места, нас покинул %s!", it->second->memberName.c_str()); ++i; } } diff --git a/src/grp/grp.main.h b/src/grp/grp.main.h index 7c9673697..3bce35b57 100644 --- a/src/grp/grp.main.h +++ b/src/grp/grp.main.h @@ -20,16 +20,14 @@ enum GM_TYPE {GM_CHAR, GM_CHARMEE}; enum RQ_TYPE {RQ_GROUP, RQ_PERSON, RQ_ANY}; -class grpActMode:std::bitset<4> { -public: - enum GRP_COMM : short { - GC_LEADER = 0, // только лидеру - GC_CHAR = 1, // только персонажу - GC_REST = 2, // всем остальным - GC_ROOM = 3 // эхо в комнату - }; - grpActMode(GRP_COMM val); + +enum GRP_COMM : short { + GC_LEADER = 1, // только лидеру + GC_CHAR = 2, // только персонажу + GC_REST = 4, // всем остальным + GC_ROOM = 8 // эхо в комнату }; + enum RQ_R {RQ_R_OK, RQ_R_NO_GROUP, RQ_R_OVERFLOW, RQ_REFRESH}; enum INV_R {INV_R_OK, INV_R_NO_PERSON, INV_R_BUSY, INV_R_REFRESH}; @@ -91,6 +89,7 @@ class GroupPenalties // класс-коллекция персонажей обоих типов что ли.. class Group : public grp_mt { private: + constexpr static int DEFAULT_100 = 100; // ид группы в ростере u_long _uid = 0; //макс.количество игроков @@ -103,6 +102,8 @@ class Group : public grp_mt { CHAR_DATA* _leader = nullptr; // макс.уровень игрока в группе, для расчета штрафа u_short _maxPlayerLevel = 1; + // количество игроков + u_short _pcCount = 0; public: u_long getUid() const; const std::string &getLeaderName() const; @@ -137,7 +138,7 @@ class Group : public grp_mt { static void _printPCLine(CHAR_DATA* ch, CHAR_DATA* pc, int header); bool _sameGroup(CHAR_DATA * ch, CHAR_DATA * vict); public: - constexpr static int DEFAULT_100 = 100; + void addFollowers(CHAR_DATA* leader); void addMember(CHAR_DATA *member, bool silent = false); @@ -153,7 +154,7 @@ class Group : public grp_mt { void leaveGroup(CHAR_DATA* vict); // void sendToGroup(GRP_COMM mode, const char *msg, ...); - void actToGroup(CHAR_DATA* ch, CHAR_DATA* vict, grpActMode mode, const char *msg, ...); + void actToGroup(CHAR_DATA* ch, CHAR_DATA* vict, int mode, const char *msg, ...); u_short calcExpMultiplicator(const CHAR_DATA* player); public: // всякий унаследованный стафф diff --git a/src/grp/grp.roster.cpp b/src/grp/grp.roster.cpp index c5717d004..b875312a9 100644 --- a/src/grp/grp.roster.cpp +++ b/src/grp/grp.roster.cpp @@ -19,7 +19,7 @@ void GroupRoster::restorePlayerGroup(CHAR_DATA *ch) { return; if (!grp->_restoreMember(ch)) return; - grp->actToGroup(nullptr, ch,grpActMode(GC_LEADER | GC_REST), "$N заново присоединил$A к вашей группе."); + grp->actToGroup(nullptr, ch, GC_LEADER | GC_REST, "$N заново присоединил$U к вашей группе."); } void GroupRoster::processGroupCommands(CHAR_DATA *ch, char *argument) { @@ -293,7 +293,7 @@ void GroupRoster::makeRequest(CHAR_DATA *author, char* target) { return; case RQ_R::RQ_R_OK: send_to_char("Заявка на вступление в группу отправлена.\r\n", author); - grp->actToGroup(nullptr, author, grpActMode(GC_LEADER), "Получена заявка от $N1 на вступление в группу.\r\n"); + grp->actToGroup(nullptr, author, GC_LEADER, "Получена заявка от $N1 на вступление в группу.\r\n"); break; case RQ_R::RQ_REFRESH: send_to_char("Заявка успешно продлена.\r\n", author); diff --git a/src/interpreter.cpp b/src/interpreter.cpp index 487e1f10f..d75a0284e 100644 --- a/src/interpreter.cpp +++ b/src/interpreter.cpp @@ -2050,6 +2050,10 @@ int perform_dupe_check(DESCRIPTOR_DATA * d) PLR_FLAGS(d->character).unset(PLR_WRITING); STATE(d) = CON_PLAYING; + if (d->character->personGroup != nullptr) { + d->character->personGroup->actToGroup(nullptr, d->character.get(), GC_LEADER | GC_REST, "$N восстановил$G связь."); + } + switch (mode) { case RECON: From 7eea7ef38df52c4d08960139b9f31fc3bf89bbfd Mon Sep 17 00:00:00 2001 From: bikbai Date: Sun, 17 Jan 2021 15:34:37 +0300 Subject: [PATCH 24/40] =?UTF-8?q?=D0=9C=D0=B5=D1=80=D0=B6=20=D0=B2=D0=B5?= =?UTF-8?q?=D1=82=D0=BA=D0=B8=20master?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 154 ++++++++++++----------- src/ability.rollsystem.hpp | 2 +- src/act.informative.cpp | 3 +- src/act.item.cpp | 2 +- src/act.movement.cpp | 2 +- src/act.other.cpp | 2 +- src/chars/char.cpp | 2 +- src/chars/char.hpp | 2 +- src/chars/char_player.cpp | 2 +- src/chars/mount.cpp | 2 +- src/class.cpp | 2 +- src/{cmd.imm => cmd.wiz}/act.wizard.cpp | 6 +- src/{cmd.imm => cmd.wiz}/act.wizard.hpp | 0 src/cmd.wiz/stat.cpp | 1 + src/cmd/cmd.generic.h | 4 +- src/cmd/follow.cpp | 160 +----------------------- src/cmd/follow.h | 11 -- src/cmd/hire.cpp | 2 +- src/constants.cpp | 2 +- src/core/affect_data.cpp | 2 +- src/craft.cpp | 2 +- src/dg/dg_db_scripts.cpp | 2 +- src/dg/dg_mobcmd.cpp | 5 +- src/dg/dg_objcmd.cpp | 7 +- src/dg/dg_scripts.cpp | 2 +- src/dg/dg_scripts.h | 2 +- src/dg/dg_wldcmd.cpp | 4 +- src/exchange.cpp | 2 +- src/features.hpp | 2 +- src/features.itemset.hpp | 2 +- src/fightsystem/fight.cpp | 3 +- src/fightsystem/fight_hit.cpp | 2 +- src/fightsystem/fight_stuff.cpp | 2 +- src/fightsystem/mobact.cpp | 2 +- src/global.objects.hpp | 2 +- src/graph.cpp | 2 +- src/grp/follow.cpp | 68 +--------- src/grp/follow.h | 9 -- src/grp/grp.main.h | 3 + src/handler.cpp | 44 ++----- src/house.cpp | 2 +- src/interpreter.cpp | 13 +- src/item.creation.cpp | 2 +- src/item.creation.hpp | 2 +- src/liquid.cpp | 2 +- src/magic.cpp | 2 +- src/medit.cpp | 2 +- src/modify.cpp | 2 +- src/morph.cpp | 2 +- src/morph.hpp | 2 +- src/noob.cpp | 2 +- src/obj.hpp | 2 +- src/obj_sets.cpp | 2 +- src/obj_sets_olc.cpp | 2 +- src/oedit.cpp | 2 +- src/poison.cpp | 2 +- src/skills/kick.cpp | 1 - src/{ => skills}/skills.cpp | 0 src/{ => skills}/skills.h | 0 src/spec_procs.cpp | 30 +---- src/spell_parser.cpp | 2 +- src/spell_parser.hpp | 2 +- src/spells.cpp | 3 +- src/spells.h | 2 +- src/stuff.cpp | 2 +- src/utils.cpp | 2 +- src/weather.cpp | 2 +- 67 files changed, 162 insertions(+), 457 deletions(-) rename src/{cmd.imm => cmd.wiz}/act.wizard.cpp (99%) rename src/{cmd.imm => cmd.wiz}/act.wizard.hpp (100%) delete mode 100644 src/cmd/follow.h delete mode 100644 src/grp/follow.h rename src/{ => skills}/skills.cpp (100%) rename src/{ => skills}/skills.h (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9611eb21e..f96c97025 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,7 +43,9 @@ set(SOURCES src/chars/player_races.cpp src/chars/world.characters.cpp src/class.cpp - src/cmd.imm/act.wizard.cpp + src/cmd.wiz/act.wizard.cpp + src/cmd.wiz/stat.cpp + src/cmd/follow.cpp src/cmd/hire.cpp src/cmd/mercenary.cpp src/cmd/order.cpp @@ -60,6 +62,7 @@ set(SOURCES src/config.cpp src/constants.cpp src/core/affect_data.cpp + src/core/leveling.cpp src/corpse.cpp src/daily_quest.cpp src/db.cpp @@ -103,6 +106,7 @@ set(SOURCES src/glory_misc.cpp src/graph.cpp src/grp/follow.cpp + src/grp/grouppenalties.cpp src/grp/grp.cmdprocessor.cpp src/grp/grp.group.cpp src/grp/grp.request.cpp @@ -167,7 +171,7 @@ set(SOURCES src/shops.implementation.cpp src/shops.implementation.cpp src/shutdown.parameters.cpp - src/skills.cpp + src/skills/skills.cpp src/skills/backstab.cpp src/skills/bash.cpp src/skills/block.cpp @@ -211,89 +215,61 @@ set(SOURCES src/world.objects.cpp src/zedit.cpp src/zone.table.cpp - src/core/leveling.cpp - src/grp/grouppenalties.cpp) + ) set(HEADERS src/core/leveling.h - src/grp/follow.h - src/grp/grp.main.h - src/core/affect_data.h - src/fightsystem/common.h - src/fightsystem/assist.h - src/fightsystem/start.fight.h - src/skills/styles.h - src/skills/manadrain.h - src/skills/townportal.h - src/skills/turnundead.h - src/skills/mighthit.h - src/skills/expendientcut.h - src/skills/block.h - src/skills/protect.h - src/skills/parry.h - src/skills/throw.h - src/skills/ironwind.h - src/skills/disarm.h - src/act.movement.hpp - src/strengthening.hpp src/ability.constants.hpp src/ability.rollsystem.hpp - src/chars/mount.h - src/chars/accounts.hpp - src/action.targeting.hpp - src/blocking.queue.hpp - src/influxdb.hpp - src/heartbeat.commands.hpp - src/weather.hpp - src/limits.hpp - src/cmd.imm/act.wizard.hpp - src/heartbeat.hpp - src/speedwalks.hpp - src/chars/world.characters.hpp - src/shops.implementation.hpp - src/global.objects.hpp - src/find.obj.id.by.vnum.hpp - src/id.hpp - src/compact.trie.hpp - src/radix.trie.hpp - src/object.prototypes.hpp - src/world.objects.hpp - src/utils.string.hpp + src/act.movement.hpp src/act.other.hpp + src/action.targeting.hpp src/AffectHandler.hpp src/auction.h src/backtrace.hpp src/ban.hpp src/birth_places.hpp + src/blocking.queue.hpp src/boards.changelog.loaders.hpp src/boards.constants.hpp src/boards.formatters.hpp src/boards.h src/boards.message.hpp src/boards.types.hpp - src/bonus.h src/bonus.command.parser.hpp + src/bonus.h src/bonus.types.hpp src/boot.constants.hpp src/boot.data.files.hpp src/boot.index.hpp src/cache.hpp src/celebrates.hpp - src/chars/char.hpp src/char_obj_utils.inl + src/chars/accounts.hpp + src/chars/char.hpp src/chars/char_player.hpp + src/chars/mount.h + src/chars/player_i.hpp + src/chars/player_races.hpp + src/chars/world.characters.hpp src/class.hpp + src/cmd.wiz/act.wizard.hpp + src/cmd.wiz/stat.h + src/cmd/cmd.generic.h src/comm.h src/command.shutdown.hpp src/commands.hpp + src/compact.trie.hpp src/conf.h src/config.hpp src/constants.h + src/core/affect_data.h src/coredump.hpp src/corpse.hpp src/daily_quest.hpp src/db.h src/deathtrap.hpp + src/debug.utils.hpp src/depot.hpp src/description.h src/dg/dg_db_scripts.hpp @@ -307,60 +283,72 @@ set(HEADERS src/exchange.h src/ext_money.hpp src/external.trigger.hpp - src/features.itemset.hpp src/features.hpp - src/fightsystem/fight_constants.hpp + src/features.itemset.hpp + src/fightsystem/assist.h + src/fightsystem/common.h src/fightsystem/fight.h + src/fightsystem/fight.penalties.hpp + src/fightsystem/fight_constants.hpp src/fightsystem/fight_hit.hpp src/fightsystem/fight_stuff.hpp - src/fightsystem/fight.penalties.hpp + src/fightsystem/mobact.hpp + src/fightsystem/pk.h + src/fightsystem/start.fight.h src/file_crc.hpp + src/find.obj.id.by.vnum.hpp src/genchar.h - src/glory_const.hpp + src/global.objects.hpp src/glory.hpp + src/glory_const.hpp src/glory_misc.hpp + src/graph.h + src/grp/grp.main.h src/handler.h + src/heartbeat.commands.hpp + src/heartbeat.hpp src/help.hpp - src/house_exp.hpp src/house.h + src/house_exp.hpp + src/id.hpp src/ignores.hpp src/ignores.loader.hpp src/im.h + src/influxdb.hpp src/interpreter.h src/item.creation.hpp src/levenshtein.hpp + src/limits.hpp src/liquid.hpp src/logger.hpp src/magic.h src/mail.h src/map.hpp src/meat.maker.hpp - src/fightsystem/mobact.hpp - src/mobmax.hpp src/mob_stat.hpp + src/mobmax.hpp src/modify.h src/morph.hpp - src/named_stuff.hpp src/name_list.hpp + src/named_stuff.hpp src/noob.hpp - src/obj_enchant.hpp src/obj.hpp - src/objsave.h + src/obj_enchant.hpp src/obj_sets.hpp src/obj_sets_stuff.hpp + src/object.prototypes.hpp + src/objsave.h src/olc.h src/parcel.hpp src/parse.hpp src/password.hpp - src/fightsystem/pk.h - src/chars/player_i.hpp - src/chars/player_races.hpp src/poison.hpp src/privilege.hpp src/pugiconfig.hpp src/pugixml.hpp - src/quested.hpp src/quest.hpp + src/quested.hpp + src/radix.trie.hpp src/random.hpp src/remember.hpp src/reset_stats.hpp @@ -368,11 +356,36 @@ set(HEADERS src/screen.h src/sets_drop.hpp src/shop_ext.hpp + src/shops.implementation.hpp src/shutdown.parameters.hpp - src/skills.h + src/skills/skills.h + src/skills/backstab.h + src/skills/bash.h + src/skills/block.h + src/skills/chopoff.h + src/skills/disarm.h + src/skills/expendientcut.h + src/skills/flee.h + src/skills/ironwind.h + src/skills/kick.h + src/skills/manadrain.h + src/skills/mighthit.h + src/skills/parry.h + src/skills/protect.h + src/skills/resque.h + src/skills/strangle.h + src/skills/stun.h + src/skills/stupor.h + src/skills/styles.h + src/skills/throw.h + src/skills/townportal.h + src/skills/turnundead.h src/spam.hpp + src/speedwalks.hpp src/spell_parser.hpp src/spells.h + src/stigmas.hpp + src/strengthening.hpp src/structs.h src/stuff.hpp src/sysdep.h @@ -382,21 +395,12 @@ set(HEADERS src/title.hpp src/top.h src/utils.h + src/utils.string.hpp + src/weather.hpp + src/world.objects.hpp src/xml_loading_helper.hpp - src/debug.utils.hpp - src/stigmas.hpp src/zone.table.hpp - src/skills/flee.h - src/skills/backstab.h - src/skills/bash.h - src/skills/stun.h - src/skills/resque.h - src/skills/kick.h - src/skills/strangle.h - src/skills/chopoff.h - src/skills/stupor.h - src/graph.h - src/cmd/cmd.generic.h) +) set(CONFIGURATION_FILES lib.template/misc/configuration.xml) # Build types diff --git a/src/ability.rollsystem.hpp b/src/ability.rollsystem.hpp index 9202f7d62..38d0338c2 100644 --- a/src/ability.rollsystem.hpp +++ b/src/ability.rollsystem.hpp @@ -12,7 +12,7 @@ #include "chars/char.hpp" #include "fightsystem/fight_constants.hpp" #include "features.hpp" -#include "skills.h" +#include "skills/skills.h" #include diff --git a/src/act.informative.cpp b/src/act.informative.cpp index 37f6c2f11..8bb93aa0b 100644 --- a/src/act.informative.cpp +++ b/src/act.informative.cpp @@ -57,7 +57,7 @@ #include "screen.h" #include "sets_drop.hpp" #include "shutdown.parameters.hpp" -#include "skills.h" +#include "skills/skills.h" #include "spells.h" #include "structs.h" #include "sysdep.h" @@ -97,7 +97,6 @@ extern std::vector cities; // extern functions long find_class_bitvector(char arg); TIME_INFO_DATA *real_time_passed(time_t t2, time_t t1); -int compute_armor_class(CHAR_DATA * ch); int pk_count(CHAR_DATA * ch); // local functions const char *show_obj_to_char(OBJ_DATA * object, CHAR_DATA * ch, int mode, int show_state, int how); diff --git a/src/act.item.cpp b/src/act.item.cpp index a8404cdeb..68c8df653 100644 --- a/src/act.item.cpp +++ b/src/act.item.cpp @@ -36,7 +36,7 @@ #include "fightsystem/pk.h" #include "poison.hpp" #include "room.hpp" -#include "skills.h" +#include "skills/skills.h" #include "spells.h" #include "mobmax.hpp" #include "meat.maker.hpp" diff --git a/src/act.movement.cpp b/src/act.movement.cpp index 0e3bcd320..671780215 100644 --- a/src/act.movement.cpp +++ b/src/act.movement.cpp @@ -23,7 +23,7 @@ #include "handler.h" #include "db.h" #include "spells.h" -#include "skills.h" +#include "skills/skills.h" #include "house.h" #include "constants.h" #include "dg/dg_scripts.h" diff --git a/src/act.other.cpp b/src/act.other.cpp index b441eed93..3a8175806 100644 --- a/src/act.other.cpp +++ b/src/act.other.cpp @@ -43,7 +43,7 @@ #include "room.hpp" #include "screen.h" #include "shop_ext.hpp" -#include "skills.h" +#include "skills/skills.h" #include "spells.h" #include "structs.h" #include "sysdep.h" diff --git a/src/chars/char.cpp b/src/chars/char.cpp index a17608b8a..6160c757b 100644 --- a/src/chars/char.cpp +++ b/src/chars/char.cpp @@ -14,7 +14,7 @@ #include "interpreter.h" #include "boards.h" #include "privilege.hpp" -#include "skills.h" +#include "skills/skills.h" #include "constants.h" #include "char_player.hpp" #include "spells.h" diff --git a/src/chars/char.hpp b/src/chars/char.hpp index bc168a929..979b06315 100644 --- a/src/chars/char.hpp +++ b/src/chars/char.hpp @@ -11,7 +11,7 @@ #include "room.hpp" #include "ignores.hpp" #include "im.h" -#include "skills.h" +#include "skills/skills.h" #include "utils.h" #include "structs.h" #include "conf.h" diff --git a/src/chars/char_player.cpp b/src/chars/char_player.cpp index 172ed6e10..42b4d9c41 100644 --- a/src/chars/char_player.cpp +++ b/src/chars/char_player.cpp @@ -13,7 +13,7 @@ #include "file_crc.hpp" #include "spells.h" #include "constants.h" -#include "skills.h" +#include "skills/skills.h" #include "ignores.loader.hpp" #include "im.h" #include "olc.h" diff --git a/src/chars/mount.cpp b/src/chars/mount.cpp index 7a7841e21..0fb648b56 100644 --- a/src/chars/mount.cpp +++ b/src/chars/mount.cpp @@ -2,7 +2,7 @@ // #include "mount.h" -#include "cmd/follow.h" +#include "grp/grp.main.h" #include "handler.h" void make_horse(CHAR_DATA * horse, CHAR_DATA * ch) diff --git a/src/class.cpp b/src/class.cpp index 99cda721e..993896061 100644 --- a/src/class.cpp +++ b/src/class.cpp @@ -40,7 +40,7 @@ #include "noob.hpp" #include "obj.hpp" #include "screen.h" -#include "skills.h" +#include "skills/skills.h" #include "spam.hpp" #include "spell_parser.hpp" #include "spells.h" diff --git a/src/cmd.imm/act.wizard.cpp b/src/cmd.wiz/act.wizard.cpp similarity index 99% rename from src/cmd.imm/act.wizard.cpp rename to src/cmd.wiz/act.wizard.cpp index 4353a115f..eda936bb1 100644 --- a/src/cmd.imm/act.wizard.cpp +++ b/src/cmd.wiz/act.wizard.cpp @@ -13,7 +13,7 @@ ************************************************************************ */ -#include "cmd.imm/act.wizard.hpp" +#include "act.wizard.hpp" #include "action.targeting.hpp" #include "ban.hpp" @@ -67,8 +67,9 @@ #include "screen.h" #include "sets_drop.hpp" #include "shop_ext.hpp" -#include "skills.h" +#include "skills/skills.h" #include "spells.h" +#include "stat.h" #include "structs.h" #include "sysdep.h" #include "time_utils.hpp" @@ -127,7 +128,6 @@ void rename_char(CHAR_DATA * ch, char *oname); int _parse_name(char *arg, char *name); int Valid_Name(char *name); int reserved_word(const char *name); -int compute_armor_class(CHAR_DATA * ch); extern bool can_be_reset(zone_rnum zone); extern bool is_empty(zone_rnum zone_nr); void list_feats(CHAR_DATA * ch, CHAR_DATA * vict, bool all_feats); diff --git a/src/cmd.imm/act.wizard.hpp b/src/cmd.wiz/act.wizard.hpp similarity index 100% rename from src/cmd.imm/act.wizard.hpp rename to src/cmd.wiz/act.wizard.hpp diff --git a/src/cmd.wiz/stat.cpp b/src/cmd.wiz/stat.cpp index c57c63790..c9937ac92 100644 --- a/src/cmd.wiz/stat.cpp +++ b/src/cmd.wiz/stat.cpp @@ -5,6 +5,7 @@ #include "chars/player_races.hpp" #include "char_obj_utils.inl" #include "description.h" +#include "fightsystem/fight.h" #include "fightsystem/fight_hit.hpp" #include "fightsystem/pk.h" #include "olc.h" diff --git a/src/cmd/cmd.generic.h b/src/cmd/cmd.generic.h index a484a1df9..755c7abed 100644 --- a/src/cmd/cmd.generic.h +++ b/src/cmd/cmd.generic.h @@ -2,7 +2,7 @@ #define BYLINS_CMD_GENERIC_H #include "chars/char.hpp" -#include "skills.h" +#include "skills/skills.h" #include @@ -59,4 +59,6 @@ void do_track(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/); void do_hidetrack(CHAR_DATA *ch, char* /*argument*/, int/* cmd*/, int/* subcmd*/); void do_whoami(CHAR_DATA *ch, char *, int, int); +void do_follow(CHAR_DATA *ch, char *argument, int cmd, int subcmd); + #endif //BYLINS_CMD_GENERIC_H diff --git a/src/cmd/follow.cpp b/src/cmd/follow.cpp index c7cd1bf07..6a862160c 100644 --- a/src/cmd/follow.cpp +++ b/src/cmd/follow.cpp @@ -1,165 +1,9 @@ -#include "cmd/follow.h" +#include "cmd/cmd.generic.h" #include "fightsystem/fight.h" +#include "grp/grp.main.h" #include "handler.h" -void perform_drop_gold(CHAR_DATA * ch, int amount); - -// Called when stop following persons, or stopping charm // -// This will NOT do if a character quits/dies!! // -// При возврате 1 использовать ch нельзя, т.к. прошли через extract_char -// TODO: по всем вызовам не проходил, может еще где-то коряво вызывается, кроме передачи скакунов -- Krodo -// при персонаже на входе - пуржить не должно полюбому, если начнет, как минимум в change_leader будут глюки -bool stop_follower(CHAR_DATA * ch, int mode) -{ - struct follow_type *j, *k; - int i; - - //log("[Stop follower] Start function(%s->%s)",ch ? GET_NAME(ch) : "none", - // ch->master ? GET_NAME(ch->master) : "none"); - - if (!ch->has_master()) - { - log("SYSERR: stop_follower(%s) without master", GET_NAME(ch)); - return (FALSE); - } - - // для смены лидера без лишнего спама - if (!IS_SET(mode, SF_SILENCE)) - { - act("Вы прекратили следовать за $N4.", FALSE, ch, 0, ch->get_master(), TO_CHAR); - act("$n прекратил$g следовать за $N4.", TRUE, ch, 0, ch->get_master(), TO_NOTVICT | TO_ARENA_LISTEN); - } - - //log("[Stop follower] Stop horse"); - if (ch->get_master()->get_horse() == ch && ch->get_master()->ahorse()) - { - ch->drop_from_horse(); - } - else - { - act("$n прекратил$g следовать за вами.", TRUE, ch, 0, ch->get_master(), TO_VICT); - } - - //log("[Stop follower] Remove from followers list"); - if (!ch->get_master()->followers) - { - log("[Stop follower] SYSERR: Followers absent for %s (master %s).", GET_NAME(ch), GET_NAME(ch->get_master())); - } - else if (ch->get_master()->followers->follower == ch) // Head of follower-list? - { - k = ch->get_master()->followers; - ch->get_master()->followers = k->next; - if (!ch->get_master()->followers - && !ch->get_master()->has_master()) - { - //AFF_FLAGS(ch->get_master()).unset(EAffectFlag::AFF_GROUP); - ch->get_master()->removeGroupFlags(); - } - free(k); - } - else // locate follower who is not head of list - { - for (k = ch->get_master()->followers; k->next && k->next->follower != ch; k = k->next); - if (!k->next) - { - log("[Stop follower] SYSERR: Undefined %s in %s followers list.", GET_NAME(ch), GET_NAME(ch->get_master())); - } - else - { - j = k->next; - k->next = j->next; - free(j); - } - } - - ch->set_master(nullptr); - //AFF_FLAGS(ch).unset(EAffectFlag::AFF_GROUP); - ch->removeGroupFlags(); - - if (AFF_FLAGGED(ch, EAffectFlag::AFF_CHARM) - || AFF_FLAGGED(ch, EAffectFlag::AFF_HELPER) - || IS_SET(mode, SF_CHARMLOST)) - { - if (affected_by_spell(ch, SPELL_CHARM)) - { - affect_from_char(ch, SPELL_CHARM); - } - EXTRACT_TIMER(ch) = 5; - AFF_FLAGS(ch).unset(EAffectFlag::AFF_CHARM); - - if (ch->get_fighting()) - { - stop_fighting(ch, TRUE); - } - - if (IS_NPC(ch)) - { - if (MOB_FLAGGED(ch, MOB_CORPSE)) - { - act("Налетевший ветер развеял $n3, не оставив и следа.", TRUE, ch, 0, 0, TO_ROOM | TO_ARENA_LISTEN); - GET_LASTROOM(ch) = GET_ROOM_VNUM(ch->in_room); - perform_drop_gold(ch, ch->get_gold()); - ch->set_gold(0); - extract_char(ch, FALSE); - return (TRUE); - } - else if (AFF_FLAGGED(ch, EAffectFlag::AFF_HELPER)) - { - AFF_FLAGS(ch).unset(EAffectFlag::AFF_HELPER); - } - } - } - - if (IS_NPC(ch) - && !MOB_FLAGGED(ch, MOB_PLAYER_SUMMON) //Не ресетим флаги, если моб призван игроком - && (i = GET_MOB_RNUM(ch)) >= 0) - { - MOB_FLAGS(ch) = MOB_FLAGS(mob_proto + i); - } - - return (FALSE); -} - -// * Called when a character that follows/is followed dies -bool die_follower(CHAR_DATA * ch) -{ - struct follow_type *j, *k = ch->followers; - - if (ch->has_master() && stop_follower(ch, SF_FOLLOWERDIE)) - { - // чармиса спуржили в stop_follower - return true; - } - - if (ch->ahorse()) { - AFF_FLAGS(ch).unset(EAffectFlag::AFF_HORSE); - } - - for (k = ch->followers; k; k = j) - { - j = k->next; - stop_follower(k->follower, SF_MASTERDIE); - } - return false; -} - -// Check if making CH follow VICTIM will create an illegal // -// Follow "Loop/circle" // -bool circle_follow(CHAR_DATA * ch, CHAR_DATA * victim) -{ - for (auto k = victim; k; k = k->get_master()) { - if (k->get_master() == k) { - k->set_master(nullptr); - return false; - } - if (k == ch) { - return true; - } - } - return false; -} - void do_follow(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) { CHAR_DATA *leader; diff --git a/src/cmd/follow.h b/src/cmd/follow.h deleted file mode 100644 index 1e8d7fb86..000000000 --- a/src/cmd/follow.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef BYLINS_FOLLOW_H -#define BYLINS_FOLLOW_H - -#include "chars/char.hpp" - -void do_follow(CHAR_DATA *ch, char *argument, int cmd, int subcmd); -bool stop_follower(CHAR_DATA * ch, int mode); -bool die_follower(CHAR_DATA * ch); -bool circle_follow(CHAR_DATA * ch, CHAR_DATA * victim); - -#endif //BYLINS_FOLLOW_H diff --git a/src/cmd/hire.cpp b/src/cmd/hire.cpp index 3ce93d9f0..0aae17fef 100644 --- a/src/cmd/hire.cpp +++ b/src/cmd/hire.cpp @@ -1,6 +1,6 @@ #include "cmd.generic.h" -#include "grp/follow.h" + #include "grp/grp.main.h" #include "handler.h" #include "screen.h" diff --git a/src/constants.cpp b/src/constants.cpp index 8dddde7e2..a2a06f88c 100644 --- a/src/constants.cpp +++ b/src/constants.cpp @@ -17,7 +17,7 @@ #include "constants.h" #include "spells.h" -#include "skills.h" +#include "skills/skills.h" #include "interpreter.h" // alias_data #include "house.h" diff --git a/src/core/affect_data.cpp b/src/core/affect_data.cpp index f131fec8a..4bfdf571b 100644 --- a/src/core/affect_data.cpp +++ b/src/core/affect_data.cpp @@ -4,7 +4,7 @@ #include "chars/char_player.hpp" #include "chars/world.characters.hpp" #include "class.hpp" -#include "cmd/follow.h" +#include "grp/grp.main.h" #include "deathtrap.hpp" #include "handler.h" #include "magic.h" diff --git a/src/craft.cpp b/src/craft.cpp index b354e40e9..fec8dae10 100644 --- a/src/craft.cpp +++ b/src/craft.cpp @@ -14,7 +14,7 @@ #include "time_utils.hpp" #include "xml_loading_helper.hpp" #include "parse.hpp" -#include "skills.h" +#include "skills/skills.h" #include "comm.h" #include "db.h" #include "utils.h" diff --git a/src/dg/dg_db_scripts.cpp b/src/dg/dg_db_scripts.cpp index c3fed4d5d..d3811dd18 100644 --- a/src/dg/dg_db_scripts.cpp +++ b/src/dg/dg_db_scripts.cpp @@ -22,7 +22,7 @@ #include "dg_event.h" #include "comm.h" #include "spells.h" -#include "skills.h" +#include "skills/skills.h" #include "im.h" #include "features.hpp" #include "chars/char.hpp" diff --git a/src/dg/dg_mobcmd.cpp b/src/dg/dg_mobcmd.cpp index fbcfa02fd..6b802025b 100644 --- a/src/dg/dg_mobcmd.cpp +++ b/src/dg/dg_mobcmd.cpp @@ -26,12 +26,11 @@ #include "chars/char.hpp" #include "comm.h" #include "core/leveling.h" -#include "conf.h" #include "db.h" -#include "dg_scripts.h" #include "features.hpp" #include "fightsystem/fight.h" #include "fightsystem/fight_hit.hpp" +#include "grp/grp.main.h" #include "handler.h" #include "im.h" #include "interpreter.h" @@ -39,7 +38,7 @@ #include "obj.hpp" #include "object.prototypes.hpp" #include "room.hpp" -#include "skills.h" +#include "skills/townportal.h" #include "spell_parser.hpp" #include "spells.h" #include "structs.h" diff --git a/src/dg/dg_objcmd.cpp b/src/dg/dg_objcmd.cpp index 829dcf3c1..9b794213c 100644 --- a/src/dg/dg_objcmd.cpp +++ b/src/dg/dg_objcmd.cpp @@ -11,22 +11,19 @@ #include "chars/char.hpp" #include "comm.h" -#include "conf.h" +#include "core/leveling.h" #include "db.h" -#include "dg_scripts.h" #include "features.hpp" #include "fightsystem/fight.h" +#include "grp/grp.main.h" #include "handler.h" #include "im.h" #include "interpreter.h" #include "logger.hpp" #include "magic.h" -#include "name_list.hpp" #include "obj.hpp" #include "object.prototypes.hpp" #include "room.hpp" -#include "screen.h" -#include "skills.h" #include "skills/townportal.h" #include "spell_parser.hpp" #include "spells.h" diff --git a/src/dg/dg_scripts.cpp b/src/dg/dg_scripts.cpp index a86a218eb..62dbef049 100644 --- a/src/dg/dg_scripts.cpp +++ b/src/dg/dg_scripts.cpp @@ -38,7 +38,7 @@ #include "privilege.hpp" #include "room.hpp" #include "screen.h" -#include "skills.h" +#include "skills/skills.h" #include "spell_parser.hpp" #include "spells.h" #include "structs.h" diff --git a/src/dg/dg_scripts.h b/src/dg/dg_scripts.h index 65caef7c8..d6b8144e3 100644 --- a/src/dg/dg_scripts.h +++ b/src/dg/dg_scripts.h @@ -12,7 +12,7 @@ #ifndef _DG_SCRIPTS_H_ #define _DG_SCRIPTS_H_ -#include "skills.h" +#include "skills/skills.h" #include "structs.h" #include "logger.hpp" diff --git a/src/dg/dg_wldcmd.cpp b/src/dg/dg_wldcmd.cpp index 723fa3034..d0b81b0b2 100644 --- a/src/dg/dg_wldcmd.cpp +++ b/src/dg/dg_wldcmd.cpp @@ -10,7 +10,7 @@ **************************************************************************/ #include "chars/char.hpp" -#include "cmd/follow.h" +#include "grp/grp.main.h" #include "comm.h" #include "core/leveling.h" #include "db.h" @@ -24,7 +24,7 @@ #include "obj.hpp" #include "object.prototypes.hpp" #include "room.hpp" -#include "skills.h" +#include "skills/skills.h" #include "skills/townportal.h" #include "spell_parser.hpp" #include "spells.h" diff --git a/src/exchange.cpp b/src/exchange.cpp index eb7fd6213..04e00d685 100644 --- a/src/exchange.cpp +++ b/src/exchange.cpp @@ -20,7 +20,7 @@ #include "screen.h" #include "im.h" #include "constants.h" -#include "skills.h" +#include "skills/skills.h" #include "chars/char.hpp" #include "chars/char_player.hpp" #include "named_stuff.hpp" diff --git a/src/features.hpp b/src/features.hpp index f9bb44843..e5a387877 100644 --- a/src/features.hpp +++ b/src/features.hpp @@ -10,7 +10,7 @@ #define __FEATURES_HPP__ #include "features.itemset.hpp" -#include "skills.h" +#include "skills/skills.h" #include "structs.h" #include "conf.h" diff --git a/src/features.itemset.hpp b/src/features.itemset.hpp index a0c889068..3a017659a 100644 --- a/src/features.itemset.hpp +++ b/src/features.itemset.hpp @@ -6,7 +6,7 @@ */ #include "obj.hpp" -#include "skills.h" +#include "skills/skills.h" #include "structs.h" #include "utils.h" diff --git a/src/fightsystem/fight.cpp b/src/fightsystem/fight.cpp index e9c961651..3bdb40175 100644 --- a/src/fightsystem/fight.cpp +++ b/src/fightsystem/fight.cpp @@ -38,7 +38,7 @@ #include "constants.h" #include "dg/dg_scripts.h" #include "im.h" -#include "skills.h" +#include "skills/skills.h" #include "features.hpp" #include "random.hpp" #include "chars/char.hpp" @@ -2390,4 +2390,5 @@ void perform_violence() } } + // vim: ts=4 sw=4 tw=0 noet syntax=cpp : diff --git a/src/fightsystem/fight_hit.cpp b/src/fightsystem/fight_hit.cpp index f6847918b..e5408d24f 100644 --- a/src/fightsystem/fight_hit.cpp +++ b/src/fightsystem/fight_hit.cpp @@ -14,7 +14,7 @@ #include "pk.h" #include "poison.hpp" #include "screen.h" -#include "skills.h" +#include "skills/skills.h" // extern int extra_aco(int class_num, int level); diff --git a/src/fightsystem/fight_stuff.cpp b/src/fightsystem/fight_stuff.cpp index 95360eeb2..c638adeee 100644 --- a/src/fightsystem/fight_stuff.cpp +++ b/src/fightsystem/fight_stuff.cpp @@ -27,7 +27,7 @@ #include "room.hpp" #include "screen.h" #include "sets_drop.hpp" -#include "skills.h" +#include "skills/skills.h" #include "skills/flee.h" #include "spell_parser.hpp" #include "spells.h" diff --git a/src/fightsystem/mobact.cpp b/src/fightsystem/mobact.cpp index c67df8446..83c939132 100644 --- a/src/fightsystem/mobact.cpp +++ b/src/fightsystem/mobact.cpp @@ -37,7 +37,7 @@ #include "comm.h" #include "handler.h" #include "magic.h" -#include "skills.h" +#include "skills/skills.h" #include "fightsystem/pk.h" #include "random.hpp" #include "chars/char.hpp" diff --git a/src/global.objects.hpp b/src/global.objects.hpp index 59f4d2498..b68c85fa1 100644 --- a/src/global.objects.hpp +++ b/src/global.objects.hpp @@ -10,7 +10,7 @@ #include "shops.implementation.hpp" #include "world.objects.hpp" #include "chars/world.characters.hpp" -#include "cmd.imm/act.wizard.hpp" +#include "cmd.wiz/act.wizard.hpp" #include "influxdb.hpp" #include "zone.table.hpp" #include "daily_quest.hpp" diff --git a/src/graph.cpp b/src/graph.cpp index 22d46ac5f..39f0e799b 100644 --- a/src/graph.cpp +++ b/src/graph.cpp @@ -23,7 +23,7 @@ #include "handler.h" #include "db.h" #include "spells.h" -#include "skills.h" +#include "skills/skills.h" #include "features.hpp" #include "random.hpp" diff --git a/src/grp/follow.cpp b/src/grp/follow.cpp index 0afc3e1d3..83f2c9534 100644 --- a/src/grp/follow.cpp +++ b/src/grp/follow.cpp @@ -2,7 +2,7 @@ // Created by ubuntu on 19/12/20. // -#include "follow.h" +#include "grp/grp.main.h" #include "fightsystem/fight.h" #include "handler.h" @@ -152,72 +152,6 @@ bool circle_follow(CHAR_DATA * ch, CHAR_DATA * victim) return false; } -void do_follow(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) -{ - CHAR_DATA *leader; - struct follow_type *f; - one_argument(argument, smallBuf); - - if (IS_NPC(ch) && AFF_FLAGGED(ch, EAffectFlag::AFF_CHARM) && ch->get_fighting()) - return; - if (*smallBuf) { - if (!str_cmp(smallBuf, "я") || !str_cmp(smallBuf, "self") || !str_cmp(smallBuf, "me")){ - if (!ch->has_master()) { - send_to_char("Но вы ведь ни за кем не следуете...\r\n", ch); - } - else { - stop_follower(ch, SF_EMPTY); - } - return; - } - if (!(leader = get_char_vis(ch, smallBuf, FIND_CHAR_ROOM))) { - send_to_char(NOPERSON, ch); - return; - } - } - else { - send_to_char("За кем вы хотите следовать?\r\n", ch); - return; - } - - if (ch->get_master() == leader){ - act("Вы уже следуете за $N4.", FALSE, ch, 0, leader, TO_CHAR); - return; - } - - if (AFF_FLAGGED(ch, EAffectFlag::AFF_CHARM) && ch->has_master()) { - act("Но вы можете следовать только за $N4!", FALSE, ch, 0, ch->get_master(), TO_CHAR); - } - else // Not Charmed follow person - { - if (leader == ch) { - if (!ch->has_master()) { - send_to_char("Вы уже следуете за собой.\r\n", ch); - return; - } - stop_follower(ch, SF_EMPTY); - } - else { - if (circle_follow(ch, leader)) { - send_to_char("Так у вас целый хоровод получится.\r\n", ch); - return; - } - - if (ch->has_master()) { - stop_follower(ch, SF_EMPTY); - } - ch->removeGroupFlags(); - for (f = ch->followers; f; f = f->next) - { - //AFF_FLAGS(f->follower).unset(EAffectFlag::AFF_GROUP); - f->follower->removeGroupFlags(); - } - - leader->add_follower(ch); - } - } -} - // возвращает true, если последователь - чармис или ему похожая тварь // при указании второго параметра - проверяет, что эта тварь персональная bool isGroupedFollower(CHAR_DATA* master, CHAR_DATA* vict) { diff --git a/src/grp/follow.h b/src/grp/follow.h deleted file mode 100644 index aa83943c7..000000000 --- a/src/grp/follow.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef BYLINS_FOLLOW_H -#define BYLINS_FOLLOW_H - -#include "chars/char.hpp" - -void do_follow(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/); -bool circle_follow(CHAR_DATA * ch, CHAR_DATA * victim); - -#endif //BYLINS_FOLLOW_H diff --git a/src/grp/grp.main.h b/src/grp/grp.main.h index 3bce35b57..2693a568c 100644 --- a/src/grp/grp.main.h +++ b/src/grp/grp.main.h @@ -37,6 +37,9 @@ void do_ungroup(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/); void do_report(CHAR_DATA *ch, char* /*argument*/, int/* cmd*/, int/* subcmd*/); int max_group_size(CHAR_DATA *ch); bool isGroupedFollower(CHAR_DATA* master, CHAR_DATA* vict); +bool circle_follow(CHAR_DATA * ch, CHAR_DATA * victim); +bool die_follower(CHAR_DATA * ch); +bool stop_follower(CHAR_DATA * ch, int mode); // возвращает true, если рядом с персонажем лидер группы и прокнул скилл bool calc_leadership(CHAR_DATA * ch); diff --git a/src/handler.cpp b/src/handler.cpp index a0a6bbfb5..94eb73332 100644 --- a/src/handler.cpp +++ b/src/handler.cpp @@ -15,47 +15,23 @@ #include "handler.h" #include "chars/world.characters.hpp" -#include "object.prototypes.hpp" -#include "world.objects.hpp" -#include "obj.hpp" -#include "comm.h" -#include "db.h" -#include "glory_const.hpp" -#include "interpreter.h" -#include "spells.h" -#include "skills.h" -#include "screen.h" -#include "dg/dg_db_scripts.hpp" -#include "dg/dg_scripts.h" #include "auction.h" -#include "features.hpp" -#include "house.h" -#include "exchange.h" -#include "chars/char.hpp" +#include "backtrace.hpp" +#include "char_obj_utils.inl" #include "chars/char_player.hpp" +#include "exchange.h" +#include "fightsystem/fight.h" +#include "fightsystem/pk.h" +#include "grp/grp.main.h" +#include "house.h" #include "liquid.hpp" #include "magic.h" -#include "poison.hpp" -#include "name_list.hpp" -#include "room.hpp" #include "named_stuff.hpp" -#include "glory_const.hpp" -#include "fightsystem/fight.h" -#include "fightsystem/pk.h" -#include "ext_money.hpp" -#include "noob.hpp" -#include "obj_sets.hpp" -#include "char_obj_utils.inl" -#include "constants.h" +#include "object.prototypes.hpp" +#include "screen.h" #include "spell_parser.hpp" -#include "logger.hpp" -#include "structs.h" -#include "sysdep.h" -#include "conf.h" +#include "world.objects.hpp" #include "zone.table.hpp" -#include "backtrace.hpp" - -#include #include #include diff --git a/src/house.cpp b/src/house.cpp index 63ad89f83..a20453f37 100644 --- a/src/house.cpp +++ b/src/house.cpp @@ -17,7 +17,7 @@ #include "fightsystem/pk.h" #include "screen.h" #include "boards.h" -#include "skills.h" +#include "skills/skills.h" #include "spells.h" #include "privilege.hpp" #include "chars/char.hpp" diff --git a/src/interpreter.cpp b/src/interpreter.cpp index e0db8fe18..5c0766053 100644 --- a/src/interpreter.cpp +++ b/src/interpreter.cpp @@ -23,13 +23,7 @@ #include "chars/world.characters.hpp" #include "cmd/cmd.generic.h" #include "cmd.wiz/stat.h" -#include "cmd/follow.h" -#include "cmd/hire.h" -#include "cmd/mercenary.h" -#include "cmd/order.h" -#include "cmd/retreat.h" -#include "cmd/telegram.h" -#include "cmd/track.h" +#include "cmd/cmd.generic.h" #include "comm.h" #include "constants.h" #include "craft.commands.hpp" @@ -40,13 +34,14 @@ #include "fightsystem/assist.h" #include "fightsystem/mobact.hpp" #include "depot.hpp" -#include "dg_scripts.h" +#include "dg/dg_scripts.h" #include "features.hpp" #include "fightsystem/assist.h" #include "fightsystem/mobact.hpp" #include "fightsystem/pk.h" #include "fightsystem/start.fight.h" #include "genchar.h" +#include "grp/grp.main.h" #include "glory.hpp" #include "glory_const.hpp" #include "glory_misc.hpp" @@ -69,7 +64,7 @@ #include "privilege.hpp" #include "room.hpp" #include "screen.h" -#include "skills.h" +#include "skills/skills.h" #include "skills/bash.h" #include "skills/block.h" #include "skills/chopoff.h" diff --git a/src/item.creation.cpp b/src/item.creation.cpp index 6eb99016d..29b2715b9 100644 --- a/src/item.creation.cpp +++ b/src/item.creation.cpp @@ -13,7 +13,7 @@ #include "obj.hpp" #include "screen.h" #include "spells.h" -#include "skills.h" +#include "skills/skills.h" #include "constants.h" #include "comm.h" #include "interpreter.h" diff --git a/src/item.creation.hpp b/src/item.creation.hpp index 7ec23fe87..a9ded35d8 100644 --- a/src/item.creation.hpp +++ b/src/item.creation.hpp @@ -10,7 +10,7 @@ #ifndef __ITEM_CREATION_HPP__ #define __ITEM_CREATION_HPP__ -#include "skills.h" +#include "skills/skills.h" #include "interpreter.h" #include "features.hpp" #include "conf.h" diff --git a/src/liquid.cpp b/src/liquid.cpp index 04dd766a5..f7907743c 100644 --- a/src/liquid.cpp +++ b/src/liquid.cpp @@ -16,7 +16,7 @@ #include "comm.h" #include "handler.h" #include "magic.h" -#include "skills.h" +#include "skills/skills.h" #include "room.hpp" #include "screen.h" #include "features.hpp" diff --git a/src/magic.cpp b/src/magic.cpp index 72460f19c..276aec00e 100644 --- a/src/magic.cpp +++ b/src/magic.cpp @@ -42,7 +42,7 @@ #include "random.hpp" #include "room.hpp" #include "screen.h" -#include "skills.h" +#include "skills/skills.h" #include "spells.h" #include "structs.h" #include "sysdep.h" diff --git a/src/medit.cpp b/src/medit.cpp index 08e03adef..9fd5d9dd2 100644 --- a/src/medit.cpp +++ b/src/medit.cpp @@ -19,7 +19,7 @@ #include "features.hpp" #include "im.h" #include "chars/char.hpp" -#include "skills.h" +#include "skills/skills.h" #include "name_list.hpp" #include "room.hpp" #include "corpse.hpp" diff --git a/src/modify.cpp b/src/modify.cpp index 4ac560e45..2507b9f97 100644 --- a/src/modify.cpp +++ b/src/modify.cpp @@ -28,7 +28,7 @@ #include "house.h" #include "privilege.hpp" #include "chars/char.hpp" -#include "skills.h" +#include "skills/skills.h" #include "genchar.h" #include "logger.hpp" #include "utils.h" diff --git a/src/morph.cpp b/src/morph.cpp index 97e66b4eb..6475354ea 100644 --- a/src/morph.cpp +++ b/src/morph.cpp @@ -9,7 +9,7 @@ #include "spells.h" #include "chars/char.hpp" #include "comm.h" -#include "skills.h" +#include "skills/skills.h" #include "db.h" #include "logger.hpp" #include "utils.h" diff --git a/src/morph.hpp b/src/morph.hpp index f092643f1..c62736837 100644 --- a/src/morph.hpp +++ b/src/morph.hpp @@ -1,7 +1,7 @@ #ifndef MORPH_HPP_INCLUDED #define MORPH_HPP_INCLUDED -#include "skills.h" +#include "skills/skills.h" #include "comm.h" #include diff --git a/src/noob.cpp b/src/noob.cpp index fa51997da..63ccd8ba2 100644 --- a/src/noob.cpp +++ b/src/noob.cpp @@ -12,7 +12,7 @@ #include "room.hpp" #include "birth_places.hpp" #include "handler.h" -#include "skills.h" +#include "skills/skills.h" #include "logger.hpp" #include "utils.h" #include "structs.h" diff --git a/src/obj.hpp b/src/obj.hpp index 96feb4237..4b23e64d1 100644 --- a/src/obj.hpp +++ b/src/obj.hpp @@ -8,7 +8,7 @@ #include "id.hpp" #include "obj_enchant.hpp" #include "spells.h" -#include "skills.h" +#include "skills/skills.h" #include "structs.h" #include "sysdep.h" #include "conf.h" diff --git a/src/obj_sets.cpp b/src/obj_sets.cpp index dab17e7a2..6928fcba2 100644 --- a/src/obj_sets.cpp +++ b/src/obj_sets.cpp @@ -16,7 +16,7 @@ #include "constants.h" #include "handler.h" #include "chars/char_player.hpp" -#include "skills.h" +#include "skills/skills.h" #include "screen.h" #include "modify.h" #include "spells.h" diff --git a/src/obj_sets_olc.cpp b/src/obj_sets_olc.cpp index 5cbee7227..a3f8a27cd 100644 --- a/src/obj_sets_olc.cpp +++ b/src/obj_sets_olc.cpp @@ -12,7 +12,7 @@ #include "constants.h" #include "handler.h" #include "chars/char_player.hpp" -#include "skills.h" +#include "skills/skills.h" #include "screen.h" #include "modify.h" #include "spells.h" diff --git a/src/oedit.cpp b/src/oedit.cpp index 7ff1f7da2..ba4d8e2a0 100644 --- a/src/oedit.cpp +++ b/src/oedit.cpp @@ -25,7 +25,7 @@ #include "depot.hpp" #include "chars/char.hpp" #include "house.h" -#include "skills.h" +#include "skills/skills.h" #include "parcel.hpp" #include "liquid.hpp" #include "name_list.hpp" diff --git a/src/poison.cpp b/src/poison.cpp index 79e248de7..a3a3b12a8 100644 --- a/src/poison.cpp +++ b/src/poison.cpp @@ -14,7 +14,7 @@ #include "handler.h" #include "db.h" #include "comm.h" -#include "skills.h" +#include "skills/skills.h" #include "room.hpp" #include "fightsystem/fight.h" diff --git a/src/skills/kick.cpp b/src/skills/kick.cpp index 56ac9cb35..3198d4b5d 100644 --- a/src/skills/kick.cpp +++ b/src/skills/kick.cpp @@ -4,7 +4,6 @@ #include "fightsystem/fight_hit.hpp" #include "fightsystem/common.h" #include "spells.h" -#include "handler.h" #include "protect.h" diff --git a/src/skills.cpp b/src/skills/skills.cpp similarity index 100% rename from src/skills.cpp rename to src/skills/skills.cpp diff --git a/src/skills.h b/src/skills/skills.h similarity index 100% rename from src/skills.h rename to src/skills/skills.h diff --git a/src/spec_procs.cpp b/src/spec_procs.cpp index 5b1f598d2..4316b1c46 100644 --- a/src/spec_procs.cpp +++ b/src/spec_procs.cpp @@ -14,47 +14,19 @@ #include "act.movement.hpp" #include "char_obj_utils.inl" -#include "chars/char.hpp" #include "chars/char_player.hpp" #include "chars/mount.h" #include "chars/player_races.hpp" #include "chars/world.characters.hpp" -#include "cmd/cmd.generic.h" -#include "comm.h" -#include "constants.h" #include "core/leveling.h" -#include "db.h" #include "depot.hpp" -#include "dg/dg_scripts.h" -#include "features.hpp" -#include "char_obj_utils.inl" -#include "chars/char.hpp" -#include "chars/char_player.hpp" -#include "chars/mount.h" -#include "chars/player_races.hpp" -#include "chars/world.characters.hpp" -#include "cmd/follow.h" -#include "cmd/mercenary.h" -#include "comm.h" -#include "constants.h" -#include "db.h" -#include "depot.hpp" -#include "dg_scripts.h" -#include "features.hpp" +#include "grp/grp.main.h" #include "fightsystem/fight.h" #include "fightsystem/fight_hit.hpp" -#include "handler.h" #include "house.h" -#include "interpreter.h" -#include "logger.hpp" #include "magic.h" -#include "obj.hpp" #include "screen.h" -#include "skills.h" #include "spell_parser.hpp" -#include "spells.h" -#include "structs.h" -#include "sysdep.h" #include "temp_spells.hpp" #include diff --git a/src/spell_parser.cpp b/src/spell_parser.cpp index 8a8180a65..c06f86569 100644 --- a/src/spell_parser.cpp +++ b/src/spell_parser.cpp @@ -20,7 +20,7 @@ #include "obj.hpp" #include "interpreter.h" #include "spells.h" -#include "skills.h" +#include "skills/skills.h" #include "handler.h" #include "comm.h" #include "db.h" diff --git a/src/spell_parser.hpp b/src/spell_parser.hpp index 10d5c0104..c66eaaecc 100644 --- a/src/spell_parser.hpp +++ b/src/spell_parser.hpp @@ -1,7 +1,7 @@ #ifndef __SPELL_PARSER_HPP__ #define __SPELL_PARSER_HPP__ -#include "skills.h" +#include "skills/skills.h" ESkill find_skill_num(const char *name); ESkill fix_name_and_find_skill_num(char *name); diff --git a/src/spells.cpp b/src/spells.cpp index 685b1f764..5592336e6 100644 --- a/src/spells.cpp +++ b/src/spells.cpp @@ -17,9 +17,9 @@ #include "char_obj_utils.inl" #include "chars/world.characters.hpp" #include "depot.hpp" +#include "fightsystem/fight.h" #include "fightsystem/mobact.hpp" #include "fightsystem/pk.h" -#include "grp/follow.h" #include "grp/grp.main.h" #include "handler.h" #include "house.h" @@ -55,7 +55,6 @@ ESkill get_magic_skill_number_by_spell(int spellnum); bool can_get_spell(CHAR_DATA *ch, int spellnum); bool can_get_spell_with_req(CHAR_DATA *ch, int spellnum, int req_lvl); void weight_change_object(OBJ_DATA * obj, int weight); -int compute_armor_class(CHAR_DATA * ch); char *diag_weapon_to_char(const CObjectPrototype* obj, int show_wear); void create_rainsnow(int *wtype, int startvalue, int chance1, int chance2, int chance3); int calc_anti_savings(CHAR_DATA * ch); diff --git a/src/spells.h b/src/spells.h index 951d5c167..132947b2a 100644 --- a/src/spells.h +++ b/src/spells.h @@ -11,7 +11,7 @@ #ifndef _SPELLS_H_ #define _SPELLS_H_ -#include "skills.h" +#include "skills/skills.h" #include "structs.h" // there was defined type "byte" if it had been missing struct ROOM_DATA; // forward declaration to avoid inclusion of room.hpp and any dependencies of that header. diff --git a/src/stuff.cpp b/src/stuff.cpp index 2eff51bfd..adc40869d 100644 --- a/src/stuff.cpp +++ b/src/stuff.cpp @@ -20,7 +20,7 @@ #include "room.hpp" #include "corpse.hpp" #include "screen.h" -#include "skills.h" +#include "skills/skills.h" #include "sets_drop.hpp" #include "logger.hpp" #include "utils.h" diff --git a/src/utils.cpp b/src/utils.cpp index 17bd2da4a..9e2f0f773 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -37,7 +37,7 @@ #include "depot.hpp" #include "objsave.h" #include "fightsystem/fight.h" -#include "skills.h" +#include "skills/skills.h" #include "exchange.h" #include "sets_drop.hpp" #include "structs.h" diff --git a/src/weather.cpp b/src/weather.cpp index ed9a6f1f0..cca085193 100644 --- a/src/weather.cpp +++ b/src/weather.cpp @@ -18,7 +18,7 @@ #include "sysdep.h" #include "structs.h" #include "spells.h" -#include "skills.h" +#include "skills/skills.h" #include "logger.hpp" #include "utils.h" #include "comm.h" From f3971b9b80da0641c196b96f68bb69b36f67455f Mon Sep 17 00:00:00 2001 From: Demidov Date: Thu, 21 Jan 2021 00:36:56 +0300 Subject: [PATCH 25/40] =?UTF-8?q?=D0=91=D0=B0=D0=B3=D1=84=D0=B8=D0=BA?= =?UTF-8?q?=D1=81,=20=D1=84=D0=B8=D0=BD=D0=B0=D0=BB=D1=8C=D0=BD=D0=BE?= =?UTF-8?q?=D0=B5=20=D1=82=D0=B5=D1=81=D1=82=D0=B8=D1=80=D0=BE=D0=B2=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D0=B2=D1=81=D1=8F=D0=BA=D0=BE=D0=B3=D0=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 3 ++- src/act.other.cpp | 4 ++-- src/chars/char.cpp | 11 +++++---- src/chars/char.hpp | 2 +- src/cmd/follow.cpp | 50 ++++++++++++---------------------------- src/core/affect_data.cpp | 2 +- src/grp/follow.cpp | 27 ++++++---------------- src/grp/grp.group.cpp | 10 +++++--- src/grp/grp.main.h | 2 +- src/grp/grp.roster.cpp | 13 +++++++---- src/objsave.cpp | 2 +- src/scripting.cpp | 2 +- src/spec_procs.cpp | 6 ----- src/structs.cpp | 2 +- src/structs.h | 2 +- 15 files changed, 55 insertions(+), 83 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f96c97025..f3398701c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -761,7 +761,8 @@ endif () if (CMAKE_HOST_UNIX) if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang") set(CMAKE_CXX_FLAGS_RELEASE "-ggdb3 -O0 -Wall -Wextra") - set(CMAKE_CXX_FLAGS_DEBUG "-ggdb3 -O0 -Wall -Wextra -D_GLIBCXX_DEBUG -D_GLIBXX_DEBUG_PEDANTIC ${ASAN_FLAGS} ${DEBUG_CRYPT}") +# set(CMAKE_CXX_FLAGS_DEBUG "-ggdb3 -O0 -Wall -Wextra -D_GLIBCXX_DEBUG -D_GLIBXX_DEBUG_PEDANTIC ${ASAN_FLAGS} ${DEBUG_CRYPT}") + set(CMAKE_CXX_FLAGS_DEBUG "-ggdb3 -O0 -Wall -Wextra -D_GLIBCXX_DEBUG -D_GLIBXX_DEBUG_PEDANTIC ${TESTBUILD_DEFINITIONS} ${ASAN_FLAGS} ${DEBUG_CRYPT}") set(CMAKE_CXX_FLAGS_TEST " -Og -Wall -Wextra ${TESTBUILD_DEFINITIONS} -DLOG_AUTOFLUSH") set(CMAKE_CXX_FLAGS_FASTTEST " -Ofast -Wall -Wextra ${TESTBUILD_DEFINITIONS}") diff --git a/src/act.other.cpp b/src/act.other.cpp index 3a8175806..b883db14e 100644 --- a/src/act.other.cpp +++ b/src/act.other.cpp @@ -1607,8 +1607,8 @@ void do_gen_tog(CHAR_DATA *ch, char *argument, int/* cmd*/, int subcmd) "Режим вывода тестовой информации включен.\r\n"}, {"Режим контроля смены IP-адреса персонажа выключен.\r\n", "Режим контроля смены IP-адреса персонажа включен.\r\n"}, - {"После изменения порядка следования (след я) вы автоматически покинете группу.\r\n", - "Изменение порядка следования не влияет на членство в группе. Используйте команду груп покинуть.\r\n"} + {"При изменении порядка следования (след я) вы не покинете группу. Используйте команду груп покинуть.\r\n", + "При изменении порядка следования (след я) вы автоматически покинете группу.\r\n"} }; if (IS_NPC(ch)) diff --git a/src/chars/char.cpp b/src/chars/char.cpp index 6160c757b..f68d391cd 100644 --- a/src/chars/char.cpp +++ b/src/chars/char.cpp @@ -2032,11 +2032,14 @@ void CHAR_DATA::msdp_report(const std::string& name) { } } -void CHAR_DATA::removeGroupFlags() { - AFF_FLAGS(this).unset(EAffectFlag::AFF_GROUP); - PRF_FLAGS(this).unset(PRF_SKIRMISHER); - if (personGroup != nullptr && IS_CHARMICE(this)) +void CHAR_DATA::removeGroupFlags(bool reboot) { + if (personGroup == nullptr) + return; + // чармис всегда, персонаж по настройке, или при ребуте + if (IS_CHARMICE(this) || PRF_FLAGGED(this, PRF_FOLLOW_GRP_EXIT) || reboot) { + PRF_FLAGS(this).unset(PRF_SKIRMISHER); personGroup->_removeMember(this); + } } void CHAR_DATA::add_follower(CHAR_DATA* ch) { diff --git a/src/chars/char.hpp b/src/chars/char.hpp index 979b06315..2e1fbe193 100644 --- a/src/chars/char.hpp +++ b/src/chars/char.hpp @@ -638,7 +638,7 @@ class CHAR_DATA : public ProtectedCharacterData void set_role(const role_t& new_role) { role_ = new_role; } void msdp_report(const std::string& name); - void removeGroupFlags(); + void removeGroupFlags(bool reboot = false); void add_follower(CHAR_DATA* ch); /** Do NOT call this before having checked if a circle of followers * will arise. CH will follow leader diff --git a/src/cmd/follow.cpp b/src/cmd/follow.cpp index 6a862160c..7d2a64baf 100644 --- a/src/cmd/follow.cpp +++ b/src/cmd/follow.cpp @@ -12,74 +12,54 @@ void do_follow(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) if (IS_NPC(ch) && AFF_FLAGGED(ch, EAffectFlag::AFF_CHARM) && ch->get_fighting()) return; - if (*smallBuf) - { - if (!str_cmp(smallBuf, "я") || !str_cmp(smallBuf, "self") || !str_cmp(smallBuf, "me")) - { - if (!ch->has_master()) - { + if (*smallBuf) { + if (!str_cmp(smallBuf, "я") || !str_cmp(smallBuf, "self") || !str_cmp(smallBuf, "me")) { + if (!ch->has_master()) { send_to_char("Но вы ведь ни за кем не следуете...\r\n", ch); } - else - { + else { stop_follower(ch, SF_EMPTY); } return; } - if (!(leader = get_char_vis(ch, smallBuf, FIND_CHAR_ROOM))) - { + if (!(leader = get_char_vis(ch, smallBuf, FIND_CHAR_ROOM))) { send_to_char(NOPERSON, ch); return; } } - else - { + else { send_to_char("За кем вы хотите следовать?\r\n", ch); return; } - if (ch->get_master() == leader) - { + if (ch->get_master() == leader) { act("Вы уже следуете за $N4.", FALSE, ch, 0, leader, TO_CHAR); return; } - if (AFF_FLAGGED(ch, EAffectFlag::AFF_CHARM) - && ch->has_master()) - { + if (AFF_FLAGGED(ch, EAffectFlag::AFF_CHARM) && ch->has_master()) { act("Но вы можете следовать только за $N4!", FALSE, ch, 0, ch->get_master(), TO_CHAR); } - else // Not Charmed follow person - { - if (leader == ch) - { - if (!ch->has_master()) - { + else { + if (leader == ch) { + if (!ch->has_master()) { send_to_char("Вы уже следуете за собой.\r\n", ch); return; } stop_follower(ch, SF_EMPTY); } - else - { - if (circle_follow(ch, leader)) - { + else { + if (circle_follow(ch, leader)) { send_to_char("Так у вас целый хоровод получится.\r\n", ch); return; } - - if (ch->has_master()) - { + if (ch->has_master()) { stop_follower(ch, SF_EMPTY); } - //AFF_FLAGS(ch).unset(EAffectFlag::AFF_GROUP); ch->removeGroupFlags(); - for (f = ch->followers; f; f = f->next) - { - //AFF_FLAGS(f->follower).unset(EAffectFlag::AFF_GROUP); + for (f = ch->followers; f; f = f->next) { f->follower->removeGroupFlags(); } - leader->add_follower(ch); } } diff --git a/src/core/affect_data.cpp b/src/core/affect_data.cpp index 4bfdf571b..337e6ff11 100644 --- a/src/core/affect_data.cpp +++ b/src/core/affect_data.cpp @@ -131,7 +131,7 @@ void apply_natural_affects(CHAR_DATA *ch) std::array char_saved_aff = { - EAffectFlag::AFF_GROUP, + EAffectFlag::AFF_UNUSED1, EAffectFlag::AFF_HORSE }; diff --git a/src/grp/follow.cpp b/src/grp/follow.cpp index 83f2c9534..c50fe323c 100644 --- a/src/grp/follow.cpp +++ b/src/grp/follow.cpp @@ -17,29 +17,22 @@ bool stop_follower(CHAR_DATA * ch, int mode) struct follow_type *j, *k; int i; - //log("[Stop follower] Start function(%s->%s)",ch ? GET_NAME(ch) : "none", - // ch->master ? GET_NAME(ch->master) : "none"); - - if (!ch->has_master()) - { + if (!ch->has_master()) { log("SYSERR: stop_follower(%s) without master", GET_NAME(ch)); return (FALSE); } // для смены лидера без лишнего спама - if (!IS_SET(mode, SF_SILENCE)) - { + if (!IS_SET(mode, SF_SILENCE)) { act("Вы прекратили следовать за $N4.", FALSE, ch, 0, ch->get_master(), TO_CHAR); act("$n прекратил$g следовать за $N4.", TRUE, ch, 0, ch->get_master(), TO_NOTVICT | TO_ARENA_LISTEN); } //log("[Stop follower] Stop horse"); - if (ch->get_master()->get_horse() == ch && ch->get_master()->ahorse()) - { + if (ch->get_master()->get_horse() == ch && ch->get_master()->ahorse()) { ch->drop_from_horse(); } - else - { + else { act("$n прекратил$g следовать за вами.", TRUE, ch, 0, ch->get_master(), TO_VICT); } @@ -49,10 +42,6 @@ bool stop_follower(CHAR_DATA * ch, int mode) } else if (ch->get_master()->followers->follower == ch) { k = ch->get_master()->followers; ch->get_master()->followers = k->next; - - if (!ch->get_master()->followers && !ch->get_master()->has_master()) { - ch->get_master()->removeGroupFlags(); - } free(k); } else { // locate follower who is not head of list @@ -68,6 +57,7 @@ bool stop_follower(CHAR_DATA * ch, int mode) } ch->set_master(nullptr); + // чармис покидает группу всегда, персонаж по настроению ch->removeGroupFlags(); if (IS_CHARMICE(ch)|| IS_SET(mode, SF_CHARMLOST)) { @@ -96,11 +86,8 @@ bool stop_follower(CHAR_DATA * ch, int mode) } } } - - if (IS_NPC(ch) - && !MOB_FLAGGED(ch, MOB_PLAYER_SUMMON) //Не ресетим флаги, если моб призван игроком - && (i = GET_MOB_RNUM(ch)) >= 0) - { +//Не ресетим флаги, если моб призван игроком + if (IS_NPC(ch)&& !MOB_FLAGGED(ch, MOB_PLAYER_SUMMON) && (i = GET_MOB_RNUM(ch)) >= 0) { MOB_FLAGS(ch) = MOB_FLAGS(mob_proto + i); } diff --git a/src/grp/grp.group.cpp b/src/grp/grp.group.cpp index 0fbf19b79..5fae56555 100644 --- a/src/grp/grp.group.cpp +++ b/src/grp/grp.group.cpp @@ -208,7 +208,7 @@ bool Group::_removeMember(int memberUID) { send_to_char(member, "Вы покинули группу.\r\n"); member->personGroup = nullptr; } - actToGroup(nullptr, it->second->member, GC_ROOM , "%n покинул$g группу."); + actToGroup(nullptr, it->second->member, GC_LEADER | GC_REST , "$N покинул$G группу."); if (it->second->type == GM_CHAR) _pcCount--; this->erase(memberUID); // finally remove it @@ -620,13 +620,17 @@ void Group::actToGroup(CHAR_DATA* ch, CHAR_DATA* vict, int mode, const char *msg act(smallBuf, TRUE, ch, nullptr, vict, TO_ROOM | TO_ARENA_LISTEN); } -void Group::addFollowers(CHAR_DATA *leader) { +bool Group::addFollowers(CHAR_DATA *leader) { + bool result = false; for (auto f = leader->followers; f; f = f->next) { if (IS_NPC(f->follower)) continue; - if ((u_short)this->get_size() < (u_short)_memberCap) + if ((u_short)this->get_size() < (u_short)_memberCap) { this->addMember(f->follower); + result = true; + } } + return result; } // метод может вернуть мусор :( diff --git a/src/grp/grp.main.h b/src/grp/grp.main.h index 2693a568c..852252947 100644 --- a/src/grp/grp.main.h +++ b/src/grp/grp.main.h @@ -143,7 +143,7 @@ class Group : public grp_mt { public: - void addFollowers(CHAR_DATA* leader); + bool addFollowers(CHAR_DATA* leader); void addMember(CHAR_DATA *member, bool silent = false); void expellMember(char* memberName); bool _restoreMember(CHAR_DATA *member); diff --git a/src/grp/grp.roster.cpp b/src/grp/grp.roster.cpp index b875312a9..c1f900c76 100644 --- a/src/grp/grp.roster.cpp +++ b/src/grp/grp.roster.cpp @@ -32,7 +32,7 @@ void GroupRoster::processGroupCommands(CHAR_DATA *ch, char *argument) { const std::string strREJECT = "отклонить reject"; const std::string strEXPELL = "выгнать expell"; const std::string strLEADER = "лидер leader"; - const std::string strLEAVE = "покинуть leave"; + const std::string strLEAVE = "покинуть выйти leave"; const std::string strDISBAND = "распустить clear"; const std::string strWORLD = "мир world"; const std::string strALL = "все all";// старый режим, создание группы и добавление всех последователей. @@ -61,7 +61,7 @@ void GroupRoster::processGroupCommands(CHAR_DATA *ch, char *argument) { return; } else if (isname(subcmd, strMAKE.c_str())) { if (grp != nullptr && grp->getLeader() == ch){ - send_to_char(ch, "Только великим правителям, навроде Цесаря Иулия, было дозволено водить много легионов!\r\n"); + send_to_char(ch, "Только великим правителям, навроде Цесаря Иулия, было под силу водить много легионов!\r\n"); return; } if (grp != nullptr) // в группе - покидаем @@ -72,18 +72,21 @@ void GroupRoster::processGroupCommands(CHAR_DATA *ch, char *argument) { grp->listMembers(ch); return; } else if (isname(subcmd, strALL.c_str())) { - // если в группе, печатаем полный список. + // если не в группе, создаем и добавляем всех последователей if (grp == nullptr) { grp = groupRoster.addGroup(ch).get(); - grp->addFollowers(ch); + if (!grp->addFollowers(ch)) + send_to_char(ch, "А окромя вас, в группу то добавить и некого...\r\n"); return; } + // если не лидер и в группе, печатаем полный список. if (grp->getLeader() != ch){ grp->printGroup(ch); return; } // сюда приходим лидером и добавляем всех, кто следует. - grp->addFollowers(ch); + if (!grp->addFollowers(ch)) + send_to_char(ch, "Все, кто за вами следует, уже в группе.\r\n"); return; } else if (isname(subcmd, strLEAVE.c_str())){ grp->leaveGroup(ch); diff --git a/src/objsave.cpp b/src/objsave.cpp index 4baa9fa42..1590697ee 100644 --- a/src/objsave.cpp +++ b/src/objsave.cpp @@ -3448,7 +3448,7 @@ void Crash_save_all_rent(void) save_char_objects(ch.get(), RENT_FORCED, 0); log("Saving char: %s", GET_NAME(ch)); PLR_FLAGS(ch).unset(PLR_CRASH); - (ch.get())->removeGroupFlags(); + (ch.get())->removeGroupFlags(true); AFF_FLAGS(ch.get()).unset(EAffectFlag::AFF_HORSE); extract_char(ch.get(), FALSE); } diff --git a/src/scripting.cpp b/src/scripting.cpp index a5e17957a..055fcdc04 100644 --- a/src/scripting.cpp +++ b/src/scripting.cpp @@ -2014,7 +2014,7 @@ BOOST_PYTHON_MODULE(constants) DEFINE_ENUM_CONSTANT(EAffectFlag::AFF_SENSE_LIFE); DEFINE_ENUM_CONSTANT(EAffectFlag::AFF_WATERWALK); DEFINE_ENUM_CONSTANT(EAffectFlag::AFF_SANCTUARY); - DEFINE_ENUM_CONSTANT(EAffectFlag::AFF_GROUP); + DEFINE_ENUM_CONSTANT(EAffectFlag::AFF_UNUSED1); DEFINE_ENUM_CONSTANT(EAffectFlag::AFF_CURSE); DEFINE_ENUM_CONSTANT(EAffectFlag::AFF_INFRAVISION); DEFINE_ENUM_CONSTANT(EAffectFlag::AFF_POISON); diff --git a/src/spec_procs.cpp b/src/spec_procs.cpp index 4316b1c46..3d6b319a4 100644 --- a/src/spec_procs.cpp +++ b/src/spec_procs.cpp @@ -2799,18 +2799,12 @@ void npc_group(CHAR_DATA * ch) continue; } - if (vict == leader) { - AFF_FLAGS(vict).set(EAffectFlag::AFF_GROUP); - continue; - } - if (!vict->has_master()) { leader->add_follower(vict); } else if (vict->get_master() != leader) { stop_follower(vict, SF_EMPTY); leader->add_follower(vict); } - AFF_FLAGS(vict).set(EAffectFlag::AFF_GROUP); } } diff --git a/src/structs.cpp b/src/structs.cpp index a7b94bab2..a955f677f 100644 --- a/src/structs.cpp +++ b/src/structs.cpp @@ -678,7 +678,7 @@ void init_EAffectFlag_ITEM_NAMES() EAffectFlag_name_by_value[EAffectFlag::AFF_SENSE_LIFE] = "AFF_SENSE_LIFE"; EAffectFlag_name_by_value[EAffectFlag::AFF_WATERWALK] = "AFF_WATERWALK"; EAffectFlag_name_by_value[EAffectFlag::AFF_SANCTUARY] = "AFF_SANCTUARY"; - EAffectFlag_name_by_value[EAffectFlag::AFF_GROUP] = "AFF_GROUP"; + EAffectFlag_name_by_value[EAffectFlag::AFF_UNUSED1] = "AFF_UNUSED1"; EAffectFlag_name_by_value[EAffectFlag::AFF_CURSE] = "AFF_CURSE"; EAffectFlag_name_by_value[EAffectFlag::AFF_INFRAVISION] = "AFF_INFRAVISION"; EAffectFlag_name_by_value[EAffectFlag::AFF_POISON] = "AFF_POISON"; diff --git a/src/structs.h b/src/structs.h index 6c0816957..545a149c0 100644 --- a/src/structs.h +++ b/src/structs.h @@ -637,7 +637,7 @@ enum class EAffectFlag: uint32_t AFF_SENSE_LIFE = 1u << 5, ///< Char can sense hidden life AFF_WATERWALK = 1u << 6, ///< Char can walk on water AFF_SANCTUARY = 1u << 7, ///< Char protected by sanct. - AFF_GROUP = 1u << 8, ///< (R) Char is grouped + AFF_UNUSED1 = 1u << 8, ///< (R) Char is grouped AFF_CURSE = 1u << 9, ///< Char is cursed AFF_INFRAVISION = 1u << 10, ///< Char can see in dark AFF_POISON = 1u << 11, ///< (R) Char is poisoned From 9092ddfdf5c6552f1f60bf887fa536cf21b0d732 Mon Sep 17 00:00:00 2001 From: Demidov Date: Thu, 21 Jan 2021 11:18:35 +0300 Subject: [PATCH 26/40] =?UTF-8?q?=D0=9F=D0=BE=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=B8=D0=BB=20=D0=B4=D0=B5=D1=84=D0=B8=D0=BD=D0=B8=D1=86=D0=B8?= =?UTF-8?q?=D1=8E=20=D0=B1=D0=B8=D0=BB=D0=B4=D0=B0,=20=D1=87=D1=82=D0=BE?= =?UTF-8?q?=D0=B1=20=D0=B2=20=D0=B1=D0=BE=D1=8E=20=D0=BD=D0=B5=20=D1=81?= =?UTF-8?q?=D0=BB=D0=BE=D0=BC=D0=B0=D0=BB=D0=BE=D1=81=D1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f3398701c..b324a405d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -761,8 +761,8 @@ endif () if (CMAKE_HOST_UNIX) if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang") set(CMAKE_CXX_FLAGS_RELEASE "-ggdb3 -O0 -Wall -Wextra") -# set(CMAKE_CXX_FLAGS_DEBUG "-ggdb3 -O0 -Wall -Wextra -D_GLIBCXX_DEBUG -D_GLIBXX_DEBUG_PEDANTIC ${ASAN_FLAGS} ${DEBUG_CRYPT}") - set(CMAKE_CXX_FLAGS_DEBUG "-ggdb3 -O0 -Wall -Wextra -D_GLIBCXX_DEBUG -D_GLIBXX_DEBUG_PEDANTIC ${TESTBUILD_DEFINITIONS} ${ASAN_FLAGS} ${DEBUG_CRYPT}") + set(CMAKE_CXX_FLAGS_DEBUG "-ggdb3 -O0 -Wall -Wextra -D_GLIBCXX_DEBUG -D_GLIBXX_DEBUG_PEDANTIC ${ASAN_FLAGS} ${DEBUG_CRYPT}") +# set(CMAKE_CXX_FLAGS_DEBUG "-ggdb3 -O0 -Wall -Wextra -D_GLIBCXX_DEBUG -D_GLIBXX_DEBUG_PEDANTIC ${TESTBUILD_DEFINITIONS} ${ASAN_FLAGS} ${DEBUG_CRYPT}") set(CMAKE_CXX_FLAGS_TEST " -Og -Wall -Wextra ${TESTBUILD_DEFINITIONS} -DLOG_AUTOFLUSH") set(CMAKE_CXX_FLAGS_FASTTEST " -Ofast -Wall -Wextra ${TESTBUILD_DEFINITIONS}") From a617e250e3c573d2188915c9d7c98e761b455a1d Mon Sep 17 00:00:00 2001 From: Demidov Date: Fri, 22 Jan 2021 13:15:11 +0300 Subject: [PATCH 27/40] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=B8=D0=BB=20=D0=BA=D1=80=D0=B5=D1=88=20=D0=BD=D0=B0=20=D0=BF?= =?UTF-8?q?=D1=83=D1=80=D0=B6=D0=B5=20=D0=B1=D0=BE=D1=81=D1=81-=D0=BC?= =?UTF-8?q?=D0=BE=D0=B1=D0=B0.=20=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=B8=D0=BB=20=D0=B4=D0=B5=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20?= =?UTF-8?q?=D0=BA=D1=83=D0=BD.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/act.item.cpp | 28 ++++++++++++++-------------- src/act.other.cpp | 3 +-- src/ext_money.cpp | 24 +++++++++++------------- src/grp/grp.cmdprocessor.cpp | 4 ++-- src/grp/grp.main.h | 3 +++ src/interpreter.cpp | 2 -- 6 files changed, 31 insertions(+), 33 deletions(-) diff --git a/src/act.item.cpp b/src/act.item.cpp index 68c8df653..af95d4a5a 100644 --- a/src/act.item.cpp +++ b/src/act.item.cpp @@ -12,40 +12,42 @@ * $Revision$ * ************************************************************************ */ -#include "cmd/cmd.generic.h" -#include "world.objects.hpp" -#include "object.prototypes.hpp" -#include "logger.hpp" -#include "obj.hpp" +#include "char_obj_utils.inl" #include "chars/char.hpp" +#include "cmd/cmd.generic.h" #include "comm.h" +#include "conf.h" #include "constants.h" #include "db.h" #include "depot.hpp" #include "dg/dg_scripts.h" #include "features.hpp" #include "fightsystem/fight.h" +#include "fightsystem/pk.h" +#include "global.objects.hpp" +#include "grp/grp.main.h" #include "handler.h" #include "house.h" #include "im.h" #include "interpreter.h" #include "liquid.hpp" +#include "logger.hpp" #include "magic.h" +#include "meat.maker.hpp" +#include "mobmax.hpp" #include "named_stuff.hpp" +#include "obj.hpp" +#include "object.prototypes.hpp" #include "objsave.h" -#include "fightsystem/pk.h" #include "poison.hpp" #include "room.hpp" #include "skills/skills.h" #include "spells.h" -#include "mobmax.hpp" -#include "meat.maker.hpp" +#include "strengthening.hpp" #include "structs.h" #include "sysdep.h" -#include "conf.h" -#include "char_obj_utils.inl" -#include "global.objects.hpp" -#include "strengthening.hpp" +#include "world.objects.hpp" + #include @@ -83,8 +85,6 @@ bool unique_stuff(const CHAR_DATA *ch, const OBJ_DATA *obj); // from class.cpp int invalid_no_class(CHAR_DATA * ch, const OBJ_DATA * obj); -void do_split(CHAR_DATA *ch, char *argument, int cmd, int subcmd); -void do_split(CHAR_DATA *ch, char *argument, int cmd, int subcmd,int currency); void do_remove(CHAR_DATA *ch, char *argument, int cmd, int subcmd); void do_put(CHAR_DATA *ch, char *argument, int cmd, int subcmd); void do_get(CHAR_DATA *ch, char *argument, int cmd, int subcmd); diff --git a/src/act.other.cpp b/src/act.other.cpp index b883db14e..5b3daf34e 100644 --- a/src/act.other.cpp +++ b/src/act.other.cpp @@ -94,8 +94,7 @@ void do_spells(CHAR_DATA *ch, char *argument, int cmd, int subcmd); void do_features(CHAR_DATA *ch, char *argument, int cmd, int subcmd); void do_skills(CHAR_DATA *ch, char *argument, int cmd, int subcmd); void do_visible(CHAR_DATA *ch, char *argument, int cmd, int subcmd); -void do_split(CHAR_DATA *ch, char *argument, int cmd, int subcmd); -void do_split(CHAR_DATA *ch, char *argument, int cmd, int subcmd,int currency); + void do_use(CHAR_DATA *ch, char *argument, int cmd, int subcmd); void do_wimpy(CHAR_DATA *ch, char *argument, int cmd, int subcmd); void do_display(CHAR_DATA *ch, char *argument, int cmd, int subcmd); diff --git a/src/ext_money.cpp b/src/ext_money.cpp index 7b1034f2d..5f2c97918 100644 --- a/src/ext_money.cpp +++ b/src/ext_money.cpp @@ -662,9 +662,8 @@ namespace ExtMoney // то, что чар находился в комнате с мобом не менее половины раундов дамагера) void drop_torc(CHAR_DATA *mob) { - if (!mob->get_role(MOB_ROLE_BOSS) - || has_connected_bosses(mob)) - { + CHAR_DATA* m; + if (!mob->get_role(MOB_ROLE_BOSS) || has_connected_bosses(mob)) { return; } @@ -679,10 +678,10 @@ namespace ExtMoney return; } auto grp = d->character->personGroup; + // если чар в группе, хватаем лидера CHAR_DATA *leader = grp != nullptr? d->character->personGroup->getLeader() : d->character.get(); - int members = 1; - members = leader->personGroup->get_size(IN_ROOM(mob)); + int members = grp != nullptr? leader->personGroup->get_size(IN_ROOM(mob)) : 1; const int zone_lvl = zone_table[mob_index[GET_MOB_RNUM(mob)].zone].mob_level; const int drop = calc_drop_torc(zone_lvl, members); @@ -691,20 +690,19 @@ namespace ExtMoney return; } - if (IN_ROOM(leader) == IN_ROOM(mob) - && GET_GOD_FLAG(leader, GF_REMORT) - && (GET_UNIQUE(leader) == damager.first - || mob->get_attacker(leader, ATTACKER_ROUNDS) >= damager.second / 2)) { + if (SAME_ROOM(leader, mob) && GET_GOD_FLAG(leader, GF_REMORT) + && (GET_UNIQUE(leader) == damager.first || mob->get_attacker(leader, ATTACKER_ROUNDS) >= damager.second / 2)) { gain_torc(leader, drop); } // он был один, сваливаем if (grp == nullptr) return; - for (const auto& m : *grp) { - if (m.second->member == nullptr || IN_ROOM(mob) != IN_ROOM(m.second->member)) + for (const auto& it : *grp) { + m = it.second->member; + if (m == nullptr || !SAME_ROOM(mob, m) || m == leader) // лидеру уже раздали return; - if (GET_GOD_FLAG(m.second->member, GF_REMORT) && mob->get_attacker(m.second->member, ATTACKER_ROUNDS) >= damager.second / 2) { - gain_torc(m.second->member, drop); + if (GET_GOD_FLAG(m, GF_REMORT) && mob->get_attacker(m, ATTACKER_ROUNDS) >= damager.second / 2) { + gain_torc(m, drop); } } } diff --git a/src/grp/grp.cmdprocessor.cpp b/src/grp/grp.cmdprocessor.cpp index 647e6dbce..3ba2d62c7 100644 --- a/src/grp/grp.cmdprocessor.cpp +++ b/src/grp/grp.cmdprocessor.cpp @@ -192,7 +192,7 @@ void do_report(CHAR_DATA *ch, char* /*argument*/, int/* cmd*/, int/* subcmd*/) void do_split(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/,int currency) { - int amount, num, share, rest; + int amount, num = 0, share, rest; CHAR_DATA* m; @@ -231,7 +231,7 @@ void do_split(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/,int cur } for (auto it : *grp) { - if (it.second->type == GM_CHAR &&SAME_ROOM(it.second->member, ch) ) + if (it.second->type == GM_CHAR && SAME_ROOM(it.second->member, ch) ) num++; } diff --git a/src/grp/grp.main.h b/src/grp/grp.main.h index 852252947..791bffda8 100644 --- a/src/grp/grp.main.h +++ b/src/grp/grp.main.h @@ -31,6 +31,9 @@ enum GRP_COMM : short { enum RQ_R {RQ_R_OK, RQ_R_NO_GROUP, RQ_R_OVERFLOW, RQ_REFRESH}; enum INV_R {INV_R_OK, INV_R_NO_PERSON, INV_R_BUSY, INV_R_REFRESH}; +void do_split(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/,int currency); +void do_split(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/); + void do_grequest(CHAR_DATA *ch, char *argument, int, int); void do_group2(CHAR_DATA *ch, char *argument, int, int); void do_ungroup(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/); diff --git a/src/interpreter.cpp b/src/interpreter.cpp index 5c0766053..8fff6b871 100644 --- a/src/interpreter.cpp +++ b/src/interpreter.cpp @@ -307,8 +307,6 @@ void do_sneak(CHAR_DATA *ch, char *argument, int cmd, int subcmd); void do_snoop(CHAR_DATA *ch, char *argument, int cmd, int subcmd); void do_spec_comm(CHAR_DATA *ch, char *argument, int cmd, int subcmd); void do_spell_capable(CHAR_DATA *ch, char *argument, int cmd, int subcmd); -void do_split(CHAR_DATA *ch, char *argument, int cmd, int subcmd); -void do_split(CHAR_DATA *ch, char *argument, int cmd, int subcmd,int currency); void do_fry(CHAR_DATA *ch, char *argument, int cmd, int subcmd); void do_steal(CHAR_DATA *ch, char *argument, int cmd, int subcmd); void do_switch(CHAR_DATA *ch, char *argument, int cmd, int subcmd); From c79db6dd5e2bcec0a7e7417d293aad13a90a909e Mon Sep 17 00:00:00 2001 From: Demidov Date: Sat, 23 Jan 2021 14:54:38 +0300 Subject: [PATCH 28/40] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=B8=D0=BB=20=D1=80=D0=B0=D0=B7=D0=B4=D0=B0=D1=87=D1=83=20?= =?UTF-8?q?=D1=8D=D1=81=D0=BF=D1=8B.=20=D0=A0=D0=B5=D0=B0=D0=BD=D0=B8?= =?UTF-8?q?=D0=BC=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BB=20=D0=BA=D0=BE=D0=BC?= =?UTF-8?q?=D0=B0=D0=BD=D0=B4=D1=83=20=D1=80=D0=B0=D0=B7=D0=B3=D1=80=D1=83?= =?UTF-8?q?=D0=BF=D0=BF=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D1=82=D1=8C=20(?= =?UTF-8?q?=D0=B0=D0=BD=D0=B0=D0=BB=D0=BE=D0=B3=20=D0=B3=D1=80=D1=83=D0=BF?= =?UTF-8?q?=D0=BF=D0=B0=20=D1=80=D0=B0=D1=81=D0=BF=D1=83=D1=81=D1=82=D0=B8?= =?UTF-8?q?=D1=82=D1=8C)=20=D0=B8=20=D1=80=D0=B0=D0=B7=D0=B3=D1=80=D1=83?= =?UTF-8?q?=D0=BF=D0=BF=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D1=82=D1=8C=20=D0=B8?= =?UTF-8?q?=D0=BC=D1=8F=20(=D0=B0=D0=BD=D0=B0=D0=BB=D0=BE=D0=B3=20=D0=B3?= =?UTF-8?q?=D1=80=D1=83=D0=BF=D0=BF=D0=B0=20=D0=B2=D1=8B=D0=B3=D0=BD=D0=B0?= =?UTF-8?q?=D1=82=D1=8C=20=D0=B8=D0=BC=D1=8F).?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/act.informative.cpp | 2 +- src/act.other.cpp | 6 +++--- src/grp/grp.cmdprocessor.cpp | 11 +++++++++-- src/grp/grp.group.cpp | 5 +++-- src/grp/grp.main.h | 22 ++++++++++++++++------ src/grp/grp.roster.cpp | 29 +++++++++++++++++++++++++++++ src/interpreter.cpp | 6 +++--- 7 files changed, 64 insertions(+), 17 deletions(-) diff --git a/src/act.informative.cpp b/src/act.informative.cpp index 8bb93aa0b..c9ff69776 100644 --- a/src/act.informative.cpp +++ b/src/act.informative.cpp @@ -5922,7 +5922,7 @@ void do_toggle(CHAR_DATA *ch, char* /*argument*/, int/* cmd*/, int/* subcmd*/) " Потеря связи : %-3s " " Ингредиенты : %-3s " " Вспомнить : %-3u \r\n" - " Автогрупвыход : %-3s " + " ГрупВыход : %-3s " " : %-3s " " : %-3s \r\n", ONOFF(PRF_FLAGGED(ch, PRF_AUTOEXIT)), diff --git a/src/act.other.cpp b/src/act.other.cpp index 5b3daf34e..c2d69e7eb 100644 --- a/src/act.other.cpp +++ b/src/act.other.cpp @@ -1269,7 +1269,7 @@ const char *gen_tog_type[] = "маппер", "mapper", "тестер", "tester", "контроль IP", "IP control", - "автогрупвыход", "followgroupexit", + "групвыход", "followgroupexit", "\n" }; @@ -1606,8 +1606,8 @@ void do_gen_tog(CHAR_DATA *ch, char *argument, int/* cmd*/, int subcmd) "Режим вывода тестовой информации включен.\r\n"}, {"Режим контроля смены IP-адреса персонажа выключен.\r\n", "Режим контроля смены IP-адреса персонажа включен.\r\n"}, - {"При изменении порядка следования (след я) вы не покинете группу. Используйте команду груп покинуть.\r\n", - "При изменении порядка следования (след я) вы автоматически покинете группу.\r\n"} + {"При изменении порядка следования (след я) вы автоматически покинете группу.\r\n", + "При изменении порядка следования (след я) вы не покинете группу. Используйте команду груп покинуть.\r\n"} }; if (IS_NPC(ch)) diff --git a/src/grp/grp.cmdprocessor.cpp b/src/grp/grp.cmdprocessor.cpp index 3ba2d62c7..7bc698fbc 100644 --- a/src/grp/grp.cmdprocessor.cpp +++ b/src/grp/grp.cmdprocessor.cpp @@ -12,6 +12,7 @@ #include "grp.main.h" + extern GroupRoster& groupRoster; void do_grequest(CHAR_DATA *ch, char *argument, int, int){ @@ -63,8 +64,14 @@ int max_group_size(CHAR_DATA *ch) return MAX_GROUPED_FOLLOWERS + (int) VPOSI((ch->get_skill(SKILL_LEADERSHIP) - 80) / 5, 0, 4) + bonus_commander; } -void do_group2(CHAR_DATA *ch, char *argument, int, int){ - groupRoster.processGroupCommands(ch, argument); +void do_group2(CHAR_DATA *ch, char *argument, int, int subcmd){ + if (subcmd < 0 || subcmd > 12) + return; + if (subcmd == 0) { + groupRoster.processGroupCommands(ch, argument); + return; + } + groupRoster.processGroupScmds(ch, argument, (GRP_SUBCMD)subcmd); } void do_ungroup(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) { diff --git a/src/grp/grp.group.cpp b/src/grp/grp.group.cpp index 5fae56555..9fcf91043 100644 --- a/src/grp/grp.group.cpp +++ b/src/grp/grp.group.cpp @@ -755,9 +755,10 @@ void Group::gainExp(CHAR_DATA * victim) { // погнали раздавать опыт for (auto m: *this) { ch = m.second->member; - if (ch != nullptr && SAME_ROOM(ch, victim) && m.second->type == GM_CHAR) { - expMultiplicator = calcExpMultiplicator(ch); // цифра процента + if (ch == nullptr || ch->purged() || m.second->type == GM_CHARMEE || !SAME_ROOM(ch, victim) ) { + continue; } + expMultiplicator = calcExpMultiplicator(ch); // цифра процента // если прокнула лидерка, то добавляем еще 20 if (leader_inroom && calc_leadership(_leader) and inroom_members > 1) expMultiplicator += 20; diff --git a/src/grp/grp.main.h b/src/grp/grp.main.h index 791bffda8..b2507407a 100644 --- a/src/grp/grp.main.h +++ b/src/grp/grp.main.h @@ -28,6 +28,21 @@ enum GRP_COMM : short { GC_ROOM = 8 // эхо в комнату }; +enum GRP_SUBCMD : short { + GCMD_HELP = 1, + GCMD_MAKE = 2, + GCMD_LIST = 3, + GCMD_INVITE = 4, + GCMD_APPROVE = 5, + GCMD_REJECT = 6, + GCMD_EXPELL = 7, + GCMD_LEADER = 8, + GCMD_LEAVE = 9, + GCMD_DISBAND = 10, + GCMD_IMMINFO = 11, + GCMD_ALL = 12 +}; + enum RQ_R {RQ_R_OK, RQ_R_NO_GROUP, RQ_R_OVERFLOW, RQ_REFRESH}; enum INV_R {INV_R_OK, INV_R_NO_PERSON, INV_R_BUSY, INV_R_REFRESH}; @@ -62,14 +77,9 @@ struct char_info { sclock_t expiryTime; // время, когда запись автоматом удаляется после проверок. }; -struct PenaltyCalcData { - int penalty; -}; - using grp_mt = std::map>; using grp_ptr = std::shared_ptr; using rq_ptr = std::shared_ptr; -using cd_v = std::vector; using npc_r = std::unordered_set *; const duration DEF_EXPIRY_TIME = 600s; @@ -78,7 +88,6 @@ inline bool IN_GROUP(CHAR_DATA* ch) {return ch != nullptr && ch->personGroup != inline bool IN_SAME_GROUP(CHAR_DATA* p1, CHAR_DATA* p2) {return IN_GROUP(p1) && IN_GROUP(p2) && p1->personGroup == p2->personGroup;} // класс, хранящий обвязку штрафов по экспе для группы -// наркоман писал, не иначе class GroupPenalties { public: @@ -191,6 +200,7 @@ class GroupRoster { void charDataPurged(CHAR_DATA* ch); // очистка заявок при пурже персонажа void restorePlayerGroup(CHAR_DATA* ch); // возвращает игрока в группу после смерти void processGroupCommands(CHAR_DATA *ch, char *argument); + void processGroupScmds(CHAR_DATA *ch, char *argument, GRP_SUBCMD subcmd); void printList(CHAR_DATA *ch); private: u_long _currentGroupIndex = 0; diff --git a/src/grp/grp.roster.cpp b/src/grp/grp.roster.cpp index c1f900c76..56fd3d1bb 100644 --- a/src/grp/grp.roster.cpp +++ b/src/grp/grp.roster.cpp @@ -22,6 +22,33 @@ void GroupRoster::restorePlayerGroup(CHAR_DATA *ch) { grp->actToGroup(nullptr, ch, GC_LEADER | GC_REST, "$N заново присоединил$U к вашей группе."); } +void GroupRoster::processGroupScmds(CHAR_DATA *ch, char *argument, GRP_SUBCMD subcmd) { + switch (subcmd) { + case GRP_SUBCMD::GCMD_DISBAND: { + auto grp = ch->personGroup; + if (grp == nullptr) { + send_to_char(ch, "Дабы выгнать кого, сперва надобно в ватаге состояти.\r\n"); + return; + } + if (ch != grp->getLeader()) { + send_to_char(ch, "Негоже простому ратнику ватагой командовати.\r\n"); + return; + } + if (!*argument) + groupRoster.removeGroup(grp->getUid()); + else + grp->expellMember(argument); + return; + } + default:{ + sprintf(buf, "GroupRoster::processGroupScmds: вызов не поддерживаемой команды.\r\n"); + mudlog(buf, BRF, LVL_IMMORT, SYSLOG, TRUE); + send_to_char(ch, "Не поддерживается!\r\n"); + return; + } + } +} + void GroupRoster::processGroupCommands(CHAR_DATA *ch, char *argument) { const std::string strHELP = "справка помощь"; @@ -396,3 +423,5 @@ void GroupRoster::charDataPurged(CHAR_DATA *ch) { } } + + diff --git a/src/interpreter.cpp b/src/interpreter.cpp index 8fff6b871..aca139895 100644 --- a/src/interpreter.cpp +++ b/src/interpreter.cpp @@ -720,11 +720,11 @@ cpp_extern const struct command_info cmd_info[] = {"прыжок", POS_SLEEPING, do_goto, LVL_GOD, 0, 0}, {"разбудить", POS_RESTING, do_wake, 0, SCMD_WAKEUP, -1}, - {"разгруппировать", POS_DEAD, do_ungroup, 0, 0, 500}, + {"разгруппировать", POS_DEAD, do_group2, 0, GRP_SUBCMD::GCMD_DISBAND, 500}, {"разделить", POS_RESTING, do_split, 1, 0, 500}, {"разделы", POS_RESTING, do_help, 1, 0, 500}, {"разжечь", POS_STANDING, do_fire, 0, 0, -1}, - {"распустить", POS_DEAD, do_ungroup, 0, 0, 500}, + {"распустить", POS_DEAD, do_group2, 0, GRP_SUBCMD::GCMD_DISBAND, 500}, {"рассмотреть", POS_STANDING, do_not_here, 0, 0, -1}, {"рассчитать", POS_RESTING, do_freehelpee, 0, 0, -1}, {"режим", POS_DEAD, do_mode, 0, 0, 0}, @@ -1020,7 +1020,7 @@ cpp_extern const struct command_info cmd_info[] = {"unaffect", POS_DEAD, do_wizutil, LVL_GRGOD, SCMD_UNAFFECT, 0}, {"unban", POS_DEAD, do_unban, LVL_GRGOD, 0, 0}, {"unfreeze", POS_DEAD, do_unfreeze, LVL_IMPL, 0, 0}, - {"ungroup", POS_DEAD, do_ungroup, 0, 0, -1}, + {"ungroup", POS_DEAD, do_group2, 0, GRP_SUBCMD::GCMD_DISBAND, -1}, {"unlock", POS_SITTING, do_gen_door, 0, SCMD_UNLOCK, 500}, {"uptime", POS_DEAD, do_date, LVL_IMMORT, SCMD_UPTIME, 0}, {"use", POS_SITTING, do_use, 1, SCMD_USE, 500}, From 0f4bcc5cda447752fd6386efb49ed29c74911336 Mon Sep 17 00:00:00 2001 From: Demidov Date: Mon, 25 Jan 2021 20:11:10 +0300 Subject: [PATCH 29/40] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=B8=D0=BB=20=D1=80=D0=B0=D1=81=D1=87=D0=B5=D1=82=20=D1=80?= =?UTF-8?q?=D0=B0=D0=B7=D0=BC=D0=B5=D1=80=D0=B0=20=D0=B3=D1=80=D1=83=D0=BF?= =?UTF-8?q?=D0=BF=D1=8B.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/grp/grp.group.cpp | 18 ++++++++++++++---- src/grp/grp.main.h | 3 ++- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/grp/grp.group.cpp b/src/grp/grp.group.cpp index 9fcf91043..803d9b0fd 100644 --- a/src/grp/grp.group.cpp +++ b/src/grp/grp.group.cpp @@ -99,7 +99,7 @@ void Group::_setLeader(CHAR_DATA *leader) { _leaderUID = _calcUID(leader); _leader = leader; _leaderName.assign(leader->get_pc_name()); - _memberCap = IS_NPC(leader)? 255 : max_group_size(leader); + _memberCap = IS_NPC(leader)? 255 : max_group_size(leader) + 1; // функция возвращает кол-во ПОСЛЕДОВАТЕЛЕЙ } bool Group::_isActive() { @@ -111,6 +111,8 @@ u_long Group::getUid() const { } int Group::_getMemberCap() const { + if (_leader != nullptr) + return max_group_size(_leader) + 1; return _memberCap; } @@ -119,7 +121,7 @@ const std::string &Group::getLeaderName() const { } bool Group::_isFull() { - if ((u_short)this->size() >= (u_short)_memberCap) + if (this->_pcCount >= _getMemberCap()) return true; return false; } @@ -188,6 +190,7 @@ void Group::addMember(CHAR_DATA *member, bool silent) { this->addMember(ff, true); // рекурсия! } } + _updateMemberCap(); } bool Group::_removeMember(int memberUID) { @@ -256,6 +259,7 @@ bool Group::_removeMember(int memberUID) { return false; } _promote(nxtLdr); + _updateMemberCap(); return true; } @@ -625,7 +629,7 @@ bool Group::addFollowers(CHAR_DATA *leader) { for (auto f = leader->followers; f; f = f->next) { if (IS_NPC(f->follower)) continue; - if ((u_short)this->get_size() < (u_short)_memberCap) { + if ((u_short)this->get_size() < (u_short)_getMemberCap()) { this->addMember(f->follower); result = true; } @@ -682,7 +686,7 @@ void Group::_promote(CHAR_DATA *member) { return; _setLeader(member); actToGroup(nullptr, nullptr, GC_LEADER | GC_REST, "Изменился лидер группы на %s.", _leaderName.c_str()); - auto diff = (u_short)_memberCap - (u_short)this->size(); + auto diff = (u_short)_getMemberCap() - (u_short)this->size(); if (diff < 0) diff = abs(diff); else diff = 0; if (diff > 0){ u_short i = 0; @@ -833,3 +837,9 @@ u_short Group::calcExpMultiplicator(const CHAR_DATA* player) return DEFAULT_100; } +void Group::_updateMemberCap() { + if (_leader == nullptr || _leader->purged()) + return; + _memberCap = _getMemberCap(); +} + diff --git a/src/grp/grp.main.h b/src/grp/grp.main.h index b2507407a..879c25f2d 100644 --- a/src/grp/grp.main.h +++ b/src/grp/grp.main.h @@ -107,7 +107,7 @@ class Group : public grp_mt { constexpr static int DEFAULT_100 = 100; // ид группы в ростере u_long _uid = 0; - //макс.количество игроков + //макс.количество игроков. Храним, ибо лидера могло спуржить, но он к нам вернётся.. int _memberCap = 0; // ид лидера int _leaderUID; @@ -152,6 +152,7 @@ class Group : public grp_mt { static void _printNPCLine(CHAR_DATA* ch, CHAR_DATA* npc, int header); static void _printPCLine(CHAR_DATA* ch, CHAR_DATA* pc, int header); bool _sameGroup(CHAR_DATA * ch, CHAR_DATA * vict); + void _updateMemberCap(); public: From ba193a46c71a8004529d52a4edc62d891813da63 Mon Sep 17 00:00:00 2001 From: Demidov Date: Tue, 26 Jan 2021 02:28:14 +0300 Subject: [PATCH 30/40] =?UTF-8?q?=D0=92=D0=BD=D0=B5=D0=B4=D1=80=D0=B8?= =?UTF-8?q?=D0=BB=20=D0=B0=D0=B2=D1=82=D0=BE-=D0=BE=D1=87=D0=B8=D1=81?= =?UTF-8?q?=D1=82=D0=BA=D1=83=20=D0=BF=D1=80=D0=BE=D1=81=D1=80=D0=BE=D1=87?= =?UTF-8?q?=D0=B5=D0=BD=D0=BD=D1=8B=D1=85=20=D0=B7=D0=B0=D1=8F=D0=B2=D0=BE?= =?UTF-8?q?=D0=BA=20=D0=B8=20=D1=83=D0=BC=D0=B5=D1=80=D1=88=D0=B8=D1=85=20?= =?UTF-8?q?=D0=BF=D0=B5=D1=80=D1=81=D0=BE=D0=BD=D0=B0=D0=B6=D0=B5=D0=B9.?= =?UTF-8?q?=20=D0=A7=D0=B5=D1=80=D0=B5=D0=B7=20=D0=BC=D0=B8=D0=BD=D1=83?= =?UTF-8?q?=D1=82=D1=83=20=D0=B8=D0=BB=D0=B8=206=20=D1=81=D0=B5=D0=BA?= =?UTF-8?q?=D1=83=D0=BD=D0=B4=20-=20=D1=82=D0=BE=D0=B2=D0=BE=20)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/act.item.cpp | 6 +++--- src/grp/follow.cpp | 16 ++++++++-------- src/grp/grp.cmdprocessor.cpp | 18 +++++++----------- src/grp/grp.group.cpp | 20 ++++++++++++++++---- src/grp/grp.main.h | 26 +++++++++++++++----------- src/grp/grp.roster.cpp | 25 +++++++++++++++++++++---- src/heartbeat.cpp | 2 ++ src/interpreter.cpp | 25 ++++++++++++------------- 8 files changed, 84 insertions(+), 54 deletions(-) diff --git a/src/act.item.cpp b/src/act.item.cpp index af95d4a5a..bc9546d7b 100644 --- a/src/act.item.cpp +++ b/src/act.item.cpp @@ -736,7 +736,7 @@ void split_or_clan_tax(CHAR_DATA *ch, long amount) if (ch->personGroup != nullptr && ch->personGroup->size() > 1 && PRF_FLAGGED(ch, PRF_AUTOSPLIT)) { char buf_[MAX_INPUT_LENGTH]; snprintf(buf_, sizeof(buf_), "%ld", amount); - do_split(ch, buf_, 0, 0); + grp::do_split(ch, buf_, 0, 0); } else { @@ -773,7 +773,7 @@ void get_check_money(CHAR_DATA *ch, OBJ_DATA *obj, OBJ_DATA *cont) { char local_buf[256]; sprintf(local_buf, "%d", value); - do_split(ch, local_buf, 0, 0,curr_type); + grp::do_split(ch, local_buf, 0, 0,curr_type); } extract_obj(obj); return; @@ -800,7 +800,7 @@ void get_check_money(CHAR_DATA *ch, OBJ_DATA *obj, OBJ_DATA *cont) mudlog(buf, NRM, LVL_GRGOD, MONEY_LOG, TRUE); char local_buf[256]; sprintf(local_buf, "%d", value); - do_split(ch, local_buf, 0, 0); + grp::do_split(ch, local_buf, 0, 0); } else if ((cont && IS_MOB_CORPSE(cont)) || GET_OBJ_VNUM(obj) != -1) { diff --git a/src/grp/follow.cpp b/src/grp/follow.cpp index c50fe323c..68e2d915b 100644 --- a/src/grp/follow.cpp +++ b/src/grp/follow.cpp @@ -46,14 +46,14 @@ bool stop_follower(CHAR_DATA * ch, int mode) } else { // locate follower who is not head of list for (k = ch->get_master()->followers; k->next && k->next->follower != ch; k = k->next); - if (!k->next) { - log("[Stop follower] SYSERR: Undefined %s in %s followers list.", GET_NAME(ch), GET_NAME(ch->get_master())); - } - else { - j = k->next; - k->next = j->next; - free(j); - } + if (!k->next) { + log("[Stop follower] SYSERR: Undefined %s in %s followers list.", GET_NAME(ch), GET_NAME(ch->get_master())); + } + else { + j = k->next; + k->next = j->next; + free(j); + } } ch->set_master(nullptr); diff --git a/src/grp/grp.cmdprocessor.cpp b/src/grp/grp.cmdprocessor.cpp index 7bc698fbc..6761aea79 100644 --- a/src/grp/grp.cmdprocessor.cpp +++ b/src/grp/grp.cmdprocessor.cpp @@ -15,7 +15,7 @@ extern GroupRoster& groupRoster; -void do_grequest(CHAR_DATA *ch, char *argument, int, int){ +void grp::do_grequest(CHAR_DATA *ch, char *argument, int, int){ char subcmd[MAX_INPUT_LENGTH], target[MAX_INPUT_LENGTH]; // гзаявка список // гзаявка создать Верий - отправляет заявку в группу @@ -56,7 +56,7 @@ void do_grequest(CHAR_DATA *ch, char *argument, int, int){ } -int max_group_size(CHAR_DATA *ch) +int grp::max_group_size(CHAR_DATA *ch) { int bonus_commander = 0; if (AFF_FLAGGED(ch, EAffectFlag::AFF_COMMANDER)) bonus_commander = VPOSI((ch->get_skill(SKILL_LEADERSHIP) - 120) / 10, 0 , 8); @@ -64,7 +64,7 @@ int max_group_size(CHAR_DATA *ch) return MAX_GROUPED_FOLLOWERS + (int) VPOSI((ch->get_skill(SKILL_LEADERSHIP) - 80) / 5, 0, 4) + bonus_commander; } -void do_group2(CHAR_DATA *ch, char *argument, int, int subcmd){ +void grp::do_group2(CHAR_DATA *ch, char *argument, int, int subcmd){ if (subcmd < 0 || subcmd > 12) return; if (subcmd == 0) { @@ -74,10 +74,6 @@ void do_group2(CHAR_DATA *ch, char *argument, int, int subcmd){ groupRoster.processGroupScmds(ch, argument, (GRP_SUBCMD)subcmd); } -void do_ungroup(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) { - groupRoster.processGroupCommands(ch, argument); -} - void do_gsay(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) { @@ -138,7 +134,7 @@ void do_gsay(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) } -void do_report(CHAR_DATA *ch, char* /*argument*/, int/* cmd*/, int/* subcmd*/) +void grp::do_report(CHAR_DATA *ch, char* /*argument*/, int/* cmd*/, int/* subcmd*/) { CHAR_DATA *k; struct follow_type *f; @@ -197,7 +193,7 @@ void do_report(CHAR_DATA *ch, char* /*argument*/, int/* cmd*/, int/* subcmd*/) send_to_char("Вы доложили о состоянии всем членам вашей группы.\r\n", ch); } -void do_split(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/,int currency) +void grp::do_split(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/,int currency) { int amount, num = 0, share, rest; CHAR_DATA* m; @@ -302,7 +298,7 @@ void do_split(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/,int cur } } -void do_split(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) { - do_split(ch,argument,0,0,0); +void grp::do_split(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) { + grp::do_split(ch,argument,0,0,0); } diff --git a/src/grp/grp.group.cpp b/src/grp/grp.group.cpp index 803d9b0fd..172d06e4f 100644 --- a/src/grp/grp.group.cpp +++ b/src/grp/grp.group.cpp @@ -99,7 +99,7 @@ void Group::_setLeader(CHAR_DATA *leader) { _leaderUID = _calcUID(leader); _leader = leader; _leaderName.assign(leader->get_pc_name()); - _memberCap = IS_NPC(leader)? 255 : max_group_size(leader) + 1; // функция возвращает кол-во ПОСЛЕДОВАТЕЛЕЙ + _memberCap = IS_NPC(leader)? 255 : grp::max_group_size(leader) + 1; // функция возвращает кол-во ПОСЛЕДОВАТЕЛЕЙ } bool Group::_isActive() { @@ -112,7 +112,7 @@ u_long Group::getUid() const { int Group::_getMemberCap() const { if (_leader != nullptr) - return max_group_size(_leader) + 1; + return grp::max_group_size(_leader) + 1; return _memberCap; } @@ -247,9 +247,9 @@ bool Group::_removeMember(int memberUID) { nxtLdr = nullptr; for (const auto& mit : *this){ auto m = mit.second->member; - if ( m != nullptr && !m->purged() && max_group_size(m) > nxtLdrGrpSize ) { + if ( m != nullptr && !m->purged() && grp::max_group_size(m) > nxtLdrGrpSize ) { nxtLdr = m; // нашелся!! - nxtLdrGrpSize = max_group_size(m); + nxtLdrGrpSize = grp::max_group_size(m); } } } @@ -580,6 +580,7 @@ void Group::charDataPurged(CHAR_DATA *ch) { for (auto &it : *this) { if (ch == it.second->member) { it.second->member = nullptr; + it.second->expiryTime = steady_clock::now() + DEF_EXPIRY_TIME; return; } } @@ -843,3 +844,14 @@ void Group::_updateMemberCap() { _memberCap = _getMemberCap(); } +void Group::processGarbageCollection() { + for (auto it = this->begin(); it != this->end();) { + auto m = (*it).second; + if (m->expiryTime <= steady_clock::now() && m->member == nullptr) { + _removeMember(m->memberUID); + } else { + ++it; + } + } +} + diff --git a/src/grp/grp.main.h b/src/grp/grp.main.h index 879c25f2d..712d3ab99 100644 --- a/src/grp/grp.main.h +++ b/src/grp/grp.main.h @@ -46,15 +46,17 @@ enum GRP_SUBCMD : short { enum RQ_R {RQ_R_OK, RQ_R_NO_GROUP, RQ_R_OVERFLOW, RQ_REFRESH}; enum INV_R {INV_R_OK, INV_R_NO_PERSON, INV_R_BUSY, INV_R_REFRESH}; -void do_split(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/,int currency); -void do_split(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/); - -void do_grequest(CHAR_DATA *ch, char *argument, int, int); -void do_group2(CHAR_DATA *ch, char *argument, int, int); -void do_ungroup(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/); -void do_report(CHAR_DATA *ch, char* /*argument*/, int/* cmd*/, int/* subcmd*/); -int max_group_size(CHAR_DATA *ch); -bool isGroupedFollower(CHAR_DATA* master, CHAR_DATA* vict); +namespace grp { + void gc(); // garbage collection + void do_split(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/,int currency); + void do_split(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/); + void do_grequest(CHAR_DATA *ch, char *argument, int, int); + void do_group2(CHAR_DATA *ch, char *argument, int, int); + + void do_report(CHAR_DATA *ch, char* /*argument*/, int/* cmd*/, int/* subcmd*/); + int max_group_size(CHAR_DATA *ch); +} + bool circle_follow(CHAR_DATA * ch, CHAR_DATA * victim); bool die_follower(CHAR_DATA * ch); bool stop_follower(CHAR_DATA * ch, int mode); @@ -82,7 +84,7 @@ using grp_ptr = std::shared_ptr; using rq_ptr = std::shared_ptr; using npc_r = std::unordered_set *; -const duration DEF_EXPIRY_TIME = 600s; +const duration DEF_EXPIRY_TIME = 10s; inline bool IN_GROUP(CHAR_DATA* ch) {return ch != nullptr && ch->personGroup != nullptr;} inline bool IN_SAME_GROUP(CHAR_DATA* p1, CHAR_DATA* p2) {return IN_GROUP(p1) && IN_GROUP(p2) && p1->personGroup == p2->personGroup;} @@ -172,6 +174,7 @@ class Group : public grp_mt { // void sendToGroup(GRP_COMM mode, const char *msg, ...); void actToGroup(CHAR_DATA* ch, CHAR_DATA* vict, int mode, const char *msg, ...); u_short calcExpMultiplicator(const CHAR_DATA* player); + void processGarbageCollection(); public: // всякий унаследованный стафф CHAR_DATA* get_random_pc_group(); @@ -184,7 +187,7 @@ class Group : public grp_mt { class Request { public: - sclock_t _time; + sclock_t _expiryTime; CHAR_DATA *_applicant; Group* _group; std::string _applicantName; @@ -203,6 +206,7 @@ class GroupRoster { void processGroupCommands(CHAR_DATA *ch, char *argument); void processGroupScmds(CHAR_DATA *ch, char *argument, GRP_SUBCMD subcmd); void printList(CHAR_DATA *ch); + void processGarbageCollection(); private: u_long _currentGroupIndex = 0; std::map _groupList; diff --git a/src/grp/grp.roster.cpp b/src/grp/grp.roster.cpp index 56fd3d1bb..7d1f678f0 100644 --- a/src/grp/grp.roster.cpp +++ b/src/grp/grp.roster.cpp @@ -260,7 +260,7 @@ std::tuple GroupRoster::tryMakeInvite(Group* grp, char *memb // ищем и продлеваем for (auto & it : this->_requestList){ if (vict == it->_applicant && grp == it->_group){ - it->_time = steady_clock::now() + DEF_EXPIRY_TIME; + it->_expiryTime = steady_clock::now() + DEF_EXPIRY_TIME; return std::make_tuple(INV_R::INV_R_REFRESH, vict); } } @@ -362,7 +362,7 @@ std::tuple GroupRoster::tryAddRequest(CHAR_DATA *author, char *ta // продлеваем for (auto & it : this->_requestList){ if (author == it->_applicant && grp.get() == it->_group){ - it->_time = std::chrono::steady_clock::now() + DEF_EXPIRY_TIME; + it->_expiryTime = std::chrono::steady_clock::now() + DEF_EXPIRY_TIME; return std::make_tuple(RQ_REFRESH, grp); } } @@ -404,7 +404,7 @@ void GroupRoster::acceptInvite(CHAR_DATA* who, char* author) { r->_group->addMember(r->_applicant); // и удалит заявку, если есть } -void GroupRoster::runTests(CHAR_DATA *leader) { +void GroupRoster::runTests(CHAR_DATA *) { } @@ -414,7 +414,7 @@ void GroupRoster::charDataPurged(CHAR_DATA *ch) { return; for (auto r = _requestList.begin(); r != _requestList.end();){ if (r->get()->_applicant == ch) { - _requestList.erase(r); + r = _requestList.erase(r); } else { ++r; } @@ -424,4 +424,21 @@ void GroupRoster::charDataPurged(CHAR_DATA *ch) { } } +void GroupRoster::processGarbageCollection() { + for (auto it: _groupList){ + it.second->processGarbageCollection(); + } + for (auto rq = _requestList.begin(); rq != _requestList.end();){ + if ((*rq)->_expiryTime <= steady_clock::now()) { + rq = _requestList.erase(rq); + } else { + ++rq; + } + } +} + +// expell timed out members +void grp::gc() { + groupRoster.processGarbageCollection(); +} diff --git a/src/heartbeat.cpp b/src/heartbeat.cpp index 2aa35e6b4..eb20c9d90 100644 --- a/src/heartbeat.cpp +++ b/src/heartbeat.cpp @@ -16,6 +16,7 @@ #include "title.hpp" #include "depot.hpp" #include "glory.hpp" +#include "grp/grp.main.h" #include "file_crc.hpp" #include "sets_drop.hpp" #include "mail.h" @@ -423,6 +424,7 @@ namespace Heartbeat::PulseStep("Bloody: updating", SECS_PER_MUD_HOUR * PASSES_PER_SEC, 10, std::make_shared(bloody::update)), Heartbeat::PulseStep("Room point updating", SECS_PER_MUD_HOUR * PASSES_PER_SEC, 6, std::make_shared(room_point_update)), Heartbeat::PulseStep("Temporary spells: times updating", SECS_PER_MUD_HOUR * PASSES_PER_SEC, 5, std::make_shared(Temporary_Spells::update_times)), + Heartbeat::PulseStep("Group garbage collecting", SECS_PER_MUD_HOUR * PASSES_PER_SEC, 0, std::make_shared(grp::gc)), Heartbeat::PulseStep("Exchange point updating", SECS_PER_MUD_HOUR * PASSES_PER_SEC, 2, std::make_shared(exchange_point_update)), Heartbeat::PulseStep("Players index flushing", SECS_PER_MUD_HOUR * PASSES_PER_SEC, 1, std::make_shared(flush_player_index)), Heartbeat::PulseStep("Point updating", SECS_PER_MUD_HOUR * PASSES_PER_SEC, PASSES_PER_SEC - 5, std::make_shared(point_update)), diff --git a/src/interpreter.cpp b/src/interpreter.cpp index aca139895..3b2300bf3 100644 --- a/src/interpreter.cpp +++ b/src/interpreter.cpp @@ -288,7 +288,6 @@ void do_reboot(CHAR_DATA *ch, char *argument, int cmd, int subcmd); void do_remove(CHAR_DATA *ch, char *argument, int cmd, int subcmd); void do_rent(CHAR_DATA *ch, char *argument, int cmd, int subcmd); void do_reply(CHAR_DATA *ch, char *argument, int cmd, int subcmd); -void do_report(CHAR_DATA *ch, char *argument, int cmd, int subcmd); void do_refill(CHAR_DATA *ch, char *argument, int cmd, int subcmd); void do_setall(CHAR_DATA *ch, char *argument, int cmd, int subcmd); void do_stophorse(CHAR_DATA *ch, char *argument, int cmd, int subcmd); @@ -518,20 +517,20 @@ cpp_extern const struct command_info cmd_info[] = {"гдругам", POS_SLEEPING, DoClanChannel, 0, SCMD_CHANNEL, 0}, {"где", POS_RESTING, do_where, LVL_IMMORT, 0, 0}, {"гдея", POS_RESTING, do_zone, 0, 0, 0}, - {"гзаявка", POS_DEAD, do_grequest, 0, 0, 0 }, + {"гзаявка", POS_DEAD, grp::do_grequest, 0, 0, 0 }, {"глоток", POS_RESTING, do_drink, 0, SCMD_SIP, 200}, {"города", POS_DEAD, do_cities, 0, 0, 0 }, - {"группа", POS_SLEEPING, do_group2, 1, 0, -1}, + {"группа", POS_SLEEPING, grp::do_group2, 1, 0, -1}, {"гсоюзникам", POS_SLEEPING, DoClanChannel, 0, SCMD_ACHANNEL, 0}, {"гэхо", POS_DEAD, do_gecho, LVL_GOD, 0, 0}, {"гбогам", POS_DEAD, do_wiznet, LVL_IMMORT, 0, 0}, {"дать", POS_RESTING, do_give, 0, 0, 500}, {"дата", POS_DEAD, do_date, 0, SCMD_DATE, 0}, - {"делить", POS_RESTING, do_split, 1, 0, 200}, + {"делить", POS_RESTING, grp::do_split, 1, 0, 200}, {"держать", POS_RESTING, do_grab, 0, 0, 300}, {"дметр", POS_DEAD, do_dmeter, 0, 0, 0}, - {"доложить", POS_RESTING, do_report, 0, 0, 500}, + {"доложить", POS_RESTING, grp::do_report, 0, 0, 500}, {"доски", POS_DEAD, Boards::DoBoardList, 0, 0, 0}, {"дружины", POS_DEAD, DoClanList, 0, 0, 0}, {"дрновости", POS_DEAD, Boards::DoBoard, 1, Boards::CLANNEWS_BOARD, -1}, @@ -720,11 +719,11 @@ cpp_extern const struct command_info cmd_info[] = {"прыжок", POS_SLEEPING, do_goto, LVL_GOD, 0, 0}, {"разбудить", POS_RESTING, do_wake, 0, SCMD_WAKEUP, -1}, - {"разгруппировать", POS_DEAD, do_group2, 0, GRP_SUBCMD::GCMD_DISBAND, 500}, - {"разделить", POS_RESTING, do_split, 1, 0, 500}, + {"разгруппировать", POS_DEAD, grp::do_group2, 0, GRP_SUBCMD::GCMD_DISBAND, 500}, + {"разделить", POS_RESTING, grp::do_split, 1, 0, 500}, {"разделы", POS_RESTING, do_help, 1, 0, 500}, {"разжечь", POS_STANDING, do_fire, 0, 0, -1}, - {"распустить", POS_DEAD, do_group2, 0, GRP_SUBCMD::GCMD_DISBAND, 500}, + {"распустить", POS_DEAD, grp::do_group2, 0, GRP_SUBCMD::GCMD_DISBAND, 500}, {"рассмотреть", POS_STANDING, do_not_here, 0, 0, -1}, {"рассчитать", POS_RESTING, do_freehelpee, 0, 0, -1}, {"режим", POS_DEAD, do_mode, 0, 0, 0}, @@ -875,10 +874,10 @@ cpp_extern const struct command_info cmd_info[] = {"gossip", POS_RESTING, do_gen_comm, 0, SCMD_GOSSIP, -1}, {"goto", POS_SLEEPING, do_goto, LVL_GOD, 0, 0}, {"grab", POS_RESTING, do_grab, 0, 0, 500}, - {"group", POS_RESTING, do_group2, 1, 0, 500}, + {"group", POS_RESTING, grp::do_group2, 1, 0, 500}, {"gsay", POS_SLEEPING, do_gsay, 0, 0, -1}, {"gtell", POS_SLEEPING, do_gsay, 0, 0, -1}, - {"grequest", POS_RESTING, do_grequest, 1, 0, 500}, + {"grequest", POS_RESTING, grp::do_grequest, 1, 0, 500}, {"handbook", POS_DEAD, do_gen_ps, LVL_IMMORT, SCMD_HANDBOOK, 0}, {"hcontrol", POS_DEAD, DoHcontrol, LVL_GRGOD, 0, 0}, {"help", POS_DEAD, do_help, 0, 0, 0}, @@ -958,7 +957,7 @@ cpp_extern const struct command_info cmd_info[] = {"remove", POS_RESTING, do_remove, 0, 0, 500}, {"rent", POS_STANDING, do_not_here, 1, 0, -1}, {"reply", POS_RESTING, do_reply, 0, 0, -1}, - {"report", POS_RESTING, do_report, 0, 0, -1}, + {"report", POS_RESTING, grp::do_report, 0, 0, -1}, {"reroll", POS_DEAD, do_wizutil, LVL_GRGOD, SCMD_REROLL, 0}, {"rescue", POS_FIGHTING, do_rescue, 1, 0, -1}, {"rest", POS_RESTING, do_rest, 0, 0, -1}, @@ -990,7 +989,7 @@ cpp_extern const struct command_info cmd_info[] = {"snoop", POS_DEAD, do_snoop, LVL_GRGOD, 0, 0}, {"socials", POS_DEAD, do_commands, 0, SCMD_SOCIALS, 0}, {"spells", POS_RESTING, do_spells, 0, 0, 0}, - {"split", POS_RESTING, do_split, 1, 0, 0}, + {"split", POS_RESTING, grp::do_split, 1, 0, 0}, {"stand", POS_RESTING, do_stand, 0, 0, -1}, {"stat", POS_DEAD, do_stat, LVL_GOD, 0, 0}, {"steal", POS_STANDING, do_steal, 1, 0, 300}, @@ -1020,7 +1019,7 @@ cpp_extern const struct command_info cmd_info[] = {"unaffect", POS_DEAD, do_wizutil, LVL_GRGOD, SCMD_UNAFFECT, 0}, {"unban", POS_DEAD, do_unban, LVL_GRGOD, 0, 0}, {"unfreeze", POS_DEAD, do_unfreeze, LVL_IMPL, 0, 0}, - {"ungroup", POS_DEAD, do_group2, 0, GRP_SUBCMD::GCMD_DISBAND, -1}, + {"ungroup", POS_DEAD, grp::do_group2, 0, GRP_SUBCMD::GCMD_DISBAND, -1}, {"unlock", POS_SITTING, do_gen_door, 0, SCMD_UNLOCK, 500}, {"uptime", POS_DEAD, do_date, LVL_IMMORT, SCMD_UPTIME, 0}, {"use", POS_SITTING, do_use, 1, SCMD_USE, 500}, From 87ad921ba17658a2a20b51b922e893a857437e09 Mon Sep 17 00:00:00 2001 From: Demidov Date: Tue, 26 Jan 2021 12:31:55 +0300 Subject: [PATCH 31/40] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=B8=D0=BB=20=D0=BA=D1=80=D0=B5=D1=88=20=D0=B2=20=D1=80=D0=B5?= =?UTF-8?q?=D0=B6=D0=B8=D0=BC=D0=B5=20=D0=B2=D1=8B=D0=B2=D0=BE=D0=B4=D0=B0?= =?UTF-8?q?=20=D0=BF=D0=BE=D0=BB=D0=BD=D0=BE=D0=B9=20=D0=B3=D1=80=D1=83?= =?UTF-8?q?=D0=BF=D0=BF=D1=8B=20=D0=B8=20=D0=B5=D1=91=20=D0=BE=D1=81=D1=82?= =?UTF-8?q?=D1=83=D1=82=D1=81=D1=82=D0=B2=D0=B8=D0=B8.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/grp/grp.group.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/grp/grp.group.cpp b/src/grp/grp.group.cpp index 172d06e4f..03019c71b 100644 --- a/src/grp/grp.group.cpp +++ b/src/grp/grp.group.cpp @@ -557,7 +557,7 @@ void Group::printGroup(CHAR_DATA *ch) { } // печатаем чармисов членов группы - if (PRF_FLAGGED(ch, PRF_SHOWGROUP)) { + if (PRF_FLAGGED(ch, PRF_SHOWGROUP) && ch->personGroup != nullptr) { // for (auto &it : *this ) { auto npc = it.second->member; // на всякий пожарный From 4061b1edb2b64d4107c066908626e9fbcf0a64e7 Mon Sep 17 00:00:00 2001 From: Demidov Date: Tue, 26 Jan 2021 12:50:03 +0300 Subject: [PATCH 32/40] =?UTF-8?q?=D0=A1=D1=82=D0=B0=D1=80=D0=BE=D0=B5=20?= =?UTF-8?q?=D0=BF=D0=BE=D0=B2=D0=B5=D0=B4=D0=B5=D0=BD=D0=B8=D0=B5=20"?= =?UTF-8?q?=D0=B2=D1=8B=D1=85=D0=BE=D0=B4=20=D0=B8=D0=B7=20=D0=B3=D1=80?= =?UTF-8?q?=D1=83=D0=BF=D0=BF=D1=8B,=20=D0=B5=D1=81=D0=BB=D0=B8=20=D0=BD?= =?UTF-8?q?=D0=B5=20=D1=81=D0=BB=D0=B5=D0=B4=D1=83=D0=B5=D1=88=D1=8C=20?= =?UTF-8?q?=D0=B7=D0=B0=20=D0=BB=D0=B8=D0=B4=D0=B5=D1=80=D0=BE=D0=BC"=20?= =?UTF-8?q?=D1=82=D0=B5=D0=BF=D0=B5=D1=80=D1=8C=20=D0=BF=D0=BE-=D1=83?= =?UTF-8?q?=D0=BC=D0=BE=D0=BB=D1=87=D0=B0=D0=BD=D0=B8=D1=8E.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chars/char.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/chars/char.cpp b/src/chars/char.cpp index f68d391cd..d93ea25a0 100644 --- a/src/chars/char.cpp +++ b/src/chars/char.cpp @@ -2036,7 +2036,7 @@ void CHAR_DATA::removeGroupFlags(bool reboot) { if (personGroup == nullptr) return; // чармис всегда, персонаж по настройке, или при ребуте - if (IS_CHARMICE(this) || PRF_FLAGGED(this, PRF_FOLLOW_GRP_EXIT) || reboot) { + if (IS_CHARMICE(this) || !PRF_FLAGGED(this, PRF_FOLLOW_GRP_EXIT) || reboot) { PRF_FLAGS(this).unset(PRF_SKIRMISHER); personGroup->_removeMember(this); } From 9d54767f82cb5bfab549711dc708865a9bc8a29e Mon Sep 17 00:00:00 2001 From: Demidov Date: Thu, 28 Jan 2021 13:58:55 +0300 Subject: [PATCH 33/40] =?UTF-8?q?=D0=9A=D1=80=D0=B5=D1=88=20=D0=BF=D1=80?= =?UTF-8?q?=D0=B8=20=D0=BF=D0=BE=D0=BF=D1=8B=D1=82=D0=BA=D0=B5=20=D0=BF?= =?UTF-8?q?=D1=80=D0=B8=D0=BD=D1=8F=D1=82=D1=8C=20=D0=BD=D0=B5=D1=81=D1=83?= =?UTF-8?q?=D1=89=D0=B5=D1=81=D1=82=D0=B2=D1=83=D1=8E=D1=89=D1=83=D1=8E=20?= =?UTF-8?q?=D0=B7=D0=B0=D1=8F=D0=B2=D0=BA=D1=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 3 ++- src/grp/grp.roster.cpp | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ac032fa2b..7168b7a36 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -754,7 +754,8 @@ endif () if (CMAKE_HOST_UNIX) if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang") set(CMAKE_CXX_FLAGS_RELEASE "-ggdb3 -O0 -Wall -Wextra") - set(CMAKE_CXX_FLAGS_DEBUG "-ggdb3 -O0 -Wall -Wextra -D_GLIBCXX_DEBUG -D_GLIBXX_DEBUG_PEDANTIC ${ASAN_FLAGS} ${DEBUG_CRYPT} ${TESTBUILD_DEFINITIONS} -DLOG_AUTOFLUSH") + set(CMAKE_CXX_FLAGS_DEBUG "-ggdb3 -O0 -Wall -Wextra -D_GLIBCXX_DEBUG -D_GLIBXX_DEBUG_PEDANTIC ${ASAN_FLAGS} ${DEBUG_CRYPT} -DLOG_AUTOFLUSH") +# set(CMAKE_CXX_FLAGS_DEBUG "-ggdb3 -O0 -Wall -Wextra -D_GLIBCXX_DEBUG -D_GLIBXX_DEBUG_PEDANTIC ${ASAN_FLAGS} ${DEBUG_CRYPT} ${TESTBUILD_DEFINITIONS} -DLOG_AUTOFLUSH") set(CMAKE_CXX_FLAGS_TEST " -Og -Wall -Wextra ${TESTBUILD_DEFINITIONS} -DLOG_AUTOFLUSH") set(CMAKE_CXX_FLAGS_FASTTEST " -Ofast -Wall -Wextra ${TESTBUILD_DEFINITIONS}") diff --git a/src/grp/grp.roster.cpp b/src/grp/grp.roster.cpp index 3f3413acb..d9cdedb12 100644 --- a/src/grp/grp.roster.cpp +++ b/src/grp/grp.roster.cpp @@ -370,6 +370,10 @@ void GroupRoster::acceptInvite(CHAR_DATA* who, char* author) { } auto r = findRequest(who->get_pc_name().c_str(), author, RQ_TYPE::RQ_GROUP); + if (r == nullptr) { + send_to_char(who, "Соберись! Чье приглашение принимаем?!\r\n"); + return; + } send_to_char(r->_applicant, "Вы приняли приглашение.\r\n"); r->_group->addMember(r->_applicant); // и удалит заявку, если есть } From 41ee19cf55a5bc1cc2ce49951570c1bc584a45be Mon Sep 17 00:00:00 2001 From: Demidov Date: Thu, 28 Jan 2021 17:03:02 +0300 Subject: [PATCH 34/40] =?UTF-8?q?=D0=9A=D1=80=D0=B5=D1=88=20=D0=BF=D1=80?= =?UTF-8?q?=D0=B8=20=D0=B0=D0=B2=D1=82=D0=BE=D0=BC=D0=B0=D1=82=D0=B8=D1=87?= =?UTF-8?q?=D0=B5=D1=81=D0=BA=D0=BE=D0=B9=20=D0=BE=D1=87=D0=B8=D1=81=D1=82?= =?UTF-8?q?=D0=BA=D0=B5=20=D0=B3=D1=80=D1=83=D0=BF=D0=BF=D1=8B=20=D0=BE?= =?UTF-8?q?=D1=82=20DC=20=D0=BF=D0=B5=D1=80=D1=81=D0=BE=D0=BD=D0=B0=D0=B6?= =?UTF-8?q?=D0=B5=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/grp/grp.group.cpp | 33 +++++++++++++++++++-------------- src/grp/grp.roster.cpp | 2 +- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/src/grp/grp.group.cpp b/src/grp/grp.group.cpp index 03019c71b..62d84cef3 100644 --- a/src/grp/grp.group.cpp +++ b/src/grp/grp.group.cpp @@ -75,7 +75,7 @@ Group::Group(CHAR_DATA *leader, u_long uid){ _setLeader(leader); } -CHAR_DATA *Group::getLeader() const{ +CHAR_DATA* Group::getLeader() const{ return _leader; } @@ -216,9 +216,9 @@ bool Group::_removeMember(int memberUID) { _pcCount--; this->erase(memberUID); // finally remove it // пересчитываем макс.уровень - if ( GET_LEVEL(member) == _maxPlayerLevel) { + { _maxPlayerLevel = 1; - for (auto m: *this) { + for (const auto& m: *this) { if (m.second->member == nullptr || m.second->type == GM_CHARMEE) continue; if (GET_LEVEL(m.second->member) > _maxPlayerLevel) @@ -227,7 +227,7 @@ bool Group::_removeMember(int memberUID) { } // а также удаляем чармисов персонажа из групповых - if (member->followers != nullptr) { + if (member != nullptr && member->followers != nullptr) { for (auto f = member->followers; f; f = f->next) { auto ff = f->follower; if (IS_CHARMICE(ff)) { @@ -240,10 +240,10 @@ bool Group::_removeMember(int memberUID) { } } - CHAR_DATA* nxtLdr = _leader; + CHAR_DATA* nxtLdr = getLeader(); int nxtLdrGrpSize = 0; // если ушел лидер - ищем нового, но после ухода предыдущего - if ( member == _leader) { + if (_leaderUID == memberUID) { nxtLdr = nullptr; for (const auto& mit : *this){ auto m = mit.second->member; @@ -709,9 +709,11 @@ void Group::_promote(CHAR_DATA *member) { bool same_group(CHAR_DATA * ch, CHAR_DATA * tch) { - if (!ch || !tch) - return false; - if (ch->personGroup == tch->personGroup) + if (!ch || !tch) return false; + if (ch == tch) return true; + // нпц по-умолчанию в группе, если одного алайнмента + if (IS_NPC(ch) && IS_NPC(tch) && SAME_ALIGN(ch, tch)) return true; + if (ch->personGroup != nullptr && tch->personGroup != nullptr && ch->personGroup == tch->personGroup) return true; return false; } @@ -845,13 +847,16 @@ void Group::_updateMemberCap() { } void Group::processGarbageCollection() { - for (auto it = this->begin(); it != this->end();) { - auto m = (*it).second; + std::vector expellV; // массивчик персонажей, которых надо удалить из группы + for (const auto& it : *this) { + auto m = it.second; if (m->expiryTime <= steady_clock::now() && m->member == nullptr) { - _removeMember(m->memberUID); - } else { - ++it; + expellV.push_back(m->memberUID); } } + if (!expellV.empty()) + for (auto it : expellV) { + _removeMember(it); + } } diff --git a/src/grp/grp.roster.cpp b/src/grp/grp.roster.cpp index bb9eccd1a..d382670eb 100644 --- a/src/grp/grp.roster.cpp +++ b/src/grp/grp.roster.cpp @@ -429,7 +429,7 @@ void GroupRoster::charDataPurged(CHAR_DATA *ch) { } void GroupRoster::processGarbageCollection() { - for (auto it: _groupList){ + for (const auto& it: _groupList){ it.second->processGarbageCollection(); } for (auto rq = _requestList.begin(); rq != _requestList.end();){ From 93108f13308887876f7220f106cf621d67a80075 Mon Sep 17 00:00:00 2001 From: Demidov Date: Thu, 28 Jan 2021 19:21:07 +0300 Subject: [PATCH 35/40] =?UTF-8?q?=D0=9F=D0=B8=D1=81=D0=B0=D0=BB=D0=BE=20?= =?UTF-8?q?=D0=BC=D1=83=D1=81=D0=BE=D1=80=20=D0=B2=20=D0=B3=D0=B7=D0=B0?= =?UTF-8?q?=D1=8F=20=D1=81=D0=BF=D0=B8=D1=81=20=D0=B2=20=D1=81=D0=BB=D1=83?= =?UTF-8?q?=D1=87=D0=B0=D0=B5,=20=D0=BA=D0=BE=D0=B3=D0=B4=D0=B0=20=D0=B5?= =?UTF-8?q?=D1=81=D1=82=D1=8C=20=D1=87=D1=83=D0=B6=D0=B8=D0=B5=20=D0=B7?= =?UTF-8?q?=D0=B0=D1=8F=D0=B2=D0=BA=D0=B8,=20=D0=B0=20=D1=81=D0=B2=D0=BE?= =?UTF-8?q?=D0=B8=D1=85=20=D0=BD=D0=B5=D1=82.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/grp/grp.roster.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/grp/grp.roster.cpp b/src/grp/grp.roster.cpp index d382670eb..8e751f3cc 100644 --- a/src/grp/grp.roster.cpp +++ b/src/grp/grp.roster.cpp @@ -280,8 +280,10 @@ void GroupRoster::printRequestList(CHAR_DATA *ch) { sprintf(smallBuf, "%s%s\r\n", it->_type== RQ_TYPE::RQ_PERSON? "Заявка в группу лидера ": "Приглашение от лидера группы ", it->_group->getLeaderName().c_str()); + notFound = false; } if (ch->personGroup != nullptr && ch->personGroup->getUid() == it->_group->getUid()) { + notFound = false; if (it->_type== RQ_TYPE::RQ_GROUP) { sprintf(smallBuf, "Приглашение игроку: %s\r\n", it->_applicantName.c_str() ); } @@ -289,8 +291,8 @@ void GroupRoster::printRequestList(CHAR_DATA *ch) { sprintf(smallBuf, "Заявка от игрока: %s\r\n", it->_applicantName.c_str() ); } } - send_to_char(ch, "%s", smallBuf); - notFound = false; + if (!notFound) + send_to_char(ch, "%s", smallBuf); } if (notFound) send_to_char(ch, "Заявок нет.\r\n"); From 9a6b0e4fd30dab53291e2467b1e86ad0e73224e9 Mon Sep 17 00:00:00 2001 From: Demidov Date: Sun, 31 Jan 2021 03:27:42 +0300 Subject: [PATCH 36/40] =?UTF-8?q?=D0=A3=D0=B1=D1=80=D0=B0=D0=BB=20=D0=BD?= =?UTF-8?q?=D0=B0=D1=80=D0=BA=D0=BE=D0=BC=D0=B0=D0=BD=D1=81=D0=BA=D0=B8?= =?UTF-8?q?=D0=B9=20=D0=BA=D0=BB=D0=B0=D1=81=D1=81,=20=D0=BE=D0=B1=D1=80?= =?UTF-8?q?=D0=B0=D0=B1=D0=B0=D1=82=D1=8B=D0=B2=D0=B0=D1=8E=D1=89=D0=B8?= =?UTF-8?q?=D0=B9=20=D1=82=D0=B0=D0=B1=D0=BB=D0=B8=D1=86=D1=83=20=D1=83?= =?UTF-8?q?=D1=80=D0=BE=D0=B2=D0=BD=D0=B5=D0=B9=20=D0=B2=20=D0=B3=D1=80?= =?UTF-8?q?=D1=83=D0=BF=D0=BF=D0=B5,=20=D0=BB=D0=BE=D0=B3=D0=B8=D0=BA?= =?UTF-8?q?=D1=83=20=D0=BF=D0=B5=D1=80=D0=B5=D0=BD=D1=91=D1=81=20=D0=B2=20?= =?UTF-8?q?GroupRoster=20+=20=D0=BC=D0=B5=D1=82=D0=BE=D0=B4,=20=D0=BA?= =?UTF-8?q?=D0=BE=D1=82=D0=BE=D1=80=D1=8B=D0=B9=20=D0=BF=D0=BE=20=D1=87?= =?UTF-8?q?=D0=B0=D1=80=D1=83=20=D0=BE=D1=82=D0=B4=D0=B0=D0=B5=D1=82=20?= =?UTF-8?q?=D1=80=D0=B0=D0=B7=D0=BD=D0=B8=D1=86=D1=83=20=D0=B2=20=D1=83?= =?UTF-8?q?=D1=80=D0=BE=D0=B2=D0=BD=D1=8F=D1=85.=20=D0=9D=D1=83=20=D0=B8?= =?UTF-8?q?=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB=20show=20grouping?= =?UTF-8?q?=20=D0=BA=D0=BE=D0=BC=D0=B0=D0=BD=D0=B4=D1=83.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 1 - src/act.informative.cpp | 8 +-- src/cmd.wiz/act.wizard.cpp | 5 ++ src/db.cpp | 2 +- src/grp/grouppenalties.cpp | 120 ------------------------------------- src/grp/grp.group.cpp | 3 +- src/grp/grp.main.h | 23 +++---- src/grp/grp.roster.cpp | 109 +++++++++++++++++++++++++++++++-- 8 files changed, 123 insertions(+), 148 deletions(-) delete mode 100644 src/grp/grouppenalties.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 75eebe44c..2b80a5ca4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -106,7 +106,6 @@ set(SOURCES src/glory_misc.cpp src/graph.cpp src/grp/follow.cpp - src/grp/grouppenalties.cpp src/grp/grp.cmdprocessor.cpp src/grp/grp.group.cpp src/grp/grp.request.cpp diff --git a/src/act.informative.cpp b/src/act.informative.cpp index c9ff69776..2447eeef2 100644 --- a/src/act.informative.cpp +++ b/src/act.informative.cpp @@ -3817,9 +3817,8 @@ void print_do_score_all(CHAR_DATA *ch) sprintf(buf + strlen(buf), " || %sВы можете вступить в группу с максимальной разницей %s||\r\n" " || %sв %2d %-75s%s||\r\n", - CCNRM(ch, C_NRM), CCCYN(ch, C_NRM), CCNRM(ch, C_NRM), - groupRoster.grouping[static_cast(GET_CLASS(ch))][static_cast(GET_REMORT(ch))], - (string(desc_count(groupRoster.grouping[static_cast(GET_CLASS(ch))][static_cast(GET_REMORT(ch))], WHAT_LEVEL)) + CCNRM(ch, C_NRM), CCCYN(ch, C_NRM), + CCNRM(ch, C_NRM), groupRoster.getPenalty(ch), (string(desc_count(groupRoster.getPenalty(ch), WHAT_LEVEL)) + string(" без потерь для опыта.")).substr(0, 76).c_str(), CCCYN(ch, C_NRM)); if (RENTABLE(ch)) @@ -4087,8 +4086,7 @@ void do_score(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) { sprintf(buf + strlen(buf), "Вы можете вступить в группу с максимальной разницей в %d %s без потерь для опыта.\r\n", - groupRoster.grouping[static_cast(GET_CLASS(ch))][static_cast(GET_REMORT(ch))], - desc_count(groupRoster.grouping[static_cast(GET_CLASS(ch))][static_cast(GET_REMORT(ch))], WHAT_LEVEL)); + groupRoster.getPenalty(ch), desc_count(groupRoster.getPenalty(ch), WHAT_LEVEL)); } //Напоминаем о метке, если она есть. diff --git a/src/cmd.wiz/act.wizard.cpp b/src/cmd.wiz/act.wizard.cpp index eda936bb1..8d91fb293 100644 --- a/src/cmd.wiz/act.wizard.cpp +++ b/src/cmd.wiz/act.wizard.cpp @@ -3824,6 +3824,7 @@ struct show_struct show_fields[] = {"mobstat", LVL_IMPL}, {"bosses", LVL_IMPL}, {"remort", LVL_IMPL}, // 25 + {"grouping", LVL_IMPL}, {"\n", 0} }; @@ -4337,6 +4338,10 @@ void do_show(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) case 25: // remort ExtMoney::show_config(ch); break; + case 26: { + GlobalObjects::groupRoster().show(ch, value); + break; + } default: send_to_char("Извините, неверная команда.\r\n", ch); break; diff --git a/src/db.cpp b/src/db.cpp index 62bd3acea..d330b64b4 100644 --- a/src/db.cpp +++ b/src/db.cpp @@ -2704,7 +2704,7 @@ void boot_db(void) boot_profiler.next_step("Loading grouping parameters"); log("Booting grouping parameters"); - if (GlobalObjects::groupRoster().grouping.init()) + if (GlobalObjects::groupRoster().initPenaltyTable() != 0) { exit(1); } diff --git a/src/grp/grouppenalties.cpp b/src/grp/grouppenalties.cpp deleted file mode 100644 index 87b9159e2..000000000 --- a/src/grp/grouppenalties.cpp +++ /dev/null @@ -1,120 +0,0 @@ -/* ************************************************************************ -* File: GroupPenalties.cpp Part of Bylins * -* Класс расчета всяких штрафов при вступлении в группу * -* * -* All rights reserved. See license.doc for complete information. * -* * -* Copyright (C) 1993, 94 by the Trustees of the Johns Hopkins University * -* CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991. * -* * -************************************************************************ */ - -#include "grp.main.h" - -#include "chars/char_player.hpp" -#include "db.h" -#include "logger.hpp" -#include "structs.h" -#include "sysdep.h" -#include "utils.h" - -extern GroupRoster& groupRoster; - -/* - Берет misc/grouping, первый столбик цифр считает номерами мортов, - остальные столбики - значение макс. разрыва в уровнях для конкретного - класса. На момент написания этого в конфиге присутствует 26 строк, макс. - морт равен 50 - строки с мортами с 26 по 50 копируются с 25-мортовой строки. -*/ - -int GroupPenalties::init() -{ - int clss = 0, remorts = 0, rows_assigned = 0, levels = 0, pos = 0, max_rows = MAX_REMORT+1; - - // пре-инициализация - for (remorts = 0; remorts < max_rows; remorts++) //Строк в массиве должно быть на 1 больше, чем макс. морт - { - for (clss = 0; clss < NUM_PLAYER_CLASSES; clss++) //Столбцов в массиве должно быть ровно столько же, сколько есть классов - { - m_grouping[clss][remorts] = -1; - } - } - - FILE* f = fopen(LIB_MISC "grouping", "r"); - if (!f) - { - log("Невозможно открыть файл %s", LIB_MISC "grouping"); - return 1; - } - - while (get_line(f, buf)) - { - if (!buf[0] || buf[0] == ';' || buf[0] == '\n') //Строка пустая или строка-коммент - { - continue; - } - clss = 0; - pos = 0; - while (sscanf(&buf[pos], "%d", &levels) == 1) - { - while (buf[pos] == ' ' || buf[pos] == '\t') - { - pos++; - } - if (clss == 0) //Первый проход цикла по строке - { - remorts = levels; //Номера строк - if (m_grouping[0][remorts] != -1) - { - log("Ошибка при чтении файла %s: дублирование параметров для %d ремортов", - LIB_MISC "grouping", remorts); - return 2; - } - if (remorts > MAX_REMORT || remorts < 0) - { - log("Ошибка при чтении файла %s: неверное значение количества ремортов: %d, " - "должно быть в промежутке от 0 до %d", - LIB_MISC "grouping", remorts, MAX_REMORT); - return 3; - } - } - else - { - m_grouping[clss - 1][remorts] = levels; // -1 потому что в массиве нет столбца с кол-вом мортов - } - clss++; //+Номер столбца массива - while (buf[pos] != ' ' && buf[pos] != '\t' && buf[pos] != 0) - { - pos++; //Ищем следующее число в строке конфига - } - } - if (clss != NUM_PLAYER_CLASSES+1) - { - log("Ошибка при чтении файла %s: неверный формат строки '%s', должно быть %d " - "целых чисел, прочитали %d", LIB_MISC "grouping", buf, NUM_PLAYER_CLASSES+1, clss); - return 4; - } - rows_assigned++; - } - - if (rows_assigned < max_rows) - { - for (levels = remorts; levels < max_rows; levels++) //Берем свободную переменную - { - for (clss = 0; clss < NUM_PLAYER_CLASSES; clss++) - { - m_grouping[clss][levels] = m_grouping[clss][remorts]; //Копируем последнюю строку на все морты, для которых нет строк - } - } - } - fclose(f); - return 0; -} - -int grouping_koef(int player_class, int player_remort) -{ - if ((player_class >= NUM_PLAYER_CLASSES) || (player_class < 0)) - return 1; - return groupRoster.grouping[player_class][player_remort]; - -} \ No newline at end of file diff --git a/src/grp/grp.group.cpp b/src/grp/grp.group.cpp index 62d84cef3..d43563df9 100644 --- a/src/grp/grp.group.cpp +++ b/src/grp/grp.group.cpp @@ -808,7 +808,6 @@ u_short Group::calcExpMultiplicator(const CHAR_DATA* player) const int player_remorts = static_cast(GET_REMORT(player)); const int player_class = static_cast(GET_CLASS(player)); const int player_level = GET_LEVEL(player); - auto m_grouping = groupRoster.grouping; short result = DEFAULT_100; if (IS_NPC(player)) @@ -834,7 +833,7 @@ u_short Group::calcExpMultiplicator(const CHAR_DATA* player) return result; } - if (_maxPlayerLevel - player_level > m_grouping[player_class][player_remorts]) { + if (_maxPlayerLevel - player_level > groupRoster.getPenalty(player)) { return MAX(1, DEFAULT_100 - 3 * (_maxPlayerLevel - player_level)); } return DEFAULT_100; diff --git a/src/grp/grp.main.h b/src/grp/grp.main.h index 712d3ab99..e9ebe5e8e 100644 --- a/src/grp/grp.main.h +++ b/src/grp/grp.main.h @@ -89,20 +89,6 @@ const duration DEF_EXPIRY_TIME = 10s; inline bool IN_GROUP(CHAR_DATA* ch) {return ch != nullptr && ch->personGroup != nullptr;} inline bool IN_SAME_GROUP(CHAR_DATA* p1, CHAR_DATA* p2) {return IN_GROUP(p1) && IN_GROUP(p2) && p1->personGroup == p2->personGroup;} -// класс, хранящий обвязку штрафов по экспе для группы -class GroupPenalties -{ -public: - using class_penalties_t = std::array; - using penalties_t = std::array; - - int init(); - const auto& operator[](const size_t player_class) const { return m_grouping[player_class]; } -private: - penalties_t m_grouping; -}; - - // класс-коллекция персонажей обоих типов что ли.. class Group : public grp_mt { private: @@ -230,7 +216,14 @@ class GroupRoster { void acceptInvite(CHAR_DATA* who, char* author); void deleteRequest(Request * r); Request* findRequest(const char* targetPerson, const char* group, RQ_TYPE type); - GroupPenalties grouping; +private: + using class_penalties_t = std::array; + using penalties_t = std::array; + penalties_t m_grouping; +public: + int initPenaltyTable(); + void show(CHAR_DATA* ch, char* arg); + short getPenalty(const CHAR_DATA* ch); }; diff --git a/src/grp/grp.roster.cpp b/src/grp/grp.roster.cpp index 8e751f3cc..9218026ad 100644 --- a/src/grp/grp.roster.cpp +++ b/src/grp/grp.roster.cpp @@ -8,9 +8,10 @@ GroupRoster& groupRoster = GlobalObjects::groupRoster(); +extern const char *class_name[]; GroupRoster::GroupRoster() { - + initPenaltyTable(); } void GroupRoster::restorePlayerGroup(CHAR_DATA *ch) { @@ -424,9 +425,6 @@ void GroupRoster::charDataPurged(CHAR_DATA *ch) { } else { ++r; } - - - } } @@ -448,3 +446,106 @@ void GroupRoster::processGarbageCollection() { void grp::gc() { groupRoster.processGarbageCollection(); } + +int GroupRoster::initPenaltyTable() +{ + int clss = 0, remorts = 0, rows_assigned = 0, levels = 0, pos = 0, max_rows = MAX_REMORT+1; + + // пре-инициализация + for (remorts = 0; remorts < max_rows; remorts++) //Строк в массиве должно быть на 1 больше, чем макс. морт + { + for (clss = 0; clss < NUM_PLAYER_CLASSES; clss++) //Столбцов в массиве должно быть ровно столько же, сколько есть классов + { + m_grouping[clss][remorts] = -1; + } + } + + FILE* f = fopen(LIB_MISC "grouping", "r"); + if (!f) + { + log("Невозможно открыть файл %s", LIB_MISC "grouping"); + return 1; + } + + while (get_line(f, buf)) + { + if (!buf[0] || buf[0] == ';' || buf[0] == '\n') //Строка пустая или строка-коммент + { + continue; + } + clss = 0; + pos = 0; + while (sscanf(&buf[pos], "%d", &levels) == 1) + { + while (buf[pos] == ' ' || buf[pos] == '\t') + { + pos++; + } + if (clss == 0) //Первый проход цикла по строке + { + remorts = levels; //Номера строк + if (m_grouping[0][remorts] != -1) + { + log("Ошибка при чтении файла %s: дублирование параметров для %d ремортов", + LIB_MISC "grouping", remorts); + return 2; + } + if (remorts > MAX_REMORT || remorts < 0) + { + log("Ошибка при чтении файла %s: неверное значение количества ремортов: %d, " + "должно быть в промежутке от 0 до %d", + LIB_MISC "grouping", remorts, MAX_REMORT); + return 3; + } + } + else + { + m_grouping[clss - 1][remorts] = levels; // -1 потому что в массиве нет столбца с кол-вом мортов + } + clss++; //+Номер столбца массива + while (buf[pos] != ' ' && buf[pos] != '\t' && buf[pos] != 0) + { + pos++; //Ищем следующее число в строке конфига + } + } + if (clss != NUM_PLAYER_CLASSES+1) + { + log("Ошибка при чтении файла %s: неверный формат строки '%s', должно быть %d " + "целых чисел, прочитали %d", LIB_MISC "grouping", buf, NUM_PLAYER_CLASSES+1, clss); + return 4; + } + rows_assigned++; + } + + if (rows_assigned < max_rows) + { + for (levels = remorts; levels < max_rows; levels++) //Берем свободную переменную + { + for (clss = 0; clss < NUM_PLAYER_CLASSES; clss++) + { + m_grouping[clss][levels] = m_grouping[clss][remorts]; //Копируем последнюю строку на все морты, для которых нет строк + } + } + } + fclose(f); + return 0; +} + +void GroupRoster::show(CHAR_DATA *ch, char* arg) { + send_to_char(ch, "Параметры группы:\r\n"); + for (int i = 0; i< NUM_PLAYER_CLASSES; i++) { + if (*arg && !isname(arg, class_name[i])) + continue; + send_to_char(ch, "Класс %s, реморт:уровень:\r\n", class_name[i]); + for (int j = 0; j < MAX_REMORT; j++) { + send_to_char(ch, "%d:%d ", j, m_grouping[i][j]); + } + send_to_char(ch, "\r\n"); + } +} + +short GroupRoster::getPenalty(const CHAR_DATA *ch) { + if (ch == nullptr || ch->purged()) + return 0; + return m_grouping[GET_CLASS(ch)][GET_REMORT(ch)]; +} From 29f71a88f9be280674a9cbf5b126d2f68f02b42e Mon Sep 17 00:00:00 2001 From: Demidov Date: Mon, 1 Feb 2021 15:19:24 +0300 Subject: [PATCH 37/40] =?UTF-8?q?=D0=9F=D0=BE=D1=87=D0=B8=D0=BD=D0=B8?= =?UTF-8?q?=D0=BB:=201.=20=D0=93=D1=80=D1=83=D0=BF=D0=BF=D0=BE=D0=B2=D1=8B?= =?UTF-8?q?=D0=B5=20=D0=B8=20=D0=BC=D0=B0=D1=81=D1=81=D0=BE=D0=B2=D1=8B?= =?UTF-8?q?=D0=B5=20=D0=BA=D0=B0=D1=81=D1=82=D1=8B=20=D0=BA=D0=BE=D1=80?= =?UTF-8?q?=D1=80=D0=B5=D0=BA=D1=82=D0=BD=D0=BE=20=D0=BE=D1=82=D1=80=D0=B0?= =?UTF-8?q?=D0=B1=D0=B0=D1=82=D1=8B=D0=B2=D0=B0=D1=8E=D1=82=20=D1=87=D0=B0?= =?UTF-8?q?=D1=80=D0=BC=D0=B8=D1=81=D0=BE=D0=B2=202.=20=D0=9B=D0=B8=D0=B4?= =?UTF-8?q?=D0=B5=D1=80=D0=BA=D0=B0=20=D0=BA=D0=B0=D1=87=D0=B0=D0=B5=D1=82?= =?UTF-8?q?=D1=81=D1=8F,=20=D0=B5=D1=81=D0=BB=D0=B8=20=D0=B2=20=D0=BA?= =?UTF-8?q?=D0=BB=D0=B5=D1=82=D0=BA=D0=B5=20=D1=81=20=D0=BB=D0=B8=D0=B4?= =?UTF-8?q?=D0=B5=D1=80=D0=BE=D0=BC=20=D0=B5=D1=81=D1=82=D1=8C=20=D1=87?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=20=D0=B3=D1=80=D1=83=D0=BF=D0=BF=D1=8B=203.?= =?UTF-8?q?=20=D0=92=20=D1=80=D0=B0=D1=81=D1=87=D0=B5=D1=82=D0=B5=20=D0=BA?= =?UTF-8?q?=D0=BE=D0=BB-=D0=B2=D0=B0=20=D1=87=D0=BB=D0=B5=D0=BD=D0=BE?= =?UTF-8?q?=D0=B2=20=D0=B3=D1=80=D1=83=D0=BF=D0=BF=D1=8B=20=D0=BD=D0=B5=20?= =?UTF-8?q?=D1=83=D1=87=D0=B0=D1=81=D1=82=D0=B2=D1=83=D1=8E=D1=82=20=D1=87?= =?UTF-8?q?=D0=B0=D1=80=D0=BC=D0=B8=D1=81=D1=8B=204.=20=D0=9A=D0=BE=D0=BC?= =?UTF-8?q?=D0=B0=D0=BD=D0=B4=D0=B0=20=D0=B3=D1=80=D1=83=20=D1=81=D0=BF?= =?UTF-8?q?=D0=B8=D1=81=20=D1=82=D0=B5=D0=BF=D0=B5=D1=80=D1=8C=20=D0=B2?= =?UTF-8?q?=D1=8B=D0=B2=D0=BE=D0=B4=D0=B8=D1=82=20=D1=82=D0=B8=D0=BF=20?= =?UTF-8?q?=D0=BF=D0=BE=D1=81=D0=BB=D0=B5=D0=B4=D0=BE=D0=B2=D0=B0=D1=82?= =?UTF-8?q?=D0=B5=D0=BB=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chars/char_player.cpp | 16 +++++++----- src/fightsystem/fight_stuff.cpp | 4 +-- src/grp/grp.group.cpp | 44 +++++++++++++++++---------------- src/grp/grp.main.h | 2 +- src/grp/grp.roster.cpp | 2 +- 5 files changed, 37 insertions(+), 31 deletions(-) diff --git a/src/chars/char_player.cpp b/src/chars/char_player.cpp index 42b4d9c41..7306d9f7d 100644 --- a/src/chars/char_player.cpp +++ b/src/chars/char_player.cpp @@ -1539,7 +1539,6 @@ int Player::_pfileLoad(FBFILE *fl, bool reboot, const char* name) { int num = 0, num2 = 0, num3 = 0, num4 = 0, num5 = 0, num6 = 0, i; long int lnum = 0, lnum3 = 0; unsigned long long llnum = 0; - char filename[40]; char buf[MAX_RAW_INPUT_LENGTH], line[MAX_RAW_INPUT_LENGTH], tag[6]; char line1[MAX_RAW_INPUT_LENGTH]; struct timed_type timed; @@ -1567,7 +1566,7 @@ int Player::_pfileLoad(FBFILE *fl, bool reboot, const char* name) { do { if (!fbgetline(fl, line)) { - log("SYSERROR: Wrong file ascii %d %s", id, filename); + log("SYSERROR: Wrong file ascii %d", id); return (-1); } @@ -1659,7 +1658,7 @@ int Player::_pfileLoad(FBFILE *fl, bool reboot, const char* name) { { if (!fbgetline(fl, line)) { - log("SYSERROR: Wrong file ascii %d %s", id, filename); + log("SYSERROR: Wrong file ascii %d", id); return (-1); } @@ -2143,7 +2142,7 @@ int Player::_pfileLoad(FBFILE *fl, bool reboot, const char* name) { if (pk_one->unique == lnum) break; if (pk_one) { - log("SYSERROR: duplicate entry pkillers data for %d %s", id, filename); + log("SYSERROR: duplicate entry pkillers data for %d", id); continue; } @@ -2450,7 +2449,6 @@ int Player::_pfileLoad(FBFILE *fl, bool reboot, const char* name) { // здесь мы закладываемся на то, что при ребуте это все сейчас пропускается и это нормально, // иначе в таблице crc будут пустые имена, т.к. сама плеер-таблица еще не сформирована // и в любом случае при ребуте это все пересчитывать не нужно - FileCRC::check_crc(filename, FileCRC::PLAYER, GET_UNIQUE(this)); this->account = Account::get_account(GET_EMAIL(this)); if (this->account == nullptr) @@ -2499,7 +2497,13 @@ int Player::load_char_ascii(const char *name, bool reboot, const bool find_id /* log("Can't load ascii. ID: %d; File name: \"%s\"; Current directory: \"%s\")", id, filename, getcwd(buffer, BUFFER_SIZE)); return -1; } - return _pfileLoad(fl, reboot, name); + // здесь мы закладываемся на то, что при ребуте это все сейчас пропускается и это нормально, + // иначе в таблице crc будут пустые имена, т.к. сама плеер-таблица еще не сформирована + // и в любом случае при ребуте это все пересчитывать не нужно + + auto retval = _pfileLoad(fl, reboot, name); + FileCRC::check_crc(filename, FileCRC::PLAYER, GET_UNIQUE(this)); + return retval; } diff --git a/src/fightsystem/fight_stuff.cpp b/src/fightsystem/fight_stuff.cpp index c638adeee..5fe418710 100644 --- a/src/fightsystem/fight_stuff.cpp +++ b/src/fightsystem/fight_stuff.cpp @@ -203,8 +203,8 @@ void update_die_counts(CHAR_DATA *ch, CHAR_DATA *killer, int dec_exp) void update_leadership(CHAR_DATA *victim, CHAR_DATA *killer) { - // не в группе - выходим - if (!killer || !IN_GROUP(killer)) + // не в группе, или один в клетке - выходим + if (!killer || !victim || !IN_GROUP(killer) || killer->personGroup->get_size(victim->in_room) < 2) return; auto leader = killer->personGroup->getLeader(); // лидера нет с нами diff --git a/src/grp/grp.group.cpp b/src/grp/grp.group.cpp index d43563df9..ae21e196e 100644 --- a/src/grp/grp.group.cpp +++ b/src/grp/grp.group.cpp @@ -110,9 +110,9 @@ u_long Group::getUid() const { return _uid; } -int Group::_getMemberCap() const { +int Group::_getMemberCap() { if (_leader != nullptr) - return grp::max_group_size(_leader) + 1; + _memberCap = grp::max_group_size(_leader) + 1; return _memberCap; } @@ -361,14 +361,12 @@ void Group::listMembers(CHAR_DATA *ch) { CHAR_DATA *leader; int count = 1; u_short inRoomCount = 0; + bool isCharmice = false; if (ch->personGroup) { leader = ch->personGroup->getLeader(); - for (const auto rm : world[ch->in_room]->people) { - if (IN_SAME_GROUP(ch, rm)) - inRoomCount++; - } + inRoomCount = this->get_size(ch->in_room); for (auto & it : *this ) { if (it.second->member == leader) continue; @@ -378,8 +376,14 @@ void Group::listMembers(CHAR_DATA *ch) { sprintf(smallBuf, "Лидер: %s\r\n", ch->personGroup->getLeaderName().c_str() ); send_to_char(smallBuf, ch); } - sprintf(smallBuf, "%d. Согруппник: %s\r\n", count, it.second->memberName.c_str()); + if (it.second->member != nullptr) + isCharmice = IS_CHARMICE(it.second->member); + sprintf(smallBuf, "%d. %s: %s\r\n", + count, + isCharmice? "Чармис": "Согруппник", + it.second->memberName.c_str()); send_to_char(smallBuf, ch); + isCharmice = false; } if (count == 1) send_to_char(ch, "Увы, но вы один, как перст!\r\n"); @@ -630,7 +634,7 @@ bool Group::addFollowers(CHAR_DATA *leader) { for (auto f = leader->followers; f; f = f->next) { if (IS_NPC(f->follower)) continue; - if ((u_short)this->get_size() < (u_short)_getMemberCap()) { + if ((u_short)this->get_size() <= (u_short)_getMemberCap()) { this->addMember(f->follower); result = true; } @@ -653,11 +657,13 @@ CHAR_DATA* Group::get_random_pc_group() { u_short Group::get_size(rnum_t room_rnum) { u_short retval = 0; + if (room_rnum == 0) + return _pcCount; for (const auto& it : *this){ auto c = it.second->member; - if (c == nullptr) + if (it.second->type != GM_CHAR || c== nullptr) continue; - if (room_rnum == (room_rnum == 0 ? room_rnum : c->in_room) && !c->purged() && retval < 255) + if (room_rnum == c->in_room && !c->purged() && retval < 255) retval++; } return retval; @@ -713,13 +719,13 @@ bool same_group(CHAR_DATA * ch, CHAR_DATA * tch) if (ch == tch) return true; // нпц по-умолчанию в группе, если одного алайнмента if (IS_NPC(ch) && IS_NPC(tch) && SAME_ALIGN(ch, tch)) return true; - if (ch->personGroup != nullptr && tch->personGroup != nullptr && ch->personGroup == tch->personGroup) - return true; + // чармис тоже в "группе" с хозяином + if (IS_CHARMICE(tch) && ch == tch->get_master()) return true; + // а теперь проверяем группы =) + if (IN_SAME_GROUP(ch, tch)) return true; return false; } - - /*++ Функция расчитывает всякие бонусы для группы при получении опыта, после чего вызывает функцию получения опыта для всех членов группы @@ -747,12 +753,8 @@ void Group::gainExp(CHAR_DATA * victim) { const bool leader_inroom = SAME_ROOM(_leader, victim); // прежде чем раздать опыт, считаем количество персонажей в клетке - for (auto it: *this) { - ch = it.second->member; - if (ch != nullptr && SAME_ROOM(ch, victim) && it.second->type == GM_CHAR) { - ++inroom_members; - } - } + inroom_members = this->get_size(victim->in_room); + // есть партнерка и персонажей двое и зона негрупповая if (leader_inroom and inroom_members == 2 and can_use_feat(_leader, PARTNER_FEAT) and _leader->get_zone_group() <2) expDivider = 1; @@ -767,7 +769,7 @@ void Group::gainExp(CHAR_DATA * victim) { } expMultiplicator = calcExpMultiplicator(ch); // цифра процента // если прокнула лидерка, то добавляем еще 20 - if (leader_inroom && calc_leadership(_leader) and inroom_members > 1) + if (leader_inroom && inroom_members > 1 && calc_leadership(_leader)) expMultiplicator += 20; increaseExperience(ch, victim, expDivider, expMultiplicator); } diff --git a/src/grp/grp.main.h b/src/grp/grp.main.h index e9ebe5e8e..088f94321 100644 --- a/src/grp/grp.main.h +++ b/src/grp/grp.main.h @@ -115,7 +115,7 @@ class Group : public grp_mt { Group(CHAR_DATA *leader, u_long uid); ~Group(); void _setLeader(CHAR_DATA *leader); - int _getMemberCap() const; + int _getMemberCap(); bool _isFull(); bool _isActive(); // проверка, что в группе все персонажи онлайн bool _isMember(int uid); diff --git a/src/grp/grp.roster.cpp b/src/grp/grp.roster.cpp index 9218026ad..1380c4b19 100644 --- a/src/grp/grp.roster.cpp +++ b/src/grp/grp.roster.cpp @@ -173,7 +173,7 @@ grp_ptr GroupRoster::addGroup(CHAR_DATA * leader) { ++this->_currentGroupIndex; auto group = std::make_shared(leader, _currentGroupIndex) ; this->_groupList.emplace(_currentGroupIndex, group); - send_to_char(leader, "Вы создали группу c максимальным числом последователей %d.\r\n", leader->personGroup ? leader->personGroup->_getMemberCap() : 0); + send_to_char(leader, "Вы создали группу c максимальным числом соратников %d.\r\n", leader->personGroup ? leader->personGroup->_getMemberCap() : 0); return group; } From cf860f9f838f8ed143434d56b174df40b4b1f591 Mon Sep 17 00:00:00 2001 From: Demidov Date: Mon, 1 Feb 2021 18:07:40 +0300 Subject: [PATCH 38/40] =?UTF-8?q?=D0=A3=D0=B1=D1=80=D0=B0=D0=BB=20=D0=B4?= =?UTF-8?q?=D0=B5=D0=B1=D0=B0=D0=B3-=D1=81=D0=BE=D0=BE=D0=B1=D1=89=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D1=8F=20=D0=B2=20=D0=B4=D0=B5=D1=81=D1=82=D1=80?= =?UTF-8?q?=D1=83=D0=BA=D1=82=D0=BE=D1=80=D0=B0=D1=85=20=D0=9F=D0=BE=D1=87?= =?UTF-8?q?=D0=B8=D0=BD=D0=B8=D0=BB=20=D0=BF=D1=80=D0=B8=D0=B2=D0=B8=D0=BB?= =?UTF-8?q?=D0=B5=D0=B3=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/grp/grp.group.cpp | 2 -- src/privilege.cpp | 14 +++++++------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/grp/grp.group.cpp b/src/grp/grp.group.cpp index ae21e196e..761800a17 100644 --- a/src/grp/grp.group.cpp +++ b/src/grp/grp.group.cpp @@ -137,7 +137,6 @@ bool Group::_sameGroup(CHAR_DATA *ch, CHAR_DATA *vict) { Group::~Group() { this->_clear(false); - mudlog("~Group", BRF, LVL_IMMORT, SYSLOG, TRUE); } char_info::char_info(int uid, GM_TYPE type, CHAR_DATA *m, const std::string& name) { @@ -149,7 +148,6 @@ char_info::char_info(int uid, GM_TYPE type, CHAR_DATA *m, const std::string& nam } char_info::~char_info() { - mudlog("[~char_info]", BRF, LVL_IMMORT, SYSLOG, TRUE); } void Group::addMember(CHAR_DATA *member, bool silent) { diff --git a/src/privilege.cpp b/src/privilege.cpp index d09a830aa..7d98dc614 100644 --- a/src/privilege.cpp +++ b/src/privilege.cpp @@ -34,16 +34,16 @@ * # title - одобрение/запрет чужих титулов * # остальные группы вписываете сколько хотите * # иммы: имя уид (0 не канает, теперь надо сразу писать уид) команды/группы -* +* * default = wizhelp wiznet register имя титул title holylight uptime date set (title name) rules show nohassle ; show (punishment stats player) * default_demigod = wizhelp wiznet имя rules * arena = purge * olc = oedit zedit redit olc trigedit * goto = goto прыжок poofin poofout -* +* * -* Йцук 595336650 grp (olc) hell mute dumb ban delete set (bank) -* Фыва 803863739 grp (arena goto olc boards) hell mute dumb ban delete set (bank) +* Йцук 595336650 groups (olc) hell mute dumb ban delete set (bank) +* Фыва 803863739 groups (arena goto olc boards) hell mute dumb ban delete set (bank) * * Формат файла временный, zone.ru там грозится своим форматом на lua, а xml в очередной раз решено не воротить, хотя и хотелось... */ @@ -200,7 +200,7 @@ void parse_command_line(const std::string &commands, int other_flags) fill_mode = 2; continue; } - else if ((*tmp_tok_iter) == "grp") + else if ((*tmp_tok_iter) == "groups") { fill_mode = 3; continue; @@ -237,7 +237,7 @@ void load() ReadEndString(file); continue; } - else if (name == "") + else if (name == "") { while (file >> name) @@ -246,7 +246,7 @@ void load() ReadEndString(file); continue; } - if (name == "") + if (name == "") break; file >> temp; // "=" From 8721d501487e1183b5fb31e5034492c8699d9f43 Mon Sep 17 00:00:00 2001 From: Demidov Date: Mon, 1 Feb 2021 18:32:45 +0300 Subject: [PATCH 39/40] =?UTF-8?q?=D0=9F=D1=80=D0=B8=20=D0=B4=D0=B5=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B8=20=D0=BA=D1=83=D0=BD=20=D0=B4=D0=BB?= =?UTF-8?q?=D1=8F=20=D0=B4=D0=B5=D0=BB=D1=91=D0=B6=D0=BA=D0=B8=20=D1=83?= =?UTF-8?q?=D1=87=D0=B8=D1=82=D1=8B=D0=B2=D0=B0=D1=8E=D1=82=D1=81=D1=8F=20?= =?UTF-8?q?=D1=82=D0=BE=D0=BB=D1=8C=D0=BA=D0=BE=20=D0=BF=D0=B5=D1=80=D1=81?= =?UTF-8?q?=D0=BE=D0=BD=D0=B0=D0=B6=D0=B8,=20=D0=B0=20=D0=BD=D0=B5=20?= =?UTF-8?q?=D0=BE=D0=B1=D1=89=D0=B8=D0=B9=20=D1=80=D0=B0=D0=B7=D0=BC=D0=B5?= =?UTF-8?q?=D1=80=20=D0=B3=D1=80=D1=83=D0=BF=D0=BF=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/act.item.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/act.item.cpp b/src/act.item.cpp index bc9546d7b..db1fb2179 100644 --- a/src/act.item.cpp +++ b/src/act.item.cpp @@ -733,7 +733,7 @@ int can_take_obj(CHAR_DATA * ch, OBJ_DATA * obj) void split_or_clan_tax(CHAR_DATA *ch, long amount) { - if (ch->personGroup != nullptr && ch->personGroup->size() > 1 && PRF_FLAGGED(ch, PRF_AUTOSPLIT)) { + if (ch->personGroup != nullptr && ch->personGroup->get_size() > 1 && PRF_FLAGGED(ch, PRF_AUTOSPLIT)) { char buf_[MAX_INPUT_LENGTH]; snprintf(buf_, sizeof(buf_), "%ld", amount); grp::do_split(ch, buf_, 0, 0); @@ -769,7 +769,7 @@ void get_check_money(CHAR_DATA *ch, OBJ_DATA *obj, OBJ_DATA *cont) send_to_char(buf, ch); ch->add_ice_currency(value); //Делить лед ВСЕГДА! - if (ch->personGroup != nullptr && ch->personGroup->size() > 1) + if (ch->personGroup != nullptr && ch->personGroup->get_size() > 1) { char local_buf[256]; sprintf(local_buf, "%d", value); @@ -789,7 +789,7 @@ void get_check_money(CHAR_DATA *ch, OBJ_DATA *obj, OBJ_DATA *cont) send_to_char(buf, ch); // все, что делится на группу - идет через налог (из кошельков не делится) - if (ch->personGroup != nullptr && ch->personGroup->size() > 1 + if (ch->personGroup != nullptr && ch->personGroup->get_size() > 1 && PRF_FLAGGED(ch, PRF_AUTOSPLIT) && (!cont || !system_obj::is_purse(cont))) { From 22340207a28314ed8084eb7108afff1c48e7d463 Mon Sep 17 00:00:00 2001 From: Demidov Date: Sat, 13 Feb 2021 02:26:02 +0300 Subject: [PATCH 40/40] =?UTF-8?q?=D0=9F=D1=80=D0=B8=20=D0=B4=D0=B5=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B8=20=D0=BA=D1=83=D0=BD=20=D0=B4=D0=BB?= =?UTF-8?q?=D1=8F=20=D0=B4=D0=B5=D0=BB=D1=91=D0=B6=D0=BA=D0=B8=20=D1=83?= =?UTF-8?q?=D1=87=D0=B8=D1=82=D1=8B=D0=B2=D0=B0=D1=8E=D1=82=D1=81=D1=8F=20?= =?UTF-8?q?=D1=82=D0=BE=D0=BB=D1=8C=D0=BA=D0=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chars/char_player.cpp | 4 ++-- src/chars/char_player.hpp | 2 +- src/cmd.wiz/act.wizard.cpp | 2 +- src/heartbeat.cpp | 2 +- tests/char.utilities.cpp | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/chars/char_player.cpp b/src/chars/char_player.cpp index 7306d9f7d..37b7ea6d0 100644 --- a/src/chars/char_player.cpp +++ b/src/chars/char_player.cpp @@ -1535,7 +1535,7 @@ void Player::initPlayerFields() {// character init set_who_last(time(0)); } -int Player::_pfileLoad(FBFILE *fl, bool reboot, const char* name) { +int Player::_pfileLoad(FBFILE *fl, bool reboot, const char* name, int id) { int num = 0, num2 = 0, num3 = 0, num4 = 0, num5 = 0, num6 = 0, i; long int lnum = 0, lnum3 = 0; unsigned long long llnum = 0; @@ -2501,7 +2501,7 @@ int Player::load_char_ascii(const char *name, bool reboot, const bool find_id /* // иначе в таблице crc будут пустые имена, т.к. сама плеер-таблица еще не сформирована // и в любом случае при ребуте это все пересчитывать не нужно - auto retval = _pfileLoad(fl, reboot, name); + auto retval = _pfileLoad(fl, reboot, name, id); FileCRC::check_crc(filename, FileCRC::PLAYER, GET_UNIQUE(this)); return retval; } diff --git a/src/chars/char_player.hpp b/src/chars/char_player.hpp index 7d0156ca1..6b381752c 100644 --- a/src/chars/char_player.hpp +++ b/src/chars/char_player.hpp @@ -109,7 +109,7 @@ class Player : public CHAR_DATA void save_char(); int load_char_ascii(const char *name, bool reboot = 0, const bool find_id = true); // метод загрузки файла игрока напрямую - int _pfileLoad(FBFILE *fl, bool reboot, const char* name); + int _pfileLoad(FBFILE *fl, bool reboot, const char* name, int id); bool get_disposable_flag(int num); void set_disposable_flag(int num); diff --git a/src/cmd.wiz/act.wizard.cpp b/src/cmd.wiz/act.wizard.cpp index 8d91fb293..d7405682b 100644 --- a/src/cmd.wiz/act.wizard.cpp +++ b/src/cmd.wiz/act.wizard.cpp @@ -201,7 +201,7 @@ void save_zone_count_reset() // Отправляет любой текст выбранному чару void do_send_text_to_char(CHAR_DATA *ch, char *argument, int, int) { - CHAR_DATA *vict = NULL; + CHAR_DATA *vict = nullptr; half_chop(argument, buf, buf2); diff --git a/src/heartbeat.cpp b/src/heartbeat.cpp index eb20c9d90..b6c1e7a8f 100644 --- a/src/heartbeat.cpp +++ b/src/heartbeat.cpp @@ -424,7 +424,7 @@ namespace Heartbeat::PulseStep("Bloody: updating", SECS_PER_MUD_HOUR * PASSES_PER_SEC, 10, std::make_shared(bloody::update)), Heartbeat::PulseStep("Room point updating", SECS_PER_MUD_HOUR * PASSES_PER_SEC, 6, std::make_shared(room_point_update)), Heartbeat::PulseStep("Temporary spells: times updating", SECS_PER_MUD_HOUR * PASSES_PER_SEC, 5, std::make_shared(Temporary_Spells::update_times)), - Heartbeat::PulseStep("Group garbage collecting", SECS_PER_MUD_HOUR * PASSES_PER_SEC, 0, std::make_shared(grp::gc)), + Heartbeat::PulseStep("Group garbage collecting", 600 * PASSES_PER_SEC, 1, std::make_shared(grp::gc)), Heartbeat::PulseStep("Exchange point updating", SECS_PER_MUD_HOUR * PASSES_PER_SEC, 2, std::make_shared(exchange_point_update)), Heartbeat::PulseStep("Players index flushing", SECS_PER_MUD_HOUR * PASSES_PER_SEC, 1, std::make_shared(flush_player_index)), Heartbeat::PulseStep("Point updating", SECS_PER_MUD_HOUR * PASSES_PER_SEC, PASSES_PER_SEC - 5, std::make_shared(point_update)), diff --git a/tests/char.utilities.cpp b/tests/char.utilities.cpp index 69763e1d3..a0bb28531 100644 --- a/tests/char.utilities.cpp +++ b/tests/char.utilities.cpp @@ -20,7 +20,7 @@ namespace test_utils char filename[40] = "data/plrs/sample.player"; FBFILE *fl = NULL; fl = fbopen(filename, FB_READ); - result->_pfileLoad(fl, false, "test"); + result->_pfileLoad(fl, false, "test", 0); m_result = result; sprintf(smallBuf, "Player-%d",m_uid); m_result->set_pc_name(smallBuf);