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 1eaf73bc5..2b80a5ca4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,64 +9,78 @@ 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/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.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 + 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/core/leveling.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 - 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 @@ -74,27 +88,44 @@ 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.request.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/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,153 +208,73 @@ 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/hire.cpp - src/cmd/telegram.cpp - src/cmd.wiz/stat.cpp - src/cmd/follow.cpp ) + ) set(HEADERS - src/cmd/follow.h - src/cmd.wiz/stat.h - src/cmd/telegram.h - src/cmd/hire.h - src/cmd/telegram.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 - 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/core/leveling.h 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/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_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 @@ -306,60 +282,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/fightsystem/fight.h - src/fightsystem/fight_hit.hpp - src/fightsystem/fight_stuff.hpp - src/fightsystem/fight.penalties.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/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 @@ -367,11 +355,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 @@ -381,20 +394,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) +) set(CONFIGURATION_FILES lib.template/misc/configuration.xml) # Build types @@ -571,7 +576,14 @@ 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} + ${CRAFT_FILES} + ${MISC_FILES} + readme.markdown + CONTRIBUTING.md + ) # Sort source and header files. Just to convenience. list(SORT CIRCLE_FILES) @@ -748,8 +760,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} ${TESTBUILD_DEFINITIONS}") + 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/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.comm.cpp b/src/act.comm.cpp index e8d0740f2..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" @@ -120,109 +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)) - { - return true; - } - else - { - return false; - } -} - void perform_tell(CHAR_DATA * ch, CHAR_DATA * vict, char *arg) { // shapirus: не позволим телять, если жертва не видит и включила diff --git a/src/act.informative.cpp b/src/act.informative.cpp index 87097729e..2447eeef2 100644 --- a/src/act.informative.cpp +++ b/src/act.informative.cpp @@ -12,55 +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/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 @@ -71,9 +73,11 @@ #include using std::string; +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; @@ -92,9 +96,7 @@ 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); // local functions const char *show_obj_to_char(OBJ_DATA * object, CHAR_DATA * ch, int mode, int show_state, int how); @@ -1280,7 +1282,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)) { @@ -1685,7 +1687,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)) { @@ -3815,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), - 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)) + 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)) @@ -4085,8 +4086,7 @@ 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.getPenalty(ch), desc_count(groupRoster.getPenalty(ch), WHAT_LEVEL)); } //Напоминаем о метке, если она есть. @@ -5466,48 +5466,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); - } - -} - // Generic page_string function for displaying text void do_gen_ps(CHAR_DATA *ch, char* /*argument*/, int/* cmd*/, int subcmd) { @@ -5541,11 +5499,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; @@ -5966,8 +5919,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)), @@ -6006,7 +5962,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.item.cpp b/src/act.item.cpp index 24c7f9d9d..db1fb2179 100644 --- a/src/act.item.cpp +++ b/src/act.item.cpp @@ -12,40 +12,42 @@ * $Revision$ * ************************************************************************ */ -#include "cmd/hire.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_scripts.h" +#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.h" +#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); @@ -731,32 +731,12 @@ 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->get_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 { @@ -789,10 +769,11 @@ 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->get_size() > 1) + { 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; @@ -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->get_size() > 1 && PRF_FLAGGED(ch, PRF_AUTOSPLIT) && (!cont || !system_obj::is_purse(cont))) { @@ -819,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) { @@ -1321,7 +1302,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)); @@ -2363,9 +2344,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 { @@ -2481,9 +2462,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/act.movement.cpp b/src/act.movement.cpp index 9e22a6d2c..671780215 100644 --- a/src/act.movement.cpp +++ b/src/act.movement.cpp @@ -23,10 +23,10 @@ #include "handler.h" #include "db.h" #include "spells.h" -#include "skills.h" +#include "skills/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" @@ -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) { @@ -2059,5 +2059,4 @@ void do_wake(CHAR_DATA *ch, char *argument, int/* cmd*/, int subcmd) } } - // vim: ts=4 sw=4 tw=0 noet syntax=cpp : diff --git a/src/act.other.cpp b/src/act.other.cpp index 43c9c9037..c2d69e7eb 100644 --- a/src/act.other.cpp +++ b/src/act.other.cpp @@ -17,13 +17,12 @@ #include "char_obj_utils.inl" #include "chars/char.hpp" #include "chars/char_player.hpp" -#include "cmd/follow.h" #include "comm.h" #include "conf.h" #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" @@ -44,12 +43,13 @@ #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" #include "world.objects.hpp" + #include #include #include @@ -74,18 +74,17 @@ 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); // 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); @@ -95,12 +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 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); void do_wimpy(CHAR_DATA *ch, char *argument, int cmd, int subcmd); void do_display(CHAR_DATA *ch, char *argument, int cmd, int subcmd); @@ -111,7 +105,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*/) { @@ -132,74 +125,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*/) { @@ -921,851 +846,6 @@ 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); -} - -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) { @@ -2132,63 +1212,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" +}; @@ -2255,7 +1338,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*/) @@ -2520,7 +1605,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)) @@ -2743,6 +1830,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/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/auction.cpp b/src/auction.cpp index 35c9e58bb..48c2910ea 100644 --- a/src/auction.cpp +++ b/src/auction.cpp @@ -641,7 +641,7 @@ void trans_auction(int lot) return; } - if (ch->in_room == IN_ROOM(tch)) + if (SAME_ROOM(ch, tch)) { // Проверка на нахождение в одной комнате. tmpstr = "$n стоит рядом с вами."; @@ -783,9 +783,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/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 ce071a321..d93ea25a0 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" @@ -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 @@ -39,9 +39,14 @@ #include #include +#include "grp/grp.main.h" + +extern GroupRoster& groupRoster; + std::string PlayerI::empty_const_str; MapSystem::Options PlayerI::empty_map_options; + namespace { @@ -120,6 +125,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() @@ -605,6 +611,11 @@ void CHAR_DATA::purge() free(follower); follower = next_one; } + + // чистим указатель в групе + if (this->personGroup != nullptr) + this->personGroup->charDataPurged(this); + groupRoster.charDataPurged(this); } // * Скилл с учетом всех плюсов и минусов от шмоток/яда. @@ -890,6 +901,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)); @@ -897,6 +909,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) @@ -905,6 +918,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) @@ -920,6 +934,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) @@ -927,6 +948,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) @@ -949,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) @@ -2013,9 +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); +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) { @@ -2354,7 +2378,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 e24342d13..2e1fbe193 100644 --- a/src/chars/char.hpp +++ b/src/chars/char.hpp @@ -11,12 +11,13 @@ #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" #include "core/affect_data.h" + #include #include @@ -24,6 +25,8 @@ #include #include +class Group; + // These data contain information about a players time data struct time_data { @@ -370,6 +373,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; @@ -634,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 @@ -755,7 +759,6 @@ class CHAR_DATA : public ProtectedCharacterData int souls; public: - bool isInSameRoom(const CHAR_DATA *ch) const {return (this->in_room == ch->in_room);}; room_rnum in_room; // Location (real room number) private: @@ -835,6 +838,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 @@ -891,6 +896,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/chars/char_player.cpp b/src/chars/char_player.cpp index ce07541a2..37b7ea6d0 100644 --- a/src/chars/char_player.cpp +++ b/src/chars/char_player.cpp @@ -7,19 +7,19 @@ #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" #include "spells.h" #include "constants.h" -#include "skills.h" +#include "skills/skills.h" #include "ignores.loader.hpp" #include "im.h" #include "olc.h" #include "comm.h" +#include "core/leveling.h" #include "fightsystem/pk.h" -#include "diskio.h" #include "interpreter.h" #include "genchar.h" #include "AffectHandler.hpp" @@ -48,7 +48,7 @@ #include -int level_exp(CHAR_DATA * ch, int level); + extern std::vector cities; extern std::string default_str_cities; namespace @@ -400,7 +400,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) @@ -1055,1151 +1055,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; - } - -/////////////////////////////////////////////////////////////////////////////// - - // первыми иним и парсим поля для ребута до поля "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) < level_exp(this, GET_LEVEL(this))) - { - set_exp(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, "Tglo")) { - this->setGloryRespecTime(static_cast(num)); - } - 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); -} - bool Player::get_disposable_flag(int num) { if (num < 0 || num >= DIS_TOTAL_NUM) @@ -2555,6 +1410,1111 @@ unsigned long int Player::getTelegramId() { return this->player_specials->saved.telegram_id; } +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 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; + 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", id); + 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; + } + // reboot header + switch (*tag) { + case 'N': { + if (!strcmp(tag, "Name")) + { + set_name(line); + } + break; + } + case 'L': { + if (!strcmp(tag, "LstL")) + { + set_last_logon(lnum); + } + else if (!strcmp(tag, "Levl")) + { + set_level(num); + } + break; + } + 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); + } + break; + } + case 'I': { + if (!strcmp(tag, "Id ")) { + set_idnum(lnum); + } + break; + } + case 'E': { + if (!strcmp(tag, "Exp ")) { + set_exp(lnum); + } + else if (!strcmp(tag, "EMal")) + strcpy(GET_EMAIL(this), line); + break; + } + case 'A': { + if (!strcmp(tag, "Act ")) + PLR_FLAGS(this).from_string(line); + break; + } + case 'R': { + if (!strcmp(tag, "Rebt")) + skip_file = 1; + else if (!strcmp(tag, "Rmrt")) + { + set_remort(num); + } + break; + } + default: + sprintf(buf, "SYSERR: Unknown tag %s in pfile %s", tag, name); + } + } + while (!skip_file); + + while((reboot) && (!*GET_EMAIL(this) || !*GET_LASTIP(this))) + { + if (!fbgetline(fl, line)) + { + log("SYSERROR: Wrong file ascii %d", id); + 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); + } + // если с загруженными выше полями что-то хочется делать после лоада - делайте это здесь + + //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) { + fbclose(fl); + return id; + } + this->str_to_cities(default_str_cities); + // если происходит обычный лоад плеера, то читаем файл дальше и иним все остальные поля + +/////////////////////////////////////////////////////////////////////////////// + initPlayerFields(); + + 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); + 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; + 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; + }); + } + } + 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", id); + 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; + 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); + if (num < 0 || imrecipes[num].classknow[(int) GET_CLASS(this)] != KNOW_RECIPE) + 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); + 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); + 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 будут пустые имена, т.к. сама плеер-таблица еще не сформирована + // и в любом случае при ребуте это все пересчитывать не нужно + + 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; +} + +// на счет 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; + } + // здесь мы закладываемся на то, что при ребуте это все сейчас пропускается и это нормально, + // иначе в таблице crc будут пустые имена, т.к. сама плеер-таблица еще не сформирована + // и в любом случае при ребуте это все пересчитывать не нужно + + auto retval = _pfileLoad(fl, reboot, name, id); + FileCRC::check_crc(filename, FileCRC::PLAYER, GET_UNIQUE(this)); + return retval; +} + + +// * Перерасчет максимальных родных хп персонажа. +// * При входе в игру, левеле/делевеле, добавлении/удалении славы. +void check_max_hp(CHAR_DATA *ch) +{ + GET_MAX_HIT(ch) = PlayerSystem::con_natural_hp(ch); +} + + void Player::setGloryRespecTime(time_t param) { this->player_specials->saved.lastGloryRespecTime = MAX(1, param); } diff --git a/src/chars/char_player.hpp b/src/chars/char_player.hpp index 12418f496..6b381752c 100644 --- a/src/chars/char_player.hpp +++ b/src/chars/char_player.hpp @@ -6,24 +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/mercenary.h" +#include "structs.h" +#include "sysdep.h" #include #include #include #include +#include + +// * Перерасчет максимальных родных хп персонажа. +// * При входе в игру, левеле/делевеле, добавлении/удалении славы. +void check_max_hp(CHAR_DATA *ch); // кол-во сохраняемых стартовых статов в файле const int START_STATS_TOTAL = 6; @@ -102,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); + // метод загрузки файла игрока напрямую + int _pfileLoad(FBFILE *fl, bool reboot, const char* name, int id); bool get_disposable_flag(int num); void set_disposable_flag(int num); @@ -241,8 +249,10 @@ class Player : public CHAR_DATA std::map daily_quest_timed; // Аккаунт std::shared_ptr account; - //перечень чармисов, доступных с команды наемник - std::map charmeeHistory; + //перечень чармисов, доступных с команды наемник + std::map charmeeHistory; + + void initPlayerFields(); }; namespace PlayerSystem 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/chars/player_i.hpp b/src/chars/player_i.hpp index a0320e0cc..f1ee31bda 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/class.cpp b/src/class.cpp index e5f4a350b..993896061 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/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); @@ -2068,7 +2068,7 @@ void do_start(CHAR_DATA * ch, int newbie) break; } - 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); @@ -2086,138 +2086,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. @@ -2968,505 +2836,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/act.wizard.cpp b/src/cmd.wiz/act.wizard.cpp similarity index 99% rename from src/act.wizard.cpp rename to src/cmd.wiz/act.wizard.cpp index efdbce0fc..d7405682b 100644 --- a/src/act.wizard.cpp +++ b/src/cmd.wiz/act.wizard.cpp @@ -12,6 +12,7 @@ * $Revision$ * ************************************************************************ */ + #include "act.wizard.hpp" #include "action.targeting.hpp" @@ -23,10 +24,9 @@ #include "chars/char_player.hpp" #include "chars/player_races.hpp" #include "chars/world.characters.hpp" -#include "cmd.wiz/stat.h" -#include "cmd/follow.h" #include "comm.h" #include "command.shutdown.hpp" +#include "core/leveling.h" #include "conf.h" #include "config.hpp" #include "constants.h" @@ -34,7 +34,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" @@ -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" @@ -89,6 +90,7 @@ using std::ifstream; using std::fstream; +using namespace ExpCalc; // external vars extern bool need_warn; @@ -117,7 +119,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); @@ -126,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); @@ -200,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); @@ -2818,8 +2819,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) - - GET_EXP(victim)); + ExpCalc::gain_exp_regardless(victim, ExpCalc::level_exp(victim, newlevel) - GET_EXP(victim)); victim->save_char(); } @@ -3824,6 +3824,7 @@ struct show_struct show_fields[] = {"mobstat", LVL_IMPL}, {"bosses", LVL_IMPL}, {"remort", LVL_IMPL}, // 25 + {"grouping", LVL_IMPL}, {"\n", 0} }; @@ -4335,8 +4336,12 @@ 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; + case 26: { + GlobalObjects::groupRoster().show(ch, value); + break; + } default: send_to_char("Извините, неверная команда.\r\n", ch); break; diff --git a/src/act.wizard.hpp b/src/cmd.wiz/act.wizard.hpp similarity index 100% rename from src/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 new file mode 100644 index 000000000..755c7abed --- /dev/null +++ b/src/cmd/cmd.generic.h @@ -0,0 +1,64 @@ +#ifndef BYLINS_CMD_GENERIC_H +#define BYLINS_CMD_GENERIC_H + +#include "chars/char.hpp" +#include "skills/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); + +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..7d2a64baf 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; @@ -168,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/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 ad03a2a0b..0aae17fef 100644 --- a/src/cmd/hire.cpp +++ b/src/cmd/hire.cpp @@ -1,6 +1,7 @@ -#include "hire.h" +#include "cmd.generic.h" -#include "cmd/follow.h" + +#include "grp/grp.main.h" #include "handler.h" #include "screen.h" #include @@ -304,6 +305,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/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 new file mode 100644 index 000000000..7fa0ca172 --- /dev/null +++ b/src/cmd/quit.cpp @@ -0,0 +1,81 @@ +#include "cmd.generic.h" +#include "fightsystem/fight_stuff.hpp" +#include "depot.hpp" +#include "handler.h" +#include "objsave.h" +#include "house.h" +#include "global.objects.hpp" + +extern int free_rent; +extern GroupRoster& groupRoster; + +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/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/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..a6f5fa506 --- /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, "Состоит в группе #%lu лидера %s\r\n", ch->personGroup->getUid(), ch->personGroup->getLeaderName().c_str()); +} diff --git a/src/comm.cpp b/src/comm.cpp index d129ad652..c86e2f322 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 @@ -3410,7 +3412,8 @@ 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_PLAYING; } @@ -3418,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), "зачитать свиток.возврата"); @@ -4134,13 +4139,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/comm.h b/src/comm.h index d07be8256..112a97809 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 @@ -78,6 +79,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/config.cpp b/src/config.cpp index 25b6d53e7..d36adf93e 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -49,8 +49,6 @@ * */ -int level_exp(CHAR_DATA * ch, int level); - // GAME PLAY OPTIONS // exp change limits @@ -283,23 +281,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/constants.cpp b/src/constants.cpp index 8cae44f2c..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" @@ -667,7 +667,7 @@ const char *connected_types[] = "GloryConst OLC", "NamedStuff OLC", "Select new kin", - // 50-57 + // 50-59 "Select new race", "Interactive console", "обмен гривен", @@ -676,6 +676,10 @@ const char *connected_types[] = "select new religion", "Verification", "Just connected", + "ERROR:UNDEF", + "ERROR:UNDEF", + // 60-59 + "ERROR:UNDEF" "\n" }; diff --git a/src/core/affect_data.cpp b/src/core/affect_data.cpp index f131fec8a..337e6ff11 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" @@ -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/core/leveling.cpp b/src/core/leveling.cpp new file mode 100644 index 000000000..747315d6b --- /dev/null +++ b/src/core/leveling.cpp @@ -0,0 +1,953 @@ +#include "leveling.h" + +#include "bonus.h" +#include "chars/char_player.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[]; +extern int max_exp_gain_npc; +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)); + } +/*++ + Функция начисления опыта + 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 +{ + +// релоадится через '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..0dab08f24 --- /dev/null +++ b/src/core/leveling.h @@ -0,0 +1,35 @@ +// +// 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); + void increaseExperience(CHAR_DATA * ch, CHAR_DATA * victim, int members, int koef); +} + +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 8a3ee5d46..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" @@ -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/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/db.cpp b/src/db.cpp index abc263eca..d330b64b4 100644 --- a/src/db.cpp +++ b/src/db.cpp @@ -17,6 +17,14 @@ #include "db.h" +#include +#include +#include +#include +#include +#include +#include + #include "ban.hpp" #include "birth_places.hpp" #include "boards.h" @@ -28,12 +36,11 @@ #include "chars/player_races.hpp" #include "chars/world.characters.hpp" #include "class.hpp" -#include "cmd/follow.h" #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" @@ -45,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" @@ -70,14 +78,6 @@ #include "utils.h" #include "world.objects.hpp" -#include -#include -#include -#include -#include -#include -#include - #define CRITERION_FILE "criterion.xml" #define CASES_FILE "cases.xml" #define RANDOMOBJ_FILE "randomobj.xml" @@ -231,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(); @@ -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().initPenaltyTable() != 0) { exit(1); } @@ -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; @@ -5411,17 +5410,17 @@ 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++) { const char* pname = player_table[i].name(); - if (!str_cmp(pname, arg)) + if (!str_cmp(pname, smallBuf)) { 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); } @@ -5689,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 99% rename from src/dg_db_scripts.cpp rename to src/dg/dg_db_scripts.cpp index c3fed4d5d..d3811dd18 100644 --- a/src/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_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 c383d33a0..6b802025b 100644 --- a/src/dg_mobcmd.cpp +++ b/src/dg/dg_mobcmd.cpp @@ -24,14 +24,13 @@ ***************************************************************************/ #include "chars/char.hpp" -#include "cmd/follow.h" #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 "fightsystem/fight_hit.hpp" +#include "grp/grp.main.h" #include "handler.h" #include "im.h" #include "interpreter.h" @@ -39,13 +38,12 @@ #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 "world.objects.hpp" -#include "skills/townportal.h" struct mob_command_info { @@ -847,7 +845,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 bb1f8160b..9b794213c 100644 --- a/src/dg_objcmd.cpp +++ b/src/dg/dg_objcmd.cpp @@ -10,31 +10,27 @@ **************************************************************************/ #include "chars/char.hpp" -#include "cmd/follow.h" #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" #include "structs.h" #include "sysdep.h" #include "utils.h" #include "world.objects.hpp" -#include "skills/townportal.h" extern const char *dirs[]; extern int up_obj_where(OBJ_DATA * obj); @@ -161,7 +157,7 @@ void do_oportal(OBJ_DATA *obj, char *argument, int/* cmd*/, int/* subcmd*/) { act("Лазурная пентаграмма возникла в воздухе.", FALSE, world[curroom]->first_character(), 0, 0, TO_CHAR); act("Лазурная пентаграмма возникла в воздухе.", FALSE, world[curroom]->first_character(), 0, 0, TO_ROOM); } -// Object commands +// Object commands void do_oecho(OBJ_DATA *obj, char *argument, int/* cmd*/, int/* subcmd*/) { skip_spaces(&argument); @@ -327,7 +323,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 99414d68a..62dbef049 100644 --- a/src/dg_scripts.cpp +++ b/src/dg/dg_scripts.cpp @@ -10,47 +10,44 @@ #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 "core/leveling.h" #include "constants.h" -#include "top.h" +#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 "global.objects.hpp" +#include "handler.h" +#include "house.h" +#include "interpreter.h" +#include "logger.hpp" #include "modify.h" -#include "room.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/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 "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" + +using namespace ExpCalc; #define PULSES_PER_MUD_HOUR (SECS_PER_MUD_HOUR*PASSES_PER_SEC) @@ -2303,6 +2300,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); @@ -2319,7 +2317,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) //но только если находится в той же зоне @@ -2330,9 +2328,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 @@ -3096,26 +3092,13 @@ 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) { + // мы же про одну комнату в триггере говорим, да? + 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/dg_scripts.h b/src/dg/dg_scripts.h similarity index 99% rename from src/dg_scripts.h rename to src/dg/dg_scripts.h index 65caef7c8..d6b8144e3 100644 --- a/src/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_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 7ef02c15e..d0b81b0b2 100644 --- a/src/dg_wldcmd.cpp +++ b/src/dg/dg_wldcmd.cpp @@ -10,12 +10,10 @@ **************************************************************************/ #include "chars/char.hpp" -#include "cmd/follow.h" +#include "grp/grp.main.h" #include "comm.h" -#include "conf.h" +#include "core/leveling.h" #include "db.h" -#include "deathtrap.hpp" -#include "dg_scripts.h" #include "features.hpp" #include "fightsystem/fight.h" #include "handler.h" @@ -26,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" @@ -502,7 +500,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/dps.cpp b/src/dps.cpp index ab4093b76..e01a4c6ce 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 == nullptr || !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()) { 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,15 @@ 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); - } + for (auto &it : *ch->personGroup) { + add_tmp_group_list(it.second->member); } - 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 +281,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 +295,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 +314,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 +333,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 +352,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 +365,7 @@ Dps & Dps::operator= (const Dps ©) } return *this; } - +*/ void Dps::add_exp(int exp) { if (exp >= 0) @@ -473,21 +482,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 +539,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/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/ext_money.cpp b/src/ext_money.cpp index b841023e7..5f2c97918 100644 --- a/src/ext_money.cpp +++ b/src/ext_money.cpp @@ -4,10 +4,18 @@ #include "ext_money.hpp" +#include "conf.h" #include "screen.h" +#include "db.h" +#include "logger.hpp" +#include "interpreter.h" #include "pugixml.hpp" +#include "room.hpp" #include "parse.hpp" #include "zone.table.hpp" +#include "utils.h" +#include "grp/grp.main.h" +#include "core/leveling.h" #include #include @@ -17,6 +25,9 @@ using namespace ExtMoney; using namespace Remort; +// строка, глобальная зачем то... +std::string Remort::WHERE_TO_REMORT_STR = ""; + namespace { @@ -24,888 +35,729 @@ 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; - } + // на все эти переменные смотреть init() + int TORC_EXCH_RATE = 999; + std::map plural_name_currency_map = { + { "куны" , "денег" }, + { "слава" , "славы" }, + { "лед" , "льда" }, + { "гривны", "гривен" } + }; - 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()); - -} - -// дергается из экстракт_чар, у босса берется макс дамагер, находящийся -// в той же комнате, группе готорого и раскидываются гривны, если есть -// кому раскидывать (флаг 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 = 0; - if (damager.first > 0) - { - d = DescByUID(damager.first); - } - if (!d) - { - return; - } - - CHAR_DATA *leader = (d->character->has_master() && AFF_FLAGGED(d->character, EAffectFlag::AFF_GROUP)) - ? d->character->get_master() - : d->character.get(); + 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 "неизвестной валюты"; + } - 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; - } - } + 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); + } - 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_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); + } + } - 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); - } + // обмен в сторону меньших гривен + 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); + } + } - 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); - } - } -} + // кол-во меняемых гривен + 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) + { + CHAR_DATA* m; + 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 = 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); + if (drop <= 0) + { + return; + } + + 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& it : *grp) { + m = it.second->member; + if (m == nullptr || !SAME_ROOM(mob, m) || m == leader) // лидеру уже раздали + return; + 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 { @@ -1038,4 +890,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 eae730c40..fd47a787a 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" @@ -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; @@ -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/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/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.cpp b/src/fightsystem/fight.cpp index 3c41ec669..3bdb40175 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" @@ -35,9 +36,9 @@ #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 "skills/skills.h" #include "features.hpp" #include "random.hpp" #include "chars/char.hpp" @@ -2004,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) @@ -2289,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); } @@ -2389,178 +2390,5 @@ 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 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)) - { - return FALSE; - } - - if (ch->has_master()) - { - if (IN_ROOM(ch) != IN_ROOM(ch->get_master())) - { - return FALSE; - } - leader = ch->get_master(); - } - else - { - leader = ch; - } - - 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 1f09655ff..ec942b324 100644 --- a/src/fightsystem/fight_hit.cpp +++ b/src/fightsystem/fight_hit.cpp @@ -1,19 +1,20 @@ #include "fight_hit.hpp" -#include "logger.hpp" +#include "bonus.h" +#include "dps.hpp" +#include "fight.h" +#include "fightsystem/common.h" +#include "grp/grp.main.h" #include "handler.h" -#include "screen.h" -#include "dg_scripts.h" -#include "skills.h" +#include "house_exp.hpp" +#include "core/leveling.h" +#include "logger.hpp" #include "magic.h" +#include "mobact.hpp" #include "pk.h" -#include "dps.hpp" -#include "house_exp.hpp" #include "poison.hpp" -#include "bonus.h" -#include "mobact.hpp" -#include "fightsystem/common.h" -#include "fightsystem/fight.h" +#include "screen.h" +#include "skills/skills.h" // extern int extra_aco(int class_num, int level); @@ -21,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)) { @@ -60,6 +60,21 @@ int armor_class_limit(CHAR_DATA * ch) { return -300; } +void aff_random_pc_inspiration(CHAR_DATA *ch, EApplyLocation num_apply, int time, int modi) { + CHAR_DATA *target; + AFFECT_DATA af; + + 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; + af.battleflag = AF_BATTLEDEC | AF_PULSEDEC; + af.duration = pc_duration(ch, time, 0, 0, 0, 0); + affect_join(target , af, FALSE, FALSE, FALSE, FALSE); + send_to_char(target, "&YТочный удар %s воодушевил вас, придав новых сил!&n\r\n", GET_PAD(ch,1)); +} + + int compute_armor_class(CHAR_DATA * ch) { int armorclass = GET_REAL_AC(ch); @@ -2302,18 +2317,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())) @@ -2323,54 +2334,41 @@ 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 (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; + } + } + 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) @@ -2400,111 +2398,90 @@ 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 (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) - { + // киллер - первый попавшийся убивец, хотя надо бы по дамагу =) + } 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 (AFF_FLAGGED(killer, EAffectFlag::AFF_GROUP)) - { - // т.к. помечен флагом AFF_GROUP - точно PC - group_gain(killer, victim); - } - else if ((AFF_FLAGGED(killer, EAffectFlag::AFF_CHARM) - || MOB_FLAGGED(killer, MOB_ANGEL) - || MOB_FLAGGED(killer, MOB_GHOST)) - && killer->has_master()) - // killer - зачармленный NPC с хозяином - { - // по логике надо бы сделать, что если хозяина нет в клетке, но - // кто-то из группы хозяина в клетке, то опыт накинуть согруппам, - // которые рядом с убившим моба чармисом. - if (AFF_FLAGGED(killer->get_master(), EAffectFlag::AFF_GROUP) - && 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 (killer) { + 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); - } - } - + 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; - } + die(victim, killer); +} - 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); } // обработка щитов, зб, поглощения, сообщения для огн. щита НЕ ЗДЕСЬ @@ -2566,7 +2543,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); + stopFollowOnAggro(ch, victim); if (victim != ch) // Start the attacker fighting the victim { @@ -3858,7 +3835,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_hit.hpp b/src/fightsystem/fight_hit.hpp index e472fda65..cbb2ea479 100644 --- a/src/fightsystem/fight_hit.hpp +++ b/src/fightsystem/fight_hit.hpp @@ -89,13 +89,9 @@ struct HitData flags_t m_flags; }; -int compute_armor_class(CHAR_DATA * ch); - -int check_agro_follower(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); -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 2b4a176aa..5fe418710 100644 --- a/src/fightsystem/fight_stuff.cpp +++ b/src/fightsystem/fight_stuff.cpp @@ -1,144 +1,106 @@ // 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/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); extern int material_value[]; -extern int max_exp_gain_npc; + //интервал в секундах между восстановлением кругов после рипа extern const unsigned RECALL_SPELLS_INTERVAL; const unsigned RECALL_SPELLS_INTERVAL = 28; -void process_mobmax(CHAR_DATA *ch, CHAR_DATA *killer) -{ - bool leader_partner = false; - int partner_feat = 0; - int total_group_members = 1; - CHAR_DATA *partner = nullptr; +// using - 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; - } +using namespace ExpCalc; - // На этот момент 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)); - } - } +void process_mobmax(CHAR_DATA *mob, CHAR_DATA *killer) +{ + 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*/) @@ -239,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 || !victim || !IN_GROUP(killer) || killer->personGroup->get_size(victim->in_room) < 2) + 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); + } + // если убили персонажа из группы + 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); } - // decrease LEADERSHIP - if (!IS_NPC(ch) // Член группы убит мобом - && killer - && IS_NPC(killer) - && AFF_FLAGGED(ch, EAffectFlag::AFF_GROUP) - && ch->has_master() - && ch->in_room == IN_ROOM(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); - } } bool check_tester_death(CHAR_DATA *ch, CHAR_DATA *killer) @@ -331,7 +273,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,31 +305,23 @@ 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); } // Вычисляем замакс по мобам // Решил немножко переделать, чтобы короче получилось, // кроме того, исправил ошибку с присутствием лидера в комнате - if (IS_NPC(ch) && killer) - { + if (IS_NPC(ch) && killer) { process_mobmax(ch, killer); } if (killer) @@ -571,7 +506,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())) { @@ -586,7 +521,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())) { @@ -794,356 +729,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) -{ - if ((player_class >= NUM_PLAYER_CLASSES) || (player_class < 0)) - return 1; - return grouping[player_class][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) { // не даем получать батлу с себя по зеркалу? @@ -1346,13 +931,12 @@ 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 (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/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/fightsystem/mobact.cpp b/src/fightsystem/mobact.cpp index 9ed15551a..83c939132 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" @@ -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" @@ -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; } @@ -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; } @@ -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 (charmice->in_room == charmice->get_master()->in_room) - { + if (SAME_ROOM(charmice, charmice->get_master())) { return true; } - return false; } @@ -1076,15 +1073,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 +1093,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 +1103,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 +1132,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 +1172,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 +1206,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 +1229,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 +1239,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 +1266,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 +1322,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 +1353,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/fightsystem/pk.cpp b/src/fightsystem/pk.cpp index f7f7f47fd..bc5b421f1 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" @@ -343,32 +344,21 @@ 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); - } + 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); + } + } } } @@ -1098,22 +1088,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/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/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/global.objects.cpp b/src/global.objects.cpp index 18be110fe..2382b909a 100644 --- a/src/global.objects.cpp +++ b/src/global.objects.cpp @@ -39,10 +39,11 @@ namespace DailyQuestMap daily_quests; Strengthening strengthening; obj2trigers_t obj2trigers; + GroupRoster groupRoster; }; GlobalObjectsStorage::GlobalObjectsStorage() : - ban(nullptr) + ban(nullptr), groupRoster() { } @@ -196,4 +197,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..b68c85fa1 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.wiz/act.wizard.hpp" #include "influxdb.hpp" #include "zone.table.hpp" #include "daily_quest.hpp" #include "strengthening.hpp" +#include + 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/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 8e5e64e1f..61dc7f11b 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/graph.cpp b/src/graph.cpp index 4068d2949..712a5f276 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 new file mode 100644 index 000000000..68e2d915b --- /dev/null +++ b/src/grp/follow.cpp @@ -0,0 +1,155 @@ +// +// Created by ubuntu on 19/12/20. +// + +#include "grp/grp.main.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 +bool stop_follower(CHAR_DATA * ch, int mode) +{ + struct follow_type *j, *k; + int i; + + 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) { + k = ch->get_master()->followers; + ch->get_master()->followers = k->next; + 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); + // чармис покидает группу всегда, персонаж по настроению + ch->removeGroupFlags(); + + 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()) { + stop_fighting(ch, TRUE); + } + + 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()); + 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; +} + +// возвращает 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)) // нанят + return true; + return false; +} \ No newline at end of file diff --git a/src/grp/grp.cmdprocessor.cpp b/src/grp/grp.cmdprocessor.cpp new file mode 100644 index 000000000..6761aea79 --- /dev/null +++ b/src/grp/grp.cmdprocessor.cpp @@ -0,0 +1,304 @@ +/* + * Функции, подключаемые в движок процессора команд игрока + */ + +#include "global.objects.hpp" +#include "handler.h" +#include "house.h" +#include "screen.h" +#include "msdp.constants.hpp" +#include "magic.h" +#include "remember.hpp" +#include "grp.main.h" + + + +extern GroupRoster& groupRoster; + +void grp::do_grequest(CHAR_DATA *ch, char *argument, int, int){ + char subcmd[MAX_INPUT_LENGTH], target[MAX_INPUT_LENGTH]; + // гзаявка список + // гзаявка создать Верий - отправляет заявку в группу + // гзаявка отменить Верий - отменяет заявку в группу + + 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"); + send_to_char(ch, "гзаявка принять Верий - принимает заявку от лидера группы Верий\r\n"); + return; + } + else if (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 if (isname(subcmd, "принять accept")){ + groupRoster.acceptInvite(ch, target); + return; + }else { + send_to_char("Уточните команду.\r\n", ch); + return; + } + +} + + +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); + + return MAX_GROUPED_FOLLOWERS + (int) VPOSI((ch->get_skill(SKILL_LEADERSHIP) - 80) / 5, 0, 4) + bonus_commander; +} + +void grp::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_gsay(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/) +{ + CHAR_DATA* gc; + + 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 (!IN_GROUP(ch)) { + send_to_char("Вы не являетесь членом группы!\r\n", ch); + return; + } + if (!*argument) { + send_to_char("О чем вы хотите сообщить своей группе?\r\n", ch); + return; + } + + auto group = ch->personGroup; + sprintf(smallBuf, "$n сообщил$g группе : '%s'", argument); + + 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 + ch->remember_add(buf, Remember::ALL); + ch->remember_add(buf, Remember::GROUP); + } else { + send_to_char(OK, ch); + } + continue; + + } + 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 сообщил%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); + } + } + +} + + +void grp::do_report(CHAR_DATA *ch, char* /*argument*/, int/* cmd*/, int/* subcmd*/) +{ + CHAR_DATA *k; + struct follow_type *f; + + 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", + 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); + if (IN_GROUP(ch)) + 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) { + 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); + } + } + + send_to_char("Вы доложили о состоянии всем членам вашей группы.\r\n", ch); +} + +void grp::do_split(CHAR_DATA *ch, char *argument, int/* cmd*/, int/* subcmd*/,int currency) +{ + int amount, num = 0, share, rest; + 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; + + 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; + } + + for (auto it : *grp) { + if (it.second->type == GM_CHAR && SAME_ROOM(it.second->member, ch) ) + num++; + } + + if (num) { + share = amount / num; + rest = amount % num; + } + else { + send_to_char("С кем вы хотите разделить это добро?\r\n", ch); + return; + } + + 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); + + 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 : { + m->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; + } +} + +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 new file mode 100644 index 000000000..761800a17 --- /dev/null +++ b/src/grp/grp.group.cpp @@ -0,0 +1,861 @@ +/* ************************************************************************ +* 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; + + +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; + addMember(leader, true); + _setLeader(leader); +} + +CHAR_DATA* Group::getLeader() const{ + return _leader; +} + +char_info* Group::_findMember(char *memberName) { + for (auto & it : *this) { + if (isname(memberName, it.second->memberName)) + return it.second.get(); + } + return nullptr; +} + +const char* Group::_getMemberName(int uid) { + for (auto & it : *this) { + if (it.second->memberUID == uid) + return it.second->memberName.c_str(); + } + return nullptr; +} + +void Group::_setLeader(CHAR_DATA *leader) { + _leaderUID = _calcUID(leader); + _leader = leader; + _leaderName.assign(leader->get_pc_name()); + _memberCap = IS_NPC(leader)? 255 : grp::max_group_size(leader) + 1; // функция возвращает кол-во ПОСЛЕДОВАТЕЛЕЙ +} + +bool Group::_isActive() { + return false; +} + +u_long Group::getUid() const { + return _uid; +} + +int Group::_getMemberCap() { + if (_leader != nullptr) + _memberCap = grp::max_group_size(_leader) + 1; + return _memberCap; +} + +const std::string &Group::getLeaderName() const { + return _leaderName; +} + +bool Group::_isFull() { + if (this->_pcCount >= _getMemberCap()) + return true; + return false; +} + +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(false); +} + +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() { +} + +void Group::addMember(CHAR_DATA *member, bool silent) { + if (member->personGroup == this) + return; + // в другой группе, вышвыриваем + if (member->personGroup != nullptr) + member->personGroup->_removeMember(member); + + auto it = this->find(_calcUID(member)); + if (it == this->end()) { + 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()); + mudlog(buf, BRF, LVL_IMMORT, SYSLOG, TRUE); + 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); + // добавляем чармисов группу + for (auto f = member->followers; f; f = f->next) { + auto ff = f->follower; + if (IS_CHARMICE(ff)) { + this->addMember(ff, true); // рекурсия! + } + } + _updateMemberCap(); +} + +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 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; + if (member != nullptr) { + send_to_char(member, "Вы покинули группу.\r\n"); + member->personGroup = nullptr; + } + actToGroup(nullptr, it->second->member, GC_LEADER | GC_REST , "$N покинул$G группу."); + if (it->second->type == GM_CHAR) + _pcCount--; + this->erase(memberUID); // finally remove it + // пересчитываем макс.уровень + { + _maxPlayerLevel = 1; + for (const 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 != nullptr && member->followers != nullptr) { + for (auto f = member->followers; f; f = f->next) { + auto ff = f->follower; + if (IS_CHARMICE(ff)) { + auto c = this->find(_calcUID(ff)); + if (c != this->end()) { + this->erase(c); + ff->personGroup = nullptr; + } + } + } + } + + CHAR_DATA* nxtLdr = getLeader(); + int nxtLdrGrpSize = 0; + // если ушел лидер - ищем нового, но после ухода предыдущего + if (_leaderUID == memberUID) { + nxtLdr = nullptr; + for (const auto& mit : *this){ + auto m = mit.second->member; + if ( m != nullptr && !m->purged() && grp::max_group_size(m) > nxtLdrGrpSize ) { + nxtLdr = m; // нашелся!! + nxtLdrGrpSize = grp::max_group_size(m); + } + } + } + // нового лидера не нашлось, распускаем группу + if (nxtLdr == nullptr) { + groupRoster.removeGroup(_uid); + return false; + } + _promote(nxtLdr); + _updateMemberCap(); + return true; +} + +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 == _calcUID(member)) { + it.second->member = member; + member->personGroup = this; + return true; + } + } + return false; +} + +bool Group::_isMember(int uid) { + auto it = this->find(uid); + if (it == this->end() ) + return false; + return true; +} + +void Group::_clear(bool silentMode) { + sprintf(buf, "[Group::clear()]: this: %lu", this->size()); + mudlog(buf, BRF, LVL_IMMORT, SYSLOG, TRUE); + CHAR_DATA* ch; + // чистим игроков + 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; + } + this->clear(); +} + +void Group::promote(char *applicant) { + auto member = _findMember(applicant); + if (member == nullptr || member->member == nullptr) { + actToGroup(_leader, nullptr, GC_LEADER, "Нет такого персонажа."); + return; + } + _promote(member->member); +} + +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; + } + addMember(r->_applicant); //и удаление заявки +} + +void Group::expellMember(char *memberName) { + auto vict = _findMember(memberName); + if (vict == nullptr) { + actToGroup(_leader, nullptr, GC_LEADER, "Нет такого персонажа."); + return; + } + if (vict != nullptr && vict->member != nullptr) { + 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 { + actToGroup(nullptr, nullptr, GC_ROOM, "%s был исключен из группы.'\r\n", vict->memberName.c_str()); + } + _removeMember(vict->memberUID); +} + +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(); + inRoomCount = this->get_size(ch->in_room); + for (auto & it : *this ) { + if (it.second->member == leader) + continue; + count++; + if (count == 2){ + 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); + } + 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"); + } + else + { + send_to_char("Но вы же не член (в лучшем смысле этого слова) группы!\r\n", ch); + } +} + +void Group::leaveGroup(CHAR_DATA* ch) { + if (ch->personGroup == nullptr) { + send_to_char(ch, "Нельзя стать еще более одиноким, чем сейчас.\r\n"); + return; + } + _removeMember(_calcUID(ch)); +} + +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, const char* playerName, int header) { + if (ch == nullptr) + return; + 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::_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 = 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)); + + 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 (pc!= nullptr && pc->personGroup) + if (pc->personGroup->getLeader() == pc) + leader = true; + 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 = 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)); + + 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; + + if (!IS_NPC(ch)) + ch->desc->msdp_report(msdp::constants::GROUP); + + // печатаем группу + if (ch->personGroup != nullptr ) { + send_to_char("Ваша группа состоит из:\r\n", ch); + for (auto &it : *this ){ + if (it.second->type == GM_CHARMEE) + continue; // чармисов скипуем и показываем ниже + 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++); + } + } + + // допечатываем чармисов, которые прямые последователи + 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) { + send_to_char("Но вы же не член (в лучшем смысле этого слова) группы!\r\n", ch); + return; + } + + // печатаем чармисов членов группы + if (PRF_FLAGGED(ch, PRF_SHOWGROUP) && ch->personGroup != nullptr) { // + 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 (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 : *this) { + if (ch == it.second->member) { + it.second->member = nullptr; + it.second->expiryTime = steady_clock::now() + DEF_EXPIRY_TIME; + return; + } + } +} + +/* +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 : *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"); + } +} +*/ + +// надстройка над act, регулируется набором флагов grpActMode + GRP_COMM +// ch указывается для указания цели в режиме GC_CHAR и GC_ROOM +// vict передается в аргумент функции act +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); + va_end(args); + CHAR_DATA* to; + for (auto &it : *this) { + to = it.second->member; + if ( to == nullptr || to->purged()) { + continue; + } + 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 & GC_ROOM) + act(smallBuf, TRUE, ch, nullptr, vict, TO_ROOM | TO_ARENA_LISTEN); +} + +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)_getMemberCap()) { + this->addMember(f->follower); + result = true; + } + } + return result; +} + +// метод может вернуть мусор :( +CHAR_DATA* Group::get_random_pc_group() { + u_short rnd = number(0, (u_short)this->get_size() - 1); + int i = 0; + for (const auto& it : *this){ + if (i == rnd) { + return it.second->member; // + } + i++; + } + return nullptr; +} + +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 (it.second->type != GM_CHAR || c== nullptr) + continue; + if (room_rnum == c->in_room && !c->purged() && retval < 255) + retval++; + } + return retval; +} + +bool Group::has_clan_members_in_group(CHAR_DATA *victim) { + for (const auto& it : *this) { + auto gm = it.second->member; + if (gm == nullptr || it.second->type == GM_CHARMEE) + continue; + if (CLAN(gm) && SAME_ROOM(gm, victim)) + return true; + } + 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) { + if (_leader == member) + return; + _setLeader(member); + actToGroup(nullptr, nullptr, GC_LEADER | GC_REST, "Изменился лидер группы на %s.", _leaderName.c_str()); + 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; + 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); + actToGroup(nullptr, nullptr, GC_LEADER | GC_REST, "В группе не хватает места, нас покинул %s!", it->second->memberName.c_str()); + ++i; + } + } + for (i=0; i < diff; i++){ + _removeMember(expellList[i]); + } + } + +} + + +bool same_group(CHAR_DATA * ch, CHAR_DATA * tch) +{ + if (!ch || !tch) return false; + if (ch == tch) return true; + // нпц по-умолчанию в группе, если одного алайнмента + if (IS_NPC(ch) && IS_NPC(tch) && SAME_ALIGN(ch, tch)) return true; + // чармис тоже в "группе" с хозяином + if (IS_CHARMICE(tch) && ch == tch->get_master()) return true; + // а теперь проверяем группы =) + if (IN_SAME_GROUP(ch, tch)) return true; + return false; +} + +/*++ + Функция расчитывает всякие бонусы для группы при получении опыта, + после чего вызывает функцию получения опыта для всех членов группы + Т.к. членом группы может быть только PC, то эта функция раздаст опыт только PC + + ch - обязательно член группы, из чего следует: + 1. Это не NPC + 2. Он находится в группе лидера (или сам лидер) + + Просто для PC-последователей эта функция не вызывается + +--*/ + +void Group::gainExp(CHAR_DATA * victim) { + int inroom_members = 0; + u_short expMultiplicator = 0; + u_short expDivider = 1; + CHAR_DATA* ch; + + // на всякий пожарный проверим + if (!victim) + return; + + // есть ли в комнате живой лидер + const bool leader_inroom = SAME_ROOM(_leader, victim); + + // прежде чем раздать опыт, считаем количество персонажей в клетке + 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; + else + expDivider = inroom_members; + + // погнали раздавать опыт + for (auto m: *this) { + ch = m.second->member; + if (ch == nullptr || ch->purged() || m.second->type == GM_CHARMEE || !SAME_ROOM(ch, victim) ) { + continue; + } + expMultiplicator = calcExpMultiplicator(ch); // цифра процента + // если прокнула лидерка, то добавляем еще 20 + if (leader_inroom && inroom_members > 1 && calc_leadership(_leader)) + expMultiplicator += 20; + increaseExperience(ch, victim, expDivider, expMultiplicator); + } +} + +bool calc_leadership(CHAR_DATA * ch) +{ + int prob, percent; + + if (IS_NPC(ch) || ch->personGroup == nullptr) { + return false; + } + + CHAR_DATA* leader = ch->personGroup->getLeader(); + + // если лидер умер или нет в комнате - фиг вам, а не бонусы + if (!SAME_ROOM(ch, 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; + } +} + + +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); + 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 > groupRoster.getPenalty(player)) { + return MAX(1, DEFAULT_100 - 3 * (_maxPlayerLevel - player_level)); + } + return DEFAULT_100; +} + +void Group::_updateMemberCap() { + if (_leader == nullptr || _leader->purged()) + return; + _memberCap = _getMemberCap(); +} + +void Group::processGarbageCollection() { + std::vector expellV; // массивчик персонажей, которых надо удалить из группы + for (const auto& it : *this) { + auto m = it.second; + if (m->expiryTime <= steady_clock::now() && m->member == nullptr) { + expellV.push_back(m->memberUID); + } + } + if (!expellV.empty()) + for (auto it : expellV) { + _removeMember(it); + } +} + diff --git a/src/grp/grp.main.h b/src/grp/grp.main.h new file mode 100644 index 000000000..088f94321 --- /dev/null +++ b/src/grp/grp.main.h @@ -0,0 +1,231 @@ +/* ************************************************************************ +* 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 + +#include +#include "chars/char.hpp" +#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 : short { + GC_LEADER = 1, // только лидеру + GC_CHAR = 2, // только персонажу + GC_REST = 4, // всем остальным + 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}; + +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); +// возвращает true, если рядом с персонажем лидер группы и прокнул скилл +bool calc_leadership(CHAR_DATA * ch); + +class Request; + +using namespace std::chrono; + +using sclock_t = time_point; + +struct char_info { + 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; // время, когда запись автоматом удаляется после проверок. +}; + +using grp_mt = std::map>; +using grp_ptr = std::shared_ptr; +using rq_ptr = std::shared_ptr; +using npc_r = std::unordered_set *; + +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 Group : public grp_mt { +private: + constexpr static int DEFAULT_100 = 100; + // ид группы в ростере + u_long _uid = 0; + //макс.количество игроков. Храним, ибо лидера могло спуржить, но он к нам вернётся.. + int _memberCap = 0; + // ид лидера + int _leaderUID; + // имя лидера + std::string _leaderName; + // ссылка на персонажа, АХТУНГ! Может меняться и быть невалидным + CHAR_DATA* _leader = nullptr; + // макс.уровень игрока в группе, для расчета штрафа + u_short _maxPlayerLevel = 1; + // количество игроков + u_short _pcCount = 0; +public: + u_long getUid() const; + const std::string &getLeaderName() const; + CHAR_DATA *getLeader() const; + void _clear(bool silent); + Group(CHAR_DATA *leader, u_long uid); + ~Group(); + void _setLeader(CHAR_DATA *leader); + int _getMemberCap(); + bool _isFull(); + bool _isActive(); // проверка, что в группе все персонажи онлайн + bool _isMember(int uid); + const char* _getMemberName(int uid); + char_info* _findMember(char* memberName); + bool _removeMember(CHAR_DATA* ch); + void _promote(CHAR_DATA* ch); + void charDataPurged(CHAR_DATA* ch); + 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; + 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); + 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: + + + bool addFollowers(CHAR_DATA* leader); + void addMember(CHAR_DATA *member, bool silent = false); + void expellMember(char* memberName); + bool _restoreMember(CHAR_DATA *member); + + void printGroup(CHAR_DATA *requestor); + void listMembers(CHAR_DATA *requestor); + + void promote(char *applicant); + void approveRequest(const char *applicant); + void rejectRequest(char *applicant); + void leaveGroup(CHAR_DATA* vict); + +// 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(); + // лень обвязывать, тупо переместил объект + DpsSystem::GroupListType _group_dps; + bool has_clan_members_in_group(CHAR_DATA *victim); + // группа получает экспу за убивство + void gainExp(CHAR_DATA * victim); +}; + +class Request { +public: + sclock_t _expiryTime; + CHAR_DATA *_applicant; + Group* _group; + std::string _applicantName; + int _applicantUID; + RQ_TYPE _type; + Request(CHAR_DATA* subject, Group* group, RQ_TYPE type); +}; + + +class GroupRoster { +// properties +public: + 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); + void processGarbageCollection(); +private: + u_long _currentGroupIndex = 0; + std::map _groupList; +// методы работы с группами +public: + grp_ptr addGroup(CHAR_DATA* leader); + void removeGroup(u_long uid); + 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); + 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); +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); +}; + + +//extern GroupPenalties grouping; +#endif //BYLINS_GRP_MAIN_H diff --git a/src/grp/grp.request.cpp b/src/grp/grp.request.cpp new file mode 100644 index 000000000..a05c151c7 --- /dev/null +++ b/src/grp/grp.request.cpp @@ -0,0 +1,17 @@ +// +// Created by ubuntu on 23.12.2020. +// + +#include "grp.main.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.roster.cpp b/src/grp/grp.roster.cpp new file mode 100644 index 000000000..1380c4b19 --- /dev/null +++ b/src/grp/grp.roster.cpp @@ -0,0 +1,551 @@ +// +// Created by ubuntu on 17/12/20. +// + +#include "global.objects.hpp" +#include "handler.h" +#include "grp.main.h" + + +GroupRoster& groupRoster = GlobalObjects::groupRoster(); +extern const char *class_name[]; + +GroupRoster::GroupRoster() { + initPenaltyTable(); +} + +void GroupRoster::restorePlayerGroup(CHAR_DATA *ch) { + auto grp = this->findGroup(ch->get_uid()); + if (grp == nullptr) + return; + if (!grp->_restoreMember(ch)) + return; + 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 = "справка помощь"; + 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 = "распустить clear"; + const std::string strWORLD = "мир world"; + 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 (GET_POS(ch) < POS_RESTING) { + send_to_char("Трудно управлять группой в таком состоянии.\r\n", ch); + return; + } + auto grp = ch->personGroup; + + // выбираем режим + if (!*subcmd) { + 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%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())) { + if (grp != nullptr && grp->getLeader() == ch){ + send_to_char(ch, "Только великим правителям, навроде Цесаря Иулия, было под силу водить много легионов!\r\n"); + return; + } + if (grp != nullptr) // в группе - покидаем + grp->_removeMember(ch); + groupRoster.addGroup(ch); + return; + } else if (isname(subcmd, strLIST.c_str())) { + grp->listMembers(ch); + return; + } else if (isname(subcmd, strALL.c_str())) { + // если не в группе, создаем и добавляем всех последователей + if (grp == nullptr) { + grp = groupRoster.addGroup(ch).get(); + if (!grp->addFollowers(ch)) + send_to_char(ch, "А окромя вас, в группу то добавить и некого...\r\n"); + return; + } + // если не лидер и в группе, печатаем полный список. + if (grp->getLeader() != ch){ + grp->printGroup(ch); + return; + } + // сюда приходим лидером и добавляем всех, кто следует. + if (!grp->addFollowers(ch)) + send_to_char(ch, "Все, кто за вами следует, уже в группе.\r\n"); + 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; + } else if (isname(subcmd, strTEST.c_str())) { + groupRoster.runTests(ch); + return; + } + + if (grp == nullptr || grp->getLeader() != ch ) { + send_to_char(ch, "Необходимо быть лидером группы для этого.\r\n"); + return; + } + + 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; + } +} + +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); + send_to_char(leader, "Вы создали группу c максимальным числом соратников %d.\r\n", leader->personGroup ? leader->personGroup->_getMemberCap() : 0); + return group; +} + +void GroupRoster::removeGroup(u_long uid) { + auto grp = _groupList.find(uid); + if (grp != _groupList.end()) { + _groupList.erase(uid); + return; + } +} + +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(personUID)) + 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 (isname(leaderName, it.second->getLeaderName().c_str())) + return it.second; + } + return nullptr; +} + + +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) { + send_to_char(ch, "Группа лидера %s, кол-во участников: %lu\r\n", + it.second->getLeaderName().c_str(), + it.second->size()); + } +} + +void GroupRoster::makeInvite(CHAR_DATA *leader, char *targetPerson) { + 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"); + 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->_expiryTime = steady_clock::now() + DEF_EXPIRY_TIME; + 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) + 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()); + 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() ); + } + else { + sprintf(smallBuf, "Заявка от игрока: %s\r\n", it->_applicantName.c_str() ); + } + } + if (!notFound) + send_to_char(ch, "%s", smallBuf); + } + if (notFound) + send_to_char(ch, "Заявок нет.\r\n"); +} + +void GroupRoster::makeRequest(CHAR_DATA *author, char* target) { + if (!author || 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); + grp->actToGroup(nullptr, author, GC_LEADER, "Получена заявка от $N1 на вступление в группу.\r\n"); + break; + case RQ_R::RQ_REFRESH: + send_to_char("Заявка успешно продлена.\r\n", author); + break; + } +} + +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 && isname(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->_expiryTime = std::chrono::steady_clock::now() + DEF_EXPIRY_TIME; + 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); +} + +Request *GroupRoster::findRequest(const char* targetPerson, const char* group, RQ_TYPE type) { + for (auto & it : this->_requestList){ + if (it->_type == (type == RQ_ANY ? it->_type : type) && // осподи, какой я пиздец пишу) + isname(group, it->_group->getLeaderName().c_str()) && + isname(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; + if (!*author) { + send_to_char(who, "Соберись! Чье приглашение принимаем?!\r\n"); + return; + } + + 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); // и удалит заявку, если есть +} + +void GroupRoster::runTests(CHAR_DATA *) { + +} + +// удаление заявок при пурже персонажа +void GroupRoster::charDataPurged(CHAR_DATA *ch) { + if (IS_NPC(ch)) + return; + for (auto r = _requestList.begin(); r != _requestList.end();){ + if (r->get()->_applicant == ch) { + r = _requestList.erase(r); + } else { + ++r; + } + } +} + +void GroupRoster::processGarbageCollection() { + for (const 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(); +} + +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)]; +} diff --git a/src/handler.cpp b/src/handler.cpp index 9dde12790..94eb73332 100644 --- a/src/handler.cpp +++ b/src/handler.cpp @@ -14,49 +14,25 @@ #include "handler.h" +#include "chars/world.characters.hpp" #include "auction.h" #include "backtrace.hpp" #include "char_obj_utils.inl" -#include "chars/char.hpp" #include "chars/char_player.hpp" -#include "chars/world.characters.hpp" -#include "cmd/follow.h" -#include "comm.h" -#include "conf.h" -#include "constants.h" -#include "db.h" -#include "dg_db_scripts.hpp" -#include "dg_scripts.h" #include "exchange.h" -#include "ext_money.hpp" -#include "features.hpp" #include "fightsystem/fight.h" #include "fightsystem/pk.h" -#include "glory_const.hpp" -#include "glory_const.hpp" +#include "grp/grp.main.h" #include "house.h" -#include "interpreter.h" #include "liquid.hpp" -#include "logger.hpp" #include "magic.h" -#include "name_list.hpp" #include "named_stuff.hpp" -#include "noob.hpp" -#include "obj.hpp" -#include "obj_sets.hpp" #include "object.prototypes.hpp" -#include "poison.hpp" -#include "room.hpp" #include "screen.h" -#include "skills.h" #include "spell_parser.hpp" -#include "spells.h" -#include "structs.h" -#include "sysdep.h" #include "world.objects.hpp" #include "zone.table.hpp" -#include #include #include @@ -78,7 +54,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); @@ -2261,10 +2236,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); @@ -2333,8 +2307,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,56 +2315,42 @@ 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; } } // 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); } @@ -2399,47 +2358,31 @@ 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)) - { + if ((ch->followers || ch->has_master()) && 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 +2395,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); } diff --git a/src/heartbeat.cpp b/src/heartbeat.cpp index af6d9327a..b6c1e7a8f 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" @@ -25,7 +26,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" @@ -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", 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/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/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 2a595131b..3b2300bf3 100644 --- a/src/interpreter.cpp +++ b/src/interpreter.cpp @@ -21,26 +21,27 @@ #include "chars/char.hpp" #include "chars/char_player.hpp" #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" #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" +#include "depot.hpp" +#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" @@ -63,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" @@ -90,6 +91,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" @@ -120,6 +122,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; @@ -174,7 +177,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); @@ -247,7 +249,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); @@ -283,12 +284,10 @@ 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); 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); @@ -307,8 +306,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); @@ -319,7 +316,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); @@ -521,19 +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, grp::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, 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}, @@ -594,7 +591,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}, @@ -722,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_ungroup, 0, 0, 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_ungroup, 0, 0, 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}, @@ -877,9 +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_group, 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, 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}, @@ -959,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}, @@ -991,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}, @@ -1021,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_ungroup, 0, 0, -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}, @@ -1040,7 +1038,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}, @@ -2056,6 +2054,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: @@ -2514,7 +2516,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); // изменяем порталы @@ -2609,6 +2610,8 @@ void do_entergame(DESCRIPTOR_DATA * d) } Noob::check_help_message(d->character.get()); + // возвращаем персонажа группу + groupRoster.restorePlayerGroup(d->character.get()); } //По кругу проверяем корректность параметров @@ -2991,99 +2994,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') @@ -3101,509 +3110,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); @@ -3652,8 +3597,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); @@ -3677,61 +3622,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)) { @@ -3884,217 +3821,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_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_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; + 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) 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/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/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/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 117bce1da..ddada543d 100644 --- a/src/magic.cpp +++ b/src/magic.cpp @@ -14,19 +14,42 @@ #include "magic.h" -#include "cmd/hire.h" +#include "cmd/cmd.generic.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 "handler.h" -#include "screen.h" -#include "fightsystem/pk.h" +#include "comm.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 "features.hpp" #include "fightsystem/fight.h" -#include "random.hpp" +#include "fightsystem/pk.h" +#include "grp/grp.main.h" +#include "handler.h" +#include "interpreter.h" +#include "logger.hpp" #include "modify.h" -#include "AffectHandler.hpp" -#include "corpse.hpp" +#include "obj.hpp" +#include "object.prototypes.hpp" +#include "poison.hpp" +#include "random.hpp" +#include "room.hpp" +#include "screen.h" +#include "skills/skills.h" +#include "spells.h" +#include "structs.h" +#include "sysdep.h" +#include "utils.h" +#include "world.objects.hpp" +#include "zone.table.hpp" + #include #include @@ -2950,7 +2973,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; @@ -3415,7 +3438,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 @@ -3965,7 +3988,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; @@ -3978,7 +4001,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; } @@ -4349,7 +4372,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); @@ -4404,6 +4427,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/medit.cpp b/src/medit.cpp index 034137cf4..9fd5d9dd2 100644 --- a/src/medit.cpp +++ b/src/medit.cpp @@ -14,12 +14,12 @@ #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" #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/msdp.reporters.cpp b/src/msdp.reporters.cpp index 95973456f..b3268e7a7 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 @@ -197,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); @@ -302,39 +303,14 @@ 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) - && !(AFF_FLAGGED(f->follower, EAffectFlag::AFF_CHARM) - || MOB_FLAGGED(f->follower, MOB_ANGEL) - || MOB_FLAGGED(f->follower, MOB_GHOST))) - { - 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_ANGEL) - || MOB_FLAGGED(ff->follower, MOB_GHOST))) - { - continue; - } - - append_char(group_descriptor, ch, ff->follower, false); - } - } - + auto grp = ch->personGroup; + if (grp == nullptr) + return; + 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/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/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.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/obj.hpp b/src/obj.hpp index ca6265169..4b23e64d1 100644 --- a/src/obj.hpp +++ b/src/obj.hpp @@ -8,17 +8,18 @@ #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" -#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/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/objsave.cpp b/src/objsave.cpp index 3a3dfe0d4..1590697ee 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" @@ -3448,8 +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); - //AFF_FLAGS(ch.get()).unset(EAffectFlag::AFF_GROUP); - (ch.get())->removeGroupFlags(); + (ch.get())->removeGroupFlags(true); AFF_FLAGS(ch.get()).unset(EAffectFlag::AFF_HORSE); extract_char(ch.get(), FALSE); } diff --git a/src/oedit.cpp b/src/oedit.cpp index a87e6aac7..ba4d8e2a0 100644 --- a/src/oedit.cpp +++ b/src/oedit.cpp @@ -19,13 +19,13 @@ #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" #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/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/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/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/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/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/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/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/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/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 99% rename from src/skills.cpp rename to src/skills/skills.cpp index 2e7bab654..72977167e 100644 --- a/src/skills.cpp +++ b/src/skills/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.h b/src/skills/skills.h similarity index 100% rename from src/skills.h rename to src/skills/skills.h 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/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 e065b87db..3d6b319a4 100644 --- a/src/spec_procs.cpp +++ b/src/spec_procs.cpp @@ -14,33 +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/follow.h" -#include "cmd/mercenary.h" -#include "comm.h" -#include "constants.h" -#include "db.h" +#include "core/leveling.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 @@ -2192,8 +2178,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, ch->get_master())) { return (FALSE); } @@ -2754,33 +2739,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() && SAME_ROOM(ch, 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) @@ -2793,31 +2771,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) @@ -2828,22 +2799,12 @@ void npc_group(CHAR_DATA * ch) continue; } - 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); } - AFF_FLAGS(vict).set(EAffectFlag::AFF_GROUP); } } @@ -2867,7 +2828,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) @@ -2907,7 +2868,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 df2105eb3..fd3ff9910 100644 --- a/src/spell_parser.cpp +++ b/src/spell_parser.cpp @@ -20,13 +20,13 @@ #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" #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/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 fa0c6ab59..633d4d8f6 100644 --- a/src/spells.cpp +++ b/src/spells.cpp @@ -14,43 +14,23 @@ #include "spells.h" -#include "birth_places.hpp" #include "char_obj_utils.inl" -#include "chars/char.hpp" #include "chars/world.characters.hpp" -#include "cmd/follow.h" -#include "cmd/hire.h" -#include "comm.h" -#include "conf.h" -#include "constants.h" -#include "coredump.hpp" -#include "db.h" -#include "deathtrap.hpp" #include "depot.hpp" -#include "dg_scripts.h" -#include "features.hpp" +#include "fightsystem/fight.h" #include "fightsystem/mobact.hpp" #include "fightsystem/pk.h" +#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 "modify.h" -#include "obj.hpp" -#include "obj_sets.hpp" #include "object.prototypes.hpp" #include "parcel.hpp" #include "privilege.hpp" -#include "room.hpp" #include "screen.h" -#include "skills.h" #include "skills/flee.h" #include "skills/townportal.h" -#include "structs.h" -#include "sysdep.h" #include "world.objects.hpp" #include "zone.table.hpp" @@ -75,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); @@ -1092,16 +1071,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; } } @@ -1115,6 +1091,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; @@ -1922,7 +1900,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); } @@ -2157,12 +2135,14 @@ 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_CORPSE); + // тварь же человеком создана + 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); - mob->set_level(ch->get_level()); + mob->set_level(ch->get_level()); char_to_room(mob, ch->in_room); @@ -2224,6 +2204,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/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/structs.cpp b/src/structs.cpp index 168fcd218..a955f677f 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) { - unsigned int i, j, o; + unsigned long i, j, o; if (exact) { @@ -681,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 6688afc9a..bde5ad987 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 { @@ -448,7 +449,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 +472,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 +505,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) @@ -619,7 +621,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[] @@ -635,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 @@ -792,6 +794,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 + // не забываем отражать новые состояния в connected_types -- Krodo // Character equipment positions: used as index for char_data.equipment[] // @@ -1603,6 +1606,8 @@ namespace obj_sets_olc class sedit; } +class GControl; + #ifndef HAVE_ZLIB struct z_stream; #endif @@ -1744,7 +1749,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; diff --git a/src/stuff.cpp b/src/stuff.cpp index 035ef6d57..adc40869d 100644 --- a/src/stuff.cpp +++ b/src/stuff.cpp @@ -15,12 +15,12 @@ #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" #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 6c496a437..9e2f0f773 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" @@ -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" @@ -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(). @@ -1072,58 +1049,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) { @@ -3953,4 +3878,29 @@ 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; + } +}; + +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 5799e0565..8f176492d 100644 --- a/src/utils.h +++ b/src/utils.h @@ -215,9 +215,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); @@ -566,7 +563,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) @@ -1775,6 +1771,11 @@ class StreamFlagsHolder std::ios::fmtflags m_flags; }; +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/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" diff --git a/src/world.objects.cpp b/src/world.objects.cpp index c8b7a9226..bd9046c4f 100644 --- a/src/world.objects.cpp +++ b/src/world.objects.cpp @@ -3,7 +3,7 @@ #include "object.prototypes.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/CMakeLists.txt b/tests/CMakeLists.txt index 03f9281ce..53158ca4e 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/sample.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 62cb1e3ef..b917ab854 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,46 @@ TEST(CHAR_Leaders, SimpleLoop) EXPECT_EQ(nullptr, character->get_master()); } +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++) { + pName = "Player" + std::to_string(i); + builder.create_new(pName); + auto follower = builder.get(); + leader->add_follower(follower.get()); + } + grp->addFollowers(leader.get()); + EXPECT_EQ(0, leader->personGroup->get_size(100)); + EXPECT_EQ(11, leader->personGroup->size()); + grp->promote("F7"); + EXPECT_EQ(7, leader->personGroup->size()); + +} + +TEST(CHAR_Leaders, GroupMigration) { + test_utils::GroupBuilder g; + + auto grp = g.makeFullGroup(200); + + EXPECT_EQ(11, grp->get_size(0)); + grp->promote("Player-2"); + EXPECT_EQ(7, grp->get_size(0)); + +} + // vim: ts=4 sw=4 tw=0 noet syntax=cpp : diff --git a/tests/char.utilities.cpp b/tests/char.utilities.cpp index 9e7432eb0..a0bb28531 100644 --- a/tests/char.utilities.cpp +++ b/tests/char.utilities.cpp @@ -1,138 +1,182 @@ #include "char.utilities.hpp" +#include "db.h" #include -#include +#include "chars/char.hpp" +#include "chars/char_player.hpp" +#include "chars/player_races.hpp" #include #include +#include "diskio.h" +#include "diskio.h" -namespace test_utils -{ -void CharacterBuilder::create_new() -{ - const auto result = std::make_shared(); - result->player_specials = std::make_shared(); - 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() -{ - 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_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_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_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_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()); - - auto character = character_builder.get(); - - m_result->add_follower_silently(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(result_t character) +namespace test_utils { - if (!character) - { - throw std::runtime_error("Character wasn't created."); - } -} - + 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", 0); + 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_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_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::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_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::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::make_group(CharacterBuilder& character_builder) + { + check_character_existance(); + check_character_existance(character_builder.get()); + + auto character = character_builder.get(); + + m_result->add_follower_silently(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(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::create_new(std::string name) { + create_new(); + m_result->set_pc_name(name); + m_result->set_passwd(name); + } + + GroupBuilder::GroupBuilder() { + _roster = new GroupRoster(); + } + + Group* GroupBuilder::makeFullGroup(int leadership) { + test_utils::CharacterBuilder builder; + std::string plrName = "Player-"; + + builder.create_new("Leader"); + builder.add_skill(ESkill::SKILL_LEADERSHIP, leadership); + auto leader = builder.get(); + auto grp = _roster->addGroup(leader.get()); + for (int i=0; i < grp->_getMemberCap(); i++){ + builder.create_new(plrName + std::to_string(i)); + auto f7 = builder.get(); + leader->add_follower(f7.get()); + } + grp->addFollowers(leader.get()); + return grp.get(); + } } // vim: ts=4 sw=4 tw=0 noet syntax=cpp : diff --git a/tests/char.utilities.hpp b/tests/char.utilities.hpp index 69cbd2fc4..e1fcfb63d 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 { @@ -11,12 +12,17 @@ namespace test_utils using character_t = CHAR_DATA; using result_t = character_t::shared_ptr; - void create_new(); + 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(); 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(); @@ -32,9 +38,18 @@ namespace test_utils void check_character_existance() const; static void check_character_existance(result_t character); - + int m_uid = 1; result_t m_result; + std::vector _store; + }; + + class GroupBuilder { + public: + GroupRoster* _roster; + GroupBuilder(); + Group* makeFullGroup(int leadership); }; + } #endif // __CHAR__UTILITIES_HPP__ 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 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